From f70341b0e7e63864f645fb15873cb54a3f69da92 Mon Sep 17 00:00:00 2001 From: zzxming Date: Sun, 24 May 2026 21:32:59 +0800 Subject: [PATCH] fix: inserting `Embed` into `TableColFormat` has no effect --- src/__tests__/unit/table-redo-undo.test.ts | 40 ++++++++++++++++++++++ src/formats/table-col-format.ts | 8 +++++ 2 files changed, 48 insertions(+) diff --git a/src/__tests__/unit/table-redo-undo.test.ts b/src/__tests__/unit/table-redo-undo.test.ts index 283769ac..03a6d63c 100644 --- a/src/__tests__/unit/table-redo-undo.test.ts +++ b/src/__tests__/unit/table-redo-undo.test.ts @@ -34,6 +34,46 @@ afterEach(() => { }); describe('table undo', () => { + it('undo remove column with merged cell and image should keep embed/content/format', async () => { + const quill = createQuillWithTableModule(`


`); + const tableModule = quill.getModule(TableUp.moduleName) as TableUp; + quill.setContents([ + { insert: '\n' }, + { insert: { 'table-up-col': { tableId: '1', colId: '1', full: true, width: 33.3333 } } }, + { insert: { 'table-up-col': { tableId: '1', colId: '2', full: true, width: 33.3333 } } }, + { insert: { 'table-up-col': { tableId: '1', colId: '3', full: true, width: 33.3333 } } }, + { insert: { image: 'https://live.mdnplay.dev/en-US/docs/Web/HTML/Element/img/favicon144.png' } }, + { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' }, + { insert: 'text' }, + { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' }, + { insert: 'r1c3' }, + { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' }, + { insert: 'merged' }, + { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '1', rowspan: 1, colspan: 2 } }, insert: '\n' }, + { insert: 'r2c3' }, + { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' }, + { insert: 'r3c1' }, + { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' }, + { insert: 'r3c2' }, + { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' }, + { insert: 'r3c3' }, + { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' }, + { insert: '\n' }, + ]); + await vi.runAllTimersAsync(); + const initDelta = quill.getContents(); + + const tds = quill.scroll.descendants(TableCellInnerFormat, 0); + tableModule.removeCol(tds.filter(td => td.colId === '1')); + await vi.runAllTimersAsync(); + quill.history.undo(); + await vi.runAllTimersAsync(); + + expectDelta(quill.getContents(), initDelta); + expect(quill.root.querySelector('tbody img')).not.toBeNull(); + expect(quill.root.querySelector('colgroup img')).toBeNull(); + }); + it('merge all cell undo', async () => { const quill = await createTable(3, 3); const tableModule = quill.getModule(TableUp.moduleName) as TableUp; diff --git a/src/formats/table-col-format.ts b/src/formats/table-col-format.ts index fb83217e..b6bf6ee9 100644 --- a/src/formats/table-col-format.ts +++ b/src/formats/table-col-format.ts @@ -159,6 +159,14 @@ export class TableColFormat extends BlockEmbed { console.warn('TableCol not in TableColgroup'); } } + else if (value !== this.statics.blotName) { + // Redirect non-tableCol embeds/blots (e.g. image from undo) outside table wrapper. + // Otherwise they are inserted into colgroup and then removed by allowedChildren check. + const tableWrapperBlot = findParentBlot(this, blotName.tableWrapper); + const parentBlot = tableWrapperBlot.split(this.offset(tableWrapperBlot)) as TypeParchment.Parent; + const blot = this.scroll.create(value, def); + parentBlot.parent.insertBefore(blot, parentBlot); + } else { super.insertAt(index, value, def); }