Skip to content

fix: improve editor UI#347

Merged
vitonsky merged 22 commits into
masterfrom
327-improve-editor-ui
Jul 12, 2026
Merged

fix: improve editor UI#347
vitonsky merged 22 commits into
masterfrom
327-improve-editor-ui

Conversation

@vitonsky

@vitonsky vitonsky commented Jul 6, 2026

Copy link
Copy Markdown
Member

Fix #327

We have problem with follows text

# Why Russian Classics Feel So Different
I've noticed that after finishing a Russian novel I rarely want to start another book immediately.

It's not because the stories are difficult. It's because they seem to leave unfinished conversations in my head. Instead of remembering the plot, I keep thinking about the questions the characters were asking themselves.

It leaves empty line as a paragraph, so we have 3 paragraphs in result.
When we remove that paragraph, we have 2 paragraph in Lexical, but only one paragraph in Markdown, so when MD converts back into Lexical we have only one paragraph.

Summary by CodeRabbit

Summary

  • New Features

    • Added an optional editor reference you can pass in to access the underlying editor instance.
  • Bug Fixes

    • Improved global text-selection styling consistency.
    • Strengthened Rich Text Editor Markdown handling to better preserve empty paragraphs and blank-line separation.
    • Improved code block indentation/highlighting consistency.
  • Style

    • Refreshed Rich Text Editor spacing, typography, and layout (paragraphs, headings, quotes, code, lists, tables, embeds, and separators) using a more consistent tokenized system.
    • Updated code/link styling to match theme tokens.
  • Chores / Tests

    • Expanded environment/config examples and refreshed the Vitest browser/Playwright testing setup; added/updated Rich Editor Markdown round-trip and serialization tests.
    • Updated ignore rules and bumped Rich Text editor dependencies.

@vitonsky vitonsky linked an issue Jul 6, 2026 that may be closed by this pull request
4 tasks
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@vitonsky, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 40 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 55e41061-e2c1-45e4-8fdd-a4dd2c7bbed1

📥 Commits

Reviewing files that changed from the base of the PR and between 2d653e7 and 995e814.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/__tests__/__snapshots__/markdown-roundtrips-consistency.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (29)
  • .github/workflows/codeql.yml
  • packages/app/.env.example
  • packages/app/.gitignore
  • packages/app/README.md
  • packages/app/package.json
  • packages/app/src/components/theme/base.ts
  • packages/app/src/features/NoteEditor/RichEditor/RichEditor.tsx
  • packages/app/src/features/NoteEditor/RichEditor/RichEditorContent.tsx
  • packages/app/src/features/NoteEditor/RichEditor/RichTextContainer.tsx
  • packages/app/src/features/NoteEditor/RichEditor/__tests__/spec/image.dom.test.ts
  • packages/app/src/features/NoteEditor/RichEditor/__tests__/utils/renderEditorInDOM.tsx
  • packages/app/src/features/NoteEditor/RichEditor/__tests__/utils/renderRichEditor.tsx
  • packages/app/src/features/NoteEditor/RichEditor/plugins/CodeHighlightPlugin.tsx
  • packages/app/src/features/NoteEditor/RichEditor/plugins/EditorPanelPlugin/EditorPanelPlugin.tsx
  • packages/app/src/features/NoteEditor/RichEditor/plugins/EditorPanelPlugin/utils/tree.tsx
  • packages/app/src/features/NoteEditor/RichEditor/plugins/KeyboardControlsPlugin/KeyboardControlsPlugin.ts
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/__tests__/markdown-roundtrips-consistency.test.ts
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/__tests__/markdown-samples/index.ts
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/__tests__/paragraphs.browser.test.ts
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/__tests__/utils.ts
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/convertLexicalNodeToMarkdownNode.ts
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/markdownParser.ts
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/remark/remarkLiftFormatting.ts
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/remark/remarkPreserveBlankLines.test.ts
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/remark/remarkPreserveBlankLines.ts
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/serializeMarkdownTree.test.ts
  • packages/app/src/features/NoteEditor/RichEditor/theme/RichEditor.css
  • packages/app/tsconfig.json
  • packages/app/vitest.config.mts
📝 Walkthrough

Walkthrough

This PR updates RichEditor styling, Lexical imports and editor ref access, Markdown blank-line parsing/serialization, and Vitest browser test setup, along with related environment, package, TypeScript, and CI configuration.

Changes

RichEditor and test infrastructure updates

