Composition / foldkit-builder
The keyboard, the layers, and what the editor says
The Builder places two foldkit-primitives/interaction bundles in its Model:
TreeNavigation for the layers panel (Layers, open by default) and
LiveAnnounce for a live region (Announcer). A view attaches their
Behaviors; foldkit-mixins-builder does.
- Moving keyboard focus in the layers selects the node it lands on.
- A removed node leaves the layers' keys nearby: with nothing selected,
layers.currentmoves to the node after it, else the one before, else the nearest holder left, rather than falling back to the first row. With focus in the layers, the Behavior follows it there, which selects that node. PageBuilder.commandsis the editor's commands, one table: each has anid, alabel, itskeys, where a drawn Builder offers it (placement: the node's actions, the toolbar, or by key only), andrun(model), the Message it sends now, or none while it has nothing to do. A view draws the node's actions, the toolbar, their titles and the list of shortcuts from it, andPageBuilder.keyCommand(model, key, modifiers)is derived from it: the first command one of whose keys is pressed and which has something to do, so a command added on a built one's key runs where that one has none. The built table:AttachKeys What they do to the selected node Alt+Up, Alt+Down move it among its siblings Alt+Left move it out of its parent, to just after it Alt+Right move it into the node above it, last in the first Region that takes it Mod+D duplicate it, just after it, where its Region has room Mod+C, Mod+X copy it; cut it Delete, Backspace remove it Mod+Z; Mod+Shift+Z or Mod+Y undo; redo Mod+V paste inside it, or after it, as a new node of the copy's Block goes Enter edit its first text in place Escape deselect it PageBuilder.keyPressed(model, key, modifiers)to the layers panel and the canvas, not the whole editor, so Delete in a text box edits the text. It sendsKeyPressedfor every key a command binds, andupdaterunskeyCommandagainst the Model as it is then, not as the view last drew it: a key pressed before the frame that draws a new selection acts on that selection. A bound key with nothing to do has its default held back there. A move the page refuses is refused as any edit is. A key'smodis Ctrl, or ⌘ on a Mac;modandaltmust be as given, andshiftonly where given, so Delete takes Shift+Delete too.Builder.make(name, { commands: built => ... })changes the table: another key for a command, one left out, one moved to the toolbar. What runs a key, the node's actions and the toolbar all follow it.- Every structural edit is announced, such as "Moved Heading, 2 of 3 in Section body", a Block named by its label, and so are undo, redo, and a refusal, assertively. A prop edit is not: the field being typed in already says it.
- What it says is its words.
Builder.make(name, { words })takes any ofEditWordsover the English ones (editWords): each announcement, each command's label, and each refusal the Builder makes itself. Words are text, a value a blank in them ('Moved {label}{at}'), as a form's and a view's are, so one object can hold them all. A refusalapplymakes is worded byrefusal, with blanks{code}and{message}, by default its own message; the Model'srefusedholds the worded text, so the live region and a view's alert say the same.