Conversation
WalkthroughAdds monospace code editor styles, a plain-text paste Lexical plugin, code-context detection to skip mention/hashtag processing, safe Markdown transformer sets (with/without code), replaces TRANSFORMERS usage across create/edit flows, normalizes code nodes to fenced blocks in edit flow, and adds DOM-based syntax highlighting plus a global highlight.js theme import. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant U as User
participant L as Lexical Editor
participant P as PlainTextPastePlugin
participant MS as MarkdownShortcutPlugin
U->>L: Paste (Ctrl/Cmd+V)
L->>P: PASTE_COMMAND (LOW)
alt Clipboard has files or no text
P-->>L: Return false (ignore)
L->>MS: Continue default paste handling
else Plain text present
P->>L: preventDefault()
P->>L: editor.update(insert raw text)
L-->>U: Text inserted without formatting
end
sequenceDiagram
autonumber
participant E as EditDropLexical
participant S as SAFE_* Transformers
participant F as convertCodeNodesToFences
participant M as MarkdownShortcutPlugin
E->>S: Build EDIT_MARKDOWN_TRANSFORMERS
E->>M: Configure with SAFE_MARKDOWN_TRANSFORMERS_WITHOUT_CODE
Note over M: Underscore + code transformers removed
E->>F: Post-construction, convert code nodes to fenced blocks
F-->>E: Document normalized
sequenceDiagram
autonumber
participant V as DropPartMarkdown
participant H as highlightCodeElement
participant HL as highlight.js (lazy)
V->>V: Render code block with ref
V->>H: useEffect(ref, language)
H->>HL: Lazy-load & register languages (once)
H->>V: Apply syntax highlighting markup & classes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (11)
components/waves/drops/EditDropLexical.tsx (11)
58-65: Add readonly modifiers to props.The coding guideline requires "Always add readonly before props" for all props in the interface.
As per coding guidelines.
Apply this diff:
interface EditDropLexicalProps { - initialContent: string; - initialMentions: ApiDropMentionedUser[]; - waveId: string | null; - isSaving: boolean; - onSave: (content: string, mentions: ApiDropMentionedUser[]) => void; - onCancel: () => void; + readonly initialContent: string; + readonly initialMentions: ApiDropMentionedUser[]; + readonly waveId: string | null; + readonly isSaving: boolean; + readonly onSave: (content: string, mentions: ApiDropMentionedUser[]) => void; + readonly onCancel: () => void; }
58-65: Add readonly modifier to props interface properties.As per coding guidelines, props should be marked as readonly.
Apply this diff:
interface EditDropLexicalProps { - initialContent: string; - initialMentions: ApiDropMentionedUser[]; - waveId: string | null; - isSaving: boolean; - onSave: (content: string, mentions: ApiDropMentionedUser[]) => void; - onCancel: () => void; + readonly initialContent: string; + readonly initialMentions: ApiDropMentionedUser[]; + readonly waveId: string | null; + readonly isSaving: boolean; + readonly onSave: (content: string, mentions: ApiDropMentionedUser[]) => void; + readonly onCancel: () => void; }
198-205: Remove inline comments.As per coding guidelines, comments should not be included in TypeScript/TSX files.
Apply this diff:
- // If any text node still has the full @[handle] pattern, defer to the - // mention transformer rather than trying to stitch pieces together. const hasUnprocessedMentions = textNodes.some((node) => /@\[\w+\]/.test(node.getTextContent()) ); if (hasUnprocessedMentions) {
225-226: Remove inline comments.As per coding guidelines, comments should not be included in TypeScript/TSX files.
Apply this diff:
-// Plugin to handle keyboard shortcuts -// Plugin to handle focus on mount function FocusPlugin({ isApp }: { isApp: boolean }) {
231-244: Remove inline comments.As per coding guidelines, comments should not be included in TypeScript/TSX files.
Apply this diff:
const focusEditor = () => { - // Try Lexical's focus first editor.focus(); - // Also try DOM focus as fallback requestAnimationFrame(() => { const contentEditable = document.querySelector( '[contenteditable="true"]' ) as HTMLElement; if (contentEditable && document.activeElement !== contentEditable) { contentEditable.focus(); - contentEditable.click(); // For mobile keyboard + contentEditable.click(); } }); };
247-259: Remove inline comments.As per coding guidelines, comments should not be included in TypeScript/TSX files.
Apply this diff:
if (isApp) { - // Multiple timing strategies for mobile reliability const timeouts = [ - setTimeout(focusEditor, 100), // Quick attempt - setTimeout(focusEditor, 350), // After menu close - setTimeout(focusEditor, 600), // Final attempt + setTimeout(focusEditor, 100), + setTimeout(focusEditor, 350), + setTimeout(focusEditor, 600), ]; return () => timeouts.forEach(clearTimeout); } else { - // Desktop: immediate focus focusEditor(); }
293-296: Remove inline comment.As per coding guidelines, comments should not be included in TypeScript/TSX files.
Apply this diff:
- // Check if mentions dropdown is open if (mentionsRef.current?.isMentionsOpen()) { - // Let the mentions plugin handle the Enter key return false;
299-302: Remove inline comment.As per coding guidelines, comments should not be included in TypeScript/TSX files.
Apply this diff:
if (event?.shiftKey) { - // Allow Shift+Enter for new lines return false; }
304-314: Remove inline comments.As per coding guidelines, comments should not be included in TypeScript/TSX files.
Apply this diff:
if (!isSaving) { - // Check if content has changed (similar to original logic) editor.getEditorState().read(() => { const currentMarkdown = $convertToMarkdownString(EDIT_MARKDOWN_TRANSFORMERS); - // If no changes, just cancel (silent exit) if (currentMarkdown.trim() === initialContent.trim()) { onCancel();
378-388: Remove inline comment.As per coding guidelines, comments should not be included in TypeScript/TSX files.
Apply this diff:
setMentionedUsers((prev) => { - // Avoid duplicates if ( prev.some(
400-404: Remove inline comment.As per coding guidelines, comments should not be included in TypeScript/TSX files.
Apply this diff:
- // If no changes, silently exit edit mode without API call if (markdown.trim() === initialContent.trim()) { onCancel(); return;
🧹 Nitpick comments (2)
components/drops/view/part/dropPartMarkdown/highlight.ts (1)
1-33: LGTM! Efficient lazy loading with language registration.The implementation follows highlight.js best practices by:
- Using core + per-language registration for minimal bundle size
- Memoizing the highlighter promise to avoid redundant loads
- Registering common languages (TypeScript, JavaScript, JSON, Bash) with sensible aliases
Based on learnings
Consider expanding language support in the future if users request additional languages (e.g., Python, Go, Rust, SQL). The current selection covers web development use cases well.
components/drops/create/lexical/plugins/mentions/MentionsPlugin.tsx (1)
215-246: Consider extracting duplicate code-context detection logic.This code-context detection logic is identical to the implementation in
HashtagsPlugin.tsx(lines 243-270). The duplication violates the DRY principle.Consider extracting to a shared utility:
Create a new file
components/drops/create/lexical/utils/codeContextDetection.ts:import { $getSelection, $isRangeSelection } from "lexical"; import type { LexicalNode } from "lexical"; import { $isCodeNode } from "@lexical/code"; import type { LexicalEditor } from "lexical"; export function isInCodeContext(editor: LexicalEditor): boolean { return editor.getEditorState().read(() => { const selection = $getSelection(); if (!$isRangeSelection(selection)) { return false; } if (selection.hasFormat("code")) { return true; } const anchorNode = selection.anchor.getNode(); const focusNode = selection.focus.getNode(); const isNodeWithinCode = (node: LexicalNode | null) => { if (!node) { return false; } if ($isCodeNode(node)) { return true; } const topLevel = node.getTopLevelElement(); return $isCodeNode(topLevel); }; return isNodeWithinCode(anchorNode) || isNodeWithinCode(focusNode); }); }Then use it in both plugins:
const checkForMentionMatch = useCallback( (text: string) => { - const shouldSkip = editor.getEditorState().read(() => { - const selection = $getSelection(); - if (!$isRangeSelection(selection)) { - return false; - } - - if (selection.hasFormat("code")) { - return true; - } - - const anchorNode = selection.anchor.getNode(); - const focusNode = selection.focus.getNode(); - - const isNodeWithinCode = (node: LexicalNode | null) => { - if (!node) { - return false; - } - - if ($isCodeNode(node)) { - return true; - } - - const topLevel = node.getTopLevelElement(); - return $isCodeNode(topLevel); - }; - - return isNodeWithinCode(anchorNode) || isNodeWithinCode(focusNode); - }); - - if (shouldSkip) { + if (isInCodeContext(editor)) { return null; } const slashMatch = checkForSlashTriggerMatch(text, editor);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
components/drops/create/lexical/lexical.styles.scss(1 hunks)components/drops/create/lexical/plugins/PlainTextPastePlugin.tsx(1 hunks)components/drops/create/lexical/plugins/hashtags/HashtagsPlugin.tsx(2 hunks)components/drops/create/lexical/plugins/mentions/MentionsPlugin.tsx(2 hunks)components/drops/create/lexical/transformers/markdownTransformers.ts(1 hunks)components/drops/create/utils/CreateDropContent.tsx(4 hunks)components/drops/create/utils/CreateDropWrapper.tsx(3 hunks)components/drops/view/part/DropPartMarkdown.tsx(5 hunks)components/drops/view/part/dropPartMarkdown/highlight.ts(1 hunks)components/waves/CreateDropContent.tsx(3 hunks)components/waves/CreateDropInput.tsx(2 hunks)components/waves/drops/EditDropLexical.tsx(7 hunks)styles/globals.scss(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Do not include any comments in the code
Use react-query for data fetching
Always add readonly before propsUse TypeScript for implementation code
Files:
components/drops/view/part/dropPartMarkdown/highlight.tscomponents/waves/CreateDropInput.tsxcomponents/drops/create/lexical/plugins/mentions/MentionsPlugin.tsxcomponents/drops/create/utils/CreateDropWrapper.tsxcomponents/drops/create/utils/CreateDropContent.tsxcomponents/drops/create/lexical/transformers/markdownTransformers.tscomponents/waves/CreateDropContent.tsxcomponents/drops/view/part/DropPartMarkdown.tsxcomponents/drops/create/lexical/plugins/PlainTextPastePlugin.tsxcomponents/waves/drops/EditDropLexical.tsxcomponents/drops/create/lexical/plugins/hashtags/HashtagsPlugin.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursorrules)
**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for stylingUse React functional components with hooks
Files:
components/waves/CreateDropInput.tsxcomponents/drops/create/lexical/plugins/mentions/MentionsPlugin.tsxcomponents/drops/create/utils/CreateDropWrapper.tsxcomponents/drops/create/utils/CreateDropContent.tsxcomponents/waves/CreateDropContent.tsxcomponents/drops/view/part/DropPartMarkdown.tsxcomponents/drops/create/lexical/plugins/PlainTextPastePlugin.tsxcomponents/waves/drops/EditDropLexical.tsxcomponents/drops/create/lexical/plugins/hashtags/HashtagsPlugin.tsx
🧠 Learnings (1)
📚 Learning: 2025-09-28T12:29:11.627Z
Learnt from: CR
PR: 6529-Collections/6529seize-frontend#0
File: .cursorrules:0-0
Timestamp: 2025-09-28T12:29:11.627Z
Learning: Applies to **/*.tsx : Use TailwindCSS for styling
Applied to files:
styles/globals.scss
🧬 Code graph analysis (6)
components/waves/CreateDropInput.tsx (2)
components/drops/create/lexical/plugins/PlainTextPastePlugin.tsx (1)
PlainTextPastePlugin(14-52)components/drops/create/lexical/transformers/markdownTransformers.ts (1)
SAFE_MARKDOWN_TRANSFORMERS(38-38)
components/drops/create/utils/CreateDropWrapper.tsx (1)
components/drops/create/lexical/transformers/markdownTransformers.ts (1)
SAFE_MARKDOWN_TRANSFORMERS(38-38)
components/drops/create/utils/CreateDropContent.tsx (2)
components/drops/create/lexical/transformers/markdownTransformers.ts (1)
SAFE_MARKDOWN_TRANSFORMERS(38-38)components/drops/create/lexical/plugins/PlainTextPastePlugin.tsx (1)
PlainTextPastePlugin(14-52)
components/waves/CreateDropContent.tsx (1)
components/drops/create/lexical/transformers/markdownTransformers.ts (1)
SAFE_MARKDOWN_TRANSFORMERS(38-38)
components/drops/view/part/DropPartMarkdown.tsx (1)
components/drops/view/part/dropPartMarkdown/highlight.ts (1)
highlightCodeElement(35-70)
components/waves/drops/EditDropLexical.tsx (4)
components/drops/create/lexical/transformers/markdownTransformers.ts (1)
SAFE_MARKDOWN_TRANSFORMERS_WITHOUT_CODE(41-45)components/drops/create/lexical/transformers/MentionTransformer.ts (1)
MENTION_TRANSFORMER(4-32)components/drops/create/lexical/transformers/HastagTransformer.ts (1)
HASHTAG_TRANSFORMER(4-32)components/drops/create/lexical/plugins/PlainTextPastePlugin.tsx (1)
PlainTextPastePlugin(14-52)
🪛 ast-grep (0.39.5)
components/drops/view/part/dropPartMarkdown/highlight.ts
[warning] 63-63: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: element.innerHTML = value
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 63-63: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: element.innerHTML = value
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
🔇 Additional comments (33)
styles/globals.scss (1)
6-6: LGTM! Appropriate highlight.js theme for the dark UI.The vs2015 theme import aligns with the application's dark background color (#161617) and provides consistent syntax highlighting styles across the codebase.
components/drops/view/part/DropPartMarkdown.tsx (4)
3-3: LGTM! Appropriate imports for code highlighting.The added imports (
useEffect,useRef,highlightCodeElement) are correctly used to implement syntax highlighting for code blocks.Also applies to: 5-5, 30-30
132-133: LGTM! Sanitization correctly extended for language hints.Allowing
classNameoncodeandpreelements is necessary for language-specific syntax highlighting while maintaining security through tag and attribute restrictions.
178-241: LGTM! Well-structured code renderers with proper hook usage.The implementation correctly:
- Separates inline and block code rendering paths
- Uses
useReffor DOM access anduseEffectfor highlighting side effects- Extracts language hints via standard regex pattern
- Includes SSR safety checks (
typeof window === "undefined")- Re-highlights when content or language changes (proper dependency array)
261-266: LGTM! Correct conditional rendering for inline vs. block code.The conditional logic appropriately delegates to
InlineCodeRendererfor inline code andCodeBlockRendererfor code blocks based on the standardinlineprop from react-markdown.components/drops/view/part/dropPartMarkdown/highlight.ts (1)
35-70: LGTM! Robust highlighting implementation with proper safeguards.The function correctly:
- Validates text content before processing
- Checks element connectivity to prevent race conditions
- Validates language hints against registered languages
- Uses appropriate highlight.js APIs (highlightElement for known languages, highlightAuto for detection)
- Adds proper CSS classes and attributes for styling
Regarding the static analysis warning about
innerHTML: The assignment on line 64 is safe becausehljs.highlightAuto()returns sanitized HTML. Highlight.js escapes all user content and only adds syntax highlighting markup (spans with CSS classes). This is the intended API usage for auto-detection highlighting.Based on learnings
components/drops/create/lexical/lexical.styles.scss (1)
43-51: LGTM! Consistent code styling for the editor.The styles appropriately:
- Use a standard monospace font stack for code readability
- Match the color scheme (#e5e7eb / iron-200) used in rendered code
- Apply transparent background to
.editor-codeto avoid conflicts with editor stylingcomponents/waves/CreateDropInput.tsx (2)
61-62: LGTM! Appropriate imports for safe markdown handling.The imports correctly bring in the centralized safe markdown transformers and the new plain text paste plugin.
291-292: LGTM—plugin ordering and SAFE_MARKDOWN_TRANSFORMERS usage are correct. Please manually verify thatDragDropPastePluginregisters its paste command with a higher priority thanPlainTextPastePluginso file pastes bypass plain-text handling.components/waves/CreateDropContent.tsx (2)
26-26: LGTM! Clean import structure for safe transformers.The replacement of
TRANSFORMERSwithSAFE_MARKDOWN_TRANSFORMERSfrom the centralized module aligns with the PR's goal of using curated, safe transformer sets.Also applies to: 54-54
492-498: LGTM! Correct transformer composition.The spread of
SAFE_MARKDOWN_TRANSFORMERSwith custom transformers (mentions, hashtags, images, emojis) maintains the existing functionality while using the safer base set.components/drops/create/utils/CreateDropWrapper.tsx (2)
24-24: LGTM! Consistent import pattern.The transformer imports align with the centralized safe transformer approach used across the PR.
Also applies to: 37-37
196-204: LGTM! Correct markdown conversion.The
getMarkdownfunction properly combinesSAFE_MARKDOWN_TRANSFORMERSwith custom transformers for consistent markdown generation.components/drops/create/utils/CreateDropContent.tsx (4)
30-30: LGTM! Complete import set for code formatting feature.All necessary imports are present: markdown converter, safe transformers, and the new plain-text paste plugin.
Also applies to: 66-66, 72-72
288-288: LGTM! Correct plugin ordering.
PlainTextPastePluginis positioned beforeMarkdownShortcutPlugin, ensuring plain-text paste handling takes precedence over markdown transformation during paste operations.
289-289: LGTM! Updated transformer set for markdown shortcuts.The
MarkdownShortcutPluginnow usesSAFE_MARKDOWN_TRANSFORMERS, consistent with the PR's safe transformer strategy.
198-203: LGTM! Consistent transformer usage for character counting.The markdown conversion for character counting correctly uses
SAFE_MARKDOWN_TRANSFORMERScombined with custom transformers.components/drops/create/lexical/plugins/mentions/MentionsPlugin.tsx (1)
10-12: LGTM! Correct imports for code-context detection.All necessary lexical utilities are imported to support the code-context skip logic.
components/drops/create/lexical/plugins/hashtags/HashtagsPlugin.tsx (2)
10-12: LGTM! Correct imports for code-context detection.All necessary lexical utilities are imported to support the code-context skip logic.
243-274: LGTM! Correct code-context detection logic.The implementation correctly prevents hashtag processing in code contexts by checking inline code formatting and code block nodes. This mirrors the approach in
MentionsPlugin.tsx.Note: The duplication between this plugin and
MentionsPlugin.tsxhas been flagged for refactoring in the MentionsPlugin review.components/drops/create/lexical/plugins/PlainTextPastePlugin.tsx (3)
1-12: LGTM!The imports and constant definition are correct.
14-49: LGTM!The paste handler logic is correct: it delegates file pastes, aborts on empty text, prevents default paste, and inserts raw text into the current selection. The LOW priority ensures other handlers can intercede if needed.
51-51: LGTM!Returning null is correct for a plugin with no UI.
components/drops/create/lexical/transformers/markdownTransformers.ts (3)
1-8: LGTM!The imports and type guard are correct.
10-21: LGTM!The filter logic correctly excludes transformers with underscore tags while preserving other transformers.
23-45: LGTM!The
isCodeTransformerfunction safely checks for code dependencies, and the exported transformer sets are correctly filtered.components/waves/drops/EditDropLexical.tsx (7)
14-14: LGTM!The new imports are correct and necessary for the markdown transformation and paste plugin features.
Also applies to: 22-26, 34-34, 55-56
69-73: LGTM!The
EDIT_MARKDOWN_TRANSFORMERSconstant correctly combines the safe transformers without code and the custom mention/hashtag transformers.
75-104: LGTM!The
convertCodeNodesToFencesfunction correctly traverses the editor tree and convertsCodeNodeinstances to fenced markdown paragraphs. The optional chaining ongetLanguageand the normalization logic are both correct.
184-188: LGTM!Using
EDIT_MARKDOWN_TRANSFORMERSfor markdown parsing and callingconvertCodeNodesToFencesensures code blocks remain as fenced markdown in edit mode. This is consistent with the transformer set that excludes code parsing.
307-308: LGTM!Using
EDIT_MARKDOWN_TRANSFORMERSfor markdown export ensures consistency with the import transformers and correctly handles mentions, hashtags, and code blocks.Also applies to: 398-398
436-439: LGTM!The
MarkdownShortcutPlugincorrectly usesSAFE_MARKDOWN_TRANSFORMERS_WITHOUT_CODEto exclude code transformers, andPlainTextPastePluginis properly integrated into the editor pipeline.
75-104: Tree mutation during traversal may cause issues.The
convertCodeNodesToFencesfunction mutates the Lexical tree while traversing it with a stack. Whennode.replace(paragraph)is called on line 95, it modifies the tree structure, but the stack may contain stale references to nodes that were part of the replaced subtree. Additionally, thecontinuestatement on line 97 skips processing children of the newly created paragraph, which is likely intentional but worth verifying.Verify that:
- Replacing a node during stack-based traversal doesn't invalidate other references in the stack
- The
continueafterreplaceis intentional (skipping children of the new paragraph)- Test with nested structures (e.g., code blocks inside lists or quotes) to ensure correct behavior
Based on learnings about Lexical's node model: Lexical uses immutable EditorState and a reconciler, so node replacement should be safe within an
editor.update()context. However, confirm this handles all edge cases correctly.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
components/waves/drops/EditDropLexical.tsx (1)
75-104: Consider validating the language string.The function correctly converts code nodes to fenced Markdown. However,
trimmedLanguageat line 84 doesn't validate that the language string is safe for fence syntax (e.g., doesn't contain backticks or newlines). While editor-controlled languages are typically safe, consider adding validation if language can be user-supplied.Apply this diff to add basic validation:
if ($isCodeNode(node)) { const language = node.getLanguage?.() ?? ""; - const trimmedLanguage = language.trim(); + const trimmedLanguage = language.trim().replace(/[`\n\r]/g, ""); const codeText = node.getTextContent();
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
components/drops/create/lexical/plugins/PlainTextPastePlugin.tsx(1 hunks)components/drops/create/lexical/plugins/hashtags/HashtagsPlugin.tsx(2 hunks)components/drops/create/lexical/plugins/mentions/MentionsPlugin.tsx(2 hunks)components/drops/create/lexical/transformers/markdownTransformers.ts(1 hunks)components/drops/create/lexical/utils/codeContextDetection.ts(1 hunks)components/drops/view/part/dropPartMarkdown/highlight.ts(1 hunks)components/waves/drops/EditDropLexical.tsx(8 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- components/drops/create/lexical/plugins/PlainTextPastePlugin.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Do not include any comments in the code
Use react-query for data fetching
Always add readonly before propsUse TypeScript for implementation code
Files:
components/drops/create/lexical/utils/codeContextDetection.tscomponents/drops/create/lexical/plugins/hashtags/HashtagsPlugin.tsxcomponents/drops/create/lexical/plugins/mentions/MentionsPlugin.tsxcomponents/drops/create/lexical/transformers/markdownTransformers.tscomponents/drops/view/part/dropPartMarkdown/highlight.tscomponents/waves/drops/EditDropLexical.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursorrules)
**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for stylingUse React functional components with hooks
Files:
components/drops/create/lexical/plugins/hashtags/HashtagsPlugin.tsxcomponents/drops/create/lexical/plugins/mentions/MentionsPlugin.tsxcomponents/waves/drops/EditDropLexical.tsx
🧬 Code graph analysis (3)
components/drops/create/lexical/plugins/hashtags/HashtagsPlugin.tsx (1)
components/drops/create/lexical/utils/codeContextDetection.ts (1)
isInCodeContext(5-34)
components/drops/create/lexical/plugins/mentions/MentionsPlugin.tsx (1)
components/drops/create/lexical/utils/codeContextDetection.ts (1)
isInCodeContext(5-34)
components/waves/drops/EditDropLexical.tsx (5)
generated/models/ApiDropMentionedUser.ts (1)
ApiDropMentionedUser(15-48)components/drops/create/lexical/transformers/markdownTransformers.ts (1)
SAFE_MARKDOWN_TRANSFORMERS_WITHOUT_CODE(44-48)components/drops/create/lexical/transformers/MentionTransformer.ts (1)
MENTION_TRANSFORMER(4-32)components/drops/create/lexical/transformers/HastagTransformer.ts (1)
HASHTAG_TRANSFORMER(4-32)components/drops/create/lexical/plugins/PlainTextPastePlugin.tsx (1)
PlainTextPastePlugin(14-51)
🪛 ast-grep (0.39.5)
components/drops/view/part/dropPartMarkdown/highlight.ts
[warning] 84-84: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: element.innerHTML = value
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 84-84: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: element.innerHTML = value
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (20)
components/drops/create/lexical/plugins/hashtags/HashtagsPlugin.tsx (2)
27-27: LGTM!The import is correctly structured and aligns with the new code-context detection feature.
242-244: LGTM!The code-context guard correctly prevents hashtag matching within code blocks, ensuring consistent behavior with mentions.
components/drops/create/lexical/utils/codeContextDetection.ts (3)
1-10: LGTM!The imports and function structure follow Lexical best practices by using
getEditorState().read()for safe state access.
12-14: LGTM!Correctly detects inline code formatting on the selection.
19-30: Guard against anulltop-level element before calling$isCodeNode
The lexical API may returnnullfor detached nodes, so add a check ontopLevelbefore invoking$isCodeNode. For example:const isNodeWithinCode = (node: LexicalNode | null) => { if (!node) return false; if ($isCodeNode(node)) return true; const topLevel = node.getTopLevelElement(); if (!topLevel) return false; return $isCodeNode(topLevel); };components/drops/create/lexical/plugins/mentions/MentionsPlugin.tsx (2)
26-26: LGTM!The import is correctly structured.
214-216: LGTM!The code-context guard correctly prevents mention matching within code blocks, maintaining consistency with the hashtags plugin.
components/drops/create/lexical/transformers/markdownTransformers.ts (5)
1-3: LGTM!The imports and constant definition are clean and appropriate for the filtering logic.
5-8: LGTM!The type guard correctly identifies object transformers for safe property access.
10-21: LGTM!The filtering logic correctly excludes underscore-based transformers while preserving others.
23-40: LGTM!The code transformer detection logic is robust with appropriate type guards.
42-48: LGTM!The exports are clean and correctly define the two transformer variants for safe Markdown handling.
components/waves/drops/EditDropLexical.tsx (7)
14-14: LGTM!The new imports are correctly structured and support the code formatting and transformer changes.
Also applies to: 22-26, 34-34, 55-56
59-65: LGTM!The
readonlymodifiers correctly enforce immutability per coding guidelines.
69-73: LGTM!The transformer array correctly combines safe Markdown transformers with custom mention and hashtag transformers for edit mode.
179-182: LGTM!The use of
EDIT_MARKDOWN_TRANSFORMERSand post-import code fence conversion correctly normalizes content for edit mode.
229-229: LGTM!The focus improvements with
click()and adjusted timeouts enhance reliability in app mode.Also applies to: 236-238
288-289: LGTM!Correctly uses
EDIT_MARKDOWN_TRANSFORMERSfor consistent Markdown conversion.
377-377: LGTM!The transformer usage in save logic and plugin configuration is correct. The
PlainTextPastePluginintegration completes the safe paste handling.Also applies to: 414-417
components/drops/view/part/dropPartMarkdown/highlight.ts (1)
5-91: Nice lazy highlighter setup. Memoizing the dynamic imports keeps the bundle lean and prevents repeated registrations, and the hint + auto-detect flow is clean.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
components/drops/view/part/dropPartMarkdown/highlight.ts (1)
5-52: Consider expanding language support if needed.The current implementation loads 8 languages (TypeScript, JavaScript, JSON, Bash, Python, Go, Rust, SQL) which covers common use cases. If users need additional languages, consider adding them or making the language set configurable.
To add more languages, extend the dynamic imports and registration. For example, to add Java:
import("highlight.js/lib/languages/sql"), + import("highlight.js/lib/languages/java"), ]); const hljs: HLJSApi = core.default;Then register it:
hljs.registerLanguage("sql", sqlModule.default); + const javaModule = await import("highlight.js/lib/languages/java"); + hljs.registerLanguage("java", javaModule.default);components/drops/view/part/DropPartMarkdown.tsx (3)
120-198: LGTM!The
createMarkdownComponentsfactory correctly modularizes component creation and enables dependency injection. The higher-ordercreateHeadingRendererfunction is a clean pattern for handling multiple heading levels.The
as anycast on line 139 is necessary due to TypeScript's limitations with generic element types, but you could improve type safety with a more specific constraint:- return ( - <TagComponent {...(mergedProps as any)}> - {customRenderer(children)} - </TagComponent> - ); + return ( + <TagComponent {...(mergedProps as ComponentPropsWithoutRef<T>)}> + {customRenderer(children)} + </TagComponent> + );
126-150: Type safety concern withas anycast.Line 139 uses
as anyto bypass TypeScript's type checking when spreading props to the dynamically chosen tag. While this works, it reduces type safety and could mask prop type mismatches.Consider using a more type-safe approach:
- const TagComponent = Tag; - const mergedProps = { - ...(props as Record<string, unknown>), - className: mergeClassNames(headingClassName, className), - }; - - return ( - <TagComponent {...(mergedProps as any)}> - {customRenderer(children)} - </TagComponent> - ); + return ( + <Tag + {...props} + className={mergeClassNames(headingClassName, className)} + > + {customRenderer(children)} + </Tag> + );This eliminates the intermediate variable and type casts while maintaining the same functionality.
89-100: Refactor effect to depend on rendered text instead ofchildren.
Replace[language, children]with[language, codeText], wherecodeTextis a memoized string ofchildren(e.g. viaReact.Children.toArray(children).join('')) or derived fromcodeRef.current.textContent. This avoids referential changes to the ReactNode triggering extra highlights.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
components/drops/view/part/DropPartMarkdown.tsx(4 hunks)components/drops/view/part/dropPartMarkdown/highlight.ts(1 hunks)components/waves/drops/EditDropLexical.tsx(8 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Do not include any comments in the code
Use react-query for data fetching
Always add readonly before propsUse TypeScript for implementation code
Files:
components/drops/view/part/dropPartMarkdown/highlight.tscomponents/drops/view/part/DropPartMarkdown.tsxcomponents/waves/drops/EditDropLexical.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursorrules)
**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for stylingUse React functional components with hooks
Files:
components/drops/view/part/DropPartMarkdown.tsxcomponents/waves/drops/EditDropLexical.tsx
🧬 Code graph analysis (2)
components/drops/view/part/DropPartMarkdown.tsx (1)
components/drops/view/part/dropPartMarkdown/highlight.ts (1)
highlightCodeElement(54-89)
components/waves/drops/EditDropLexical.tsx (5)
generated/models/ApiDropMentionedUser.ts (1)
ApiDropMentionedUser(15-48)components/drops/create/lexical/transformers/markdownTransformers.ts (1)
SAFE_MARKDOWN_TRANSFORMERS_WITHOUT_CODE(44-48)components/drops/create/lexical/transformers/MentionTransformer.ts (1)
MENTION_TRANSFORMER(4-32)components/drops/create/lexical/transformers/HastagTransformer.ts (1)
HASHTAG_TRANSFORMER(4-32)components/drops/create/lexical/plugins/PlainTextPastePlugin.tsx (1)
PlainTextPastePlugin(14-51)
🪛 ast-grep (0.39.5)
components/drops/view/part/dropPartMarkdown/highlight.ts
[warning] 82-82: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: element.innerHTML = value
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 82-82: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: element.innerHTML = value
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (15)
components/drops/view/part/dropPartMarkdown/highlight.ts (2)
1-4: LGTM!The module-level memoization pattern for lazy-loading the highlighter is appropriate and ensures the highlighter is only loaded once per application lifecycle.
54-89: Static analysis warning is a false positive.The innerHTML assignment on line 82 is flagged by static analysis, but this is safe because:
- The input
textcomes fromelement.textContent, not directly from user input at this point- highlight.js escapes HTML entities in the input before applying syntax highlighting
- The returned
valuecontains only safe HTML markup with<span>tags for syntax highlightingThe implementation correctly handles language detection, validates hints, and adds appropriate classes.
However, consider adding error handling for robustness:
export const highlightCodeElement = async ( element: HTMLElement, languageHint?: string | null ) => { + try { const text = element.textContent ?? ""; if (!text.trim()) { return; } const hljs = await loadHighlighter(); if (!element.isConnected) { return; } const hintedLanguage = languageHint?.toLowerCase() ?? null; const language = hintedLanguage && hljs.getLanguage(hintedLanguage) ? hintedLanguage : null; element.classList.add("hljs"); if (language) { element.classList.add(`language-${language}`); hljs.highlightElement(element); return; } const { value, language: detectedLanguage } = hljs.highlightAuto(text); element.innerHTML = value; element.dataset.highlighted = "yes"; if (detectedLanguage) { element.classList.add(`language-${detectedLanguage}`); } + } catch (error) { + console.error("Failed to highlight code element:", error); + } };components/drops/view/part/DropPartMarkdown.tsx (13)
3-3: LGTM!The new imports (
useEffect,useRef,highlightCodeElement) are necessary for the code syntax highlighting functionality.Also applies to: 5-5, 30-30
34-36: LGTM!The
mergeClassNamesutility correctly filters out falsy values and joins valid class names.
38-70: LGTM!The type definitions and
InlineCodeRendererare well-structured. The inline code renderer appropriately renders without syntax highlighting.
72-118: LGTM!The
CodeBlockRenderercorrectly:
- Extracts language hints from className
- Guards against SSR issues with
globalThis.windowcheck- Calls
highlightCodeElementwith appropriate dependencies- Switches between inline and block rendering based on the
inlineprop
298-299: LGTM!Adding
classNameto the allowed attributes forcodeandpreelements is necessary for syntax highlighting, as highlight.js addslanguage-*classes. This is safe from an XSS perspective.
309-318: LGTM!The
markdownComponentsuseMemo correctly instantiates the component factory with appropriate dependencies.
1-30: LGTM!The imports are clean and all dependencies are used appropriately in the component.
34-36: LGTM!The utility function correctly filters and joins class names.
38-52: LGTM!Type definitions are well-structured and provide clear contracts for the component factory pattern.
54-70: LGTM!The inline code renderer correctly handles props and applies appropriate styling for inline code display.
117-118: LGTM!Clean conditional rendering logic that correctly delegates to the appropriate renderer based on the
inlineprop.
298-299: LGTM! Security consideration addressed.Allowing
classNameattributes oncodeandpreelements is necessary for syntax highlighting with highlight.js. TheclassNameattribute is safe to allow as it only affects styling and doesn't introduce XSS risks.
309-318: LGTM!The factory pattern correctly creates and memoizes the Markdown components, with appropriate dependencies to ensure updates when the renderers change.
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
components/drops/view/part/DropPartMarkdown.tsx(4 hunks)components/waves/drops/EditDropLexical.tsx(8 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Do not include any comments in the code
Use react-query for data fetching
Always add readonly before propsUse TypeScript for implementation code
Files:
components/drops/view/part/DropPartMarkdown.tsxcomponents/waves/drops/EditDropLexical.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursorrules)
**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for stylingUse React functional components with hooks
Files:
components/drops/view/part/DropPartMarkdown.tsxcomponents/waves/drops/EditDropLexical.tsx
🧬 Code graph analysis (2)
components/drops/view/part/DropPartMarkdown.tsx (1)
components/drops/view/part/dropPartMarkdown/highlight.ts (1)
highlightCodeElement(54-89)
components/waves/drops/EditDropLexical.tsx (5)
generated/models/ApiDropMentionedUser.ts (1)
ApiDropMentionedUser(15-48)components/drops/create/lexical/transformers/markdownTransformers.ts (1)
SAFE_MARKDOWN_TRANSFORMERS_WITHOUT_CODE(44-48)components/drops/create/lexical/transformers/MentionTransformer.ts (1)
MENTION_TRANSFORMER(4-32)components/drops/create/lexical/transformers/HastagTransformer.ts (1)
HASHTAG_TRANSFORMER(4-32)components/drops/create/lexical/plugins/PlainTextPastePlugin.tsx (1)
PlainTextPastePlugin(14-51)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (8)
components/waves/drops/EditDropLexical.tsx (8)
58-65: LGTM! Props follow coding guidelines.All props correctly marked as
readonly, aligning with the TypeScript coding guidelines.
69-73: LGTM! Appropriate transformer composition.The
EDIT_MARKDOWN_TRANSFORMERScorrectly excludes code transformers since code nodes are handled separately byconvertCodeNodesToFences.
75-109: LGTM! Correct handling of embedded backticks in code fences.The function now properly computes the fence length to handle code containing backtick sequences, addressing the previous review concern. The stack-based traversal and node replacement logic are correct.
179-220: LGTM! Correct transformer usage and code normalization.The plugin correctly uses
EDIT_MARKDOWN_TRANSFORMERSand callsconvertCodeNodesToFencesto normalize code blocks after markdown conversion. The mention reconstruction logic remains intact.
234-234: Verify that the.click()call is necessary.The added
.click()call might trigger unintended side effects or interfere with user interaction. Ensure this is required for the app environment and doesn't cause issues like triggering click handlers or selection changes.
293-294: LGTM! Consistent transformer usage.Correctly uses
EDIT_MARKDOWN_TRANSFORMERSfor markdown conversion in the keyboard command handler.
382-382: LGTM! Consistent transformer usage.Correctly uses
EDIT_MARKDOWN_TRANSFORMERSfor markdown conversion when saving.
171-171: Verify console statement is appropriate.While logging is useful for debugging, ensure this warning provides enough context in production. Consider whether this should be logged to a monitoring service rather than just the console.
|
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
components/drops/view/part/DropPartMarkdown.tsx (1)
102-118: Re-run highlighter each render with guards — resolvedEffect now runs after every render with SSR and empty-text guards. This addresses the prior highlighting class loss issue.
🧹 Nitpick comments (3)
__tests__/components/waves/drops/EditDropLexical.test.tsx (1)
63-71: Prefer boolean prop for contentEditableUse a boolean rather than the string "true" to match React types.
- contentEditable="true" + contentEditablecomponents/drops/view/part/DropPartMarkdown.tsx (2)
280-317: Consider allowing span in sanitizer for future SSR/pre-highlightingIf you ever move to server-side or pre-rendered highlighting, spans from hljs would be stripped. Optional future-proofing:
allowedTags: [ "p", "br", "strong", "em", "a", "code", "pre", + "span", "blockquote", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol", "li", "img", ], allowedAttributes: { a: ["href", "title"], img: ["src", "alt", "title"], code: ["className"], pre: ["className"], + span: ["className"], },
73-101: Language detection is fine; minor robustness tweak optionalRegex-based language extraction is OK. Optionally normalize className to a single string (split/join) before regex to tolerate array-like values.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
__tests__/components/drops/create/utils/CreateDropContent.component.test.tsx(1 hunks)__tests__/components/waves/drops/EditDropLexical.test.tsx(3 hunks)components/drops/view/part/DropPartMarkdown.tsx(4 hunks)components/waves/drops/EditDropLexical.tsx(8 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Do not include any comments in the code
Use react-query for data fetching
Always add readonly before propsUse TypeScript for implementation code
Files:
__tests__/components/drops/create/utils/CreateDropContent.component.test.tsxcomponents/waves/drops/EditDropLexical.tsxcomponents/drops/view/part/DropPartMarkdown.tsx__tests__/components/waves/drops/EditDropLexical.test.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursorrules)
**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for stylingUse React functional components with hooks
Files:
__tests__/components/drops/create/utils/CreateDropContent.component.test.tsxcomponents/waves/drops/EditDropLexical.tsxcomponents/drops/view/part/DropPartMarkdown.tsx__tests__/components/waves/drops/EditDropLexical.test.tsx
**/{__tests__/**/*.{ts,tsx},*.test.tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/{__tests__/**/*.{ts,tsx},*.test.tsx}: Place tests in tests directories or alongside components as ComponentName.test.tsx
Mock external dependencies and APIs in tests
Files:
__tests__/components/drops/create/utils/CreateDropContent.component.test.tsx__tests__/components/waves/drops/EditDropLexical.test.tsx
__tests__/**
📄 CodeRabbit inference engine (tests/AGENTS.md)
Place Jest test suites under the
__tests__directory mirroring source folders (e.g., components, contexts, hooks, utils)
Files:
__tests__/components/drops/create/utils/CreateDropContent.component.test.tsx__tests__/components/waves/drops/EditDropLexical.test.tsx
__tests__/components/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (tests/AGENTS.md)
Use
@testing-library/reactand@testing-library/user-eventfor React component tests
Files:
__tests__/components/drops/create/utils/CreateDropContent.component.test.tsx__tests__/components/waves/drops/EditDropLexical.test.tsx
🧠 Learnings (1)
📚 Learning: 2025-09-28T12:32:36.068Z
Learnt from: CR
PR: 6529-Collections/6529seize-frontend#0
File: AGENTS.md:0-0
Timestamp: 2025-09-28T12:32:36.068Z
Learning: Applies to **/{__tests__/**/*.{ts,tsx},*.test.tsx} : Mock external dependencies and APIs in tests
Applied to files:
__tests__/components/drops/create/utils/CreateDropContent.component.test.tsx__tests__/components/waves/drops/EditDropLexical.test.tsx
🧬 Code graph analysis (2)
components/waves/drops/EditDropLexical.tsx (5)
generated/models/ApiDropMentionedUser.ts (1)
ApiDropMentionedUser(15-48)components/drops/create/lexical/transformers/markdownTransformers.ts (1)
SAFE_MARKDOWN_TRANSFORMERS_WITHOUT_CODE(44-48)components/drops/create/lexical/transformers/MentionTransformer.ts (1)
MENTION_TRANSFORMER(4-32)components/drops/create/lexical/transformers/HastagTransformer.ts (1)
HASHTAG_TRANSFORMER(4-32)components/drops/create/lexical/plugins/PlainTextPastePlugin.tsx (1)
PlainTextPastePlugin(14-51)
components/drops/view/part/DropPartMarkdown.tsx (1)
components/drops/view/part/dropPartMarkdown/highlight.ts (1)
highlightCodeElement(54-89)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (15)
__tests__/components/drops/create/utils/CreateDropContent.component.test.tsx (1)
25-25: Mocking PlainTextPastePlugin is correct hereGood isolation; prevents plugin behavior from affecting this test.
__tests__/components/waves/drops/EditDropLexical.test.tsx (3)
81-90: OnChangePlugin stub is solidTriggers onChange once with a minimal editorState; matches behavior-driven tests without over-mocking.
253-287: Keyboard command registration assertions are robustValidates registration and handler effects; good coverage of Enter/Escape flows.
149-151: No changes needed:@/alias is already configured
The Jest config’s moduleNameMapper includes"^@/(.*)$": "<rootDir>/$1", so imports using@/…resolve correctly.components/drops/view/part/DropPartMarkdown.tsx (1)
134-136: Inline vs block code split is cleanGood separation; keeps inline code lightweight while enabling DOM-based highlighting for blocks.
components/waves/drops/EditDropLexical.tsx (10)
14-56: LGTM: Import additions support new functionality.The new imports ($createParagraphNode, $createTextNode, $isElementNode, $isCodeNode, SAFE_MARKDOWN_TRANSFORMERS_WITHOUT_CODE, PlainTextPastePlugin) are all used in the implementation for code fence conversion and plain-text paste handling.
58-65: LGTM: Props follow coding guidelines.All props correctly use the
readonlymodifier as required by the coding guidelines.
69-73: LGTM: Transformer composition is correct.The
EDIT_MARKDOWN_TRANSFORMERSarray correctly composes the base transformers (without code) with mention and hashtag transformers, aligning with the PR's objective to use safe transformer sets.
75-109: LGTM: Code fence conversion handles embedded backticks correctly.The function properly computes the fence length based on the maximum consecutive backtick sequence in the code content, ensuring that embedded backticks don't prematurely terminate the fence. This addresses the concern from previous reviews.
111-177: LGTM: Mention reconstruction logic is sound.The functions correctly handle the case where mention markers are split across adjacent text nodes during markdown parsing, with appropriate error handling and return values.
179-220: LGTM: Initial content loading sequence is correct.The plugin properly converts markdown using the composed transformers, then post-processes code nodes to fenced blocks, and finally reconstructs any split mentions with a safety limit.
222-253: LGTM: Focus plugin adjustments for reliability.The addition of
.click()and adjusted timeout values appear to improve focus behavior on mobile/app environments.
255-314: LGTM: Keyboard handler uses correct transformers.The keyboard plugin correctly uses
EDIT_MARKDOWN_TRANSFORMERSfor markdown conversion when detecting changes, ensuring consistency with the initial content loading.
378-391: LGTM: Save handler uses correct transformers.The save handler correctly uses
EDIT_MARKDOWN_TRANSFORMERSfor markdown conversion and includes a guard to avoid saving unchanged content.
419-422: LGTM: Plugin order and transformers are correct.
PlainTextPastePluginis correctly registered beforeMarkdownShortcutPlugin, and the shortcut plugin usesSAFE_MARKDOWN_TRANSFORMERS_WITHOUT_CODE(excluding mention/hashtag transformers to avoid conflicts with the dedicated mention/hashtag plugins).

Summary by CodeRabbit
New Features
Bug Fixes
Style