Layer / File(s) Summary
Theme and editor styling
packages/app/src/components/theme/base.ts, packages/app/src/features/NoteEditor/RichEditor/theme/RichEditor.css, packages/app/src/features/NoteEditor/RichEditor/RichTextContainer.tsx
Changes selection styling and rewrites RichEditor spacing, layout, list, table, code, link, and block styles around shared tokens.
Editor ref and Lexical wiring
packages/app/src/features/NoteEditor/RichEditor/RichEditor.tsx, packages/app/src/features/NoteEditor/RichEditor/RichEditorContent.tsx, packages/app/src/features/NoteEditor/RichEditor/plugins/*
Exposes the Lexical editor through editorRef and switches code-node integrations to @lexical/code-core.
Markdown parsing and serialization
packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/*
Updates blank-line preservation, empty-paragraph handling, Markdown serialization, and round-trip coverage.
Browser test harness and configuration
packages/app/src/features/NoteEditor/RichEditor/__tests__/*, packages/app/vitest.config.mts, packages/app/package.json, packages/app/.env.example, .github/workflows/codeql.yml
Adds DOM/browser rendering helpers and tests, Vitest browser projects, Playwright setup, environment settings, and CI installation steps.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant RichEditor
  participant MarkdownParser
  participant BrowserTests
  User->>RichEditor: Type paragraphs and empty lines
  RichEditor->>MarkdownParser: Serialize editor document
  MarkdownParser-->>RichEditor: Markdown with preserved paragraph gaps
  BrowserTests->>RichEditor: Render editor and inspect paragraphs
  BrowserTests->>MarkdownParser: Parse serialized Markdown
  MarkdownParser-->>BrowserTests: AST with matching paragraph nodes
Loading
🚥 Pre-merge checks | ✅ 1 | ❌ 4

❌ Failed checks (2 warnings, 2 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR adds substantial Markdown/parser, testing, config, dependency, and workflow work that is unrelated to issue #327. Move the Markdown/parser, test setup, and tooling changes into separate PRs so this one stays focused on the editor UI fixes.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is related to the editor work, but it is too generic to clearly describe the main change. Use a more specific title that names the primary change, such as the Markdown blank-line fix or the rich editor UI styling updates.
Linked Issues check ❓ Inconclusive The summary shows link and selection fixes, but separator coloring is not clearly evidenced, so full compliance cannot be confirmed. Verify or implement a visible non-transparent separator color alongside the link, header, and selection styling changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 327-improve-editor-ui

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/app/src/features/NoteEditor/RichEditor/theme/RichEditor.css`:
- Line 105: The RichEditor theme is using color tokens that point at the parent
variables instead of the actual leaf values, so the text and link colors won’t
resolve correctly. Update the color references in the RichEditor CSS to use the
`-base` Chakra variables for the typography and link tokens, and verify the
affected styles in the RichEditor stylesheet still point to the correct token
names.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a178e3f1-68ce-4815-a0cd-12fef1dcdd63

📥 Commits

Reviewing files that changed from the base of the PR and between 2d653e7 and 19539a2.

📒 Files selected for processing (2)
  • packages/app/src/components/theme/base.ts
  • packages/app/src/features/NoteEditor/RichEditor/theme/RichEditor.css

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (7)
packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/remark/remarkPreserveBlankLines.ts (2)

41-48: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Inserted empty paragraph positions use offset: 0 which is inaccurate.

The offset field is set to 0 for all inserted empty paragraphs regardless of their actual position in the source. While this doesn't affect serialization (which only uses line), it could cause issues if any downstream code relies on offset for source mapping or error reporting.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/remark/remarkPreserveBlankLines.ts`
around lines 41 - 48, The inserted empty paragraph in remarkPreserveBlankLines
uses a fixed offset of 0, which makes its source position inaccurate. Update the
emptyLine position object so the offset is derived from the actual blank line
location instead of a constant, and keep the line/column mapping consistent with
the surrounding logic in remarkPreserveBlankLines. Use the existing line-based
position construction to locate where the paragraph is created and adjust both
start and end offsets accordingly.

65-67: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unused remarkPreserveBlankLines wrapper. markdownParser.ts already calls fillGapsWithParagraphs directly, so this plugin wrapper has no remaining references.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/remark/remarkPreserveBlankLines.ts`
around lines 65 - 67, The remarkPreserveBlankLines plugin wrapper is now unused
because markdownParser already invokes fillGapsWithParagraphs directly. Remove
the remarkPreserveBlankLines export/wrapper from remarkPreserveBlankLines.ts,
and update any remaining imports or references so the codebase uses
fillGapsWithParagraphs directly without the extra Plugin wrapper.
packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/markdownParser.ts (1)

58-71: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant children.length check in join logic.

hasChildren already verifies children.length > 0, so when !hasChildren(left) is false (meaning hasChildren returned true), left.children.length === 0 can never be true. The second condition is dead code.

♻️ Simplify empty-paragraph checks
 			(left, right) => {
 				if (left.type === 'paragraph' || right.type === 'paragraph') {
-					const isLeftEmpty = !hasChildren(left) || left.children.length === 0;
-					const isRightEmpty =
-						!hasChildren(right) || right.children.length === 0;
+					const isLeftEmpty = !hasChildren(left);
+					const isRightEmpty = !hasChildren(right);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/markdownParser.ts`
around lines 58 - 71, The join logic in markdownParser’s paragraph handling has
a redundant empty-check because hasChildren already guarantees children exist,
so the extra children.length comparison is dead code. Simplify the left/right
emptiness checks inside the join callback by relying only on hasChildren for the
paragraph nodes, and keep the early return 0 behavior for empty paragraphs while
preserving the existing null fallback.
packages/app/src/features/NoteEditor/RichEditor/__tests__/utils/renderRichEditor.tsx (1)

142-144: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add null guard to getEditor() for consistency with renderEditorInDOM.tsx.

renderEditorInDOM.tsx throws a descriptive error when editorRef.current is null, but this version returns it directly. A caller that invokes getEditor().read(...) before the editor is mounted would get a confusing TypeError: Cannot read properties of null instead of a clear message.

♻️ Proposed refactor
 getEditor() {
-    return editorRef.current;
+    const editor = editorRef.current;
+    if (!editor) throw new Error('Editor instance is not set');
+    return editor;
 },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/app/src/features/NoteEditor/RichEditor/__tests__/utils/renderRichEditor.tsx`
around lines 142 - 144, The getEditor helper currently returns editorRef.current
directly, which can surface a null TypeError if called before mount. Update
getEditor() in renderRichEditor to match the null handling used by
renderEditorInDOM.tsx: check editorRef.current, and throw a descriptive error
when it is missing before returning the editor instance. Keep the behavior
consistent for callers like getEditor().read(...) by ensuring the method always
either returns a valid editor or fails with a clear message.
packages/app/src/features/NoteEditor/RichEditor/plugins/CodeHighlightPlugin.tsx (1)

8-10: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Syntax highlighting is disabled — track the TODO.

The plugin now registers only registerCodeIndentation instead of registerCodeHighlighting, meaning code blocks lose syntax highlighting. The TODO at line 8 acknowledges this. Since registerCodeHighlighting requires Prism.js (a browser-specific dependency not available in @lexical/code-core), re-adding it may require importing from @lexical/code separately or adding a Prism dependency.

Do you want me to help re-introduce registerCodeHighlighting alongside registerCodeIndentation, or open an issue to track this?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/app/src/features/NoteEditor/RichEditor/plugins/CodeHighlightPlugin.tsx`
around lines 8 - 10, The CodeHighlightPlugin currently only calls
registerCodeIndentation in its useEffect, so syntax highlighting is no longer
being registered; update this plugin to also restore registerCodeHighlighting as
noted by the existing TODO. Locate the change in CodeHighlightPlugin and wire
the highlighting registration back in alongside registerCodeIndentation, using
the appropriate browser-side Prism-backed implementation if needed rather than
the `@lexical/code-core-only` path.
packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/remark/remarkLiftFormatting.ts (1)

30-37: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Type guard return type is too narrow for RootContent input.

hasChildren now accepts RootContent but still narrows to PhrasingContent & { children: PhrasingContent[] }. A RootContent node like table has children but is not PhrasingContent, so the guard is semantically incorrect for non-phrasing inputs. This doesn't cause runtime issues in current callers (the join handler only accesses children.length, and flattenToLeaf passes PhrasingContent), but it could mislead future consumers.

♻️ Proposed fix — use a broader type guard
 export function hasChildren(
 	node: RootContent,
-): node is PhrasingContent & { children: PhrasingContent[] } {
+): node is RootContent & { children: RootContent[] } {
 	return (
 		'children' in node &&
 		Array.isArray((node as { children?: unknown }).children) &&
 		(node as { children: unknown[] }).children.length > 0
 	);
 }

Then update flattenToLeaf to cast node.children to PhrasingContent[] when needed, since the input there is already PhrasingContent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/remark/remarkLiftFormatting.ts`
around lines 30 - 37, The `hasChildren` type guard is too narrow for its
`RootContent` parameter because it claims the node is `PhrasingContent` even
when broader root nodes like `table` can match. Broaden the return type in
`hasChildren` to reflect any `RootContent` with a `children` array, then adjust
`flattenToLeaf` to explicitly cast `node.children` to `PhrasingContent[]` where
that phrasing-specific assumption is valid. Keep the existing runtime check
logic and update only the typings around `hasChildren` and `flattenToLeaf`.
packages/app/src/features/NoteEditor/RichEditor/RichEditorContent.tsx (1)

72-72: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add dependency array to useImperativeHandle.

Without deps, editorRef.current = editor is reassigned on every render. Since editor from useLexicalComposerContext is stable, adding [editor] avoids unnecessary work.

♻️ Proposed fix
-	useImperativeHandle(editorRef, () => editor);
+	useImperativeHandle(editorRef, () => editor, [editor]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app/src/features/NoteEditor/RichEditor/RichEditorContent.tsx` at
line 72, The useImperativeHandle call in RichEditorContent is missing a
dependency array, causing editorRef to be reassigned on every render. Update the
useImperativeHandle invocation to depend on editor from
useLexicalComposerContext, so the handle is only refreshed when the editor
instance changes. This should be applied in RichEditorContent where editorRef is
wired to editor.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/app/src/features/NoteEditor/RichEditor/__tests__/utils/renderEditorInDOM.tsx`:
- Around line 72-106: The renderEditorInDOM helper is missing translation
context, so RichEditor plugins that call useTranslation can fail in DOM tests.
Update the renderEditor function to wrap RichEditor with I18nextProvider or the
app’s LocalesProvider, alongside the existing Provider, ThemeProvider, and
MockWorkspaceProvider. Keep the change localized to renderEditorInDOM and use
the existing editorPanelContext and RichEditor symbols to locate the wrapper.

In
`@packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/remark/remarkPreserveBlankLines.test.ts`:
- Around line 20-24: The test in remarkPreserveBlankLines.test.ts has the wrong
expectation for fillGapsWithParagraphs when parsing Hello\n\n\nWorld; update the
assertion in the “Must add 1 paragraph for gap in 2 lines” case to expect 4
children instead of 3 so it matches the actual blank-line preservation behavior
and round-trip consistency.
- Around line 26-30: The expectation in the fillGapsWithParagraphs test is wrong
for the 10-blank-line case. Update the assertion in
remarkPreserveBlankLines.test so the expected children count matches the actual
behavior of fillGapsWithParagraphs and markdownProcessor.parse for a 10-line
gap, using the existing test name and helper function references to locate it.
Keep the test aligned with round-trip preservation so it asserts the full
paragraph count produced by the parser/fillGapsWithParagraphs flow.

In `@packages/app/src/features/NoteEditor/RichEditor/theme/RichEditor.css`:
- Line 148: Update the RichEditor.css color tokens that currently reference
var(--chakra-colors-typography) and var(--chakra-colors-link) to use the
matching base leaves instead. In the RichEditor styles where the editor text and
link styling are defined, switch those declarations to
var(--chakra-colors-typography-base) and var(--chakra-colors-link-base) so the
CSS resolves correctly and the intended editor/link colors are applied.

In `@packages/app/tsconfig.json`:
- Line 36: The Vitest test type setup is using the root
`@testing-library/jest-dom` types, which only target Jest matchers. Update the
types entry in the app tsconfig to use `@testing-library/jest-dom/vitest`
alongside vitest/globals so Vitest files keep the correct matcher typings,
including expect.element and the jest-dom matchers.

---

Nitpick comments:
In
`@packages/app/src/features/NoteEditor/RichEditor/__tests__/utils/renderRichEditor.tsx`:
- Around line 142-144: The getEditor helper currently returns editorRef.current
directly, which can surface a null TypeError if called before mount. Update
getEditor() in renderRichEditor to match the null handling used by
renderEditorInDOM.tsx: check editorRef.current, and throw a descriptive error
when it is missing before returning the editor instance. Keep the behavior
consistent for callers like getEditor().read(...) by ensuring the method always
either returns a valid editor or fails with a clear message.

In
`@packages/app/src/features/NoteEditor/RichEditor/plugins/CodeHighlightPlugin.tsx`:
- Around line 8-10: The CodeHighlightPlugin currently only calls
registerCodeIndentation in its useEffect, so syntax highlighting is no longer
being registered; update this plugin to also restore registerCodeHighlighting as
noted by the existing TODO. Locate the change in CodeHighlightPlugin and wire
the highlighting registration back in alongside registerCodeIndentation, using
the appropriate browser-side Prism-backed implementation if needed rather than
the `@lexical/code-core-only` path.

In
`@packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/markdownParser.ts`:
- Around line 58-71: The join logic in markdownParser’s paragraph handling has a
redundant empty-check because hasChildren already guarantees children exist, so
the extra children.length comparison is dead code. Simplify the left/right
emptiness checks inside the join callback by relying only on hasChildren for the
paragraph nodes, and keep the early return 0 behavior for empty paragraphs while
preserving the existing null fallback.

In
`@packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/remark/remarkLiftFormatting.ts`:
- Around line 30-37: The `hasChildren` type guard is too narrow for its
`RootContent` parameter because it claims the node is `PhrasingContent` even
when broader root nodes like `table` can match. Broaden the return type in
`hasChildren` to reflect any `RootContent` with a `children` array, then adjust
`flattenToLeaf` to explicitly cast `node.children` to `PhrasingContent[]` where
that phrasing-specific assumption is valid. Keep the existing runtime check
logic and update only the typings around `hasChildren` and `flattenToLeaf`.

In
`@packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/remark/remarkPreserveBlankLines.ts`:
- Around line 41-48: The inserted empty paragraph in remarkPreserveBlankLines
uses a fixed offset of 0, which makes its source position inaccurate. Update the
emptyLine position object so the offset is derived from the actual blank line
location instead of a constant, and keep the line/column mapping consistent with
the surrounding logic in remarkPreserveBlankLines. Use the existing line-based
position construction to locate where the paragraph is created and adjust both
start and end offsets accordingly.
- Around line 65-67: The remarkPreserveBlankLines plugin wrapper is now unused
because markdownParser already invokes fillGapsWithParagraphs directly. Remove
the remarkPreserveBlankLines export/wrapper from remarkPreserveBlankLines.ts,
and update any remaining imports or references so the codebase uses
fillGapsWithParagraphs directly without the extra Plugin wrapper.

In `@packages/app/src/features/NoteEditor/RichEditor/RichEditorContent.tsx`:
- Line 72: The useImperativeHandle call in RichEditorContent is missing a
dependency array, causing editorRef to be reassigned on every render. Update the
useImperativeHandle invocation to depend on editor from
useLexicalComposerContext, so the handle is only refreshed when the editor
instance changes. This should be applied in RichEditorContent where editorRef is
wired to editor.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 96dd503a-85ed-45ed-8b6c-b8bbb446545d

📥 Commits

Reviewing files that changed from the base of the PR and between 19539a2 and e0fd1e0.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/__tests__/__snapshots__/markdown-roundtrips-consistency.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (24)
  • packages/app/.env.example
  • packages/app/.gitignore
  • packages/app/package.json
  • packages/app/src/features/NoteEditor/RichEditor/RichEditor.tsx
  • packages/app/src/features/NoteEditor/RichEditor/RichEditorContent.tsx
  • packages/app/src/features/NoteEditor/RichEditor/__tests__/utils/renderEditorInDOM.tsx
  • packages/app/src/features/NoteEditor/RichEditor/__tests__/utils/renderRichEditor.tsx
  • packages/app/src/features/NoteEditor/RichEditor/plugins/CodeHighlightPlugin.tsx
  • packages/app/src/features/NoteEditor/RichEditor/plugins/EditorPanelPlugin/EditorPanelPlugin.tsx
  • packages/app/src/features/NoteEditor/RichEditor/plugins/EditorPanelPlugin/utils/tree.tsx
  • packages/app/src/features/NoteEditor/RichEditor/plugins/KeyboardControlsPlugin/KeyboardControlsPlugin.ts
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/__tests__/markdown-roundtrips-consistency.test.ts
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/__tests__/markdown-samples/index.ts
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/__tests__/paragraphs.browser.test.ts
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/__tests__/utils.ts
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/convertLexicalNodeToMarkdownNode.ts
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/markdownParser.ts
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/remark/remarkLiftFormatting.ts
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/remark/remarkPreserveBlankLines.test.ts
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/remark/remarkPreserveBlankLines.ts
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/serializeMarkdownTree.test.ts
  • packages/app/src/features/NoteEditor/RichEditor/theme/RichEditor.css
  • packages/app/tsconfig.json
  • packages/app/vitest.config.mts
✅ Files skipped from review due to trivial changes (4)
  • packages/app/src/features/NoteEditor/RichEditor/plugins/EditorPanelPlugin/utils/tree.tsx
  • packages/app/.gitignore
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/tests/markdown-samples/index.ts
  • packages/app/src/features/NoteEditor/RichEditor/plugins/Markdown/tests/utils.ts

Comment thread packages/app/tsconfig.json

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (1)
packages/app/src/features/NoteEditor/RichEditor/RichTextContainer.tsx (1)

22-22: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Place className after {...props} to prevent silent override.

If a consumer passes className via props, the spread at line 23 will overwrite "RichEditor", removing the --re-* CSS custom properties that all editor spacing depends on. Moving className after the spread (or merging) ensures the base class is always present.

♻️ Proposed fix
 					<Box
 						w="100%"
 						maxH="100%"
 						outline="none"
 						padding="1rem 1rem 5rem"
 						overflow="auto"
-						className="RichEditor"
 						{...props}
+						className="RichEditor"
 						asChild
 					>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app/src/features/NoteEditor/RichEditor/RichTextContainer.tsx` at
line 22, Ensure the base “RichEditor” class is always retained in the
RichTextContainer element by placing className after the {...props} spread or
merging the consumer-provided className with it. Update the JSX props order near
the RichEditor class without changing other prop behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/app/src/features/NoteEditor/RichEditor/theme/RichEditor.css`:
- Around line 510-528: Update the color variable references at the noted
RichEditor.css locations to use the defined typography.base and link.base
tokens, replacing the non-existent typography and link variables while
preserving link hover styling.

---

Nitpick comments:
In `@packages/app/src/features/NoteEditor/RichEditor/RichTextContainer.tsx`:
- Line 22: Ensure the base “RichEditor” class is always retained in the
RichTextContainer element by placing className after the {...props} spread or
merging the consumer-provided className with it. Update the JSX props order near
the RichEditor class without changing other prop behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1d487325-8629-470e-85d8-7de8c499217a

📥 Commits

Reviewing files that changed from the base of the PR and between 3e573fc and 436558d.

📒 Files selected for processing (4)
  • .github/workflows/codeql.yml
  • packages/app/src/features/NoteEditor/RichEditor/RichEditorContent.tsx
  • packages/app/src/features/NoteEditor/RichEditor/RichTextContainer.tsx
  • packages/app/src/features/NoteEditor/RichEditor/theme/RichEditor.css
💤 Files with no reviewable changes (1)
  • packages/app/src/features/NoteEditor/RichEditor/RichEditorContent.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/codeql.yml (1)

25-59: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Restrict the test job’s GITHUB_TOKEN permissions.

Without an explicit permissions block, this job inherits repository/org defaults while executing repository-controlled install, test, and build commands. Set the minimum required permissions, such as contents: read, and grant additional permissions only to jobs that need them.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/codeql.yml around lines 25 - 59, Add a job-level
permissions block to the tests job, granting only contents: read for checkout
and repository access. Do not grant write or unrelated permissions; leave
additional permissions to jobs that explicitly require them.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In @.github/workflows/codeql.yml:
- Around line 25-59: Add a job-level permissions block to the tests job,
granting only contents: read for checkout and repository access. Do not grant
write or unrelated permissions; leave additional permissions to jobs that
explicitly require them.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: da71a8cc-45f4-4adc-afb3-d88bf636ac8b

📥 Commits

Reviewing files that changed from the base of the PR and between 436558d and 96f8f18.

📒 Files selected for processing (3)
  • .github/workflows/codeql.yml
  • packages/app/README.md
  • packages/app/src/features/NoteEditor/RichEditor/theme/RichEditor.css
💤 Files with no reviewable changes (1)
  • packages/app/src/features/NoteEditor/RichEditor/theme/RichEditor.css

@vitonsky

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.


Your plan includes PR reviews subject to rate limits. More reviews will be available in 40 minutes.

@vitonsky vitonsky merged commit 2efb7ed into master Jul 12, 2026
6 checks passed
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.

Improve editor UI

1 participant