Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/four-owls-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@wangeditor-next/plugin-formula': patch
---

fix formula absolute value delimiter rendering by switching formula card output to htmlAndMathml and documenting required css import
3 changes: 3 additions & 0 deletions packages/plugin-formula/README-en.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@ pnpm add @wangeditor-next/plugin-formula
```js
import { Boot, IEditorConfig, IToolbarConfig } from '@wangeditor-next/editor'
import formulaModule from '@wangeditor-next/plugin-formula'
import 'katex/dist/katex.min.css'

// Register
// You should register this before create editor, and register only once (not repeatedly).
Boot.registerModule(formulaModule)
```

Ensure KaTeX stylesheet is loaded on the page, otherwise formulas will render as untypeset text.

### Menu config

```js
Expand Down
3 changes: 3 additions & 0 deletions packages/plugin-formula/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@ pnpm add @wangeditor-next/plugin-formula
```js
import { Boot, IEditorConfig, IToolbarConfig } from '@wangeditor-next/editor'
import formulaModule from '@wangeditor-next/plugin-formula'
import 'katex/dist/katex.min.css'

// 注册。要在创建编辑器之前注册,且只能注册一次,不可重复注册。
Boot.registerModule(formulaModule)
```

需要确保页面引入 KaTeX 样式,否则公式会退化为未排版状态。

### 配置

```js
Expand Down
18 changes: 18 additions & 0 deletions packages/plugin-formula/__tests__/register-custom-elem.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import '../src/register-custom-elem'

describe('plugin-formula custom element', () => {
it('renders htmlAndMathml output and keeps stretchy absolute delimiters', () => {
const elem = document.createElement('w-e-formula-card')
const formula = String.raw`\left|-2\frac{4}{7}\right|`

elem.setAttribute('data-value', formula)
document.body.appendChild(elem)

expect(elem.shadowRoot).toBeNull()
expect(elem.querySelector('.katex')).not.toBeNull()
expect(elem.querySelector('.katex-html')).not.toBeNull()
expect(elem.querySelector('.katex-html .delimsizing.mult')).not.toBeNull()

elem.remove()
})
Comment on lines +11 to +17

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Ensure cleanup runs even when an assertion fails.

elem.remove() should be in finally to avoid leaking DOM state on failed assertions.

Suggested change
 it('renders htmlAndMathml output and keeps stretchy absolute delimiters', () => {
   const elem = document.createElement('w-e-formula-card')
   const formula = String.raw`\left|-2\frac{4}{7}\right|`
 
-  elem.setAttribute('data-value', formula)
-  document.body.appendChild(elem)
-
-  expect(elem.shadowRoot).toBeNull()
-  expect(elem.querySelector('.katex')).not.toBeNull()
-  expect(elem.querySelector('.katex-html')).not.toBeNull()
-  expect(elem.querySelector('.katex-html .delimsizing.mult')).not.toBeNull()
-
-  elem.remove()
+  try {
+    elem.setAttribute('data-value', formula)
+    document.body.appendChild(elem)
+
+    expect(elem.shadowRoot).toBeNull()
+    expect(elem.querySelector('.katex')).not.toBeNull()
+    expect(elem.querySelector('.katex-html')).not.toBeNull()
+    expect(elem.querySelector('.katex-html .delimsizing.mult')).not.toBeNull()
+  } finally {
+    elem.remove()
+  }
 })
🤖 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 `@packages/plugin-formula/__tests__/register-custom-elem.test.ts` around lines
11 - 17, The test currently calls elem.remove() after assertions which will be
skipped if an assertion throws; wrap the assertions (the block that checks
elem.shadowRoot and the various elem.querySelector(...) expectations) in a
try/finally and move elem.remove() into the finally so cleanup always runs;
locate the test that creates the element (variable elem) in
packages/plugin-formula/__tests__/register-custom-elem.test.ts and ensure
elem.remove() is executed in the finally block surrounding those expectations.

})
Comment on lines +3 to +18

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Add the required round-trip serialization coverage for this style-related change.

This adds a useful DOM regression test, but it still misses the required round-trip matrix (render/toHtml/parseHtml, inline/class style modes, default/custom config, and setHtml(getHtml()) / toHtml(parseHtml(html))) for serialization/style changes.

As per coding guidelines, "**/*.test.{ts,tsx}: Test round-trip export/import for inline and class style modes, default and custom configuration values, across render/toHtml/parseHtml paths, and with setHtml(getHtml())/toHtml(parseHtml(html)) when modifying serialization or styles".

🤖 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 `@packages/plugin-formula/__tests__/register-custom-elem.test.ts` around lines
3 - 18, Add round-trip serialization tests for the plugin-formula style change:
extend the existing test for the custom element 'w-e-formula-card' to exercise
render -> toHtml -> parseHtml and setHtml(getHtml()) / toHtml(parseHtml(html))
paths across both inline and class style modes and for default and custom
configuration values; specifically, invoke the module's render (or equivalent
render helper), call toHtml() on the editor state, parseHtml(...) to rehydrate
DOM, and assert that parsed output/rendered DOM still contains '.katex',
'.katex-html' and '.delimsizing.mult' for each combination (inline/class ×
default/custom) and for both round-trip flows. Ensure you reuse the existing
element creation (elem = document.createElement('w-e-formula-card')) and
attribute handling (setAttribute('data-value', ...)) while adding assertions
after setHtml(getHtml()) and toHtml(parseHtml(html)) to fully cover the required
matrix.

28 changes: 18 additions & 10 deletions packages/plugin-formula/src/register-custom-elem/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import './native-shim'
import katex from 'katex'

class WangEditorFormulaCard extends HTMLElement {
private span: HTMLElement
private span: HTMLElement | null = null

// 监听的 attr
static get observedAttributes() {
Expand All @@ -17,13 +17,6 @@ class WangEditorFormulaCard extends HTMLElement {

constructor() {
super()
const shadow = this.attachShadow({ mode: 'open' })
const document = shadow.ownerDocument
const span = document.createElement('span')

span.style.display = 'inline-block'
shadow.appendChild(span)
this.span = span
}

// connectedCallback() {
Expand All @@ -45,10 +38,25 @@ class WangEditorFormulaCard extends HTMLElement {
}
}

private ensureSpan() {
if (this.span) { return this.span }

const document = this.ownerDocument || window.document
const span = document.createElement('span')

span.style.display = 'inline-block'
this.appendChild(span)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve clipboard text by isolating rendered KaTeX DOM

Appending the KaTeX render container into the custom element’s light DOM makes setFragmentData/getPlainText in core traverse KaTeX’s internal HTML+MathML nodes when users copy content, so copying a formula now yields duplicated/garbled plain text (hidden MathML annotation plus visual HTML text) in external paste targets. This regression appears when selecting formula nodes and using copy/cut; the previous Shadow DOM implementation avoided leaking these internal nodes into clipboard extraction.

Useful? React with 👍 / 👎.

this.span = span

return span
}

private render(value: string) {
katex.render(value, this.span, {
const span = this.ensureSpan()

katex.render(value, span, {
throwOnError: false,
output: 'mathml',
output: 'htmlAndMathml',
})
}
}
Expand Down
Loading