Fallback link-preview UI so links show actions even without metadata
Clickable external-link action added to link thumbnails
Image-block support in the markdown composer (uploads disabled) and improved editor autofocus behavior
Composer now auto-switches to card mode for long text, attachments, or detected markdown; adaptive immersive text sizing
Bug Fixes
Improved clipboard paste handling for file-only pastes
Style
Enhanced markdown editor and message-card styling, scrollbars, and layout variables
Closes #267
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit
* **New Features**
* Fallback link-preview UI so links show actions even without metadata
* Clickable external-link action added to link thumbnails
* Image-block support in the markdown composer (uploads disabled) and improved editor autofocus behavior
* Composer now auto-switches to card mode for long text, attachments, or detected markdown; adaptive immersive text sizing
* **Bug Fixes**
* Improved clipboard paste handling for file-only pastes
* **Style**
* Enhanced markdown editor and message-card styling, scrollbars, and layout variables
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Enables Milkdown ImageBlock (uploads rejected), constrains image block height, and changes autofocus to dispatch an editor action moving cursor to document end.
Text editor card-mode state refactoring js/desktop/src/features/compose/text-editor.tsx
Replaces forceCardMode with carded state; auto-switches to card mode when length, attachments, or markdown formatting indicate; updates shortcut and adds entry animations.
Link preview fallback UI and integration js/desktop/src/components/link-preview-card.tsx, js/desktop/src/features/compose/attachment-strip.tsx, js/desktop/src/features/particles/text-particle-view.tsx
Adds LinkPreviewCardFallback and uses it where metadata is missing so links remain visible and actionable (open/copy).
Registers paste listener in capture phase; intercepts only file-only pastes and calls stopPropagation() after preventing default before forwarding files.
Immersive text sizing helper js/desktop/src/lib/immersive-text.ts
Adds getImmersiveTextStyle(length) to centralize size/weight choices for immersive text display.
🎯 3 (Moderate) | ⏱️ ~25 minutes
"🐰
I hop through links when metadata's gone,
I pad the editor until the caret's dawn.
Paste gently, focus to the doc's last line,
Carded modes and fallbacks light the sign.
Compose is steady — nibble, copy, open, shine."
🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name
Status
Explanation
Resolution
Docstring Coverage
⚠️ Warning
Docstring coverage is 21.43% 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 'fix: text compose papercuts' is vague and generic, using the non-descriptive term 'papercuts' without clearly conveying what specific issues are being addressed in the compose functionality.
Consider a more specific title that mentions key fixes, such as 'fix: text editor padding, cursor placement, and link fallback rendering' or similar to better convey the scope of changes.
✅ Passed checks (3 passed)
Check name
Status
Explanation
Description Check
✅ Passed
Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check
✅ Passed
The pull request successfully addresses all seven coding requirements from issue #267: padding fixes (CSS variables and styles), cursor placement at document end, heading size reduction, width consistency via shared styling variables, improved paste handling via file-only detection, and link metadata fallback rendering.
Out of Scope Changes check
✅ Passed
All changes are directly related to fixing text compose issues specified in issue #267. Supporting additions like the Milkdown library dependency and ImageBlock configuration are necessary to implement the required fixes and are not out of scope.
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing Touches📝 Generate docstrings
Create stacked PR
Commit on current branch
🧪 Generate unit tests (beta)
Create PR with unit tests
Commit unit tests in branch fix-text-compose-papercuts
Comment @coderabbitai help to get the list of available commands and usage tips.
<!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/flowy-live/llink/pull/270?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- walkthrough_start -->
<details>
<summary>📝 Walkthrough</summary>
## Walkthrough
Refactors desktop compose: adds URL/markdown helpers, ensures link-preview fallbacks, enables Milkdown ImageBlock (uploads disabled), improves autofocus, replaces forceCardMode with session-latched carded logic, refines paste handling, and updates editor/card styling.
## Changes
**Compose Editor Improvements**
|Layer / File(s)|Summary|
|---|---|
|**Utility helpers for URL and markdown detection** <br> `js/desktop/src/lib/link-metadata.ts`, `js/desktop/src/lib/markdown.ts`, `js/desktop/package.json`|New `domainFromUrl` extracts normalized hostnames; `hasMarkdownFormatting` detects markdown via regex. `@milkdown/kit` added (7.21.1).|
|**Markdown editor and global styling** <br> `js/desktop/src/features/compose/markdown-editor.css`, `js/desktop/src/styles/globals.css`|Sets ProseMirror caret color, heading typography overrides, hides Milkdown image uploader UI, suppresses read-only image controls, and adds `.scrollbar-card` styling plus layout CSS variables.|
|**Markdown editor ImageBlock & focus behavior** <br> `js/desktop/src/features/compose/markdown-editor.tsx`|Enables Milkdown `ImageBlock` (uploads rejected), constrains image block height, and changes autofocus to dispatch an editor action moving cursor to document end.|
|**Text editor card-mode state refactoring** <br> `js/desktop/src/features/compose/text-editor.tsx`|Replaces `forceCardMode` with `carded` state; auto-switches to card mode when length, attachments, or markdown formatting indicate; updates shortcut and adds entry animations.|
|**Link preview fallback UI and integration** <br> `js/desktop/src/components/link-preview-card.tsx`, `js/desktop/src/features/compose/attachment-strip.tsx`, `js/desktop/src/features/particles/text-particle-view.tsx`|Adds `LinkPreviewCardFallback` and uses it where metadata is missing so links remain visible and actionable (open/copy).|
|**Paste event capture-phase handling** <br> `js/desktop/src/hooks/use-file-input.ts`|Registers paste listener in capture phase; intercepts only file-only pastes and calls stopPropagation() after preventing default before forwarding files.|
|**Immersive text sizing helper** <br> `js/desktop/src/lib/immersive-text.ts`|Adds `getImmersiveTextStyle(length)` to centralize size/weight choices for immersive text display.|
🎯 3 (Moderate) | ⏱️ ~25 minutes
> "🐰
> I hop through links when metadata's gone,
> I pad the editor until the caret's dawn.
> Paste gently, focus to the doc's last line,
> Carded modes and fallbacks light the sign.
> Compose is steady — nibble, copy, open, shine."
</details>
<!-- walkthrough_end -->
<!-- pre_merge_checks_walkthrough_start -->
<details>
<summary>🚥 Pre-merge checks | ✅ 3 | ❌ 2</summary>
### ❌ Failed checks (1 warning, 1 inconclusive)
| Check name | Status | Explanation | Resolution |
| :----------------: | :------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 21.43% 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 'fix: text compose papercuts' is vague and generic, using the non-descriptive term 'papercuts' without clearly conveying what specific issues are being addressed in the compose functionality. | Consider a more specific title that mentions key fixes, such as 'fix: text editor padding, cursor placement, and link fallback rendering' or similar to better convey the scope of changes. |
<details>
<summary>✅ Passed checks (3 passed)</summary>
| Check name | Status | Explanation |
| :------------------------: | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Linked Issues check | ✅ Passed | The pull request successfully addresses all seven coding requirements from issue `#267`: padding fixes (CSS variables and styles), cursor placement at document end, heading size reduction, width consistency via shared styling variables, improved paste handling via file-only detection, and link metadata fallback rendering. |
| Out of Scope Changes check | ✅ Passed | All changes are directly related to fixing text compose issues specified in issue `#267`. Supporting additions like the Milkdown library dependency and ImageBlock configuration are necessary to implement the required fixes and are not out of scope. |
</details>
<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>
</details>
<!-- pre_merge_checks_walkthrough_end -->
<!-- finishing_touch_checkbox_start -->
<details>
<summary>✨ Finishing Touches</summary>
<details>
<summary>📝 Generate docstrings</summary>
- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> Create stacked PR
- [ ] <!-- {"checkboxId": "3e1879ae-f29b-4d0d-8e06-d12b7ba33d98"} --> Commit on current branch
</details>
<details>
<summary>🧪 Generate unit tests (beta)</summary>
- [ ] <!-- {"checkboxId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} --> Create PR with unit tests
- [ ] <!-- {"checkboxId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} --> Commit unit tests in branch `fix-text-compose-papercuts`
</details>
</details>
<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->
---
<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>
<!-- tips_end -->
<!-- internal state start -->
<!-- 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 -->
<!-- internal state end -->
coderabbitai[bot]
(Migrated from github.com)
reviewed 2026-06-11 18:36:58 +00:00
coderabbitai[bot]
(Migrated from github.com)
left a comment
Copy Link
Copy Source
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
The crepe.create() promise has no .catch() handler. If editor initialization fails (e.g., due to invalid markdown, missing dependencies, or runtime errors), the rejection will be unhandled and could crash the component or leave it in an inconsistent state.
🛡️ Proposed fix to add error boundary
crepe.create().then(() => {
if (cancelled) {
crepe.destroy();
return;
}
created = crepe;
if (autoFocus && !readOnly) {
// Place the caret at the end of the document — the editor often mounts
// mid-typing (immersive → card flip), where start-of-doc would strand
// the user. Selection-only transactions don't echo markdownUpdated.
crepe.editor.action((ctx) => {
const view = ctx.get(editorViewCtx);
view.dispatch(
view.state.tr
.setSelection(Selection.atEnd(view.state.doc))
.scrollIntoView(),
);
view.focus();
});
}
- });
+ }).catch((error) => {
+ console.error('Failed to create markdown editor:', error);
+ // Optionally: notify user or fallback to textarea
+ });
🤖 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 `@js/desktop/src/features/compose/markdown-editor.tsx` around lines 87 - 107,
Attach a .catch to the crepe.create() promise to handle initialization failures:
in the catch handler call crepe.destroy(), clear/leave unset the created
variable, perform any component cleanup (e.g., set cancelled or call the
existing cleanup path), log the error with a descriptive message (or call a
provided onError callback if available) so the rejection is not unhandled, and
avoid running the success-path logic (autoFocus/readOnly/crepe.editor.action)
when creation fails.
🧹 Nitpick comments (2)
js/desktop/src/lib/markdown.ts (1)
1-5: 💤 Low value
Consider detecting markdown links and images.
The regex currently detects headings, lists, code, bold, italic, and blockquotes, but doesn't detect markdown links [text](url) or images . These are common markdown patterns that might warrant switching to card mode.
Since this is a heuristic for UI mode switching, the current implementation is acceptable, but adding link/image detection would make it more comprehensive.
The pattern !?\[.+?\]\(.+?\) matches both [text](url) and .
🤖 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 `@js/desktop/src/lib/markdown.ts` around lines 1 - 5, The regex in
hasMarkdownFormatting currently misses markdown links and images; update the
pattern returned by hasMarkdownFormatting to also match link/image syntax (e.g.
include a branch like !?\[.+?\]\(.+?\) in the alternation) so both [text](url)
and  trigger true; ensure the new subpattern is added to the existing
alternation in the returned RegExp (preserving the /m flag and other checks) so
the function continues to act as a comprehensive heuristic for switching UI
modes.
js/desktop/package.json (1)
67-67: ⚡ Quick win
Verify version pinning consistency across Milkdown packages.
The newly added @milkdown/kit is pinned to exact version 7.21.1, while the existing @milkdown/crepe on line 66 uses a caret range ^7.21.1.
For packages from the same monorepo, it's typically recommended to use consistent version ranges to ensure compatibility. Was the exact pinning intentional, or should both packages use the same versioning strategy?
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@js/desktop/package.json` at line 67, The package versioning is inconsistent:
`@milkdown/kit` is pinned to 7.21.1 while `@milkdown/crepe` uses ^7.21.1; update the
version spec so both Milkdown packages use the same range (e.g., change
"`@milkdown/kit`" to "^7.21.1" to match "`@milkdown/crepe`") and then
reinstall/update the lockfile (npm/yarn/pnpm) to ensure the dependency tree is
consistent.
🤖 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 `@js/desktop/src/features/compose/text-editor.tsx`:
- Around line 67-72: The state update setCarded(true) must not run during
render; move the conditional that checks shouldCard (computed from textContent,
IMMERSIVE_CHAR_LIMIT, hasEnrichments, hasMarkdownFormatting) into a useEffect
that watches the dependencies (textContent, hasEnrichments, carded or
shouldCard) and calls setCarded(true) when shouldCard becomes true and carded is
false; keep the existing immersive = !carded logic in render but ensure all
state mutations happen inside that useEffect to avoid React render-phase
updates.
---
Outside diff comments:
In `@js/desktop/src/features/compose/markdown-editor.tsx`:
- Around line 87-107: Attach a .catch to the crepe.create() promise to handle
initialization failures: in the catch handler call crepe.destroy(), clear/leave
unset the created variable, perform any component cleanup (e.g., set cancelled
or call the existing cleanup path), log the error with a descriptive message (or
call a provided onError callback if available) so the rejection is not
unhandled, and avoid running the success-path logic
(autoFocus/readOnly/crepe.editor.action) when creation fails.
---
Nitpick comments:
In `@js/desktop/package.json`:
- Line 67: The package versioning is inconsistent: `@milkdown/kit` is pinned to
7.21.1 while `@milkdown/crepe` uses ^7.21.1; update the version spec so both
Milkdown packages use the same range (e.g., change "`@milkdown/kit`" to "^7.21.1"
to match "`@milkdown/crepe`") and then reinstall/update the lockfile
(npm/yarn/pnpm) to ensure the dependency tree is consistent.
In `@js/desktop/src/lib/markdown.ts`:
- Around line 1-5: The regex in hasMarkdownFormatting currently misses markdown
links and images; update the pattern returned by hasMarkdownFormatting to also
match link/image syntax (e.g. include a branch like !?\[.+?\]\(.+?\) in the
alternation) so both [text](url) and  trigger true; ensure the new
subpattern is added to the existing alternation in the returned RegExp
(preserving the /m flag and other checks) so the function continues to act as a
comprehensive heuristic for switching UI modes.
🪄 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: c025be3d-4a0c-46a6-8ada-baf969e9803b
📥 Commits
Reviewing files that changed from the base of the PR and between 66d53497c5 and 5b199434bf.
**Actionable comments posted: 1**
> [!CAUTION]
> Some comments are outside the diff and can’t be posted inline due to platform limitations.
>
>
>
> <details>
> <summary>⚠️ Outside diff range comments (1)</summary><blockquote>
>
> <details>
> <summary>js/desktop/src/features/compose/markdown-editor.tsx (1)</summary><blockquote>
>
> `87-107`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_
>
> **Add error handling for editor creation.**
>
> The `crepe.create()` promise has no `.catch()` handler. If editor initialization fails (e.g., due to invalid markdown, missing dependencies, or runtime errors), the rejection will be unhandled and could crash the component or leave it in an inconsistent state.
>
>
>
>
>
> <details>
> <summary>🛡️ Proposed fix to add error boundary</summary>
>
> ```diff
> crepe.create().then(() => {
> if (cancelled) {
> crepe.destroy();
> return;
> }
> created = crepe;
> if (autoFocus && !readOnly) {
> // Place the caret at the end of the document — the editor often mounts
> // mid-typing (immersive → card flip), where start-of-doc would strand
> // the user. Selection-only transactions don't echo markdownUpdated.
> crepe.editor.action((ctx) => {
> const view = ctx.get(editorViewCtx);
> view.dispatch(
> view.state.tr
> .setSelection(Selection.atEnd(view.state.doc))
> .scrollIntoView(),
> );
> view.focus();
> });
> }
> - });
> + }).catch((error) => {
> + console.error('Failed to create markdown editor:', error);
> + // Optionally: notify user or fallback to textarea
> + });
> ```
> </details>
>
> <details>
> <summary>🤖 Prompt for AI Agents</summary>
>
> ```
> Verify each finding against current code. Fix only still-valid issues, skip the
> rest with a brief reason, keep changes minimal, and validate.
>
> In `@js/desktop/src/features/compose/markdown-editor.tsx` around lines 87 - 107,
> Attach a .catch to the crepe.create() promise to handle initialization failures:
> in the catch handler call crepe.destroy(), clear/leave unset the created
> variable, perform any component cleanup (e.g., set cancelled or call the
> existing cleanup path), log the error with a descriptive message (or call a
> provided onError callback if available) so the rejection is not unhandled, and
> avoid running the success-path logic (autoFocus/readOnly/crepe.editor.action)
> when creation fails.
> ```
>
> </details>
>
> <!-- cr-comment:v1:4ce607e06ed47caa1379e078 -->
>
> </blockquote></details>
>
> </blockquote></details>
<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>
<details>
<summary>js/desktop/src/lib/markdown.ts (1)</summary><blockquote>
`1-5`: _💤 Low value_
**Consider detecting markdown links and images.**
The regex currently detects headings, lists, code, bold, italic, and blockquotes, but doesn't detect markdown links `[text](url)` or images ``. These are common markdown patterns that might warrant switching to card mode.
Since this is a heuristic for UI mode switching, the current implementation is acceptable, but adding link/image detection would make it more comprehensive.
<details>
<summary>📝 Suggested enhancement</summary>
```diff
export function hasMarkdownFormatting(content: string): boolean {
- return /^#{1,6} |^\s*[-*+] |^\s*\d+\. |^```|`[^`]+`|\*\*|__|\*\S[^*]*\*|\b_\S[^_]*_\b|^>/m.test(
+ return /^#{1,6} |^\s*[-*+] |^\s*\d+\. |^```|`[^`]+`|\*\*|__|\*\S[^*]*\*|\b_\S[^_]*_\b|^>|!?\[.+?\]\(.+?\)/m.test(
content,
);
}
```
The pattern `!?\[.+?\]\(.+?\)` matches both `[text](url)` and ``.
</details>
<details>
<summary>🤖 Prompt for AI Agents</summary>
```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@js/desktop/src/lib/markdown.ts` around lines 1 - 5, The regex in
hasMarkdownFormatting currently misses markdown links and images; update the
pattern returned by hasMarkdownFormatting to also match link/image syntax (e.g.
include a branch like !?\[.+?\]\(.+?\) in the alternation) so both [text](url)
and  trigger true; ensure the new subpattern is added to the existing
alternation in the returned RegExp (preserving the /m flag and other checks) so
the function continues to act as a comprehensive heuristic for switching UI
modes.
```
</details>
<!-- cr-comment:v1:cb3cbbd7841ae4664d3e3934 -->
</blockquote></details>
<details>
<summary>js/desktop/package.json (1)</summary><blockquote>
`67-67`: _⚡ Quick win_
**Verify version pinning consistency across Milkdown packages.**
The newly added `@milkdown/kit` is pinned to exact version `7.21.1`, while the existing `@milkdown/crepe` on line 66 uses a caret range `^7.21.1`.
For packages from the same monorepo, it's typically recommended to use consistent version ranges to ensure compatibility. Was the exact pinning intentional, or should both packages use the same versioning strategy?
<details>
<summary>🔧 Suggested fix for consistency</summary>
```diff
- "`@milkdown/kit`": "7.21.1",
+ "`@milkdown/kit`": "^7.21.1",
```
</details>
<details>
<summary>🤖 Prompt for AI Agents</summary>
```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@js/desktop/package.json` at line 67, The package versioning is inconsistent:
`@milkdown/kit` is pinned to 7.21.1 while `@milkdown/crepe` uses ^7.21.1; update the
version spec so both Milkdown packages use the same range (e.g., change
"`@milkdown/kit`" to "^7.21.1" to match "`@milkdown/crepe`") and then
reinstall/update the lockfile (npm/yarn/pnpm) to ensure the dependency tree is
consistent.
```
</details>
<!-- cr-comment:v1:27315621e4465e526145454b -->
</blockquote></details>
</blockquote></details>
<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>
```
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 `@js/desktop/src/features/compose/text-editor.tsx`:
- Around line 67-72: The state update setCarded(true) must not run during
render; move the conditional that checks shouldCard (computed from textContent,
IMMERSIVE_CHAR_LIMIT, hasEnrichments, hasMarkdownFormatting) into a useEffect
that watches the dependencies (textContent, hasEnrichments, carded or
shouldCard) and calls setCarded(true) when shouldCard becomes true and carded is
false; keep the existing immersive = !carded logic in render but ensure all
state mutations happen inside that useEffect to avoid React render-phase
updates.
---
Outside diff comments:
In `@js/desktop/src/features/compose/markdown-editor.tsx`:
- Around line 87-107: Attach a .catch to the crepe.create() promise to handle
initialization failures: in the catch handler call crepe.destroy(), clear/leave
unset the created variable, perform any component cleanup (e.g., set cancelled
or call the existing cleanup path), log the error with a descriptive message (or
call a provided onError callback if available) so the rejection is not
unhandled, and avoid running the success-path logic
(autoFocus/readOnly/crepe.editor.action) when creation fails.
---
Nitpick comments:
In `@js/desktop/package.json`:
- Line 67: The package versioning is inconsistent: `@milkdown/kit` is pinned to
7.21.1 while `@milkdown/crepe` uses ^7.21.1; update the version spec so both
Milkdown packages use the same range (e.g., change "`@milkdown/kit`" to "^7.21.1"
to match "`@milkdown/crepe`") and then reinstall/update the lockfile
(npm/yarn/pnpm) to ensure the dependency tree is consistent.
In `@js/desktop/src/lib/markdown.ts`:
- Around line 1-5: The regex in hasMarkdownFormatting currently misses markdown
links and images; update the pattern returned by hasMarkdownFormatting to also
match link/image syntax (e.g. include a branch like !?\[.+?\]\(.+?\) in the
alternation) so both [text](url) and  trigger true; ensure the new
subpattern is added to the existing alternation in the returned RegExp
(preserving the /m flag and other checks) so the function continues to act as a
comprehensive heuristic for switching UI modes.
```
</details>
<details>
<summary>🪄 Autofix (Beta)</summary>
Fix all unresolved CodeRabbit comments on this PR:
- [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended)
- [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes
</details>
---
<details>
<summary>ℹ️ Review info</summary>
<details>
<summary>⚙️ Run configuration</summary>
**Configuration used**: defaults
**Review profile**: CHILL
**Plan**: Pro Plus
**Run ID**: `c025be3d-4a0c-46a6-8ada-baf969e9803b`
</details>
<details>
<summary>📥 Commits</summary>
Reviewing files that changed from the base of the PR and between 66d53497c5c0c67cb162e1f61a66ee449875d973 and 5b199434bfc0d26413e27ad1837cc14e659ca054.
</details>
<details>
<summary>📒 Files selected for processing (10)</summary>
* `js/desktop/package.json`
* `js/desktop/src/components/link-preview-card.tsx`
* `js/desktop/src/features/compose/attachment-strip.tsx`
* `js/desktop/src/features/compose/markdown-editor.css`
* `js/desktop/src/features/compose/markdown-editor.tsx`
* `js/desktop/src/features/compose/text-editor.tsx`
* `js/desktop/src/features/particles/text-particle-view.tsx`
* `js/desktop/src/hooks/use-file-input.ts`
* `js/desktop/src/lib/link-metadata.ts`
* `js/desktop/src/lib/markdown.ts`
</details>
</details>
<!-- This is an auto-generated comment by CodeRabbit for review status -->
Fix setState during render — causes React warnings and potential bugs.
The pattern if (shouldCard && !carded) setCarded(true); calls a state setter directly during the render phase, which violates React's rules and will trigger warnings like "Cannot update a component from inside the function body of a different component."
This must be moved into a useEffect to update state safely after render.
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@js/desktop/src/features/compose/text-editor.tsx` around lines 67 - 72, The
state update setCarded(true) must not run during render; move the conditional
that checks shouldCard (computed from textContent, IMMERSIVE_CHAR_LIMIT,
hasEnrichments, hasMarkdownFormatting) into a useEffect that watches the
dependencies (textContent, hasEnrichments, carded or shouldCard) and calls
setCarded(true) when shouldCard becomes true and carded is false; keep the
existing immersive = !carded logic in render but ensure all state mutations
happen inside that useEffect to avoid React render-phase updates.
_⚠️ Potential issue_ | _🔴 Critical_ | _⚡ Quick win_
**Fix setState during render — causes React warnings and potential bugs.**
The pattern `if (shouldCard && !carded) setCarded(true);` calls a state setter directly during the render phase, which violates React's rules and will trigger warnings like "Cannot update a component from inside the function body of a different component."
This must be moved into a `useEffect` to update state safely after render.
<details>
<summary>🔒 Required fix</summary>
```diff
const hasEnrichments = attachmentCount > 0 || linkPreviews.length > 0;
const shouldCard =
textContent.length >= IMMERSIVE_CHAR_LIMIT ||
hasEnrichments ||
hasMarkdownFormatting(textContent);
- if (shouldCard && !carded) setCarded(true);
const immersive = !carded;
+
+ useEffect(() => {
+ if (shouldCard && !carded) {
+ setCarded(true);
+ }
+ }, [shouldCard, carded]);
```
</details>
<details>
<summary>🤖 Prompt for AI Agents</summary>
```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@js/desktop/src/features/compose/text-editor.tsx` around lines 67 - 72, The
state update setCarded(true) must not run during render; move the conditional
that checks shouldCard (computed from textContent, IMMERSIVE_CHAR_LIMIT,
hasEnrichments, hasMarkdownFormatting) into a useEffect that watches the
dependencies (textContent, hasEnrichments, carded or shouldCard) and calls
setCarded(true) when shouldCard becomes true and carded is false; keep the
existing immersive = !carded logic in render but ensure all state mutations
happen inside that useEffect to avoid React render-phase updates.
```
</details>
<!-- fingerprinting:phantom:poseidon:puma -->
<!-- cr-comment:v1:4af5402e746e945c6f904bbe -->
<!-- This is an auto-generated comment by CodeRabbit -->
coderabbitai[bot]
(Migrated from github.com)
reviewed 2026-06-11 19:08:06 +00:00
coderabbitai[bot]
(Migrated from github.com)
left a comment
setState during render still present — must use useEffect.
The pattern if (shouldCard) setCarded(true); calls a state setter directly during the render phase, which violates React's core rule: "Never call set functions during rendering." While the !carded guard prevents infinite loops, this still triggers warnings and can cause issues with React 19's concurrent rendering and batching.
This is the same issue flagged in the previous review. Move the state update into a useEffect as originally recommended:
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@js/desktop/src/features/compose/text-editor.tsx` around lines 62 - 67, The
check that computes shouldCard (using textContent, IMMERSIVE_CHAR_LIMIT,
hasEnrichments, hasMarkdownFormatting) must not call setCarded during render;
move the side-effect into a useEffect that watches the same dependencies plus
carded and calls setCarded(true) when shouldCard is true (and optionally
setCarded(false) when it becomes false, if desired). Replace the inline if
(shouldCard) setCarded(true) with a useEffect that computes shouldCard inside it
and invokes setCarded accordingly to avoid calling the state setter during
rendering.
🤖 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.
Duplicate comments:
In `@js/desktop/src/features/compose/text-editor.tsx`:
- Around line 62-67: The check that computes shouldCard (using textContent,
IMMERSIVE_CHAR_LIMIT, hasEnrichments, hasMarkdownFormatting) must not call
setCarded during render; move the side-effect into a useEffect that watches the
same dependencies plus carded and calls setCarded(true) when shouldCard is true
(and optionally setCarded(false) when it becomes false, if desired). Replace the
inline if (shouldCard) setCarded(true) with a useEffect that computes shouldCard
inside it and invokes setCarded accordingly to avoid calling the state setter
during rendering.
ℹ️ Review info⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 705610ec-394f-4e6b-922b-c8eb76b6c858
📥 Commits
Reviewing files that changed from the base of the PR and between 5b199434bf and e821cd02ef.
<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>
<details>
<summary>js/desktop/src/features/compose/text-editor.tsx (1)</summary><blockquote>
`62-67`: _⚠️ Potential issue_ | _🔴 Critical_ | _⚡ Quick win_
**setState during render still present — must use useEffect.**
The pattern `if (shouldCard) setCarded(true);` calls a state setter directly during the render phase, which violates React's core rule: "Never call set functions during rendering." While the `!carded` guard prevents infinite loops, this still triggers warnings and can cause issues with React 19's concurrent rendering and batching.
This is the same issue flagged in the previous review. Move the state update into a `useEffect` as originally recommended:
<details>
<summary>🔒 Required fix</summary>
```diff
const hasEnrichments = attachmentCount > 0 || linkPreviews.length > 0;
const shouldCard =
- !carded &&
- (textContent.length >= IMMERSIVE_CHAR_LIMIT ||
- hasEnrichments ||
- hasMarkdownFormatting(textContent));
- if (shouldCard) setCarded(true);
+ textContent.length >= IMMERSIVE_CHAR_LIMIT ||
+ hasEnrichments ||
+ hasMarkdownFormatting(textContent);
const immersive = !carded;
+
+ useEffect(() => {
+ if (shouldCard && !carded) {
+ setCarded(true);
+ }
+ }, [shouldCard, carded]);
```
</details>
<details>
<summary>🤖 Prompt for AI Agents</summary>
```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@js/desktop/src/features/compose/text-editor.tsx` around lines 62 - 67, The
check that computes shouldCard (using textContent, IMMERSIVE_CHAR_LIMIT,
hasEnrichments, hasMarkdownFormatting) must not call setCarded during render;
move the side-effect into a useEffect that watches the same dependencies plus
carded and calls setCarded(true) when shouldCard is true (and optionally
setCarded(false) when it becomes false, if desired). Replace the inline if
(shouldCard) setCarded(true) with a useEffect that computes shouldCard inside it
and invokes setCarded accordingly to avoid calling the state setter during
rendering.
```
</details>
<!-- cr-comment:v1:b7fab31ce4e5972ede95003e -->
</blockquote></details>
</blockquote></details>
<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>
```
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Duplicate comments:
In `@js/desktop/src/features/compose/text-editor.tsx`:
- Around line 62-67: The check that computes shouldCard (using textContent,
IMMERSIVE_CHAR_LIMIT, hasEnrichments, hasMarkdownFormatting) must not call
setCarded during render; move the side-effect into a useEffect that watches the
same dependencies plus carded and calls setCarded(true) when shouldCard is true
(and optionally setCarded(false) when it becomes false, if desired). Replace the
inline if (shouldCard) setCarded(true) with a useEffect that computes shouldCard
inside it and invokes setCarded accordingly to avoid calling the state setter
during rendering.
```
</details>
---
<details>
<summary>ℹ️ Review info</summary>
<details>
<summary>⚙️ Run configuration</summary>
**Configuration used**: defaults
**Review profile**: CHILL
**Plan**: Pro Plus
**Run ID**: `705610ec-394f-4e6b-922b-c8eb76b6c858`
</details>
<details>
<summary>📥 Commits</summary>
Reviewing files that changed from the base of the PR and between 5b199434bfc0d26413e27ad1837cc14e659ca054 and e821cd02ef994f38079134f28b80d2324c0ac6d7.
</details>
<details>
<summary>📒 Files selected for processing (9)</summary>
* `js/desktop/src/components/link-preview-card.tsx`
* `js/desktop/src/features/compose/attachment-strip.tsx`
* `js/desktop/src/features/compose/markdown-editor.css`
* `js/desktop/src/features/compose/markdown-editor.tsx`
* `js/desktop/src/features/compose/text-editor.tsx`
* `js/desktop/src/features/particles/text-particle-view.tsx`
* `js/desktop/src/hooks/use-file-input.ts`
* `js/desktop/src/lib/immersive-text.ts`
* `js/desktop/src/styles/globals.css`
</details>
<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (4)</summary>
* js/desktop/src/hooks/use-file-input.ts
* js/desktop/src/features/compose/attachment-strip.tsx
* js/desktop/src/features/compose/markdown-editor.tsx
* js/desktop/src/features/particles/text-particle-view.tsx
</details>
</details>
<!-- This is an auto-generated comment by CodeRabbit for review status -->
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #267
Summary by CodeRabbit
New Features
Bug Fixes
Style
📝 Walkthrough
Walkthrough
Refactors desktop compose: adds URL/markdown helpers, ensures link-preview fallbacks, enables Milkdown ImageBlock (uploads disabled), improves autofocus, replaces forceCardMode with session-latched carded logic, refines paste handling, and updates editor/card styling.
Changes
Compose Editor Improvements
js/desktop/src/lib/link-metadata.ts,js/desktop/src/lib/markdown.ts,js/desktop/package.jsondomainFromUrlextracts normalized hostnames;hasMarkdownFormattingdetects markdown via regex.@milkdown/kitadded (7.21.1).js/desktop/src/features/compose/markdown-editor.css,js/desktop/src/styles/globals.css.scrollbar-cardstyling plus layout CSS variables.js/desktop/src/features/compose/markdown-editor.tsxImageBlock(uploads rejected), constrains image block height, and changes autofocus to dispatch an editor action moving cursor to document end.js/desktop/src/features/compose/text-editor.tsxforceCardModewithcardedstate; auto-switches to card mode when length, attachments, or markdown formatting indicate; updates shortcut and adds entry animations.js/desktop/src/components/link-preview-card.tsx,js/desktop/src/features/compose/attachment-strip.tsx,js/desktop/src/features/particles/text-particle-view.tsxLinkPreviewCardFallbackand uses it where metadata is missing so links remain visible and actionable (open/copy).js/desktop/src/hooks/use-file-input.tsjs/desktop/src/lib/immersive-text.tsgetImmersiveTextStyle(length)to centralize size/weight choices for immersive text display.🎯 3 (Moderate) | ⏱️ ~25 minutes
🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
#267: padding fixes (CSS variables and styles), cursor placement at document end, heading size reduction, width consistency via shared styling variables, improved paste handling via file-only detection, and link metadata fallback rendering.#267. Supporting additions like the Milkdown library dependency and ImageBlock configuration are necessary to implement the required fixes and are not out of scope.✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing Touches
📝 Generate docstrings
🧪 Generate unit tests (beta)
fix-text-compose-papercutsComment
@coderabbitai helpto get the list of available commands and usage tips.Actionable comments posted: 1
🧹 Nitpick comments (2)
🤖 Prompt for all review comments with AI agents
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID:
c025be3d-4a0c-46a6-8ada-baf969e9803b📥 Commits
Reviewing files that changed from the base of the PR and between
66d53497c5and5b199434bf.📒 Files selected for processing (10)
js/desktop/package.jsonjs/desktop/src/components/link-preview-card.tsxjs/desktop/src/features/compose/attachment-strip.tsxjs/desktop/src/features/compose/markdown-editor.cssjs/desktop/src/features/compose/markdown-editor.tsxjs/desktop/src/features/compose/text-editor.tsxjs/desktop/src/features/particles/text-particle-view.tsxjs/desktop/src/hooks/use-file-input.tsjs/desktop/src/lib/link-metadata.tsjs/desktop/src/lib/markdown.ts⚠️ Potential issue | 🔴 Critical | ⚡ Quick win
Fix setState during render — causes React warnings and potential bugs.
The pattern
if (shouldCard && !carded) setCarded(true);calls a state setter directly during the render phase, which violates React's rules and will trigger warnings like "Cannot update a component from inside the function body of a different component."This must be moved into a
useEffectto update state safely after render.🔒 Required fix
🤖 Prompt for AI Agents
♻️ Duplicate comments (1)
🤖 Prompt for all review comments with AI agents
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID:
705610ec-394f-4e6b-922b-c8eb76b6c858📥 Commits
Reviewing files that changed from the base of the PR and between
5b199434bfande821cd02ef.📒 Files selected for processing (9)
js/desktop/src/components/link-preview-card.tsxjs/desktop/src/features/compose/attachment-strip.tsxjs/desktop/src/features/compose/markdown-editor.cssjs/desktop/src/features/compose/markdown-editor.tsxjs/desktop/src/features/compose/text-editor.tsxjs/desktop/src/features/particles/text-particle-view.tsxjs/desktop/src/hooks/use-file-input.tsjs/desktop/src/lib/immersive-text.tsjs/desktop/src/styles/globals.css🚧 Files skipped from review as they are similar to previous changes (4)