feat: let canvas extensions select a section - #1282
Conversation
A panel extension can read the document and insert into it, but has no way to move the selection to a known place. That blocks two things a structural authoring plugin needs: jumping the canvas to a section the way the outline rail does, and editing a section at all -- insertHTML already replaces the current selection, so the edit loop works as soon as something can position that selection. Without it the only route left is writing source via admin.da.live underneath a live collab session, which risks clobbering or being clobbered by the y.js document. selectSection reuses the same "split top-level nodes on horizontal_rule" walk that deleteSection and moveSection already use, and ends in TextSelection plus scrollIntoView. Selecting the whole section rather than only scrolling to it is deliberate: it gives the jump and leaves a replaceable range under sendHTML, so editing needs no second API. Refs adobe#1281 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EmqV8wmTVrzTsEYCd5y5rT
Jumping and range-selecting are different asks, and only the first has precedent. The outline addresses blocks and prose items, never sections -- its section row is a drag handle with add/delete, not a click target -- and the jump it performs is canvasBus.editorSelectState -> _scrollDocToBlock. Driving the view directly, as the first version did, works but goes around all of that: the outline highlight stays stale and _broadcastSelectedNode never runs, so the wysiwyg side never learns about the selection. scrollToBlock now emits on the bus with the same blockIndex address instead. selectSection stays, for a different reason than navigation: getBlockPositions excludes NON_BLOCK_TABLE_NAMES, so a section's metadata table has no blockIndex and cannot be addressed at all. The section range is what makes it reachable, and leaves something for sendHTML to replace. Refs adobe#1281 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EmqV8wmTVrzTsEYCd5y5rT
|
Revised after a good challenge from @keepthebyte: the outline can't jump to a section either — it only addresses blocks and prose items. Its section row is a drag handle with add/delete, not a click target. That's worth taking seriously, because it means the first version of this PR invented a section-level navigation concept the host doesn't have. Two things fall out, and they pull in opposite directions: Navigation should reuse what exists. The outline's jump is A section range is still needed, for a different reason. So: One open question I didn't want to decide unilaterally: |
Closes #1281 — please read that first for the motivation. Opening this as a draft: it is a concrete suggestion to argue about, not a merge request. Happy for the team to take the idea and implement it their own way.
What
Adds one action to the canvas extension channel,
selectSection, backed by aselectSection(view, sectionIndex)helper ineditor-utils/blocks.js.Why
A BYO panel extension can already read the document (the handshake hands it
{ org, repo, ref, path }and the IMS token, so it can fetch page source fromadmin.da.live) and insert into it (sendHTML→insertHTML). What it cannot do is move the selection, and that blocks two things:ew-page-outlinedoes.insertHTMLdoesreplaceRange(from, to, …), so the edit loop already works — it just needs something to put the selection on the right node first. Without that the only remaining route is writing source viaadmin.da.liveunderneath a live collab session, which we are deliberately not doing.Selecting the whole section rather than only scrolling to it is the load-bearing choice: it delivers the jump and leaves a replaceable range under the existing
sendHTML, so editing needs no second API.Implementation notes
getSectionRangesreuses the same "split top-level nodes onhorizontal_rule" walk thatdeleteSectionandmoveSectionalready use, rather than introducing a second notion of what a section is.TextSelection+scrollIntoView()+view.focus(), mirroring what the outline path does.{ action: 'selectSectionResult', details: { ok } }so a plugin can feature-detect and degrade rather than fail silently — our prototype shows an explicit "this host does not support it yet" note on timeout.Open questions for the team
sectionIndexis what the existing block/section helpers already speak, but a prose-index or node address would be more general and would survive section reordering better. Happy to change.selectSectionsits alongside the other verbs iniframe-protocol.js; if extension capabilities are meant to be gated or declared per plugin, this should probably go through that instead.scrollToSection) for plugins that want to navigate without disturbing the user's selection? We did not add one because the replaceable range is the point, but the two uses are separable.Testing
Verified against a real customer page in a local canvas: clicking a section in a plugin panel scrolls the editor to it, and a subsequent
getSelection→ edit →sendHTMLround-trips the section's prose HTML — preserving block content and any non-audienceStyletokens, and updating only what the plugin intended.This is driven by an authoring plugin we built for section-scoped personalization; happy to share it if that helps evaluate the shape.