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.current moves 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.commands is the editor's commands, one table: each has an id, a label, its keys, where a drawn Builder offers it (placement: the node's actions, the toolbar, or by key only), and run(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, and PageBuilder.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:
    KeysWhat they do to the selected node
    Alt+Up, Alt+Downmove it among its siblings
    Alt+Leftmove it out of its parent, to just after it
    Alt+Rightmove it into the node above it, last in the first Region that takes it
    Mod+Dduplicate it, just after it, where its Region has room
    Mod+C, Mod+Xcopy it; cut it
    Delete, Backspaceremove it
    Mod+Z; Mod+Shift+Z or Mod+Yundo; redo
    Mod+Vpaste inside it, or after it, as a new node of the copy's Block goes
    Enteredit its first text in place
    Escapedeselect it
    Attach 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 sends KeyPressed for every key a command binds, and update runs keyCommand against 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's mod is Ctrl, or ⌘ on a Mac; mod and alt must be as given, and shift only 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 of EditWords over 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 refusal apply makes is worded by refusal, with blanks {code} and {message}, by default its own message; the Model's refused holds the worded text, so the live region and a view's alert say the same.