Composition / foldkit-builder
Copy, cut and paste
A copy is a node and all it holds, as Composition.takeTree takes it. It goes
in two places: the Model's clipboard, and the system clipboard as JSON,
{ "format": "foldkit-composition", "tree": … }, so a copy made in one tab
pastes in another.
CopyAsked({ id })keeps the copy and writes it out;CutAsked({ id })also removes the node, as one undoable edit. A node its Region cannot do without is not cut, and nothing is copied.PasteAsked()reads the system clipboard in a Command, which answersClipboardRead({ text }). Text read wins; the Model's copy is used only when the browser would not let the clipboard be read.- The text is untrusted. It is decoded strictly (a key it should not have
is refused, not dropped), checked by its own ids (
Composition.treeRefusal), so a refusal names what was copied, then each node gets a newly minted id byComposition.rekey, and the tree goes in by oneinsertTree, which checks every node against the Catalog. Anything that does not decode or fit (text that is not part of a page, an unknown Block, a prop of the wrong type, a child it names but does not hold) is refused whole: nothing goes in, andrefusedsays why. Text that is not part of a page, or no copy at all, isbuilder:nothing-to-paste; the rest areapply's own codes. - It goes where a new node of its root's Block would (
placeFor): inside the selection when it fits there, else after it, else last among the roots, or, for a Block that is no root, last in the last Region with room that takes it, so a node cut and pasted with nothing selected comes back. Where it can go nowhere, it is refused before ids are minted, asbuilder:no-place, with the Block's label.
The announcer debounces and clears on Effect's clock through Commands named
LiveAnnounce.read and LiveAnnounce.clear. A runtime runs them beside
everything else; a test that follows each Command in turn should leave them
out, as it has no clock to wait on.