Skip to content

Format text#1529

Merged
simo6529 merged 6 commits intomainfrom
format-text
Oct 10, 2025
Merged

Format text#1529
simo6529 merged 6 commits intomainfrom
format-text

Conversation

@simo6529
Copy link
Copy Markdown
Collaborator

@simo6529 simo6529 commented Oct 10, 2025

Summary by CodeRabbit

  • Improvements

    • More consistent Markdown export/import across the editor via a unified normalization/export path.
    • Improved handling of blank lines: sequences of 3+ newlines are preserved and represented for accurate visual spacing.
    • Normalized line endings for predictable rendering across platforms.
    • More accurate character counting for editor content.
  • Bug Fixes

    • Editing now reliably preserves intentional blank paragraphs.
    • Save/Cancel comparisons are more accurate, reducing false “unsaved changes” prompts.
    • Rendering of multiple blank lines in drop content now displays the expected spacing.

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 10, 2025

Walkthrough

Replaces direct Lexical markdown conversion with a new normalization/export pipeline, adds blank-line placeholder utilities, updates create/edit components to use exportDropMarkdown/normalizeDropMarkdown, adjusts view newline consolidation, and updates tests to mock and cover the new normalization/export behavior.

Changes

Cohort / File(s) Summary of changes
Markdown normalization/export module
components/waves/drops/normalizeDropMarkdown.ts
New module: normalizeDropMarkdown and exportDropMarkdown implemented; handles blank-paragraph sentinel insertion/collapse, integrates with transformers, and normalizes line endings. Default export is normalizeDropMarkdown.
Blank-line placeholder utilities
components/waves/drops/blankLinePlaceholders.ts
New utility adding/removing a zero-width-space placeholder for runs of 3+ newlines (addBlankLinePlaceholders, removeBlankLinePlaceholders, default export).
Create flow components
components/drops/create/utils/CreateDropContent.tsx, components/drops/create/utils/CreateDropWrapper.tsx, components/waves/CreateDropContent.tsx
Switched markdown generation from Lexical’s $convertToMarkdownString (editorState.read) to exportDropMarkdown(editorState, transformers); removed direct lexical conversion import and adjusted char-count/getMarkdown logic.
Edit flow component
components/waves/drops/EditDropLexical.tsx
Normalize initial content via normalizeDropMarkdown, apply addBlankLinePlaceholders for editor input, use exportDropMarkdown on save, strip placeholders before comparisons, and wire normalized content through plugins/handlers.
View rendering adjustment
components/drops/view/part/dropPartMarkdown/content.tsx
Consolidation threshold changed to 3+ consecutive newlines and filler generation altered to use sequences of &nbsp; separated by \n\n.
Tests: mocks and coverage updated
__tests__/components/CreateDropWrapper.test.tsx, __tests__/components/drops/create/utils/CreateDropWrapper.test.tsx, __tests__/components/drops/create/utils/CreateDropContent.component.test.tsx, __tests__/components/waves/drops/EditDropLexical.test.tsx
Replaced mocks of @lexical/markdown with mocks for the new normalizeDropMarkdown module; expose __esModule, default, normalizeDropMarkdown, and exportDropMarkdown in tests; adjusted expectations and setup to use exportDropMarkdown.
Tests: normalization/export behavior
__tests__/components/waves/drops/normalizeDropMarkdown.test.ts
New tests for exportDropMarkdown blank-paragraph collapsing and normalizeDropMarkdown CRLF→LF normalization; mocks Lexical converter and uses a stub EditorState.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Editor as Lexical Editor
  participant Norm as normalizeDropMarkdown / exportDropMarkdown
  participant BL as blankLinePlaceholders
  participant View as Renderer

  User->>Editor: Edit content
  Editor->>Norm: exportDropMarkdown(editorState, transformers)
  Note over Norm: Insert blank-paragraph sentinels\nRun transformers\nCollapse placeholders\nNormalize line endings
  Norm-->>Editor: Markdown string
  Editor->>BL: removeBlankLinePlaceholders(markdown) (if needed)
  BL-->>Editor: sanitized markdown
  Editor->>View: Render/process content
  Note over View: Consolidate runs of ≥3 newlines\nGenerate filler using &nbsp;
  View-->>User: Rendered output
