Desktop: Fixes #14979: Fixed copying and cutting table columns in Rich Text editor#15113
Desktop: Fixes #14979: Fixed copying and cutting table columns in Rich Text editor#15113bhaktideshmukh wants to merge 1 commit intolaurent22:devfrom
Conversation
…ns in Rich Text editor
|
All contributors have signed the CLA ✍️ ✅ |
📝 WalkthroughWalkthroughFixes table column and multi-cell selection copy-cut operations in the rich text editor by detecting table context selections and routing them through native browser clipboard commands instead of standard content conversion handlers. Changes
Suggested labels
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 error, 1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsx (2)
1480-1487: The 50ms timeout is a potential race condition risk.The arbitrary 50ms delay assumes the native copy completes and populates the clipboard within that time. On slower systems or under heavy load, this could fail silently (clipboard might not yet contain the expected content).
Consider:
- Adding a brief comment explaining why 50ms was chosen.
- Alternatively, increasing the timeout slightly for safety margin (e.g., 100ms).
The pattern itself is sound - letting TinyMCE's native handler populate the clipboard first, then re-copying with both text and HTML formats via
copyHtmlToClipboard.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsx` around lines 1480 - 1487, The current setTimeout(…, 50) used after detecting selected table cells (code around editor.dom.select('td…'), editor.dom.select('tr…')) is a fragile race; replace the magic 50ms with a clearly named constant (e.g., CLIPBOARD_COPY_DELAY) and increase it to a safer value (e.g., 100–200ms) or make it configurable, add a one-line comment explaining that this wait allows TinyMCE's native copy to populate the clipboard before calling clipboard.readHTML() and copyHtmlToClipboard(), and keep the same logic that only triggers when multiple cells/rows are selected.
1480-1505: Consider extracting the table selection detection logic.The same condition for detecting multi-cell/row table selection appears in three places:
useContextMenu.ts(lines 74-75)onCopy(lines 1480-1481)onCut(lines 1497-1498)♻️ Suggested helper function
+function hasTableCellsOrRowsSelected(editor: Editor): boolean { + const selectedCells = editor.dom.select('td[data-mce-selected="1"], th[data-mce-selected="1"]'); + return selectedCells.length > 1 || editor.dom.select('tr[data-mce-selected="1"]').length > 0; +}This could be placed in a utils file and reused across both
TinyMCE.tsxanduseContextMenu.ts.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsx` around lines 1480 - 1505, Extract the repeated multi-cell/row detection into a shared helper (e.g., isTableMultiSelection(editor): boolean) placed in a utils file and export it; replace the inline checks in TinyMCE.tsx (inside onCopy and onCut) and in useContextMenu.ts with calls to this helper so all three locations use the same logic (use editor.dom.select('td[data-mce-selected="1"], th[data-mce-selected="1"]') and editor.dom.select('tr[data-mce-selected="1"]') internally), update imports, and ensure behavior remains identical (including the >1 check and any surrounding setTimeout logic).packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.ts (1)
84-91: Consider using a more specific union type for the new event strings.The type
TinyMceEditorEvents|stringis quite broad. For better type safety, consider defining specific string literals:type ExecCommandEvent = 'execCommandCopy' | 'execCommandCut';Then use
TinyMceEditorEvents | ExecCommandEventfor the parameter type.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.ts` around lines 84 - 91, The parameter type for fireEditorEvent is too broad (TinyMceEditorEvents|string); define a narrow union for the new command strings (e.g., type ExecCommandEvent = 'execCommandCopy' | 'execCommandCut') and change the function signature to accept TinyMceEditorEvents | ExecCommandEvent so the branches for execCommandCopy and execCommandCut are type-safe while preserving existing TinyMceEditorEvents handling in fireEditorEvent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsx`:
- Around line 1480-1487: The current setTimeout(…, 50) used after detecting
selected table cells (code around editor.dom.select('td…'),
editor.dom.select('tr…')) is a fragile race; replace the magic 50ms with a
clearly named constant (e.g., CLIPBOARD_COPY_DELAY) and increase it to a safer
value (e.g., 100–200ms) or make it configurable, add a one-line comment
explaining that this wait allows TinyMCE's native copy to populate the clipboard
before calling clipboard.readHTML() and copyHtmlToClipboard(), and keep the same
logic that only triggers when multiple cells/rows are selected.
- Around line 1480-1505: Extract the repeated multi-cell/row detection into a
shared helper (e.g., isTableMultiSelection(editor): boolean) placed in a utils
file and export it; replace the inline checks in TinyMCE.tsx (inside onCopy and
onCut) and in useContextMenu.ts with calls to this helper so all three locations
use the same logic (use editor.dom.select('td[data-mce-selected="1"],
th[data-mce-selected="1"]') and editor.dom.select('tr[data-mce-selected="1"]')
internally), update imports, and ensure behavior remains identical (including
the >1 check and any surrounding setTimeout logic).
In
`@packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.ts`:
- Around line 84-91: The parameter type for fireEditorEvent is too broad
(TinyMceEditorEvents|string); define a narrow union for the new command strings
(e.g., type ExecCommandEvent = 'execCommandCopy' | 'execCommandCut') and change
the function signature to accept TinyMceEditorEvents | ExecCommandEvent so the
branches for execCommandCopy and execCommandCut are type-safe while preserving
existing TinyMceEditorEvents handling in fireEditorEvent.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 93bd15ce-0ee7-4aba-b5b1-d590e8d7545e
📒 Files selected for processing (3)
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsxpackages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.tspackages/app-desktop/gui/NoteEditor/utils/contextMenu.ts
|
I have read the CLA Document and I hereby sign the CLA |
|
@laurent22 Please review this PR, let me know if any changes are required. If not, please approve, so I'll merge this PR, thanks. |
|
@laurent22 Whom should I reach out to review this PR? |
|
You just need to wait and be patient |
|
Please see Claude's review: Critical / Bugs1. Race condition with 50ms
|
Problem
Previously, when working in the Rich Text editor, copying, cutting, or pasting isolated table columns behaved incorrectly
Solution
This PR fixes issues with cutting, copying, and pasting table columns by letting the TinyMCE Table Plugin handle things naturally instead of forcing strict formatting rules.
For cut and copy, when table cells are selected, we skip the custom override so the plugin can properly delete or copy full columns. We still capture the result afterward so Joplin can use it.
For paste, if table content is detected, we avoid the usual Markdown conversion and instead use TinyMCE’s native paste, which keeps the column structure and spacing intact while still processing images correctly.
Fixes #14979 .
Testing
Existing Behavior
Recording.2026-04-15.194557.mp4
Current Behavior
Screen.Recording.2026-04-15.191709.mp4