Skip to content

Syntax-highlighted code blocks #1500

Merged
simo6529 merged 22 commits intomainfrom
code-formatting
Oct 1, 2025
Merged

Syntax-highlighted code blocks #1500
simo6529 merged 22 commits intomainfrom
code-formatting

Conversation

@simo6529
Copy link
Copy Markdown
Collaborator

@simo6529 simo6529 commented Oct 1, 2025

Summary by CodeRabbit

  • New Features

    • Syntax-highlighted code blocks with language detection and improved inline code rendering.
    • Plain-text paste handling to strip formatting on paste.
    • Safer Markdown transformer sets to reduce risky markdown behavior.
    • Drop markdown now supports quote-click handling and text-size option.
  • Bug Fixes

    • Mentions and hashtags no longer trigger inside code areas.
    • Pasted files are ignored by the plain-text paste handler.
  • Style

    • Monospace font, color, and background updates for code editors.
    • Added global syntax-highlighting theme with lazy-loaded highlighter.

Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Oct 1, 2025

Walkthrough

Adds 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

Cohort / File(s) Summary
Code editor styles
components/drops/create/lexical/lexical.styles.scss
Adds .editor-code and .editor-text-code with monospace font-family and color: #e5e7eb; .editor-code also sets background-color: transparent.
Plain-text paste plugin
components/drops/create/lexical/plugins/PlainTextPastePlugin.tsx
New Lexical plugin that registers a LOW-priority PASTE_COMMAND handler to insert plain text only, ignoring file pastes and preventing default paste when inserting.
Skip mentions/hashtags in code
components/drops/create/lexical/plugins/hashtags/HashtagsPlugin.tsx, components/drops/create/lexical/plugins/mentions/MentionsPlugin.tsx
Import and use isInCodeContext; early-return to disable hashtag/mention matching when selection is in code context.
Code-context detection util
components/drops/create/lexical/utils/codeContextDetection.ts
New exported isInCodeContext(editor: LexicalEditor): boolean that reads selection to determine if anchor/focus are within code nodes or code-formatted ranges.
Safe markdown transformers
components/drops/create/lexical/transformers/markdownTransformers.ts
New module exporting SAFE_MARKDOWN_TRANSFORMERS and SAFE_MARKDOWN_TRANSFORMERS_WITHOUT_CODE by filtering TRANSFORMERS to remove underscore-tag transformers and optionally code transformers.
Create flows use safe transformers + plain paste
components/drops/create/utils/CreateDropContent.tsx, components/drops/create/utils/CreateDropWrapper.tsx, components/waves/CreateDropContent.tsx, components/waves/CreateDropInput.tsx
Replace TRANSFORMERS with SAFE_MARKDOWN_TRANSFORMERS; add PlainTextPastePlugin to the editor plugin pipeline; update MarkdownShortcutPlugin to use safe sets.
Edit flow: transformers, fencing, readonly props
components/waves/drops/EditDropLexical.tsx
Introduces EDIT_MARKDOWN_TRANSFORMERS (safe-without-code + mention/hashtag transformers), adds convertCodeNodesToFences post-processing, uses PlainTextPastePlugin, and marks several EditDropLexicalProps fields readonly.
Markdown code rendering + highlighting
components/drops/view/part/DropPartMarkdown.tsx, components/drops/view/part/dropPartMarkdown/highlight.ts, styles/globals.scss
Adds InlineCodeRenderer and CodeBlockRenderer that call highlightCodeElement; new highlightCodeElement lazily loads highlight.js, registers languages, applies highlighting and language classes; imports highlight.js vs2015 theme in globals.
Tests updated/mocked
__tests__/components/drops/create/utils/CreateDropContent.component.test.tsx, __tests__/components/waves/drops/EditDropLexical.test.tsx
Adds mock for PlainTextPastePlugin; major overhaul of EditDropLexical tests with extensive Lexical mocks, mention selection handler capture, and updated expectations around markdown/mention saving and key command handling.

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
Loading
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
Loading
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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

I nibble bytes and tidy paste,
Quiet mentions hide in code’s space.
Fences form where snippets lay,
Highlighted lines show bright the way,
A rabbit hops — safe paths today. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “Syntax-highlighted code blocks” succinctly captures a central feature added in this PR—namely the code block syntax highlighting via the highlight.ts module, updated rendering components, and global styling—without including extraneous detail. Though the PR also introduces auxiliary features such as code-context detection and plain text paste handling, the title remains specific and relevant to a core, user-facing enhancement.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch code-formatting

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 37fe357 and e0f6269.

📒 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 props

Use TypeScript for implementation code

Files:

  • components/drops/view/part/dropPartMarkdown/highlight.ts
  • components/waves/CreateDropInput.tsx
  • components/drops/create/lexical/plugins/mentions/MentionsPlugin.tsx
  • components/drops/create/utils/CreateDropWrapper.tsx
  • components/drops/create/utils/CreateDropContent.tsx
  • components/drops/create/lexical/transformers/markdownTransformers.ts
  • components/waves/CreateDropContent.tsx
  • components/drops/view/part/DropPartMarkdown.tsx
  • components/drops/create/lexical/plugins/PlainTextPastePlugin.tsx
  • components/waves/drops/EditDropLexical.tsx
  • components/drops/create/lexical/plugins/hashtags/HashtagsPlugin.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursorrules)

