From 22c95882b40f28ed4cf0551d50a7358b61f50283 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 23:25:19 +0900 Subject: [PATCH 1/4] chore: bump up pnpm to v10.26.0 (#2210) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cdc3b49fd73..21848048a2d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@milkdown/monorepo", "version": "0.0.0", "private": true, - "packageManager": "pnpm@10.25.0", + "packageManager": "pnpm@10.26.0", "engines": { "node": ">=22" }, From 6e90c4351b19c5fa8a7417a494402b1845923912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=A1=E5=B1=B1=E7=94=B5=E8=BD=A6?= Date: Mon, 15 Dec 2025 22:26:06 +0800 Subject: [PATCH 2/4] feat: tooltip auto update (#2204) * feat: tooltip auto update * feat: autoUpdate only when outside parentElement --- .../src/link-tooltip/edit/edit-view.ts | 7 +- .../src/link-tooltip/preview/preview-view.ts | 6 +- .../plugin-tooltip/src/tooltip-provider.ts | 97 ++++++++++++------- 3 files changed, 69 insertions(+), 41 deletions(-) diff --git a/packages/components/src/link-tooltip/edit/edit-view.ts b/packages/components/src/link-tooltip/edit/edit-view.ts index 7c3ba4f243e..abb1c977122 100644 --- a/packages/components/src/link-tooltip/edit/edit-view.ts +++ b/packages/components/src/link-tooltip/edit/edit-view.ts @@ -110,9 +110,10 @@ export class LinkEditTooltip implements PluginView { view.dispatch( view.state.tr.setSelection(TextSelection.create(view.state.doc, from, to)) ) - this.#provider.show({ - getBoundingClientRect: () => posToDOMRect(view, from, to), - }) + this.#provider.show( + { getBoundingClientRect: () => posToDOMRect(view, from, to) }, + view + ) requestAnimationFrame(() => { this.#content.querySelector('input')?.focus() }) diff --git a/packages/components/src/link-tooltip/preview/preview-view.ts b/packages/components/src/link-tooltip/preview/preview-view.ts index 6ad5089306f..1c711c58695 100644 --- a/packages/components/src/link-tooltip/preview/preview-view.ts +++ b/packages/components/src/link-tooltip/preview/preview-view.ts @@ -20,6 +20,7 @@ export class LinkPreviewTooltip implements PluginView { #onEdit = ref(() => {}) #onRemove = ref(() => {}) #app: App + #editorView: EditorView #hovering = false @@ -27,6 +28,7 @@ export class LinkPreviewTooltip implements PluginView { readonly ctx: Ctx, view: EditorView ) { + this.#editorView = view this.#config = ref(this.ctx.get(linkTooltipConfig.key)) this.#app = createApp(PreviewLink, { config: this.#config, @@ -77,9 +79,7 @@ export class LinkPreviewTooltip implements PluginView { this.#hide() } - this.#provider.show({ - getBoundingClientRect: () => rect, - }) + this.#provider.show({ getBoundingClientRect: () => rect }, this.#editorView) this.#provider.element.addEventListener('mouseenter', this.#onMouseEnter) this.#provider.element.addEventListener('mouseleave', this.#onMouseLeave) } diff --git a/packages/plugins/plugin-tooltip/src/tooltip-provider.ts b/packages/plugins/plugin-tooltip/src/tooltip-provider.ts index e3d3d49b7de..358b80bdd40 100644 --- a/packages/plugins/plugin-tooltip/src/tooltip-provider.ts +++ b/packages/plugins/plugin-tooltip/src/tooltip-provider.ts @@ -8,7 +8,13 @@ import type { import type { EditorState } from '@milkdown/prose/state' import type { EditorView } from '@milkdown/prose/view' -import { computePosition, flip, offset, shift } from '@floating-ui/dom' +import { + autoUpdate, + computePosition, + flip, + offset, + shift, +} from '@floating-ui/dom' import { posToDOMRect } from '@milkdown/prose' import { TextSelection } from '@milkdown/prose/state' import { throttle } from 'lodash-es' @@ -53,6 +59,9 @@ export class TooltipProvider { /// @internal #initialized = false + /// @internal + #cleanupAutoUpdate?: () => void + /// @internal readonly #offset?: OffsetOptions @@ -87,6 +96,32 @@ export class TooltipProvider { this.#updater = throttle(this.#onUpdate, this.#debounce) } + /// @internel + #updatePosition = (reference: VirtualElement) => { + computePosition(reference, this.element, { + placement: this.#floatingUIOptions.placement ?? 'top', + middleware: [ + flip(), + offset(this.#offset), + shift(this.#shift), + ...this.#middleware, + ], + ...this.#floatingUIOptions, + }) + .then(({ x, y }) => { + Object.assign(this.element.style, { + left: `${x}px`, + top: `${y}px`, + }) + }) + .catch(console.error) + } + + /// @internal + #shouldAutoUpdate = (editorView: EditorView) => { + return this.#root !== editorView.dom.parentElement + } + /// @internal #onUpdate = (view: EditorView, prevState?: EditorState): void => { const { state, composing } = view @@ -105,6 +140,9 @@ export class TooltipProvider { if (composing || isSame) return + this.#cleanupAutoUpdate?.() + this.#cleanupAutoUpdate = void 0 + if (!this.#shouldShow(view, prevState)) { this.hide() return @@ -112,23 +150,16 @@ export class TooltipProvider { const virtualEl: VirtualElement = { getBoundingClientRect: () => posToDOMRect(view, from, to), + contextElement: view.dom, + } + + if (this.#shouldAutoUpdate(view)) { + this.#cleanupAutoUpdate = autoUpdate(virtualEl, this.element, () => + this.#updatePosition(virtualEl) + ) + } else { + this.#updatePosition(virtualEl) } - computePosition(virtualEl, this.element, { - placement: this.#floatingUIOptions.placement ?? 'top', - middleware: [ - flip(), - offset(this.#offset), - shift(this.#shift), - ...this.#middleware, - ], - }) - .then(({ x, y }) => { - Object.assign(this.element.style, { - left: `${x}px`, - top: `${y}px`, - }) - }) - .catch(console.error) this.show() } @@ -160,31 +191,27 @@ export class TooltipProvider { /// Destroy the tooltip. destroy = () => { + this.#cleanupAutoUpdate?.() this.#updater.cancel() } /// Show the tooltip. - show = (virtualElement?: VirtualElement) => { + show = (virtualElement?: VirtualElement, editorView?: EditorView) => { this.element.dataset.show = 'true' if (virtualElement) { - computePosition(virtualElement, this.element, { - placement: 'top', - middleware: [ - flip(), - offset(this.#offset), - shift(this.#shift), - ...this.#middleware, - ], - ...this.#floatingUIOptions, - }) - .then(({ x, y }) => { - Object.assign(this.element.style, { - left: `${x}px`, - top: `${y}px`, - }) - }) - .catch(console.error) + this.#cleanupAutoUpdate?.() + this.#cleanupAutoUpdate = void 0 + + const reference = { ...virtualElement, contextElement: editorView?.dom } + + if (editorView && this.#shouldAutoUpdate(editorView)) { + this.#cleanupAutoUpdate = autoUpdate(reference, this.element, () => + this.#updatePosition(reference) + ) + } else { + this.#updatePosition(reference) + } } this.onShow() From 950c329bb46ce139bd80427273dcba86dd4f508a Mon Sep 17 00:00:00 2001 From: Rainke Date: Mon, 15 Dec 2025 22:29:49 +0800 Subject: [PATCH 3/4] fix(plugin-block): add dragend event listener to block handle element (#2199) Co-authored-by: hep4 --- packages/plugins/plugin-block/src/block-service.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/plugins/plugin-block/src/block-service.ts b/packages/plugins/plugin-block/src/block-service.ts index d0fc6363547..98296431623 100644 --- a/packages/plugins/plugin-block/src/block-service.ts +++ b/packages/plugins/plugin-block/src/block-service.ts @@ -108,6 +108,7 @@ export class BlockService { dom.addEventListener('mousedown', this.#handleMouseDown) dom.addEventListener('mouseup', this.#handleMouseUp) dom.addEventListener('dragstart', this.#handleDragStart) + dom.addEventListener('dragend', this.#handleDragEnd) } /// Remove mouse event to the dom. @@ -115,6 +116,7 @@ export class BlockService { dom.removeEventListener('mousedown', this.#handleMouseDown) dom.removeEventListener('mouseup', this.#handleMouseUp) dom.removeEventListener('dragstart', this.#handleDragStart) + dom.removeEventListener('dragend', this.#handleDragEnd) } /// Unbind the notify function. @@ -171,6 +173,13 @@ export class BlockService { } } + /// @internal + #handleDragEnd = () => { + if (this.#view) { + this.#dragEnd(this.#view) + } + } + /// @internal keydownCallback = (view: EditorView) => { this.#hide() From 5e33ea30565eba5582141464ea859186de3de727 Mon Sep 17 00:00:00 2001 From: Juan Jose Rueda Date: Mon, 15 Dec 2025 10:07:44 -0500 Subject: [PATCH 4/4] fix: handle dom tables properly when copy pasting (#2206) * fix: handle dom tables properly when copy pasting * fix: typecheck --------- Co-authored-by: Saul-Mirone --- .../plugins/preset-gfm/src/node/table/schema.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/plugins/preset-gfm/src/node/table/schema.ts b/packages/plugins/preset-gfm/src/node/table/schema.ts index 99da7ee0743..19d9f975be2 100644 --- a/packages/plugins/preset-gfm/src/node/table/schema.ts +++ b/packages/plugins/preset-gfm/src/node/table/schema.ts @@ -71,7 +71,19 @@ export const tableHeaderRowSchema = $nodeSchema('table_header_row', () => ({ ...originalSchema.table_row, disableDropCursor: true, content: '(table_header)*', - parseDOM: [{ tag: 'tr[data-is-header]' }], + parseDOM: [ + { tag: 'tr[data-is-header]' }, + { + tag: 'tr', + getAttrs: (dom: HTMLElement) => { + if (dom instanceof HTMLElement) { + const hasHeader = dom.querySelector('th') + return hasHeader ? {} : false + } + return false + }, + }, + ], toDOM() { return ['tr', { 'data-is-header': true }, 0] },