Summary
A canvas panel extension can read the document and insert into it, but it has no way to move the selection to a known place in the document. That one gap blocks a class of "manage the structure of this page" plugins, and it is the only thing standing between the current extension API and a working authoring tool we have prototyped.
Context
We are prototyping author tooling for section-scoped personalization on a customer site: sections carry an audience via section metadata (Style: audience-member, audience-silver), and each targeted section carries a generated note. Authoring this by hand is error-prone — the audience list is an unvalidated string, and a typo silently shows the section to everyone.
We built the tooling as a BYO panel extension, deliberately, to avoid asking for core changes. That works well for two of three jobs:
- Read — the handshake in
setupIframeChannel provides { org, repo, ref, path } plus the IMS token, so the plugin fetches the page source from admin.da.live and reports what each section targets. This is more capable than we first assumed and worth calling out in the docs.
- Insert —
sendHTML reaches insertHTML, which does replaceRange(from, to, …), so a plugin can insert correctly-formed content through the editor and stay collab-safe.
- Locate / edit an existing section — not possible.
The gap
channel.port1.onmessage in blocks/canvas/ew-panel-extensions/iframe-protocol.js handles sendText, sendHTML, setHash, setHref, closeLibrary, showPanel, setPrompt and getSelection. None of these can position the selection, and the canvas hash carries no node anchor. So a plugin cannot:
- Jump the canvas to a section the way
ew-page-outline does when you click an entry. Clicking an item in a plugin's own list of sections can't scroll the editor to it.
- Edit an existing section. Because
insertHTML replaces the current selection, the edit loop already works — but only if something can put the selection on the right node first. Without that, the only route left is writing the source via admin.da.live underneath a live collab session, which risks clobbering or being clobbered by the y.js document. We are not doing that, so editing is simply unavailable to plugins.
Proposed change
Expose what the outline rail already does internally, as one action:
if (action === 'selectSection') {
const ok = editorView ? selectSection(editorView, details?.index) : false;
channel.port1.postMessage({ action: 'selectSectionResult', details: { ok } });
return;
}
backed by a selectSection(view, sectionIndex) helper in editor-utils/blocks.js, which reuses the same "split top-level nodes on horizontal_rule" walk that deleteSection and moveSection already use, and ends in TextSelection + scrollIntoView().
Selecting the whole section rather than just scrolling to it is deliberate: it gives the plugin the jump and leaves a replaceable range under sendHTML, so the existing insert action covers editing with no second API.
Happy to be told this belongs somewhere else, or should be shaped differently (a node/prose-index address rather than a section index would be more general, but section index is what the existing helpers already speak).
Not asking for
Anything that lets a plugin mutate the document without going through the editor. The value here is precisely that edits stay inside ProseMirror and collab.
Summary
A canvas panel extension can read the document and insert into it, but it has no way to move the selection to a known place in the document. That one gap blocks a class of "manage the structure of this page" plugins, and it is the only thing standing between the current extension API and a working authoring tool we have prototyped.
Context
We are prototyping author tooling for section-scoped personalization on a customer site: sections carry an audience via section metadata (
Style: audience-member, audience-silver), and each targeted section carries a generated note. Authoring this by hand is error-prone — the audience list is an unvalidated string, and a typo silently shows the section to everyone.We built the tooling as a BYO panel extension, deliberately, to avoid asking for core changes. That works well for two of three jobs:
setupIframeChannelprovides{ org, repo, ref, path }plus the IMS token, so the plugin fetches the page source fromadmin.da.liveand reports what each section targets. This is more capable than we first assumed and worth calling out in the docs.sendHTMLreachesinsertHTML, which doesreplaceRange(from, to, …), so a plugin can insert correctly-formed content through the editor and stay collab-safe.The gap
channel.port1.onmessageinblocks/canvas/ew-panel-extensions/iframe-protocol.jshandlessendText,sendHTML,setHash,setHref,closeLibrary,showPanel,setPromptandgetSelection. None of these can position the selection, and the canvas hash carries no node anchor. So a plugin cannot:ew-page-outlinedoes when you click an entry. Clicking an item in a plugin's own list of sections can't scroll the editor to it.insertHTMLreplaces the current selection, the edit loop already works — but only if something can put the selection on the right node first. Without that, the only route left is writing the source viaadmin.da.liveunderneath a live collab session, which risks clobbering or being clobbered by the y.js document. We are not doing that, so editing is simply unavailable to plugins.Proposed change
Expose what the outline rail already does internally, as one action:
backed by a
selectSection(view, sectionIndex)helper ineditor-utils/blocks.js, which reuses the same "split top-level nodes onhorizontal_rule" walk thatdeleteSectionandmoveSectionalready use, and ends inTextSelection+scrollIntoView().Selecting the whole section rather than just scrolling to it is deliberate: it gives the plugin the jump and leaves a replaceable range under
sendHTML, so the existing insert action covers editing with no second API.Happy to be told this belongs somewhere else, or should be shaped differently (a node/prose-index address rather than a section index would be more general, but section index is what the existing helpers already speak).
Not asking for
Anything that lets a plugin mutate the document without going through the editor. The value here is precisely that edits stay inside ProseMirror and collab.