**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for styling

Use React functional components with hooks

Files:

  • components/waves/CreateDropInput.tsx
  • components/drops/create/lexical/plugins/mentions/MentionsPlugin.tsx
  • components/drops/create/utils/CreateDropWrapper.tsx
  • components/drops/create/utils/CreateDropContent.tsx
  • components/waves/CreateDropContent.tsx
  • components/drops/view/part/DropPartMarkdown.tsx
  • components/drops/create/lexical/plugins/PlainTextPastePlugin.tsx
  • components/waves/drops/EditDropLexical.tsx
  • components/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 className on code and pre elements 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 useRef for DOM access and useEffect for 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 InlineCodeRenderer for inline code and CodeBlockRenderer for code blocks based on the standard inline prop 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 because hljs.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-code to avoid conflicts with editor styling
components/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 that DragDropPastePlugin registers its paste command with a higher priority than PlainTextPastePlugin so file pastes bypass plain-text handling.

components/waves/CreateDropContent.tsx (2)

26-26: LGTM! Clean import structure for safe transformers.

The replacement of TRANSFORMERS with SAFE_MARKDOWN_TRANSFORMERS from 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_TRANSFORMERS with 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 getMarkdown function properly combines SAFE_MARKDOWN_TRANSFORMERS with 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.

PlainTextPastePlugin is positioned before MarkdownShortcutPlugin, ensuring plain-text paste handling takes precedence over markdown transformation during paste operations.


289-289: LGTM! Updated transformer set for markdown shortcuts.

The MarkdownShortcutPlugin now uses SAFE_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_TRANSFORMERS combined 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.tsx has 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 isCodeTransformer function 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_TRANSFORMERS constant correctly combines the safe transformers without code and the custom mention/hashtag transformers.


75-104: LGTM!

The convertCodeNodesToFences function correctly traverses the editor tree and converts CodeNode instances to fenced markdown paragraphs. The optional chaining on getLanguage and the normalization logic are both correct.


184-188: LGTM!

Using EDIT_MARKDOWN_TRANSFORMERS for markdown parsing and calling convertCodeNodesToFences ensures 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_TRANSFORMERS for 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 MarkdownShortcutPlugin correctly uses SAFE_MARKDOWN_TRANSFORMERS_WITHOUT_CODE to exclude code transformers, and PlainTextPastePlugin is properly integrated into the editor pipeline.


75-104: Tree mutation during traversal may cause issues.

The convertCodeNodesToFences function mutates the Lexical tree while traversing it with a stack. When node.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, the continue statement on line 97 skips processing children of the newly created paragraph, which is likely intentional but worth verifying.

Verify that:

  1. Replacing a node during stack-based traversal doesn't invalidate other references in the stack
  2. The continue after replace is intentional (skipping children of the new paragraph)
  3. 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.

Comment thread components/drops/create/lexical/plugins/PlainTextPastePlugin.tsx Outdated
Comment thread components/drops/create/lexical/transformers/markdownTransformers.ts Outdated
Comment thread components/drops/create/lexical/transformers/markdownTransformers.ts Outdated
Comment thread components/waves/drops/EditDropLexical.tsx Outdated
Comment thread components/waves/drops/EditDropLexical.tsx Outdated
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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, trimmedLanguage at 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

📥 Commits

Reviewing files that changed from the base of the PR and between e0f6269 and e48b2ea.

📒 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 props

Use TypeScript for implementation code

Files:

  • components/drops/create/lexical/utils/codeContextDetection.ts
  • components/drops/create/lexical/plugins/hashtags/HashtagsPlugin.tsx
  • components/drops/create/lexical/plugins/mentions/MentionsPlugin.tsx
  • components/drops/create/lexical/transformers/markdownTransformers.ts
  • components/drops/view/part/dropPartMarkdown/highlight.ts
  • components/waves/drops/EditDropLexical.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursorrules)

**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for styling

Use React functional components with hooks

Files:

  • components/drops/create/lexical/plugins/hashtags/HashtagsPlugin.tsx
  • components/drops/create/lexical/plugins/mentions/MentionsPlugin.tsx
  • components/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 a null top-level element before calling $isCodeNode
The lexical API may return null for detached nodes, so add a check on topLevel before 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 readonly modifiers 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_TRANSFORMERS and 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_TRANSFORMERS for consistent Markdown conversion.


377-377: LGTM!

The transformer usage in save logic and plugin configuration is correct. The PlainTextPastePlugin integration 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.

Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 createMarkdownComponents factory correctly modularizes component creation and enables dependency injection. The higher-order createHeadingRenderer function is a clean pattern for handling multiple heading levels.

The as any cast 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 with as any cast.

Line 139 uses as any to 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 of children.
Replace [language, children] with [language, codeText], where codeText is a memoized string of children (e.g. via React.Children.toArray(children).join('')) or derived from codeRef.current.textContent. This avoids referential changes to the ReactNode triggering extra highlights.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e48b2ea and 30fbf91.

