diff --git a/services/web/frontend/js/features/source-editor/extensions/visual/paste-html.ts b/services/web/frontend/js/features/source-editor/extensions/visual/paste-html.ts index 221d60d980..dfd8201833 100644 --- a/services/web/frontend/js/features/source-editor/extensions/visual/paste-html.ts +++ b/services/web/frontend/js/features/source-editor/extensions/visual/paste-html.ts @@ -99,6 +99,9 @@ const htmlToLaTeX = (documentElement: HTMLElement) => { // pre-process table elements processTables(documentElement) + // pre-process lists + processLists(documentElement) + // protect special characters in non-LaTeX text nodes protectSpecialCharacters(documentElement) @@ -214,6 +217,18 @@ const matchingParents = (element: HTMLElement, selector: string) => { return matches } +const processLists = (element: HTMLElement) => { + for (const list of element.querySelectorAll('ol,ul')) { + // if the list has only one item, replace the list with an element containing the contents of the item + if (list.childElementCount === 1) { + const div = document.createElement('div') + div.append(...list.firstElementChild!.childNodes) + list.before('\n', div, '\n') + list.remove() + } + } +} + const processTables = (element: HTMLElement) => { for (const table of element.querySelectorAll('table')) { // create a wrapper element for the table and the caption @@ -626,11 +641,13 @@ const selectors = [ end: () => `}\n\n`, }), createSelector({ + // selector: 'ul:has(> li:nth-child(2))', // only select lists with at least 2 items (once Firefox supports :has()) selector: 'ul', start: element => `\n\n${listIndent(element)}\\begin{itemize}`, end: element => `\n${listIndent(element)}\\end{itemize}\n`, }), createSelector({ + // selector: 'ol:has(> li:nth-child(2))', // only select lists with at least 2 items (once Firefox supports :has()) selector: 'ol', start: element => `\n\n${listIndent(element)}\\begin{enumerate}`, end: element => `\n${listIndent(element)}\\end{enumerate}\n`, diff --git a/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual-paste-html.spec.tsx b/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual-paste-html.spec.tsx index 276e133dbb..e345863b98 100644 --- a/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual-paste-html.spec.tsx +++ b/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual-paste-html.spec.tsx @@ -76,6 +76,19 @@ describe(' paste HTML in Visual mode', function () { cy.get('.ol-cm-item').should('have.length', 2) }) + it('removes a solitary item from a list', function () { + mountEditor() + + const data = '' + + const clipboardData = new DataTransfer() + clipboardData.setData('text/html', data) + cy.get('@content').trigger('paste', { clipboardData }) + + cy.get('@content').should('have.text', 'foo') + cy.get('.ol-cm-item').should('have.length', 0) + }) + it('handles a pasted table', function () { mountEditor() @@ -337,8 +350,8 @@ describe(' paste HTML in Visual mode', function () { const data = [ 'test', '

foo

bar

baz

', - '', - '
  1. foo

', + '', + '
  1. foo

  2. foo

', '

foo

', 'test', ].join('\n') @@ -349,9 +362,9 @@ describe(' paste HTML in Visual mode', function () { cy.get('@content').should( 'have.text', - 'testfoobarbaz foo foo\\begin{tabular}{l}foo ↩\\end{tabular}test' + 'testfoobarbaz foo foo foo foo\\begin{tabular}{l}foo ↩\\end{tabular}test' ) - cy.get('.cm-line').should('have.length', 17) + cy.get('.cm-line').should('have.length', 19) }) it('handles pasted inline code', function () {