Loading
sequenceDiagram
  autonumber
  actor User
  participant Edit as EditDropLexical
  participant Norm as normalizeDropMarkdown
  participant BL as blankLinePlaceholders

  Edit->>Norm: normalizeDropMarkdown(initialContent)
  Norm-->>Edit: normalizedInitialContent
  Edit->>BL: addBlankLinePlaceholders(normalizedInitialContent)
  BL-->>Edit: editorInitialContent (with placeholders)
  User->>Edit: Save / keyboard shortcut
  Edit->>Norm: exportDropMarkdown(editorState, transformers)
  Norm-->>Edit: exportedMarkdown (with sentinels)
  Edit->>BL: removeBlankLinePlaceholders(exportedMarkdown)
  BL-->>Edit: sanitizedMarkdown
  Edit->>Edit: Compare sanitizedMarkdown to sanitizedInitialContent -> decide save/cancel
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • ragnep
  • analyticsflowee

Poem

A rabbit hops through lines of text,
I tuck blank hops where they are next.
I normalize, then gently pry—
Newlines tidy, placeholders fly.
I thump once: export’s fresh and neat. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title Check ❓ Inconclusive The title “Format text” is overly generic and does not capture the primary changes in this pull request, which involve introducing a normalizeDropMarkdown module, updating markdown conversion logic across components and tests, and adding blank line placeholder utilities. It fails to summarize the core feature migration and enhancements to markdown handling. Please revise the title to clearly reflect the main change, for example “Migrate markdown conversion to normalizeDropMarkdown and add blank-line placeholder utilities.”
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch format-text

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

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 (2)
__tests__/components/waves/drops/normalizeDropMarkdown.test.ts (1)

64-68: Add test coverage for empty/falsy inputs.

The test only covers CRLF normalization but doesn't verify the empty/falsy input handling that's implemented in the source (lines 92-94 of normalizeDropMarkdown.ts).

Add this test case:

 describe("normalizeDropMarkdown", () => {
   it("normalizes CRLF to LF", () => {
     expect(normalizeDropMarkdown("line1\r\nline2")).toBe("line1\nline2");
   });
+
+  it("returns empty strings unchanged", () => {
+    expect(normalizeDropMarkdown("")).toBe("");
+  });
 });
components/waves/drops/normalizeDropMarkdown.ts (1)

64-78: Consider adding documentation for complex marker collapsing logic.

The logic correctly handles blank paragraph markers but could benefit from inline documentation explaining the two-pass approach (collapsing runs, then handling individual tokens).

 const collapseBlankParagraphMarkers = (markdown: string): string => {
   if (!markdown) {
     return markdown;
   }
 
+  
   const collapsedRuns = markdown.replaceAll(BLANK_RUN_REGEX, (match) => {
     const markerCount = match.split(BLANK_PARAGRAPH_SENTINEL).length - 1;
     return "\n".repeat(markerCount);
   });
 
+  
   return collapsedRuns.replaceAll(
     BLANK_PARAGRAPH_TOKEN_REGEX,
     (_match, trailingNewline: string) => `\n${trailingNewline}`
   );
 };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2d92c95 and 62657c2.