📒 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 props

Use TypeScript for implementation code

Files:

  • components/drops/view/part/dropPartMarkdown/highlight.ts
  • components/drops/view/part/DropPartMarkdown.tsx
  • components/waves/drops/EditDropLexical.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursorrules)

**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for styling

Use React functional components with hooks

Files:

  • components/drops/view/part/DropPartMarkdown.tsx
  • components/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:

  1. The input text comes from element.textContent, not directly from user input at this point
  2. highlight.js escapes HTML entities in the input before applying syntax highlighting
  3. The returned value contains only safe HTML markup with <span> tags for syntax highlighting

The 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 mergeClassNames utility correctly filters out falsy values and joins valid class names.


38-70: LGTM!

The type definitions and InlineCodeRenderer are well-structured. The inline code renderer appropriately renders without syntax highlighting.


72-118: LGTM!

The CodeBlockRenderer correctly:

  • Extracts language hints from className
  • Guards against SSR issues with globalThis.window check
  • Calls highlightCodeElement with appropriate dependencies
  • Switches between inline and block rendering based on the inline prop

298-299: LGTM!

Adding className to the allowed attributes for code and pre elements is necessary for syntax highlighting, as highlight.js adds language-* classes. This is safe from an XSS perspective.


309-318: LGTM!

The markdownComponents useMemo 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 inline prop.


298-299: LGTM! Security consideration addressed.

Allowing className attributes on code and pre elements is necessary for syntax highlighting with highlight.js. The className attribute 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.

Comment thread components/waves/drops/EditDropLexical.tsx
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 30fbf91 and 054bed6.

📒 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 props

Use TypeScript for implementation code

Files:

  • components/drops/view/part/DropPartMarkdown.tsx
  • components/waves/drops/EditDropLexical.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursorrules)

**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for styling

Use React functional components with hooks

Files:

  • components/drops/view/part/DropPartMarkdown.tsx
  • components/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_TRANSFORMERS correctly excludes code transformers since code nodes are handled separately by convertCodeNodesToFences.


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_TRANSFORMERS and calls convertCodeNodesToFences to 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_TRANSFORMERS for markdown conversion in the keyboard command handler.


382-382: LGTM! Consistent transformer usage.

Correctly uses EDIT_MARKDOWN_TRANSFORMERS for 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.

Comment thread components/drops/view/part/DropPartMarkdown.tsx
Comment thread components/waves/drops/EditDropLexical.tsx Outdated
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Oct 1, 2025

Quality Gate Passed Quality Gate passed

Issues
0 New issues
12 Accepted issues

Measures
0 Security Hotspots
No data about Coverage
0.6% Duplication on New Code

See analysis details on SonarQube Cloud

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
components/drops/view/part/DropPartMarkdown.tsx (1)

102-118: Re-run highlighter each render with guards — resolved

Effect 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 contentEditable

Use a boolean rather than the string "true" to match React types.

-      contentEditable="true"
+      contentEditable
components/drops/view/part/DropPartMarkdown.tsx (2)

280-317: Consider allowing span in sanitizer for future SSR/pre-highlighting

If 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 optional

Regex-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

📥 Commits

Reviewing files that changed from the base of the PR and between 054bed6 and 83138b7.

📒 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 props

Use TypeScript for implementation code

Files:

  • __tests__/components/drops/create/utils/CreateDropContent.component.test.tsx
  • components/waves/drops/EditDropLexical.tsx
  • components/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 styling

Use React functional components with hooks

Files:

  • __tests__/components/drops/create/utils/CreateDropContent.component.test.tsx
  • components/waves/drops/EditDropLexical.tsx
  • components/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/react and @testing-library/user-event for 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 here

Good isolation; prevents plugin behavior from affecting this test.

__tests__/components/waves/drops/EditDropLexical.test.tsx (3)

81-90: OnChangePlugin stub is solid

Triggers onChange once with a minimal editorState; matches behavior-driven tests without over-mocking.


253-287: Keyboard command registration assertions are robust

Validates 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 clean

Good 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 readonly modifier as required by the coding guidelines.


69-73: LGTM: Transformer composition is correct.

The EDIT_MARKDOWN_TRANSFORMERS array 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_TRANSFORMERS for 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_TRANSFORMERS for markdown conversion and includes a guard to avoid saving unchanged content.


419-422: LGTM: Plugin order and transformers are correct.

PlainTextPastePlugin is correctly registered before MarkdownShortcutPlugin, and the shortcut plugin uses SAFE_MARKDOWN_TRANSFORMERS_WITHOUT_CODE (excluding mention/hashtag transformers to avoid conflicts with the dedicated mention/hashtag plugins).

@simo6529 simo6529 changed the title Code formatting Syntax-highlighted code blocks Oct 1, 2025
@simo6529 simo6529 merged commit af24af0 into main Oct 1, 2025
9 checks passed
@simo6529 simo6529 deleted the code-formatting branch October 1, 2025 11:48
@coderabbitai coderabbitai Bot mentioned this pull request Oct 6, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Oct 13, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Nov 7, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Apr 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants