From cf5df476d9ab978f846beec296fed5ce8743e48a Mon Sep 17 00:00:00 2001 From: Reffat Date: Sun, 5 Oct 2025 19:11:29 +0300 Subject: [PATCH 1/7] fix(iamgeSize): corrected the insertion of the svg image --- .../yfm/ImgSize/ImagePaste/index.ts | 85 +++++++++++++++++-- src/extensions/yfm/ImgSize/const.ts | 2 + src/extensions/yfm/ImgSize/utils.ts | 16 ++++ 3 files changed, 95 insertions(+), 8 deletions(-) diff --git a/src/extensions/yfm/ImgSize/ImagePaste/index.ts b/src/extensions/yfm/ImgSize/ImagePaste/index.ts index 3fed35e4c..25f81eb31 100644 --- a/src/extensions/yfm/ImgSize/ImagePaste/index.ts +++ b/src/extensions/yfm/ImgSize/ImagePaste/index.ts @@ -12,7 +12,8 @@ import type {FileUploadHandler} from '../../../../utils'; import {clipboardUtils} from '../../../behavior/Clipboard'; import {DataTransferType} from '../../../behavior/Clipboard/utils'; import {ImageAttr, ImgSizeAttr, imageType} from '../../../specs'; -import {type CreateImageNodeOptions, isImageNode} from '../utils'; +import {DEFAULT_SVG_HEIGHT, DEFAULT_SVG_WIDTH} from '../const'; +import {type CreateImageNodeOptions, checkSvg, isImageNode} from '../utils'; import {ImagesUploadProcess} from './upload'; @@ -82,14 +83,82 @@ export const ImagePaste: ExtensionAuto = (builder, opts) => { e.preventDefault(); - const imageNode = imageType(view.state.schema).create({ - src: imageUrl, - alt: title, - }); + const isSvg = checkSvg(imageUrl); - const tr = view.state.tr.replaceSelectionWith(imageNode); - view.dispatch(tr.scrollIntoView()); - logger.event({event: 'paste-url-as-image'}); + if (isSvg) { + const img = new Image(); + let loaded = false; + + const createImageNode = ( + width: number, + height: number, + logType: string, + ) => { + const imageNode = imageType(view.state.schema).create({ + src: imageUrl, + alt: title, + [ImgSizeAttr.Width]: width, + [ImgSizeAttr.Height]: height, + }); + + const tr = view.state.tr.replaceSelectionWith(imageNode); + view.dispatch(tr.scrollIntoView()); + logger.event({ + event: 'paste-url-as-image', + type: logType, + }); + }; + + const timeoutId = setTimeout(() => { + if (!loaded) { + createImageNode( + DEFAULT_SVG_WIDTH, + DEFAULT_SVG_HEIGHT, + 'svg-timeout', + ); + } + }, 2000); + + img.onload = () => { + loaded = true; + + clearTimeout(timeoutId); + + const width = + img.naturalWidth > 0 + ? img.naturalWidth + : DEFAULT_SVG_WIDTH; + const height = + img.naturalHeight > 0 + ? img.naturalHeight + : DEFAULT_SVG_HEIGHT; + + createImageNode(width, height, 'svg-with-dimensions'); + }; + + img.onerror = () => { + loaded = true; + + clearTimeout(timeoutId); + + createImageNode( + DEFAULT_SVG_WIDTH, + DEFAULT_SVG_HEIGHT, + 'svg-error-fallback', + ); + }; + + img.src = imageUrl; + } else { + const imageNode = imageType(view.state.schema).create({ + src: imageUrl, + alt: title, + }); + + const tr = view.state.tr.replaceSelectionWith(imageNode); + view.dispatch(tr.scrollIntoView()); + logger.event({event: 'paste-url-as-image'}); + } return true; } diff --git a/src/extensions/yfm/ImgSize/const.ts b/src/extensions/yfm/ImgSize/const.ts index fcb3c4c5a..d3260e1c5 100644 --- a/src/extensions/yfm/ImgSize/const.ts +++ b/src/extensions/yfm/ImgSize/const.ts @@ -6,3 +6,5 @@ export {imageNodeName, addImageAction} from '../../markdown/Image/const'; export const IMG_MAX_HEIGHT = 600; //px export type ImageRendererState = {linkAdded: boolean}; export const imageRendererKey = new PluginKey('imageRenderer'); +export const DEFAULT_SVG_HEIGHT = 200; +export const DEFAULT_SVG_WIDTH = 300; diff --git a/src/extensions/yfm/ImgSize/utils.ts b/src/extensions/yfm/ImgSize/utils.ts index 620dff022..26b025d59 100644 --- a/src/extensions/yfm/ImgSize/utils.ts +++ b/src/extensions/yfm/ImgSize/utils.ts @@ -34,6 +34,18 @@ export const createImageNode = logger.error({error: err}); } } + + const isSvg = checkSvg(result.url) || file.type === 'image/svg+xml'; + + if (isSvg) { + const sizes = await loadImage(file).then(getImageSizeNew); + return imgType.create({ + ...attrs, + [ImgSizeAttr.Width]: sizes.width, + [ImgSizeAttr.Height]: sizes.height, + }); + } + return imgType.create(attrs); }; @@ -64,3 +76,7 @@ export function getImageSizeNew({width, height}: HTMLImageElement): { }); return {width: String(size.width), height: String(size.height)}; } + +export function checkSvg(imageUrl?: string) { + return imageUrl && (/\.svg($|\?|#)/i.test(imageUrl) || imageUrl.startsWith('data:image/svg')); +} From a9105ac769abcf14b6b1e6170625dc671588f93a Mon Sep 17 00:00:00 2001 From: Reffat Date: Wed, 8 Oct 2025 16:00:22 +0300 Subject: [PATCH 2/7] fix(ImageSize): fixed callback hell --- .../yfm/ImgSize/ImagePaste/index.ts | 121 +++++++----------- src/extensions/yfm/ImgSize/const.ts | 1 - src/extensions/yfm/ImgSize/utils.ts | 19 ++- 3 files changed, 63 insertions(+), 78 deletions(-) diff --git a/src/extensions/yfm/ImgSize/ImagePaste/index.ts b/src/extensions/yfm/ImgSize/ImagePaste/index.ts index 25f81eb31..2d10e2be6 100644 --- a/src/extensions/yfm/ImgSize/ImagePaste/index.ts +++ b/src/extensions/yfm/ImgSize/ImagePaste/index.ts @@ -12,8 +12,15 @@ import type {FileUploadHandler} from '../../../../utils'; import {clipboardUtils} from '../../../behavior/Clipboard'; import {DataTransferType} from '../../../behavior/Clipboard/utils'; import {ImageAttr, ImgSizeAttr, imageType} from '../../../specs'; -import {DEFAULT_SVG_HEIGHT, DEFAULT_SVG_WIDTH} from '../const'; -import {type CreateImageNodeOptions, checkSvg, isImageNode} from '../utils'; +import {DEFAULT_SVG_WIDTH} from '../const'; +import { + type CreateImageNodeOptions, + checkSvg, + getImageSize, + getImageSizeNew, + isImageNode, + loadImageFromUrl, +} from '../utils'; import {ImagesUploadProcess} from './upload'; @@ -85,79 +92,47 @@ export const ImagePaste: ExtensionAuto = (builder, opts) => { const isSvg = checkSvg(imageUrl); - if (isSvg) { - const img = new Image(); - let loaded = false; - - const createImageNode = ( - width: number, - height: number, - logType: string, - ) => { - const imageNode = imageType(view.state.schema).create({ - src: imageUrl, - alt: title, - [ImgSizeAttr.Width]: width, - [ImgSizeAttr.Height]: height, - }); + const imageNode = imageType(view.state.schema).create({ + src: imageUrl, + alt: title, + }); - const tr = view.state.tr.replaceSelectionWith(imageNode); - view.dispatch(tr.scrollIntoView()); - logger.event({ - event: 'paste-url-as-image', - type: logType, - }); - }; - - const timeoutId = setTimeout(() => { - if (!loaded) { - createImageNode( - DEFAULT_SVG_WIDTH, - DEFAULT_SVG_HEIGHT, - 'svg-timeout', + const tr = view.state.tr.replaceSelectionWith(imageNode); + view.dispatch(tr.scrollIntoView()); + logger.event({event: 'paste-url-as-image'}); + + if (isSvg) { + const insertedPos = tr.selection.from - imageNode.nodeSize; + + loadImageFromUrl(imageUrl) + .then((img) => { + const sizes = opts?.enableNewImageSizeCalculation + ? getImageSizeNew(img) + : getImageSize(img); + + const updateTr = view.state.tr.setNodeMarkup( + insertedPos, + undefined, + { + ...imageNode.attrs, + width: img.width || DEFAULT_SVG_WIDTH, + ...sizes, + }, ); - } - }, 2000); - - img.onload = () => { - loaded = true; - - clearTimeout(timeoutId); - - const width = - img.naturalWidth > 0 - ? img.naturalWidth - : DEFAULT_SVG_WIDTH; - const height = - img.naturalHeight > 0 - ? img.naturalHeight - : DEFAULT_SVG_HEIGHT; - - createImageNode(width, height, 'svg-with-dimensions'); - }; - - img.onerror = () => { - loaded = true; - - clearTimeout(timeoutId); - - createImageNode( - DEFAULT_SVG_WIDTH, - DEFAULT_SVG_HEIGHT, - 'svg-error-fallback', - ); - }; - - img.src = imageUrl; - } else { - const imageNode = imageType(view.state.schema).create({ - src: imageUrl, - alt: title, - }); - - const tr = view.state.tr.replaceSelectionWith(imageNode); - view.dispatch(tr.scrollIntoView()); - logger.event({event: 'paste-url-as-image'}); + view.dispatch(updateTr); + + logger.event({ + event: 'svg-dimensions-updated', + position: insertedPos, + sizes, + }); + }) + .catch((error) => { + logger.error({ + event: 'svg-dimensions-load-failed', + error: error.message, + }); + }); } return true; diff --git a/src/extensions/yfm/ImgSize/const.ts b/src/extensions/yfm/ImgSize/const.ts index d3260e1c5..5e9329d49 100644 --- a/src/extensions/yfm/ImgSize/const.ts +++ b/src/extensions/yfm/ImgSize/const.ts @@ -6,5 +6,4 @@ export {imageNodeName, addImageAction} from '../../markdown/Image/const'; export const IMG_MAX_HEIGHT = 600; //px export type ImageRendererState = {linkAdded: boolean}; export const imageRendererKey = new PluginKey('imageRenderer'); -export const DEFAULT_SVG_HEIGHT = 200; export const DEFAULT_SVG_WIDTH = 300; diff --git a/src/extensions/yfm/ImgSize/utils.ts b/src/extensions/yfm/ImgSize/utils.ts index 26b025d59..7a9470ab9 100644 --- a/src/extensions/yfm/ImgSize/utils.ts +++ b/src/extensions/yfm/ImgSize/utils.ts @@ -5,7 +5,7 @@ import {type UploadSuccessItem, getProportionalSize} from '../../../utils'; import {imageNodeName} from '../../markdown'; import {ImgSizeAttr} from '../../specs'; -import {IMG_MAX_HEIGHT} from './const'; +import {DEFAULT_SVG_WIDTH, IMG_MAX_HEIGHT} from './const'; export function isImageNode(node: Node): boolean { return node.type.name === imageNodeName; @@ -38,11 +38,13 @@ export const createImageNode = const isSvg = checkSvg(result.url) || file.type === 'image/svg+xml'; if (isSvg) { - const sizes = await loadImage(file).then(getImageSizeNew); + const sizes = await loadImage(file).then( + opts.enableNewImageSizeCalculation ? getImageSizeNew : getImageSize, + ); return imgType.create({ + width: DEFAULT_SVG_WIDTH, ...attrs, - [ImgSizeAttr.Width]: sizes.width, - [ImgSizeAttr.Height]: sizes.height, + ...sizes, }); } @@ -61,6 +63,15 @@ export async function loadImage(imgFile: File) { }); } +export async function loadImageFromUrl(url: string): Promise { + return new Promise((resolve, reject) => { + const img = new Image(); + img.onload = () => resolve(img); + img.onerror = (_e, _s, _l, _c, error) => reject(error); + img.src = url; + }); +} + export function getImageSize(img: HTMLImageElement): {[ImgSizeAttr.Height]?: string} { return {height: String(Math.min(IMG_MAX_HEIGHT, img.height))}; } From d197a89d9b8180a708c9affccb452654dce2721c Mon Sep 17 00:00:00 2001 From: Reffat Date: Wed, 8 Oct 2025 16:40:46 +0300 Subject: [PATCH 3/7] fix(iamgeSize): fixed upload image size --- src/extensions/yfm/ImgSize/utils.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/extensions/yfm/ImgSize/utils.ts b/src/extensions/yfm/ImgSize/utils.ts index 7a9470ab9..071bc63b4 100644 --- a/src/extensions/yfm/ImgSize/utils.ts +++ b/src/extensions/yfm/ImgSize/utils.ts @@ -38,12 +38,15 @@ export const createImageNode = const isSvg = checkSvg(result.url) || file.type === 'image/svg+xml'; if (isSvg) { - const sizes = await loadImage(file).then( - opts.enableNewImageSizeCalculation ? getImageSizeNew : getImageSize, - ); + const image = await loadImage(file); + + const sizes = opts.enableNewImageSizeCalculation + ? getImageSizeNew(image) + : getImageSize(image); + return imgType.create({ - width: DEFAULT_SVG_WIDTH, ...attrs, + width: image.width || DEFAULT_SVG_WIDTH, ...sizes, }); } From 5112f600b712c1f2a6968410f4a90d587c6149c0 Mon Sep 17 00:00:00 2001 From: Reffat Date: Wed, 15 Oct 2025 20:27:28 +0300 Subject: [PATCH 4/7] fix(ImageSize): fix current position and code style --- .../yfm/ImgSize/ImagePaste/index.ts | 27 ++++++++------ src/extensions/yfm/ImgSize/utils.ts | 36 +++++++++---------- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/extensions/yfm/ImgSize/ImagePaste/index.ts b/src/extensions/yfm/ImgSize/ImagePaste/index.ts index 2d10e2be6..b377cd90a 100644 --- a/src/extensions/yfm/ImgSize/ImagePaste/index.ts +++ b/src/extensions/yfm/ImgSize/ImagePaste/index.ts @@ -99,7 +99,7 @@ export const ImagePaste: ExtensionAuto = (builder, opts) => { const tr = view.state.tr.replaceSelectionWith(imageNode); view.dispatch(tr.scrollIntoView()); - logger.event({event: 'paste-url-as-image'}); + logger.log('paste-url-as-image'); if (isSvg) { const insertedPos = tr.selection.from - imageNode.nodeSize; @@ -110,20 +110,27 @@ export const ImagePaste: ExtensionAuto = (builder, opts) => { ? getImageSizeNew(img) : getImageSize(img); - const updateTr = view.state.tr.setNodeMarkup( - insertedPos, - undefined, - { - ...imageNode.attrs, - width: img.width || DEFAULT_SVG_WIDTH, - ...sizes, - }, + const currentState = view.state; + const actualPos = insertedPos; + + const nodeAtPos = currentState.doc.nodeAt(actualPos); + + if (!nodeAtPos) { + logger.error('svg-node-not-found'); + return; + } + + const updateTr = currentState.tr.setNodeAttribute( + actualPos, + 'width', + img.width || DEFAULT_SVG_WIDTH, ); + view.dispatch(updateTr); logger.event({ event: 'svg-dimensions-updated', - position: insertedPos, + position: actualPos, sizes, }); }) diff --git a/src/extensions/yfm/ImgSize/utils.ts b/src/extensions/yfm/ImgSize/utils.ts index 071bc63b4..a9fff6001 100644 --- a/src/extensions/yfm/ImgSize/utils.ts +++ b/src/extensions/yfm/ImgSize/utils.ts @@ -23,32 +23,28 @@ export const createImageNode = [ImgSizeAttr.Src]: result.url, [ImgSizeAttr.Alt]: result.name ?? file.name, }; - if (opts.needDimensions) { + + const isSvg = checkSvg(result.url) || file.type === 'image/svg+xml'; + + if (opts.needDimensions || isSvg) { try { - const sizes = await loadImage(file).then( - opts.enableNewImageSizeCalculation ? getImageSizeNew : getImageSize, - ); + const image = await loadImage(file); + + const sizes = opts.enableNewImageSizeCalculation + ? getImageSizeNew(image) + : getImageSize(image); + Object.assign(attrs, sizes); + + if (isSvg && !opts.enableNewImageSizeCalculation) { + Object.assign(attrs, {width: image.width || DEFAULT_SVG_WIDTH}); + } } catch (err) { globalLogger.error(err); logger.error({error: err}); - } - } - - const isSvg = checkSvg(result.url) || file.type === 'image/svg+xml'; - if (isSvg) { - const image = await loadImage(file); - - const sizes = opts.enableNewImageSizeCalculation - ? getImageSizeNew(image) - : getImageSize(image); - - return imgType.create({ - ...attrs, - width: image.width || DEFAULT_SVG_WIDTH, - ...sizes, - }); + if (isSvg) attrs.width = String(DEFAULT_SVG_WIDTH); + } } return imgType.create(attrs); From 118f1362937ed5b8c809f17ff89f9af24cf4d3ab Mon Sep 17 00:00:00 2001 From: Reffat Date: Fri, 17 Oct 2025 14:44:36 +0300 Subject: [PATCH 5/7] fix(ImageSize): add tracking id for preload image --- .../yfm/ImgSize/ImagePaste/index.ts | 43 +++++++++++++------ .../yfm/ImgSize/ImgSizeSpecs/const.ts | 1 + .../yfm/ImgSize/ImgSizeSpecs/index.ts | 4 ++ 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/extensions/yfm/ImgSize/ImagePaste/index.ts b/src/extensions/yfm/ImgSize/ImagePaste/index.ts index b377cd90a..bade64fc4 100644 --- a/src/extensions/yfm/ImgSize/ImagePaste/index.ts +++ b/src/extensions/yfm/ImgSize/ImagePaste/index.ts @@ -92,18 +92,21 @@ export const ImagePaste: ExtensionAuto = (builder, opts) => { const isSvg = checkSvg(imageUrl); + const trackingId = isSvg + ? `svg-${Math.random().toString(36).slice(2)}` + : undefined; + const imageNode = imageType(view.state.schema).create({ src: imageUrl, alt: title, + ...(trackingId && {id: trackingId}), }); const tr = view.state.tr.replaceSelectionWith(imageNode); view.dispatch(tr.scrollIntoView()); logger.log('paste-url-as-image'); - if (isSvg) { - const insertedPos = tr.selection.from - imageNode.nodeSize; - + if (isSvg && trackingId) { loadImageFromUrl(imageUrl) .then((img) => { const sizes = opts?.enableNewImageSizeCalculation @@ -111,26 +114,40 @@ export const ImagePaste: ExtensionAuto = (builder, opts) => { : getImageSize(img); const currentState = view.state; - const actualPos = insertedPos; - const nodeAtPos = currentState.doc.nodeAt(actualPos); + let targetPos: number | null = null; + currentState.doc.descendants((node, pos) => { + if ( + isImageNode(node) && + node.attrs.id === trackingId + ) { + targetPos = pos; + return false; + } + return true; + }); - if (!nodeAtPos) { - logger.error('svg-node-not-found'); + if (targetPos === null) { + logger.error({ + event: 'svg-node-not-found', + trackingId, + }); return; } - const updateTr = currentState.tr.setNodeAttribute( - actualPos, - 'width', - img.width || DEFAULT_SVG_WIDTH, - ); + const updateTr = currentState.tr + .setNodeAttribute( + targetPos, + 'width', + img.width || DEFAULT_SVG_WIDTH, + ) + .setNodeAttribute(targetPos, 'id', null); view.dispatch(updateTr); logger.event({ event: 'svg-dimensions-updated', - position: actualPos, + position: targetPos, sizes, }); }) diff --git a/src/extensions/yfm/ImgSize/ImgSizeSpecs/const.ts b/src/extensions/yfm/ImgSize/ImgSizeSpecs/const.ts index ae465a1b7..1a1e081aa 100644 --- a/src/extensions/yfm/ImgSize/ImgSizeSpecs/const.ts +++ b/src/extensions/yfm/ImgSize/ImgSizeSpecs/const.ts @@ -7,4 +7,5 @@ export const ImgSizeAttr = { Width: ImsizeAttr.Width, Height: ImsizeAttr.Height, Loading: 'loading', + Id: 'id', } as const; diff --git a/src/extensions/yfm/ImgSize/ImgSizeSpecs/index.ts b/src/extensions/yfm/ImgSize/ImgSizeSpecs/index.ts index b9c197eaa..785747a09 100644 --- a/src/extensions/yfm/ImgSize/ImgSizeSpecs/index.ts +++ b/src/extensions/yfm/ImgSize/ImgSizeSpecs/index.ts @@ -15,6 +15,7 @@ type ImsizeTypedAttributes = { [ImgSizeAttr.Width]: string | null; [ImgSizeAttr.Height]: string | null; [ImgSizeAttr.Loading]: string | null; + [ImgSizeAttr.Id]: string | null; }; export {ImgSizeAttr}; @@ -40,6 +41,7 @@ export const ImgSizeSpecs: ExtensionAuto = (builder, opts) [ImgSizeAttr.Height]: {default: null}, [ImgSizeAttr.Width]: {default: null}, [ImgSizeAttr.Loading]: {default: null}, + [ImgSizeAttr.Id]: {default: null}, }, placeholder: placeholderContent ? {content: placeholderContent} : opts.placeholder, group: 'inline', @@ -60,6 +62,7 @@ export const ImgSizeSpecs: ExtensionAuto = (builder, opts) ), [ImgSizeAttr.Height]: isNumber(height) ? height : null, [ImgSizeAttr.Width]: isNumber(width) ? height : null, + [ImgSizeAttr.Id]: (dom as Element).getAttribute(ImgSizeAttr.Id), }; }, }, @@ -79,6 +82,7 @@ export const ImgSizeSpecs: ExtensionAuto = (builder, opts) [ImgSizeAttr.Width]: tok.attrGet(ImgSizeAttr.Width), [ImgSizeAttr.Loading]: tok.attrGet(ImgSizeAttr.Loading), [ImgSizeAttr.Alt]: tok.children?.[0]?.content || null, + [ImgSizeAttr.Id]: tok.attrGet(ImgSizeAttr.Id), }), }, }, From 059da8c14e24bcaa0efc8e22dc3701e54df7057b Mon Sep 17 00:00:00 2001 From: Reffat Date: Fri, 17 Oct 2025 16:43:45 +0300 Subject: [PATCH 6/7] fix(ImageSize): revert utils and add sizes for url upload images --- .../yfm/ImgSize/ImagePaste/index.ts | 32 +++++++++++-------- src/extensions/yfm/ImgSize/const.ts | 1 - src/extensions/yfm/ImgSize/utils.ts | 22 +++---------- 3 files changed, 24 insertions(+), 31 deletions(-) diff --git a/src/extensions/yfm/ImgSize/ImagePaste/index.ts b/src/extensions/yfm/ImgSize/ImagePaste/index.ts index bade64fc4..913a60002 100644 --- a/src/extensions/yfm/ImgSize/ImagePaste/index.ts +++ b/src/extensions/yfm/ImgSize/ImagePaste/index.ts @@ -12,7 +12,6 @@ import type {FileUploadHandler} from '../../../../utils'; import {clipboardUtils} from '../../../behavior/Clipboard'; import {DataTransferType} from '../../../behavior/Clipboard/utils'; import {ImageAttr, ImgSizeAttr, imageType} from '../../../specs'; -import {DEFAULT_SVG_WIDTH} from '../const'; import { type CreateImageNodeOptions, checkSvg, @@ -92,9 +91,7 @@ export const ImagePaste: ExtensionAuto = (builder, opts) => { const isSvg = checkSvg(imageUrl); - const trackingId = isSvg - ? `svg-${Math.random().toString(36).slice(2)}` - : undefined; + const trackingId = `img-${Math.random().toString(36).slice(2)}`; const imageNode = imageType(view.state.schema).create({ src: imageUrl, @@ -106,12 +103,16 @@ export const ImagePaste: ExtensionAuto = (builder, opts) => { view.dispatch(tr.scrollIntoView()); logger.log('paste-url-as-image'); - if (isSvg && trackingId) { + if (trackingId) { loadImageFromUrl(imageUrl) .then((img) => { - const sizes = opts?.enableNewImageSizeCalculation - ? getImageSizeNew(img) - : getImageSize(img); + const sizes: { + [ImgSizeAttr.Height]?: string; + [ImgSizeAttr.Width]?: string; + } = + opts?.enableNewImageSizeCalculation || isSvg + ? getImageSizeNew(img) + : getImageSize(img); const currentState = view.state; @@ -129,7 +130,7 @@ export const ImagePaste: ExtensionAuto = (builder, opts) => { if (targetPos === null) { logger.error({ - event: 'svg-node-not-found', + event: 'img-node-not-found', trackingId, }); return; @@ -138,22 +139,27 @@ export const ImagePaste: ExtensionAuto = (builder, opts) => { const updateTr = currentState.tr .setNodeAttribute( targetPos, - 'width', - img.width || DEFAULT_SVG_WIDTH, + ImgSizeAttr.Height, + sizes.height, + ) + .setNodeAttribute( + targetPos, + ImgSizeAttr.Width, + sizes.width, ) .setNodeAttribute(targetPos, 'id', null); view.dispatch(updateTr); logger.event({ - event: 'svg-dimensions-updated', + event: 'img-dimensions-updated', position: targetPos, sizes, }); }) .catch((error) => { logger.error({ - event: 'svg-dimensions-load-failed', + event: 'img-dimensions-load-failed', error: error.message, }); }); diff --git a/src/extensions/yfm/ImgSize/const.ts b/src/extensions/yfm/ImgSize/const.ts index 5e9329d49..fcb3c4c5a 100644 --- a/src/extensions/yfm/ImgSize/const.ts +++ b/src/extensions/yfm/ImgSize/const.ts @@ -6,4 +6,3 @@ export {imageNodeName, addImageAction} from '../../markdown/Image/const'; export const IMG_MAX_HEIGHT = 600; //px export type ImageRendererState = {linkAdded: boolean}; export const imageRendererKey = new PluginKey('imageRenderer'); -export const DEFAULT_SVG_WIDTH = 300; diff --git a/src/extensions/yfm/ImgSize/utils.ts b/src/extensions/yfm/ImgSize/utils.ts index a9fff6001..cce35d9ba 100644 --- a/src/extensions/yfm/ImgSize/utils.ts +++ b/src/extensions/yfm/ImgSize/utils.ts @@ -5,7 +5,7 @@ import {type UploadSuccessItem, getProportionalSize} from '../../../utils'; import {imageNodeName} from '../../markdown'; import {ImgSizeAttr} from '../../specs'; -import {DEFAULT_SVG_WIDTH, IMG_MAX_HEIGHT} from './const'; +import {IMG_MAX_HEIGHT} from './const'; export function isImageNode(node: Node): boolean { return node.type.name === imageNodeName; @@ -24,29 +24,17 @@ export const createImageNode = [ImgSizeAttr.Alt]: result.name ?? file.name, }; - const isSvg = checkSvg(result.url) || file.type === 'image/svg+xml'; - - if (opts.needDimensions || isSvg) { + if (opts.needDimensions) { try { - const image = await loadImage(file); - - const sizes = opts.enableNewImageSizeCalculation - ? getImageSizeNew(image) - : getImageSize(image); - + const sizes = await loadImage(file).then( + opts.enableNewImageSizeCalculation ? getImageSizeNew : getImageSize, + ); Object.assign(attrs, sizes); - - if (isSvg && !opts.enableNewImageSizeCalculation) { - Object.assign(attrs, {width: image.width || DEFAULT_SVG_WIDTH}); - } } catch (err) { globalLogger.error(err); logger.error({error: err}); - - if (isSvg) attrs.width = String(DEFAULT_SVG_WIDTH); } } - return imgType.create(attrs); }; From 48bbc1adb46dd3ba38718b20fa2ca7bfc5c7e755 Mon Sep 17 00:00:00 2001 From: Reffat Date: Fri, 17 Oct 2025 18:19:07 +0300 Subject: [PATCH 7/7] fix(ImageSize): edits based on the review --- .../yfm/ImgSize/ImagePaste/index.ts | 62 +++++++++---------- src/extensions/yfm/ImgSize/utils.ts | 14 +++++ 2 files changed, 43 insertions(+), 33 deletions(-) diff --git a/src/extensions/yfm/ImgSize/ImagePaste/index.ts b/src/extensions/yfm/ImgSize/ImagePaste/index.ts index 913a60002..5af88cf51 100644 --- a/src/extensions/yfm/ImgSize/ImagePaste/index.ts +++ b/src/extensions/yfm/ImgSize/ImagePaste/index.ts @@ -15,6 +15,7 @@ import {ImageAttr, ImgSizeAttr, imageType} from '../../../specs'; import { type CreateImageNodeOptions, checkSvg, + findImageNode, getImageSize, getImageSizeNew, isImageNode, @@ -96,39 +97,32 @@ export const ImagePaste: ExtensionAuto = (builder, opts) => { const imageNode = imageType(view.state.schema).create({ src: imageUrl, alt: title, - ...(trackingId && {id: trackingId}), + id: trackingId, }); const tr = view.state.tr.replaceSelectionWith(imageNode); view.dispatch(tr.scrollIntoView()); - logger.log('paste-url-as-image'); - - if (trackingId) { - loadImageFromUrl(imageUrl) - .then((img) => { - const sizes: { - [ImgSizeAttr.Height]?: string; - [ImgSizeAttr.Width]?: string; - } = - opts?.enableNewImageSizeCalculation || isSvg - ? getImageSizeNew(img) - : getImageSize(img); - + logger.event({event: 'paste-url-as-image'}); + + loadImageFromUrl(imageUrl) + .then((img) => + opts?.enableNewImageSizeCalculation || isSvg + ? getImageSizeNew(img) + : getImageSize(img), + ) + .then( + (sizes: { + [ImgSizeAttr.Height]?: string; + [ImgSizeAttr.Width]?: string; + }) => { const currentState = view.state; - let targetPos: number | null = null; - currentState.doc.descendants((node, pos) => { - if ( - isImageNode(node) && - node.attrs.id === trackingId - ) { - targetPos = pos; - return false; - } - return true; - }); + const imageResult = findImageNode( + currentState.doc, + trackingId, + ); - if (targetPos === null) { + if (imageResult === null) { logger.error({ event: 'img-node-not-found', trackingId, @@ -136,6 +130,8 @@ export const ImagePaste: ExtensionAuto = (builder, opts) => { return; } + const {pos: targetPos} = imageResult; + const updateTr = currentState.tr .setNodeAttribute( targetPos, @@ -156,14 +152,14 @@ export const ImagePaste: ExtensionAuto = (builder, opts) => { position: targetPos, sizes, }); - }) - .catch((error) => { - logger.error({ - event: 'img-dimensions-load-failed', - error: error.message, - }); + }, + ) + .catch((error) => { + logger.error({ + event: 'img-dimensions-load-failed', + error: error.message, }); - } + }); return true; } diff --git a/src/extensions/yfm/ImgSize/utils.ts b/src/extensions/yfm/ImgSize/utils.ts index cce35d9ba..d3953652d 100644 --- a/src/extensions/yfm/ImgSize/utils.ts +++ b/src/extensions/yfm/ImgSize/utils.ts @@ -78,3 +78,17 @@ export function getImageSizeNew({width, height}: HTMLImageElement): { export function checkSvg(imageUrl?: string) { return imageUrl && (/\.svg($|\?|#)/i.test(imageUrl) || imageUrl.startsWith('data:image/svg')); } + +export function findImageNode(doc: Node, id: string): {node: Node; pos: number} | null { + let result: {node: Node; pos: number} | null = null; + + doc.descendants((node, pos) => { + if (isImageNode(node) && node.attrs.id === id) { + result = {node, pos}; + return false; + } + return true; + }); + + return result; +}