📒 Files selected for processing (12)
  • __tests__/components/CreateDropWrapper.test.tsx (1 hunks)
  • __tests__/components/drops/create/utils/CreateDropContent.component.test.tsx (1 hunks)
  • __tests__/components/drops/create/utils/CreateDropWrapper.test.tsx (2 hunks)
  • __tests__/components/waves/drops/EditDropLexical.test.tsx (6 hunks)
  • __tests__/components/waves/drops/normalizeDropMarkdown.test.ts (1 hunks)
  • components/drops/create/utils/CreateDropContent.tsx (2 hunks)
  • components/drops/create/utils/CreateDropWrapper.tsx (2 hunks)
  • components/drops/view/part/dropPartMarkdown/content.tsx (1 hunks)
  • components/waves/CreateDropContent.tsx (2 hunks)
  • components/waves/drops/EditDropLexical.tsx (10 hunks)
  • components/waves/drops/blankLinePlaceholders.ts (1 hunks)
  • components/waves/drops/normalizeDropMarkdown.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{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

Follow existing code style and naming conventions

Files:

  • components/drops/view/part/dropPartMarkdown/content.tsx
  • components/drops/create/utils/CreateDropWrapper.tsx
  • __tests__/components/drops/create/utils/CreateDropContent.component.test.tsx
  • components/waves/drops/normalizeDropMarkdown.ts
  • __tests__/components/waves/drops/normalizeDropMarkdown.test.ts
  • __tests__/components/drops/create/utils/CreateDropWrapper.test.tsx
  • components/drops/create/utils/CreateDropContent.tsx
  • __tests__/components/CreateDropWrapper.test.tsx
  • components/waves/CreateDropContent.tsx
  • components/waves/drops/blankLinePlaceholders.ts
  • __tests__/components/waves/drops/EditDropLexical.test.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 for UI components

Files:

  • components/drops/view/part/dropPartMarkdown/content.tsx
  • components/drops/create/utils/CreateDropWrapper.tsx
  • __tests__/components/drops/create/utils/CreateDropContent.component.test.tsx
  • __tests__/components/drops/create/utils/CreateDropWrapper.test.tsx
  • components/drops/create/utils/CreateDropContent.tsx
  • __tests__/components/CreateDropWrapper.test.tsx
  • components/waves/CreateDropContent.tsx
  • __tests__/components/waves/drops/EditDropLexical.test.tsx
  • components/waves/drops/EditDropLexical.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/normalizeDropMarkdown.test.ts
  • __tests__/components/drops/create/utils/CreateDropWrapper.test.tsx
  • __tests__/components/CreateDropWrapper.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/normalizeDropMarkdown.test.ts
  • __tests__/components/drops/create/utils/CreateDropWrapper.test.tsx
  • __tests__/components/CreateDropWrapper.test.tsx
  • __tests__/components/waves/drops/EditDropLexical.test.tsx
{**/__tests__/**,**/*.test.{ts,tsx}}

📄 CodeRabbit inference engine (AGENTS.md)

{**/__tests__/**,**/*.test.{ts,tsx}}: If coverage on a modified file is below 80%, add meaningful tests to raise it to at least 80%
Mock external dependencies and APIs in tests

Files:

  • __tests__/components/drops/create/utils/CreateDropContent.component.test.tsx
  • __tests__/components/waves/drops/normalizeDropMarkdown.test.ts
  • __tests__/components/drops/create/utils/CreateDropWrapper.test.tsx
  • __tests__/components/CreateDropWrapper.test.tsx
  • __tests__/components/waves/drops/EditDropLexical.test.tsx
**/__tests__/**

📄 CodeRabbit inference engine (AGENTS.md)

Place tests in __tests__/ directories when organizing standalone test suites

Files:

  • __tests__/components/drops/create/utils/CreateDropContent.component.test.tsx
  • __tests__/components/waves/drops/normalizeDropMarkdown.test.ts
  • __tests__/components/drops/create/utils/CreateDropWrapper.test.tsx
  • __tests__/components/CreateDropWrapper.test.tsx
  • __tests__/components/waves/drops/EditDropLexical.test.tsx
**/*.test.tsx

📄 CodeRabbit inference engine (AGENTS.md)

When co-locating tests with components, name them ComponentName.test.tsx

Files:

  • __tests__/components/drops/create/utils/CreateDropContent.component.test.tsx
  • __tests__/components/drops/create/utils/CreateDropWrapper.test.tsx
  • __tests__/components/CreateDropWrapper.test.tsx
  • __tests__/components/waves/drops/EditDropLexical.test.tsx
🧠 Learnings (5)
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
PR: 6529-Collections/6529seize-frontend#0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Keep mock implementations minimal—only what’s necessary for the test scenarios

Applied to files:

  • __tests__/components/drops/create/utils/CreateDropContent.component.test.tsx
  • __tests__/components/drops/create/utils/CreateDropWrapper.test.tsx
  • __tests__/components/CreateDropWrapper.test.tsx
📚 Learning: 2025-10-07T14:38:41.233Z
Learnt from: CR
PR: 6529-Collections/6529seize-frontend#0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T14:38:41.233Z
Learning: Applies to {**/__tests__/**,**/*.test.{ts,tsx}} : Mock external dependencies and APIs in tests

Applied to files:

  • __tests__/components/drops/create/utils/CreateDropContent.component.test.tsx
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
PR: 6529-Collections/6529seize-frontend#0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Document non-obvious expected behaviour directly in the mock file

Applied to files:

  • __tests__/components/drops/create/utils/CreateDropContent.component.test.tsx
  • __tests__/components/drops/create/utils/CreateDropWrapper.test.tsx
  • __tests__/components/waves/drops/EditDropLexical.test.tsx
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
PR: 6529-Collections/6529seize-frontend#0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Keep mocks up to date with the real implementations they represent

Applied to files:

  • __tests__/components/drops/create/utils/CreateDropWrapper.test.tsx
  • __tests__/components/CreateDropWrapper.test.tsx
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
PR: 6529-Collections/6529seize-frontend#0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Mock only external dependencies or heavy functionality; avoid over-mocking internal logic

Applied to files:

  • __tests__/components/drops/create/utils/CreateDropWrapper.test.tsx
  • __tests__/components/waves/drops/EditDropLexical.test.tsx
🧬 Code graph analysis (5)
components/drops/create/utils/CreateDropWrapper.tsx (5)
components/waves/drops/normalizeDropMarkdown.ts (1)
  • exportDropMarkdown (80-89)
components/drops/create/lexical/transformers/markdownTransformers.ts (1)
  • SAFE_MARKDOWN_TRANSFORMERS (42-42)
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/transformers/ImageTransformer.ts (1)
  • IMAGE_TRANSFORMER (4-22)
__tests__/components/waves/drops/normalizeDropMarkdown.test.ts (1)
components/waves/drops/normalizeDropMarkdown.ts (2)
  • exportDropMarkdown (80-89)
  • normalizeDropMarkdown (91-97)
components/drops/create/utils/CreateDropContent.tsx (5)
components/waves/drops/normalizeDropMarkdown.ts (1)
  • exportDropMarkdown (80-89)
components/drops/create/lexical/transformers/markdownTransformers.ts (1)
  • SAFE_MARKDOWN_TRANSFORMERS (42-42)
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/transformers/ImageTransformer.ts (1)
  • IMAGE_TRANSFORMER (4-22)
components/waves/CreateDropContent.tsx (6)
components/waves/drops/normalizeDropMarkdown.ts (1)
  • exportDropMarkdown (80-89)
components/drops/create/lexical/transformers/markdownTransformers.ts (1)
  • SAFE_MARKDOWN_TRANSFORMERS (42-42)
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/transformers/ImageTransformer.ts (1)
  • IMAGE_TRANSFORMER (4-22)
components/drops/create/lexical/transformers/EmojiTransformer.ts (1)
  • EMOJI_TRANSFORMER (6-27)
components/waves/drops/EditDropLexical.tsx (2)
components/waves/drops/normalizeDropMarkdown.ts (2)
  • normalizeDropMarkdown (91-97)
  • exportDropMarkdown (80-89)
components/waves/drops/blankLinePlaceholders.ts (2)
  • removeBlankLinePlaceholders (19-25)
  • addBlankLinePlaceholders (4-17)
🪛 ast-grep (0.39.6)
components/waves/drops/normalizeDropMarkdown.ts

[warning] 16-19: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(
(?:${BLANK_PARAGRAPH_SENTINEL}\\n\\n)+,
"g"
)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 20-23: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(
${BLANK_PARAGRAPH_SENTINEL}(\\n?),
"g"
)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

⏰ 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/view/part/dropPartMarkdown/content.tsx (1)

248-254: LGTM! Blank line consolidation logic updated correctly.

The threshold change from 4+ to 3+ consecutive newlines and the updated filler generation align with the new markdown normalization workflow introduced in this PR.

components/waves/drops/blankLinePlaceholders.ts (1)

9-16: Verify placeholder segment formatting consistency.

The placeholder segment uses single newlines between placeholders ("\u200B\n"), while components/drops/view/part/dropPartMarkdown/content.tsx (lines 248-254) uses double newlines between fillers ("&nbsp;\n\n&nbsp;"). This difference in formatting could lead to inconsistent blank line handling.

For 5 consecutive newlines:

  • This function produces: "\n\n\u200B\n\u200B\n\u200B\n" (single \n between placeholders)
  • content.tsx produces: "\n\n&nbsp;\n\n&nbsp;\n\n&nbsp;\n\n" (double \n\n between fillers)

Confirm whether this formatting difference is intentional or if both should use the same pattern.

__tests__/components/drops/create/utils/CreateDropContent.component.test.tsx (1)

31-36: LGTM! Mock implementation follows testing guidelines.

The mock is minimal and appropriate for isolating the component under test.

Based on learnings

__tests__/components/drops/create/utils/CreateDropWrapper.test.tsx (1)

19-25: LGTM! Mock implementation is appropriate.

The mock setup correctly isolates the normalization module for testing purposes.

Based on learnings

components/drops/create/utils/CreateDropContent.tsx (2)

72-74: LGTM! Import added correctly.

The import for exportDropMarkdown from the new normalization module is appropriate.


198-209: LGTM! Markdown generation migrated to new normalization workflow.

The replacement of Lexical's direct markdown conversion with exportDropMarkdown aligns with the PR's objective to centralize markdown normalization.

__tests__/components/CreateDropWrapper.test.tsx (1)

22-27: LGTM! Mock returns non-empty string for length checks.

The mock's return value of 'text' is appropriate for testing length-based logic in the wrapper component.

Based on learnings

components/drops/create/utils/CreateDropWrapper.tsx (2)

40-42: LGTM! Import added correctly.

The import for exportDropMarkdown from the normalization module is appropriate.


199-206: LGTM! Markdown generation updated consistently.

The migration to exportDropMarkdown is consistent with other components in this PR and correctly delegates to the new normalization workflow.

components/waves/CreateDropContent.tsx (2)

61-61: LGTM! Import added correctly.

The import for exportDropMarkdown is appropriate for the new normalization workflow.


489-501: LGTM! Markdown computation updated with emoji support.

The migration to exportDropMarkdown includes EMOJI_TRANSFORMER in the transformers array, which is appropriate for wave content. The memoization on editorState ensures efficient recomputation.

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

57-61: LGTM!

The imports correctly bring in the new normalization pipeline utilities and follow the coding guidelines.


354-361: LGTM!

The memoization correctly:

  • Normalizes initial content once
  • Adds placeholders for the editor display
  • Has proper dependency arrays

413-433: LGTM!

The save handler correctly:

  • Exports markdown using the new pipeline
  • Removes placeholders before saving
  • Compares sanitized values to detect actual changes
  • Includes all necessary dependencies
components/waves/drops/normalizeDropMarkdown.ts (3)

17-24: ReDoS risk is not applicable here (false positive).

Static analysis flagged potential ReDoS vulnerabilities, but these regex patterns are constructed from constant sentinel values (lines 14-16) that don't accept user input. The patterns are safe.

However, to verify the regex correctness and performance characteristics, you could add complexity comments or extract the patterns into named constants for clarity:

+const BLANK_RUN_PATTERN = `(?:${BLANK_PARAGRAPH_SENTINEL}\\n\\n)+`;
+const BLANK_PARAGRAPH_TOKEN_PATTERN = `${BLANK_PARAGRAPH_SENTINEL}(\\n?)`;
+
 const BLANK_RUN_REGEX = new RegExp(
-  `(?:${BLANK_PARAGRAPH_SENTINEL}\\n\\n)+`,
+  BLANK_RUN_PATTERN,
   "g"
 );
 const BLANK_PARAGRAPH_TOKEN_REGEX = new RegExp(
-  `${BLANK_PARAGRAPH_SENTINEL}(\\n?)`,
+  BLANK_PARAGRAPH_TOKEN_PATTERN,
   "g"
 );

26-51: LGTM!

The blank paragraph detection correctly handles all edge cases including line breaks, text nodes with zero-width spaces, and empty paragraphs.


80-97: LGTM!

Both functions correctly implement the normalization pipeline:

  • exportDropMarkdown integrates the blank paragraph transformer and collapses markers
  • normalizeDropMarkdown handles line ending normalization with proper empty-value checks
__tests__/components/waves/drops/EditDropLexical.test.tsx (3)

165-177: LGTM!

The mock correctly:

  • Provides all exports from the module (default, named exports)
  • Uses passthrough for normalizeDropMarkdown (identity function)
  • Provides a controlled return value for exportDropMarkdown

This follows the coding guidelines for mocking external dependencies.

As per coding guidelines.


209-217: LGTM!

The beforeEach setup correctly initializes the new mocks with appropriate default behavior before each test.


265-299: LGTM!

The test correctly verifies that the keyboard command handlers call exportDropMarkdown in the new pipeline instead of the old Lexical conversion function.

Comment thread components/waves/drops/blankLinePlaceholders.ts
ragnep
ragnep previously approved these changes Oct 10, 2025
Signed-off-by: Simo <simo@6529.io>
@sonarqubecloud
Copy link
Copy Markdown

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 (3)
__tests__/components/waves/drops/normalizeDropMarkdown.test.ts (3)

12-12: Consider importing the sentinel constant.

The BLANK_PARAGRAPH_SENTINEL is duplicated from the implementation. If this constant is also defined in the source file or a shared location, import it to avoid maintenance issues when the sentinel value changes.


33-64: Add test cases with non-empty transformers.

All test cases pass an empty transformers array. Add at least one test case with actual transformers to verify they are correctly passed to $convertToMarkdownString.

Example test case:

it("passes transformers to markdown conversion", () => {
  const mockTransformer = { type: "text" } as any;
  convertToMarkdownStringMock.mockReturnValue("Content");
  
  exportDropMarkdown(editorState, [mockTransformer]);
  
  expect(convertToMarkdownStringMock).toHaveBeenCalledWith(
    expect.arrayContaining([mockTransformer])
  );
});

33-64: Add assertions to verify mock calls.

The tests don't verify that $convertToMarkdownString was called. Add assertions to confirm the mock was invoked with the expected parameters.

Apply this pattern to existing tests:

 it("returns markdown unchanged when no blank markers are present", () => {
   convertToMarkdownStringMock.mockReturnValue("First\n\nSecond");
   expect(exportDropMarkdown(editorState, [])).toBe("First\n\nSecond");
+  expect(convertToMarkdownStringMock).toHaveBeenCalledTimes(1);
 });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 62657c2 and 64fbbff.

📒 Files selected for processing (1)
  • __tests__/components/waves/drops/normalizeDropMarkdown.test.ts (1 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

Follow existing code style and naming conventions

Files:

  • __tests__/components/waves/drops/normalizeDropMarkdown.test.ts
__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/waves/drops/normalizeDropMarkdown.test.ts
__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/waves/drops/normalizeDropMarkdown.test.ts
{**/__tests__/**,**/*.test.{ts,tsx}}

📄 CodeRabbit inference engine (AGENTS.md)

{**/__tests__/**,**/*.test.{ts,tsx}}: If coverage on a modified file is below 80%, add meaningful tests to raise it to at least 80%
Mock external dependencies and APIs in tests

Files:

  • __tests__/components/waves/drops/normalizeDropMarkdown.test.ts
**/__tests__/**

📄 CodeRabbit inference engine (AGENTS.md)

Place tests in __tests__/ directories when organizing standalone test suites

Files:

  • __tests__/components/waves/drops/normalizeDropMarkdown.test.ts
🧬 Code graph analysis (1)
__tests__/components/waves/drops/normalizeDropMarkdown.test.ts (1)
components/waves/drops/normalizeDropMarkdown.ts (2)
  • exportDropMarkdown (80-89)
  • normalizeDropMarkdown (91-97)
⏰ 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 (1)
__tests__/components/waves/drops/normalizeDropMarkdown.test.ts (1)

20-23: LGTM!

The EditorState stub is appropriately minimal for unit testing the read() method behavior.

Comment thread __tests__/components/waves/drops/normalizeDropMarkdown.test.ts
@simo6529 simo6529 merged commit d8e9d5d into main Oct 10, 2025
10 checks passed
@simo6529 simo6529 deleted the format-text branch October 10, 2025 08:02
@coderabbitai coderabbitai Bot mentioned this pull request Oct 28, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Nov 6, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Jan 12, 2026
@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