From 176d3be5ffb7c84338c9ad3ea071d7851aa0ec41 Mon Sep 17 00:00:00 2001 From: Andrei Tuicu Date: Wed, 13 May 2026 10:16:47 +0200 Subject: [PATCH 1/8] fix: Images stored in media bus with auth enabled not rendering --- blocks/edit/prose/index.js | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/blocks/edit/prose/index.js b/blocks/edit/prose/index.js index ef2b7419..de381706 100644 --- a/blocks/edit/prose/index.js +++ b/blocks/edit/prose/index.js @@ -337,12 +337,27 @@ function restoreCursorPosition(view) { } } +function rewriteImageSrcs(pm) { + pm.querySelectorAll('img[src]').forEach((img) => { + try { + const url = new URL(img.src); + if (url.host.endsWith('.aem.page') || url.host.endsWith('.aem.live')) { + url.host = url.host.replace(/\.aem\.(page|live)$/, '.preview.da.live'); + img.src = url.toString(); + } + } catch { + // relative or malformed src — skip + } + }); +} + function addSyncedListener(wsProvider, canWrite) { onWsSync(wsProvider, () => { - if (canWrite) { - const pm = document.querySelector('da-content')?.shadowRoot - .querySelector('da-editor')?.shadowRoot.querySelector('.ProseMirror'); - if (pm) pm.contentEditable = 'true'; + const pm = document.querySelector('da-content')?.shadowRoot + .querySelector('da-editor')?.shadowRoot.querySelector('.ProseMirror'); + if (pm) { + if (canWrite) pm.contentEditable = 'true'; + rewriteImageSrcs(pm); } }); } From 6685bd95fb1ee243e102a23bc0b6eb3fe9fa71ca Mon Sep 17 00:00:00 2001 From: Andrei Tuicu Date: Wed, 13 May 2026 10:40:36 +0200 Subject: [PATCH 2/8] fix: Images stored in media bus with auth enabled not rendering --- blocks/edit/prose/index.js | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/blocks/edit/prose/index.js b/blocks/edit/prose/index.js index de381706..4fd49fee 100644 --- a/blocks/edit/prose/index.js +++ b/blocks/edit/prose/index.js @@ -337,17 +337,25 @@ function restoreCursorPosition(view) { } } +function rewriteAemHost(urlStr) { + try { + const url = new URL(urlStr); + if (url.host.endsWith('.aem.page') || url.host.endsWith('.aem.live')) { + url.host = url.host.replace(/\.aem\.(page|live)$/, '.preview.da.live'); + return url.toString(); + } + } catch { + // relative or malformed — leave unchanged + } + return urlStr; +} + function rewriteImageSrcs(pm) { pm.querySelectorAll('img[src]').forEach((img) => { - try { - const url = new URL(img.src); - if (url.host.endsWith('.aem.page') || url.host.endsWith('.aem.live')) { - url.host = url.host.replace(/\.aem\.(page|live)$/, '.preview.da.live'); - img.src = url.toString(); - } - } catch { - // relative or malformed src — skip - } + img.src = rewriteAemHost(img.src); + }); + pm.querySelectorAll('source[srcset]').forEach((source) => { + source.srcset = rewriteAemHost(source.srcset); }); } From c22aa1eb6248341e3ce4bc541e5d73afcb170cf4 Mon Sep 17 00:00:00 2001 From: Andrei Tuicu Date: Wed, 13 May 2026 10:55:49 +0200 Subject: [PATCH 3/8] fix: Images stored in media bus with auth enabled not rendering --- blocks/edit/prose/index.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/blocks/edit/prose/index.js b/blocks/edit/prose/index.js index 4fd49fee..03293fb6 100644 --- a/blocks/edit/prose/index.js +++ b/blocks/edit/prose/index.js @@ -361,12 +361,12 @@ function rewriteImageSrcs(pm) { function addSyncedListener(wsProvider, canWrite) { onWsSync(wsProvider, () => { - const pm = document.querySelector('da-content')?.shadowRoot - .querySelector('da-editor')?.shadowRoot.querySelector('.ProseMirror'); - if (pm) { - if (canWrite) pm.contentEditable = 'true'; - rewriteImageSrcs(pm); + if (canWrite) { + const pm = document.querySelector('da-content')?.shadowRoot + .querySelector('da-editor')?.shadowRoot.querySelector('.ProseMirror'); + if (pm) pm.contentEditable = 'true'; } + rewriteImageSrcs(window.view.dom); }); } @@ -437,6 +437,7 @@ function applyDelayedPlugins(pluginsPromise, schema, canWrite, basePlugins) { // Reconfigure the view with the full plugin list const newState = window.view.state.reconfigure({ plugins: pluginList }); window.view.updateState(newState); + rewriteImageSrcs(window.view.dom); }); } From 836289a8aca243e9c30f74424ad58d7c60abe9ff Mon Sep 17 00:00:00 2001 From: Andrei Tuicu Date: Wed, 13 May 2026 11:12:32 +0200 Subject: [PATCH 4/8] fix: Images stored in media bus with auth enabled not rendering --- blocks/edit/prose/plugins/imageFocalPoint.js | 26 ++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/blocks/edit/prose/plugins/imageFocalPoint.js b/blocks/edit/prose/plugins/imageFocalPoint.js index b7c04626..1132cbea 100644 --- a/blocks/edit/prose/plugins/imageFocalPoint.js +++ b/blocks/edit/prose/plugins/imageFocalPoint.js @@ -6,6 +6,19 @@ import { getTableInfo, isInTableCell } from './tableUtils.js'; const imageFocalPointKey = new PluginKey('imageFocalPoint'); +function rewriteAemHost(urlStr) { + try { + const url = new URL(urlStr); + if (url.host.endsWith('.aem.page') || url.host.endsWith('.aem.live')) { + url.host = url.host.replace(/\.aem\.(page|live)$/, '.preview.da.live'); + return url.toString(); + } + } catch { + // relative or malformed — leave unchanged + } + return urlStr; +} + // Cache blocks data at module level let blocksDataPromise = null; async function getBlocksData() { @@ -40,7 +53,7 @@ function shouldShowFocalPoint(tableName, blocks) { } function updateImageAttributes(img, attrs) { - img.src = attrs.src; + img.src = rewriteAemHost(attrs.src); ['alt', 'title', 'width', 'height'].forEach((attr) => { if (attrs[attr]) { img[attr] = attrs[attr]; @@ -150,7 +163,16 @@ export default function imageFocalPoint() { if (isInTableCell(view.state, getPos())) { return new ImageWithFocalPointView(node, view, getPos); } - return null; + const img = document.createElement('img'); + updateImageAttributes(img, node.attrs); + return { + dom: img, + update(updated) { + if (updated.type.name !== 'image') return false; + updateImageAttributes(img, updated.attrs); + return true; + }, + }; }, }, }, From b08d1d9a999335e64ebf70840e7e0b9f19a63cbe Mon Sep 17 00:00:00 2001 From: Andrei Tuicu Date: Wed, 13 May 2026 11:16:14 +0200 Subject: [PATCH 5/8] fix: Images stored in media bus with auth enabled not rendering --- blocks/edit/prose/index.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/blocks/edit/prose/index.js b/blocks/edit/prose/index.js index 03293fb6..5123d80b 100644 --- a/blocks/edit/prose/index.js +++ b/blocks/edit/prose/index.js @@ -350,15 +350,6 @@ function rewriteAemHost(urlStr) { return urlStr; } -function rewriteImageSrcs(pm) { - pm.querySelectorAll('img[src]').forEach((img) => { - img.src = rewriteAemHost(img.src); - }); - pm.querySelectorAll('source[srcset]').forEach((source) => { - source.srcset = rewriteAemHost(source.srcset); - }); -} - function addSyncedListener(wsProvider, canWrite) { onWsSync(wsProvider, () => { if (canWrite) { @@ -366,7 +357,6 @@ function addSyncedListener(wsProvider, canWrite) { .querySelector('da-editor')?.shadowRoot.querySelector('.ProseMirror'); if (pm) pm.contentEditable = 'true'; } - rewriteImageSrcs(window.view.dom); }); } @@ -437,7 +427,6 @@ function applyDelayedPlugins(pluginsPromise, schema, canWrite, basePlugins) { // Reconfigure the view with the full plugin list const newState = window.view.state.reconfigure({ plugins: pluginList }); window.view.updateState(newState); - rewriteImageSrcs(window.view.dom); }); } @@ -493,6 +482,20 @@ export default async function initProse({ path, permissions, doc, daContent, wsP state, dispatchTransaction, nodeViews: { + image(node) { + const img = document.createElement('img'); + img.src = rewriteAemHost(node.attrs.src); + if (node.attrs.alt) img.alt = node.attrs.alt; + return { + dom: img, + update(updated) { + if (updated.type.name !== 'image') return false; + img.src = rewriteAemHost(updated.attrs.src); + if (updated.attrs.alt) img.alt = updated.attrs.alt; + return true; + }, + }; + }, diff_added(node, view, getPos) { const LocAddedView = getDiffClass('da-diff-added', getSchema, dispatchTransaction, { isUpstream: false }); return new LocAddedView(node, view, getPos); From 59104748f491f798e1e769f4aff8dc4ac1b568fd Mon Sep 17 00:00:00 2001 From: Andrei Tuicu Date: Wed, 13 May 2026 11:28:35 +0200 Subject: [PATCH 6/8] fix: Images stored in media bus with auth enabled not rendering --- blocks/edit/prose/image-utils.js | 23 ++++++++++++++++++++ blocks/edit/prose/index.js | 17 +++------------ blocks/edit/prose/plugins/imageFocalPoint.js | 16 ++------------ 3 files changed, 28 insertions(+), 28 deletions(-) create mode 100644 blocks/edit/prose/image-utils.js diff --git a/blocks/edit/prose/image-utils.js b/blocks/edit/prose/image-utils.js new file mode 100644 index 00000000..c8a3da2c --- /dev/null +++ b/blocks/edit/prose/image-utils.js @@ -0,0 +1,23 @@ +/** + * Rewrites an image src from an AEM preview/publish host to the DA preview host. + * + * Images stored in DA content may reference *.aem.page (preview) or *.aem.live + * (publish) URLs. These hosts require AEM authentication that the DA editor does + * not carry, causing 401 errors when the browser fetches them. The equivalent + * *.preview.da.live URL serves the same content and is accessible from the editor. + * + * @param {string} src - The original image src URL. + * @returns {string} The rewritten src, or the original if no rewrite was needed. + */ +export function rewriteImageSrcForEditor(src) { + try { + const url = new URL(src); + if (url.host.endsWith('.aem.page') || url.host.endsWith('.aem.live')) { + url.host = url.host.replace(/\.aem\.(page|live)$/, '.preview.da.live'); + return url.toString(); + } + } catch { + // relative or malformed src — leave unchanged + } + return src; +} diff --git a/blocks/edit/prose/index.js b/blocks/edit/prose/index.js index 5123d80b..d933f216 100644 --- a/blocks/edit/prose/index.js +++ b/blocks/edit/prose/index.js @@ -27,6 +27,7 @@ import { COLLAB_ORIGIN, DA_ORIGIN } from '../../shared/constants.js'; import { daFetch, getAuthToken } from '../../shared/utils.js'; import { getDiffClass, checkForLocNodes, addActiveView } from './diff/diff-utils.js'; import { debounce, initDaMetadata } from '../utils/helpers.js'; +import { rewriteImageSrcForEditor } from './image-utils.js'; async function checkDoc(path) { return daFetch(path, { method: 'HEAD' }); @@ -337,18 +338,6 @@ function restoreCursorPosition(view) { } } -function rewriteAemHost(urlStr) { - try { - const url = new URL(urlStr); - if (url.host.endsWith('.aem.page') || url.host.endsWith('.aem.live')) { - url.host = url.host.replace(/\.aem\.(page|live)$/, '.preview.da.live'); - return url.toString(); - } - } catch { - // relative or malformed — leave unchanged - } - return urlStr; -} function addSyncedListener(wsProvider, canWrite) { onWsSync(wsProvider, () => { @@ -484,13 +473,13 @@ export default async function initProse({ path, permissions, doc, daContent, wsP nodeViews: { image(node) { const img = document.createElement('img'); - img.src = rewriteAemHost(node.attrs.src); + img.src = rewriteImageSrcForEditor(node.attrs.src); if (node.attrs.alt) img.alt = node.attrs.alt; return { dom: img, update(updated) { if (updated.type.name !== 'image') return false; - img.src = rewriteAemHost(updated.attrs.src); + img.src = rewriteImageSrcForEditor(updated.attrs.src); if (updated.attrs.alt) img.alt = updated.attrs.alt; return true; }, diff --git a/blocks/edit/prose/plugins/imageFocalPoint.js b/blocks/edit/prose/plugins/imageFocalPoint.js index 1132cbea..547a2d5a 100644 --- a/blocks/edit/prose/plugins/imageFocalPoint.js +++ b/blocks/edit/prose/plugins/imageFocalPoint.js @@ -1,4 +1,5 @@ import { Plugin, PluginKey } from 'da-y-wrapper'; +import { rewriteImageSrcForEditor } from '../image-utils.js'; import inlinesvg from '../../../shared/inlinesvg.js'; import { openFocalPointDialog } from './focalPointDialog.js'; import { loadLibrary } from '../../da-library/helpers/helpers.js'; @@ -6,19 +7,6 @@ import { getTableInfo, isInTableCell } from './tableUtils.js'; const imageFocalPointKey = new PluginKey('imageFocalPoint'); -function rewriteAemHost(urlStr) { - try { - const url = new URL(urlStr); - if (url.host.endsWith('.aem.page') || url.host.endsWith('.aem.live')) { - url.host = url.host.replace(/\.aem\.(page|live)$/, '.preview.da.live'); - return url.toString(); - } - } catch { - // relative or malformed — leave unchanged - } - return urlStr; -} - // Cache blocks data at module level let blocksDataPromise = null; async function getBlocksData() { @@ -53,7 +41,7 @@ function shouldShowFocalPoint(tableName, blocks) { } function updateImageAttributes(img, attrs) { - img.src = rewriteAemHost(attrs.src); + img.src = rewriteImageSrcForEditor(attrs.src); ['alt', 'title', 'width', 'height'].forEach((attr) => { if (attrs[attr]) { img[attr] = attrs[attr]; From aee0536c4a546c42729fb57ace40d21315d7318c Mon Sep 17 00:00:00 2001 From: Andrei Tuicu Date: Wed, 13 May 2026 11:47:56 +0200 Subject: [PATCH 7/8] fix: Images stored in media bus with auth enabled not rendering --- blocks/edit/prose/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/blocks/edit/prose/index.js b/blocks/edit/prose/index.js index d933f216..466bdf12 100644 --- a/blocks/edit/prose/index.js +++ b/blocks/edit/prose/index.js @@ -338,7 +338,6 @@ function restoreCursorPosition(view) { } } - function addSyncedListener(wsProvider, canWrite) { onWsSync(wsProvider, () => { if (canWrite) { From 7e714accc26cba25aa303f714d2f928d5a6e5dcd Mon Sep 17 00:00:00 2001 From: Andrei Tuicu Date: Wed, 13 May 2026 11:52:42 +0200 Subject: [PATCH 8/8] fix: Images stored in media bus with auth enabled not rendering --- .../unit/blocks/edit/prose/plugins/imageFocalPoint.test.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/unit/blocks/edit/prose/plugins/imageFocalPoint.test.js b/test/unit/blocks/edit/prose/plugins/imageFocalPoint.test.js index a72d5af4..00e3d9c9 100644 --- a/test/unit/blocks/edit/prose/plugins/imageFocalPoint.test.js +++ b/test/unit/blocks/edit/prose/plugins/imageFocalPoint.test.js @@ -128,7 +128,7 @@ describe('imageFocalPoint Plugin', () => { expect(icon.classList.contains('focal-point-icon-active')).to.be.true; }); - it('does not create node view for images outside table cells', () => { + it('creates a plain image node view for images outside table cells', () => { const plugin = imageFocalPoint(); const createNodeView = plugin.props.nodeViews.image; @@ -144,8 +144,9 @@ describe('imageFocalPoint Plugin', () => { const mockView = { state: mockState, dom: document.createElement('div') }; const getPos = () => 10; - const nodeView = createNodeView({}, mockView, getPos); - expect(nodeView).to.be.null; + const nodeView = createNodeView({ attrs: { src: 'https://example.com/img.jpg' } }, mockView, getPos); + expect(nodeView).to.not.be.null; + expect(nodeView.dom.tagName).to.equal('IMG'); }); it('updates node view correctly', async () => {