From 546b655dc25d11e2e71e6e7baaca01afb16b83bd Mon Sep 17 00:00:00 2001 From: MD Rifat Hossain Date: Sat, 15 Aug 2026 01:02:21 +0600 Subject: [PATCH 1/2] fix(extension-image): reveal resizable node view when the image is already cached (#8188) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(extension-image): reveal resizable node view when the image is already cached The resize node view hides the element and clears visibility only inside el.onload. A cached image is already complete before the node view mounts, so load never fires again and the image stays permanently invisible. Reveal immediately when the image is already loaded, and on error as well so a broken src renders as an inspectable broken image rather than an invisible node. * docs: shorten the changeset to one user-facing sentence Their guidelines ask for one short sentence about visible behaviour, without internals — mine explained the root cause. The reasoning stays in the commit and the PR, where it belongs. --- .../fix-image-resize-cached-image-hidden.md | 5 ++ .../extension-image/__tests__/image.spec.ts | 56 ++++++++++++++++++- packages/extension-image/src/image.ts | 18 +++++- 3 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 .changeset/fix-image-resize-cached-image-hidden.md diff --git a/.changeset/fix-image-resize-cached-image-hidden.md b/.changeset/fix-image-resize-cached-image-hidden.md new file mode 100644 index 0000000000..64561689a3 --- /dev/null +++ b/.changeset/fix-image-resize-cached-image-hidden.md @@ -0,0 +1,5 @@ +--- +'@tiptap/extension-image': patch +--- + +Fix resizable images staying hidden when the image is cached or fails to load. diff --git a/packages/extension-image/__tests__/image.spec.ts b/packages/extension-image/__tests__/image.spec.ts index 78c25f376c..f2875452ea 100644 --- a/packages/extension-image/__tests__/image.spec.ts +++ b/packages/extension-image/__tests__/image.spec.ts @@ -3,7 +3,7 @@ import Document from '@tiptap/extension-document' import Paragraph from '@tiptap/extension-paragraph' import Text from '@tiptap/extension-text' import { Markdown } from '@tiptap/markdown' -import { afterEach, describe, expect, it } from 'vitest' +import { afterEach, describe, expect, it, vi } from 'vitest' import Image from '../src/index.js' @@ -499,4 +499,58 @@ describe('extension-image', () => { expect(img?.hasAttribute('data-custom-id')).toBe(false) }) }) + + describe('resizable image visibility', () => { + const setImageLoaded = (loaded: boolean) => { + vi.spyOn(HTMLImageElement.prototype, 'complete', 'get').mockReturnValue(loaded) + vi.spyOn(HTMLImageElement.prototype, 'naturalWidth', 'get').mockReturnValue(loaded ? 100 : 0) + } + + const mountResizableImage = () => { + editor = new Editor({ + element: createEditorEl(), + extensions: [Document, Paragraph, Text, Image.configure({ resize: { enabled: true } })], + content: { + type: 'doc', + content: [{ type: 'image', attrs: { src: imgSrc } }], + }, + }) + + return editor.view.dom.querySelector('[data-resize-container]') as HTMLElement + } + + afterEach(() => { + vi.restoreAllMocks() + }) + + it('should show the node view when the image is already loaded on mount', () => { + // A cached image is already complete before the node view mounts, so + // `load` never fires again and the image would stay hidden forever. + setImageLoaded(true) + + expect(mountResizableImage().style.visibility).toBe('') + }) + + it('should hide the node view until a not-yet-loaded image fires load', () => { + setImageLoaded(false) + + const container = mountResizableImage() + + expect(container.style.visibility).toBe('hidden') + + container.querySelector('img')?.dispatchEvent(new Event('load')) + + expect(container.style.visibility).toBe('') + }) + + it('should show the node view when the image fails to load', () => { + setImageLoaded(false) + + const container = mountResizableImage() + + container.querySelector('img')?.dispatchEvent(new Event('error')) + + expect(container.style.visibility).toBe('') + }) + }) }) diff --git a/packages/extension-image/src/image.ts b/packages/extension-image/src/image.ts index ba2048a8e7..3894d3cb9b 100644 --- a/packages/extension-image/src/image.ts +++ b/packages/extension-image/src/image.ts @@ -278,13 +278,25 @@ export const Image = Node.create({ const dom = nodeView.dom as HTMLElement // when image is loaded, show the node view to get the correct dimensions - dom.style.visibility = 'hidden' - dom.style.pointerEvents = 'none' - el.onload = () => { + const showNodeView = () => { dom.style.visibility = '' dom.style.pointerEvents = '' } + dom.style.visibility = 'hidden' + dom.style.pointerEvents = 'none' + + if (el.complete && el.naturalWidth > 0) { + // A cached image is already complete before this node view mounts, so + // `load` never fires again and the image would stay hidden forever. + showNodeView() + } else { + el.onload = showNodeView + // Reveal on error too, so a broken src renders as an inspectable broken + // image instead of an invisible node. + el.onerror = showNodeView + } + return nodeView } }, From f28cf6d54ccf7d12de02eb2c557007cbb0a849bb Mon Sep 17 00:00:00 2001 From: bdbch Date: Fri, 14 Aug 2026 21:37:55 +0200 Subject: [PATCH 2/2] chore: remove old pnpm value in package.json --- package.json | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/package.json b/package.json index 878d7d4e89..d5ff8d7352 100644 --- a/package.json +++ b/package.json @@ -109,28 +109,5 @@ "engines": { "node": ">=24" }, - "packageManager": "pnpm@11.2.2", - "pnpm": { - "patchedDependencies": { - "@changesets/assemble-release-plan@6.0.5": "patches/@changesets__assemble-release-plan.patch", - "@changesets/assemble-release-plan@6.0.9": "patches/@changesets__assemble-release-plan@6.0.9.patch" - }, - "overrides": { - "@rollup/pluginutils>picomatch": "2.3.2", - "prosemirror-model": "^1.25.11", - "@octokit/action>undici": "6.24.1", - "anymatch>picomatch": "2.3.2", - "glob": "^10.5.0", - "micromatch>picomatch": "2.3.2", - "minimatch@^3.0.5": "3.1.4", - "minimatch@^3.1.2": "3.1.4", - "qs": "^6.14.1", - "rollup": "4.60.1", - "readdirp>picomatch": "2.3.2", - "sass>immutable": "5.1.5", - "terser-webpack-plugin>serialize-javascript": "7.0.5", - "tinyglobby>picomatch": "4.0.4", - "vitest>picomatch": "4.0.4" - } - } + "packageManager": "pnpm@11.2.2" }