Desktop: Fixes #15918: Keep trailing spaces in table cells while editing (#15964)

This commit is contained in:
Laurent Cozic authored and GitHub committed 2026-07-17 23:50:33 +01:00
1 parent 22689817c7
commit 6aa4040762
2 files changed
+37 -1

No files matched your search

@@ -125,4 +125,33 @@ describe('renderTables', () => {
const editor = await createEditor(source);
expect(editor.state.doc.toString()).toBe(source);
});
test('typing a trailing space in a cell should keep it visible after live-sync', async () => {
jest.useFakeTimers();
let editor: EditorView | null = null;
try {
editor = await createEditor('| Head | b |\n|---|---|\n| x | y |');
// The widget must be connected for the live-sync flush to run.
document.body.appendChild(editor.dom);
const cell = findCellTextDivs(editor)[0];
focusCell(cell);
cell.textContent = 'Hello ';
cell.dispatchEvent(new Event('input'));
// Fire the 500ms debounced live-sync and the follow-up rAF that
// restores focus + caret to the rebuilt cell.
jest.advanceTimersByTime(600);
jest.runOnlyPendingTimers();
// The rebuilt-and-refocused cell must still show the trailing space
// rather than the markdown-trimmed "Hello".
const refocused = findCellTextDivs(editor)[0];
expect(refocused.textContent).toBe('Hello ');
} finally {
editor?.destroy();
jest.useRealTimers();
}
});
});
@@ -249,8 +249,10 @@ class TableWidget extends WidgetType {
// Called on every input so the model stays in sync even if a
// rebuild is triggered by an external event (image paste, toolbar
// command, etc.) before the deferred blur handler runs.
// Not trimmed: an edge space the user just typed is real content
// and must stay visible while editing (see #15918).
const pushToModel = () => {
const v = (textDiv.textContent || '').trim()
const v = (textDiv.textContent || '')
.replace(/\n/g, '<br>').replace(/\|/g, '\\|');
if (isHdr) table.header.cells[c].content = v;
else if (r - 1 < table.body.length) table.body[r - 1].cells[c].content = v;
@@ -278,6 +280,9 @@ class TableWidget extends WidgetType {
if (!container.isConnected) return;
const newText = serializeTable(table);
if (newText === this.tableText) return;
// The serialize/parse round-trip strips edge whitespace, so
// keep the raw value to re-inject after the rebuild (#15918).
const rawValue = textDiv.textContent || '';
this.apply(view, table, 'input.type');
// Rebuild discards this DOM — locate the same cell in the
// new widget and restore focus + caret.
@@ -288,6 +293,8 @@ class TableWidget extends WidgetType {
const target = cells && idx < cells.length ? cells[idx] as HTMLElement : null;
if (!target) return;
focus('TableWidget', target);
// Restore the raw value onfocus trimmed.
if (target.textContent !== rawValue) target.textContent = rawValue;
// Caret restoration: put it `offset` characters into
// the cell's text content.
const sel = win.getSelection();