foldkit-richtext-dom 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/events.mjs CHANGED
@@ -213,6 +213,43 @@ const unchanged = {
213
213
  structureChanged: false,
214
214
  selectionChanged: false
215
215
  };
216
+ const CARET_KEYS = {
217
+ ArrowUp: {
218
+ direction: "backward",
219
+ granularity: "line"
220
+ },
221
+ ArrowDown: {
222
+ direction: "forward",
223
+ granularity: "line"
224
+ },
225
+ ArrowLeft: {
226
+ direction: "left",
227
+ granularity: "character"
228
+ },
229
+ ArrowRight: {
230
+ direction: "right",
231
+ granularity: "character"
232
+ },
233
+ Home: {
234
+ direction: "backward",
235
+ granularity: "lineboundary"
236
+ },
237
+ End: {
238
+ direction: "forward",
239
+ granularity: "lineboundary"
240
+ }
241
+ };
242
+ /** How long held input waits for an edit nothing answers. */
243
+ const UNANSWERED_MS = 1e3;
244
+ /** The move a caret key makes, with Shift or none: other modifiers keep the browser's. */
245
+ const caretMoveOf = (event) => {
246
+ if (event.altKey || event.ctrlKey || event.metaKey) return void 0;
247
+ const move = Object.hasOwn(CARET_KEYS, event.key) ? CARET_KEYS[event.key] : void 0;
248
+ return move === void 0 ? void 0 : {
249
+ ...move,
250
+ extend: event.shiftKey
251
+ };
252
+ };
216
253
  const samePosition = (left, right) => left.node === right.node && left.offset === right.offset;
217
254
  const sameSelection = (left, right) => {
218
255
  if (left === null || right === null) return left === right;
@@ -354,6 +391,52 @@ const attach = (dom, options) => {
354
391
  * sets are unioned, which never under-invalidates.
355
392
  */
356
393
  let deferred;
394
+ /**
395
+ * Input held after asking for an edit until what is drawn holds it, in the order it came.
396
+ * The browser moves the caret through what is drawn, and the edit is drawn by a Command
397
+ * that a loaded machine runs after the next keys: three Enters and an ArrowUp went up from
398
+ * the old last line, two lines too far. A caret key waits, and an edit asked behind it
399
+ * waits too, so it lands where the move took the caret. The editor answers every edit once
400
+ * it is drawn or refused (`answered`); when none is unanswered they are made in order, and
401
+ * one that asks for another edit holds the rest again. An edit nothing answers (a command
402
+ * the editor has no Message for) lets them go after `UNANSWERED_MS`.
403
+ */
404
+ let held = [];
405
+ let unanswered = 0;
406
+ let fallback;
407
+ const release = () => {
408
+ while (held.length > 0 && unanswered === 0) held.shift()();
409
+ };
410
+ const answered = () => {
411
+ unanswered = Math.max(0, unanswered - 1);
412
+ if (unanswered > 0) return;
413
+ clearTimeout(fallback);
414
+ release();
415
+ };
416
+ const holdMove = (move) => {
417
+ held.push(() => {
418
+ const live = current.root.ownerDocument.defaultView?.getSelection();
419
+ if (live === null || live === void 0 || !ownsSelection()) return;
420
+ live.modify(move.extend ? "extend" : "move", move.direction, move.granularity);
421
+ reportSelection();
422
+ });
423
+ };
424
+ const ask = (command) => {
425
+ unanswered++;
426
+ clearTimeout(fallback);
427
+ fallback = setTimeout(() => {
428
+ unanswered = 0;
429
+ release();
430
+ }, UNANSWERED_MS);
431
+ options.onIntent(command);
432
+ };
433
+ const onIntent = (command) => {
434
+ if (held.length === 0) return ask(command);
435
+ held.push(() => {
436
+ reportSelection();
437
+ ask(command);
438
+ });
439
+ };
357
440
  const draw = (state, changeSet) => {
358
441
  const owned = ownsSelection();
359
442
  lastSelection = state.selection;
@@ -420,6 +503,14 @@ const attach = (dom, options) => {
420
503
  return (after ? caret.bottom > box.bottom - caret.height : caret.top < box.top + caret.height) ? neighbour.id : void 0;
421
504
  };
422
505
  const onEvent = (event) => {
506
+ if (unanswered > 0 && event.type === "keydown" && !composing) {
507
+ const move = caretMoveOf(event);
508
+ if (move !== void 0) {
509
+ event.preventDefault();
510
+ holdMove(move);
511
+ return;
512
+ }
513
+ }
423
514
  const live = event.type === "keydown" && !composing ? readSelection(current) : null;
424
515
  if (live?.type === "Node") {
425
516
  const key = event.key;
@@ -456,7 +547,7 @@ const attach = (dom, options) => {
456
547
  if (!RichText.takesTab(current.content, readSelection(current), options.nodes)) return;
457
548
  event.preventDefault();
458
549
  reportSelection();
459
- options.onIntent({ type: key.shiftKey ? "Outdent" : "Indent" });
550
+ onIntent({ type: key.shiftKey ? "Outdent" : "Indent" });
460
551
  return;
461
552
  }
462
553
  }
@@ -465,7 +556,7 @@ const attach = (dom, options) => {
465
556
  if (intent.preventDefault) event.preventDefault();
466
557
  if (intent.command !== void 0) {
467
558
  reportSelection();
468
- options.onIntent(intent.command);
559
+ onIntent(intent.command);
469
560
  }
470
561
  if (intent.history !== void 0) options.onHistory?.(intent.history);
471
562
  };
@@ -480,7 +571,7 @@ const attach = (dom, options) => {
480
571
  if (deferred !== void 0) draw(deferred.state, deferred.changeSet);
481
572
  else if (lastSelection !== null) restoreSelection(current, lastSelection);
482
573
  deferred = void 0;
483
- if (data != null && data.length > 0) options.onIntent({
574
+ if (data != null && data.length > 0) onIntent({
484
575
  type: "InsertText",
485
576
  text: data
486
577
  });
@@ -505,7 +596,7 @@ const attach = (dom, options) => {
505
596
  if (clipboard === void 0 || slice === void 0) return;
506
597
  event.preventDefault();
507
598
  writeClipboard(clipboard, slice);
508
- if (!(selection?.type === "Range" && selection.anchor.node === selection.focus.node && selection.anchor.offset === selection.focus.offset)) options.onIntent({ type: "DeleteBackward" });
599
+ if (!(selection?.type === "Range" && selection.anchor.node === selection.focus.node && selection.anchor.offset === selection.focus.offset)) onIntent({ type: "DeleteBackward" });
509
600
  };
510
601
  const onPaste = (event) => {
511
602
  const clipboard = event.clipboardData;
@@ -524,7 +615,7 @@ const attach = (dom, options) => {
524
615
  if (slice === void 0 || slice.blocks.length === 0) return;
525
616
  event.preventDefault();
526
617
  reportSelection();
527
- options.onIntent({
618
+ onIntent({
528
619
  type: "Paste",
529
620
  slice
530
621
  });
@@ -546,7 +637,7 @@ const attach = (dom, options) => {
546
637
  const block = id === null || id === void 0 ? void 0 : RichText.locateBlock(current.content, RichText.NodeId.make(id))?.block;
547
638
  if (block?.type !== "Node") return;
548
639
  event.preventDefault();
549
- options.onIntent({
640
+ onIntent({
550
641
  type: "SetProps",
551
642
  node: block.id,
552
643
  props: { [prop]: block.props[prop] !== true }
@@ -578,7 +669,7 @@ const attach = (dom, options) => {
578
669
  });
579
670
  return true;
580
671
  }
581
- options.onIntent({
672
+ onIntent({
582
673
  type: "InsertBlock",
583
674
  block: {
584
675
  type: "Paragraph",
@@ -650,6 +741,7 @@ const attach = (dom, options) => {
650
741
  current: () => current,
651
742
  held: () => deferred?.state.document ?? current.content,
652
743
  composing: () => composing,
744
+ answered,
653
745
  redecorate: () => {
654
746
  if (composing) return;
655
747
  const selection = readSelection(current);
@@ -675,6 +767,7 @@ const attach = (dom, options) => {
675
767
  };
676
768
  },
677
769
  detach: () => {
770
+ clearTimeout(fallback);
678
771
  ownerDocument.removeEventListener("selectionchange", reportSelection);
679
772
  target.removeEventListener("beforeinput", onEvent);
680
773
  target.removeEventListener("keydown", onEvent);
@@ -1 +1 @@
1
- {"version":3,"file":"events.mjs","names":["patchInto"],"sources":["../src/events.ts"],"sourcesContent":["/**\n * Browser events as editor intent (§§30–31). The adapter owns the subtree, so\n * every event it understands is `preventDefault`ed: the browser must not mutate\n * the DOM behind the semantic document. An event the adapter does not\n * understand is left alone (the caller lets it through), and one it understands\n * but cannot yet honor is prevented without a command — a deliberate no-op\n * rather than a silent DOM divergence.\n */\nimport * as RichText from 'foldkit-richtext'\nimport {\n patch as patchInto,\n positionToRange,\n rangeToPosition,\n repair,\n type EditorDom,\n} from './index.js'\nimport { parseHtml } from './html.js'\n\nexport interface Intent {\n /** The semantic command to run, when the event maps to one. */\n readonly command?: RichText.Command\n /** A history intent: undo or redo, which are editor intents, not commands. */\n readonly history?: 'undo' | 'redo'\n readonly preventDefault: boolean\n}\n\n/**\n * One chord an application adds to the adapter's table, or overrides in it.\n * Modifiers match exactly and the key matches what the browser reports, so\n * `Mod-b` does not fire for Shift held, and a chord that types a symbol names the\n * symbol: Shift+8 is `Mod-Shift-*` on a US layout, not `Mod-Shift-8`.\n */\nexport interface KeyBinding {\n /** `Mod-b`, `Mod-Shift-b`, `Alt-ArrowUp`. `Mod` is Meta or Control, either one. */\n readonly chord: string\n readonly command: RichText.Command\n}\n\nconst chordMatches = (chord: string, event: KeyboardEvent): boolean => {\n const parts = chord.split('-')\n const name = parts[parts.length - 1] ?? ''\n const modifiers = parts.slice(0, -1)\n const wantsMod = modifiers.includes('Mod')\n const hasMod = event.metaKey || event.ctrlKey\n if (hasMod !== wantsMod) return false\n if (event.shiftKey !== modifiers.includes('Shift')) return false\n if (event.altKey !== modifiers.includes('Alt')) return false\n return name.length === 1 ? event.key.toLowerCase() === name.toLowerCase() : event.key === name\n}\n\nconst MOD = (event: KeyboardEvent): boolean => event.metaKey || event.ctrlKey\n\nconst historyChord = (event: KeyboardEvent): 'undo' | 'redo' | undefined => {\n if (!MOD(event)) return undefined\n const key = event.key.toLowerCase()\n if (key === 'z') return event.shiftKey ? 'redo' : 'undo'\n if (key === 'y') return 'redo'\n return undefined\n}\n\nconst markChord = (event: KeyboardEvent): string | undefined => {\n if (!MOD(event)) return undefined\n switch (event.key.toLowerCase()) {\n case 'b':\n return 'Bold'\n case 'i':\n return 'Italic'\n case 'e':\n return 'Code'\n // Without Shift, the browser's save.\n case 's':\n return event.shiftKey ? 'Strikethrough' : undefined\n default:\n return undefined\n }\n}\n\n/**\n * Reads one event as intent. `composing` tells the adapter that an IME owns the\n * interaction: the browser must keep its temporary text, so composition input\n * is passed through and committed on `compositionend` instead.\n *\n * `keymap` is checked before the built-in chords, so an application adds to the\n * table or overrides an entry without forking the adapter.\n */\nexport const intentFor = (\n event: Event,\n composing = false,\n keymap: ReadonlyArray<KeyBinding> = [],\n): Intent | undefined => {\n if (event.type === 'beforeinput') {\n const input = event as Event & { readonly inputType?: string; readonly data?: string | null }\n const inputType = input.inputType ?? ''\n if (composing || inputType === 'insertCompositionText') return { preventDefault: false }\n switch (inputType) {\n case 'insertText':\n return input.data == null || input.data.length === 0\n ? { preventDefault: true }\n : { preventDefault: true, command: { type: 'InsertText', text: input.data } }\n case 'insertParagraph':\n case 'insertLineBreak':\n return { preventDefault: true, command: { type: 'SplitBlock' } }\n case 'deleteContentBackward':\n return { preventDefault: true, command: { type: 'DeleteBackward' } }\n case 'deleteContentForward':\n return { preventDefault: true, command: { type: 'DeleteForward' } }\n case 'formatBold':\n return { preventDefault: true, command: { type: 'ToggleMark', mark: 'Bold' } }\n case 'formatItalic':\n return { preventDefault: true, command: { type: 'ToggleMark', mark: 'Italic' } }\n default:\n // Paste, word deletion, autocorrect, and anything else the browser\n // invents: prevented so the DOM cannot drift, until it is implemented.\n return inputType.length === 0 ? undefined : { preventDefault: true }\n }\n }\n if (event.type !== 'keydown') return undefined\n const key = event as KeyboardEvent\n if (composing) return { preventDefault: false }\n const bound = keymap.find(binding => chordMatches(binding.chord, key))\n if (bound !== undefined) return { preventDefault: true, command: bound.command }\n const history = historyChord(key)\n if (history !== undefined) return { preventDefault: true, history }\n const mark = markChord(key)\n if (mark !== undefined) return { preventDefault: true, command: { type: 'ToggleMark', mark } }\n switch (key.key) {\n case 'Enter':\n return { preventDefault: true, command: { type: 'SplitBlock' } }\n case 'Backspace':\n return { preventDefault: true, command: { type: 'DeleteBackward' } }\n case 'Delete':\n return { preventDefault: true, command: { type: 'DeleteForward' } }\n default:\n return undefined\n }\n}\n\n/**\n * The semantic selection the browser is currently showing, if it resolves: null when the\n * page's selection is outside the editor, which is not the editor's to read.\n */\n/**\n * The atom a range holds whole, as a node selection is drawn: an atom has no text for a caret,\n * so the browser is given the range around its element.\n */\nconst selectedAtom = (dom: EditorDom, selection: Selection): RichText.NodeId | undefined => {\n const parent = selection.anchorNode\n if (parent === null || parent !== selection.focusNode) return undefined\n if (Math.abs(selection.focusOffset - selection.anchorOffset) !== 1) return undefined\n const child = parent.childNodes[Math.min(selection.anchorOffset, selection.focusOffset)]\n const id = child instanceof Element ? child.getAttribute('data-block') : null\n if (id === null) return undefined\n const block = RichText.locateBlock(dom.content, RichText.NodeId.make(id))?.block\n return block !== undefined && RichText.isAtom(undefined, block) ? block.id : undefined\n}\n\nexport const readSelection = (dom: EditorDom): RichText.Selection | null => {\n const selection = dom.root.ownerDocument.defaultView?.getSelection()\n if (selection === null || selection === undefined || selection.rangeCount === 0) return null\n const selected = selectedAtom(dom, selection)\n if (selected !== undefined) return { type: 'Node', node: selected }\n const anchor = rangeToPosition(dom, selection.anchorNode ?? dom.root, selection.anchorOffset)\n const focus = rangeToPosition(dom, selection.focusNode ?? dom.root, selection.focusOffset)\n if (anchor === undefined || focus === undefined) return null\n return { type: 'Range', anchor, focus }\n}\n\n/**\n * Where a position is in the DOM: a text node and an offset into it, or, on an empty line, the\n * line's element and the index of its `<br>`, where a browser draws a caret.\n */\nconst endpointFor = (\n dom: EditorDom,\n position: RichText.Position,\n): { node: Node; offset: number } | undefined => {\n const range = positionToRange(dom, position)\n return range === undefined ? undefined : { node: range.startContainer, offset: range.startOffset }\n}\n\n/**\n * Puts the browser selection back where the semantic state says it is. Direction\n * is preserved through `setBaseAndExtent` when the environment has it; jsdom and\n * some engines only offer a range, which cannot express direction.\n */\nexport const restoreSelection = (dom: EditorDom, selection: RichText.Selection | null): void => {\n const window = dom.root.ownerDocument.defaultView\n const live = window?.getSelection()\n if (window === null || window === undefined || live === null || live === undefined) return\n live.removeAllRanges()\n if (selection === null) return\n if (selection.type === 'Node') {\n const element = dom.elements.get(selection.node)\n if (element === undefined || element.parentNode === null) return\n const range = dom.root.ownerDocument.createRange()\n range.selectNode(element)\n live.addRange(range)\n return\n }\n const anchor = endpointFor(dom, selection.anchor)\n const focus = endpointFor(dom, selection.focus)\n if (anchor === undefined || focus === undefined) return\n if (typeof live.setBaseAndExtent === 'function') {\n live.setBaseAndExtent(anchor.node, anchor.offset, focus.node, focus.offset)\n return\n }\n const range = dom.root.ownerDocument.createRange()\n range.setStart(anchor.node, anchor.offset)\n range.setEnd(focus.node, focus.offset)\n live.addRange(range)\n}\n\n/** The clipboard type a Foldkit RichText slice travels under. */\nexport const SLICE_CLIPBOARD_TYPE = 'application/x-foldkit-richtext+json'\n\ninterface ClipboardLike {\n getData: (type: string) => string\n setData: (type: string, value: string) => void\n}\n\nexport interface AttachOptions {\n /** Called with each semantic command the browser produced. */\n readonly onIntent: (command: RichText.Command) => void\n /** Called for undo and redo, which are editor intents rather than commands. */\n readonly onHistory?: (direction: 'undo' | 'redo') => void\n /**\n * Called when the caret or range moves to a position the application did not\n * just commit. Without it the editor never learns where the caret is; the\n * adapter cannot follow a selection it is not told about.\n */\n readonly onSelection?: (selection: RichText.Selection | null) => void\n /**\n * When given, imported HTML is constrained to this vocabulary: a node kind\n * the Kit does not declare is degraded to a paragraph, never kept.\n */\n readonly kit?: RichText.Kit | undefined\n /** Chords the application adds or overrides, checked before the built-ins. */\n readonly keymap?: ReadonlyArray<KeyBinding> | undefined\n /**\n * What is drawn over the document (§129), derived from it each time it is synced — code\n * highlighting, say. A pure read: it sees the document and nothing else.\n */\n readonly decorate?: Decorate | undefined\n /** What a blank document shows (`RichText.isBlank`): \"Write something…\". */\n readonly placeholder?: string | undefined\n /**\n * What the empty paragraph a collapsed caret is in shows, as `data-hint` on it, which a\n * stylesheet draws as it draws the placeholder (usually only while the editor has the focus).\n * A blank document's block shows the placeholder instead.\n */\n readonly lineHint?: string | undefined\n /**\n * The node vocabulary, which says where Tab means something (`RichText.takesTab`): code and\n * table cells take it as `Indent` and `Outdent`; anywhere else it moves focus as it always does.\n */\n readonly nodes?: RichText.NodeRegistry | undefined\n /** Called with a block and an adornment's `action` when that adornment is clicked. */\n readonly onAction?: ((node: RichText.NodeId, action: string) => void) | undefined\n}\n\n/** What is drawn over a document, derived from it on every render (§129). */\nexport type Decorate = (document: RichText.Document) => RichText.DecorationSet\n\n/** A change set that names nothing: what a redraw of the same document patches with. */\nconst unchanged: RichText.ChangeSet = {\n dirtyNodes: new Set(),\n insertedNodes: new Set(),\n removedNodes: new Set(),\n textChanged: new Set(),\n structureChanged: false,\n selectionChanged: false,\n}\n\nexport interface Attachment {\n /** The current subtree; replaced as patches are applied. */\n readonly current: () => EditorDom\n /**\n * Applies a committed state, patching and restoring the browser selection. While the\n * browser is composing, the latest state waits and is drawn when composition ends.\n */\n readonly sync: (state: RichText.EditorState, changeSet: RichText.ChangeSet) => void\n /**\n * Draws the decorations again over what is drawn, for when they changed and the document\n * did not, and puts the browser's selection back where it was. Nothing while composing:\n * the next state drawn brings them.\n */\n readonly redecorate: () => void\n /**\n * Takes the focus with the caret or range at `selection`, scrolled into view when it is out\n * of it: for a control outside the editor that sends the reader into the text, such as a table\n * of contents or a find bar closing on its match.\n */\n readonly focus: (selection: RichText.Selection) => void\n /**\n * The latest document synced: the one waiting on a composition, or else the one drawn. A\n * patch computed from here to the next state is what brings the subtree up to it.\n */\n readonly held: () => RichText.Document\n /** True between compositionstart and compositionend. */\n readonly composing: () => boolean\n readonly detach: () => void\n}\n\nconst samePosition = (left: RichText.Position, right: RichText.Position): boolean =>\n left.node === right.node && left.offset === right.offset\n\nconst sameSelection = (\n left: RichText.Selection | null,\n right: RichText.Selection | null,\n): boolean => {\n if (left === null || right === null) return left === right\n if (left.type === 'Node' || right.type === 'Node') {\n return left.type === 'Node' && right.type === 'Node' && left.node === right.node\n }\n return samePosition(left.anchor, right.anchor) && samePosition(left.focus, right.focus)\n}\n\n/**\n * Marks the lone block of a blank document with `data-placeholder`, which a stylesheet draws\n * with `::before { content: attr(data-placeholder) }`, and clears it from any block that no\n * longer qualifies. It is an attribute rather than a text node, so it never enters the\n * content the adapter reads back or the caret mapping. The block, not the root, carries it,\n * so the text sits on the line the caret is on.\n */\nconst drawPlaceholder = (\n dom: EditorDom,\n placeholder: string,\n marked: Element | undefined,\n): Element | undefined => {\n dom.root.setAttribute('aria-placeholder', placeholder)\n const first = dom.content.children[0]\n const target =\n RichText.isBlank(dom.content) && first !== undefined ? dom.elements.get(first.id) : undefined\n // Only the element marked last can carry the attribute, so nothing is searched per patch.\n if (marked !== target) marked?.removeAttribute('data-placeholder')\n target?.setAttribute('data-placeholder', placeholder)\n return target\n}\n\n/** The empty paragraph a collapsed caret is in, in a document that is not blank. */\nconst emptyLineAt = (dom: EditorDom, selection: RichText.Selection | null): Element | undefined => {\n if (selection?.type !== 'Range' || !samePosition(selection.anchor, selection.focus)) {\n return undefined\n }\n if (RichText.isBlank(dom.content)) return undefined\n const run = RichText.locateRun(dom.content, selection.focus.node)\n const block = run === undefined ? undefined : RichText.blockAtPath(dom.content, run.path)\n return block?.type === 'Paragraph' && block.children.every(each => each.text === '')\n ? dom.elements.get(block.id)\n : undefined\n}\n\n/**\n * The caret in the nearest text before or after a block, as an arrow key leaves a selected\n * image: null when there is no text on that side.\n */\nconst caretBeside = (\n document: RichText.Document,\n node: RichText.NodeId,\n after: boolean,\n): RichText.Selection | null => {\n let seen = false\n let before: RichText.Block['children'][number] | undefined\n let next: RichText.Block['children'][number] | undefined\n RichText.eachBlock(document.children, block => {\n if (block.id === node) seen = true\n else if (!seen) before = block.children.at(-1) ?? before\n else next ??= block.children[0]\n })\n const run = after ? next : before\n if (!seen || run === undefined) return null\n const at = { node: run.id, offset: after ? 0 : run.text.length, affinity: 'after' as const }\n return { type: 'Range', anchor: at, focus: at }\n}\n\n/** What a press inside an atom belongs to rather than the editor: a control, a link. */\nconst INTERACTIVE = 'a, button, input, select, textarea, video, audio'\n\n/**\n * Wires the owned subtree to an application. The adapter never decides what an\n * edit means: it translates events into commands, hands them to `onIntent`, and\n * patches whatever the application commits.\n */\nexport const attach = (dom: EditorDom, options: AttachOptions): Attachment => {\n let current = dom\n // Every replacement of the subtree goes through here: a repair or a patch can render the\n // blank block fresh, without the attribute the last one carried.\n let marked: Element | undefined\n /**\n * The selection the application last committed, or the adapter last reported.\n * Restoring it fires `selectionchange`, so without this the adapter would\n * report the position it had just been told as if a person had moved there.\n * Affinity is ignored: it is derived from the range, not carried by it.\n */\n let lastSelection: RichText.Selection | null = null\n let hinted: Element | undefined\n let selectedBlock: Element | undefined\n /**\n * Marks the block a node selection holds with `data-selected`: the browser draws no\n * selection of its own for one, so this is the only sign of it.\n */\n const markSelected = (): void => {\n const target =\n lastSelection?.type === 'Node' ? current.elements.get(lastSelection.node) : undefined\n if (selectedBlock !== target) selectedBlock?.removeAttribute('data-selected')\n target?.setAttribute('data-selected', '')\n selectedBlock = target\n }\n /** Moves `data-hint` to the empty line the caret is on, or takes it away. */\n const hint = (): void => {\n if (options.lineHint === undefined) return\n const target = emptyLineAt(current, lastSelection)\n if (hinted !== target) hinted?.removeAttribute('data-hint')\n target?.setAttribute('data-hint', options.lineHint)\n hinted = target\n }\n const redraw = (next: EditorDom): void => {\n current = next\n if (options.placeholder !== undefined) {\n marked = drawPlaceholder(current, options.placeholder, marked)\n }\n hint()\n markSelected()\n }\n redraw(dom)\n let composing = false\n /**\n * Only a range that resolves inside the editor is news. A selection elsewhere on the page\n * (another editor, an input) leaves the editor's own where it was, and so does a live\n * selection that cannot show the Model's: the browser never holds a Node selection, so\n * reading its absence back would turn a selected block into no selection at all.\n */\n const reportSelection = (): void => {\n // While composing, the caret points into text the document does not have.\n if (composing) return\n const selection = readSelection(current)\n if (selection === null || sameSelection(selection, lastSelection)) return\n lastSelection = selection\n hint()\n markSelected()\n options.onSelection?.(selection)\n }\n /** Takes a selection the editor made itself, as a click on an image or an arrow key does. */\n const select = (selection: RichText.Selection): void => {\n restoreSelection(current, selection)\n if (sameSelection(selection, lastSelection)) return\n lastSelection = selection\n hint()\n markSelected()\n options.onSelection?.(selection)\n }\n /**\n * The browser's selection is the page's, not the editor's: the editor moves it only while\n * it is inside the editor, so a redraw never takes the caret from another editor or an input.\n * Read before a patch replaces the nodes it points into.\n */\n const ownsSelection = (): boolean => {\n const live = current.root.ownerDocument.defaultView?.getSelection()\n if (live === null || live === undefined || live.rangeCount === 0) return false\n return current.root.contains(live.anchorNode) && current.root.contains(live.focusNode)\n }\n let placeholderIds = 0\n /**\n * A state the application committed while the browser was composing: drawing it then\n * would rewrite the text under the IME, so it waits for the composition to end. Change\n * sets are unioned, which never under-invalidates.\n */\n let deferred: { state: RichText.EditorState; changeSet: RichText.ChangeSet } | undefined\n const draw = (state: RichText.EditorState, changeSet: RichText.ChangeSet): void => {\n const owned = ownsSelection()\n lastSelection = state.selection\n redraw(patchInto(current, state.document, changeSet, options.decorate?.(state.document)))\n if (owned) restoreSelection(current, state.selection)\n }\n /** For Mod+A in a kind whose text keeps line breaks (code), its text, unless that is selected. */\n const codeToSelect = (event: KeyboardEvent): RichText.Selection | undefined => {\n if (event.key.toLowerCase() !== 'a' || !MOD(event) || event.shiftKey || event.altKey) {\n return undefined\n }\n const selection = readSelection(current)\n if (selection?.type !== 'Range') return undefined\n const found = RichText.locateRun(current.content, selection.focus.node)\n const block =\n found === undefined ? undefined : RichText.blockAtPath(current.content, found.path)\n if (block?.type !== 'Node') return undefined\n const declared = options.nodes?.definitionFor(block.kind)\n if (declared?.kind !== 'node' || declared.lineBreaks !== true) return undefined\n const first = block.children[0]\n const last = block.children[block.children.length - 1]\n if (first === undefined || last === undefined) return undefined\n const whole: RichText.Selection = {\n type: 'Range',\n anchor: { node: first.id, offset: 0, affinity: 'after' },\n focus: { node: last.id, offset: last.text.length, affinity: 'after' },\n }\n return sameSelection(selection, whole) ? undefined : whole\n }\n /**\n * The atom an arrow key reaches from a caret at the edge it leaves its block by: the end or\n * start for Right and Left, the last or first line for Down and Up, with an atom beside the\n * block in its container.\n */\n const atomReached = (event: KeyboardEvent): RichText.NodeId | undefined => {\n const key = event.key\n const after = key === 'ArrowRight' || key === 'ArrowDown'\n if (!after && key !== 'ArrowLeft' && key !== 'ArrowUp') return undefined\n // With Shift a range grows, and with the others a word or a line is skipped.\n if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return undefined\n // Read live: a click just before may not have been reported yet.\n const selection = readSelection(current)\n if (selection?.type !== 'Range' || !samePosition(selection.anchor, selection.focus)) {\n return undefined\n }\n const content = current.content\n const found = RichText.locateRun(content, selection.focus.node)\n if (found === undefined) return undefined\n const index = found.path[found.path.length - 1]!\n const holder =\n found.path.length > 1 ? RichText.blockAtPath(content, found.path.slice(0, -1)) : undefined\n const siblings = holder?.type === 'Node' ? (holder.blocks ?? []) : content.children\n const block = siblings[index]\n const neighbour = siblings[index + (after ? 1 : -1)]\n if (block === undefined || neighbour === undefined) return undefined\n if (!RichText.isAtom(options.nodes, neighbour)) return undefined\n const lead = block.children.slice(0, found.index).reduce((sum, run) => sum + run.text.length, 0)\n const offset = lead + selection.focus.offset\n const length = block.children.reduce((sum, run) => sum + run.text.length, 0)\n if (offset === (after ? length : 0)) return neighbour.id\n if (key === 'ArrowRight' || key === 'ArrowLeft') return undefined\n // Down and Up leave from the last or first line, which only the layout knows.\n const caret = positionToRange(current, selection.focus)?.getBoundingClientRect()\n const box = current.elements.get(block.id)?.getBoundingClientRect()\n if (caret === undefined || box === undefined || caret.height === 0) return undefined\n const onEdgeLine = after\n ? caret.bottom > box.bottom - caret.height\n : caret.top < box.top + caret.height\n return onEdgeLine ? neighbour.id : undefined\n }\n const onEvent = (event: Event): void => {\n // From a selected atom, an arrow goes to the text beside it, where a browser would step\n // into the atom's own markup.\n // Read live, as a click just before may not have been reported yet.\n const live = event.type === 'keydown' && !composing ? readSelection(current) : null\n if (live?.type === 'Node') {\n const key = (event as KeyboardEvent).key\n const after = key === 'ArrowRight' || key === 'ArrowDown'\n if (after || key === 'ArrowLeft' || key === 'ArrowUp') {\n event.preventDefault()\n const beside = caretBeside(current.content, live.node, after)\n if (beside !== null) select(beside)\n return\n }\n }\n // In code, the first select-all selects the code, and a second the document.\n if (event.type === 'keydown' && !composing) {\n const code = codeToSelect(event as KeyboardEvent)\n if (code !== undefined) {\n event.preventDefault()\n select(code)\n return\n }\n }\n // An arrow leaving a block for an atom beside it selects the atom, which a browser would\n // step over.\n if (event.type === 'keydown' && !composing) {\n const reached = atomReached(event as KeyboardEvent)\n if (reached !== undefined) {\n event.preventDefault()\n select({ type: 'Node', node: reached })\n return\n }\n }\n // Tab is taken only where it means something, so it still leaves the editor elsewhere.\n if (event.type === 'keydown' && !composing) {\n const key = event as KeyboardEvent\n if (key.key === 'Tab' && !key.altKey && !key.ctrlKey && !key.metaKey) {\n if (!RichText.takesTab(current.content, readSelection(current), options.nodes)) return\n event.preventDefault()\n reportSelection()\n options.onIntent({ type: key.shiftKey ? 'Outdent' : 'Indent' })\n return\n }\n }\n const intent = intentFor(event, composing, options.keymap)\n if (intent === undefined) return\n if (intent.preventDefault) event.preventDefault()\n // `selectionchange` is asynchronous, so a click just before a keystroke may not have\n // been reported yet: report it first, or the edit lands at the caret before the click.\n if (intent.command !== undefined) {\n reportSelection()\n options.onIntent(intent.command)\n }\n if (intent.history !== undefined) options.onHistory?.(intent.history)\n }\n const onCompositionStart = (): void => {\n reportSelection()\n composing = true\n }\n const onCompositionEnd = (event: Event): void => {\n composing = false\n const data = (event as Event & { readonly data?: string | null }).data\n // The browser's temporary text is not in the document, whether the IME\n // committed or cancelled, so repair the subtree before anything else, then\n // put back the selection composition started from — not the caret the\n // browser moved into its own temporary text. Nothing is reported or synced\n // while composing, so that is still `lastSelection`; with none known (an end\n // without a start), the caret is left where the browser put it.\n redraw(repair(current, current.content))\n // What arrived during the composition is drawn now. Its selection is the\n // application's, which holds the caret composition started from.\n if (deferred !== undefined) draw(deferred.state, deferred.changeSet)\n else if (lastSelection !== null) restoreSelection(current, lastSelection)\n deferred = undefined\n if (data != null && data.length > 0) options.onIntent({ type: 'InsertText', text: data })\n }\n const writeClipboard = (clipboard: ClipboardLike, slice: RichText.Slice): void => {\n clipboard.setData(SLICE_CLIPBOARD_TYPE, RichText.serializeSlice(slice))\n // HTML for other applications; importing it back waits on a\n // kit-constrained parser, so paste still prefers the slice.\n clipboard.setData('text/html', RichText.toHtml(slice.blocks))\n clipboard.setData('text/plain', RichText.toText(slice.blocks))\n }\n const onCopy = (event: Event): void => {\n const clipboard = (event as Event & { readonly clipboardData?: ClipboardLike }).clipboardData\n const slice = RichText.sliceOf(current.content, readSelection(current))\n if (clipboard === undefined || slice === undefined) return\n event.preventDefault()\n writeClipboard(clipboard, slice)\n }\n const onCut = (event: Event): void => {\n // The delete below runs at the application's selection, which a click just before may\n // not have reached yet, as with a keystroke.\n reportSelection()\n const clipboard = (event as Event & { readonly clipboardData?: ClipboardLike }).clipboardData\n const selection = readSelection(current)\n const slice = RichText.sliceOf(current.content, selection)\n if (clipboard === undefined || slice === undefined) return\n event.preventDefault()\n writeClipboard(clipboard, slice)\n // A collapsed caret cuts nothing; a range is removed through the same\n // delete intent a Backspace would produce.\n const collapsed =\n selection?.type === 'Range' &&\n selection.anchor.node === selection.focus.node &&\n selection.anchor.offset === selection.focus.offset\n if (!collapsed) options.onIntent({ type: 'DeleteBackward' })\n }\n const onPaste = (event: Event): void => {\n const clipboard = (event as Event & { readonly clipboardData?: ClipboardLike }).clipboardData\n if (clipboard === undefined) return\n const payload = clipboard.getData(SLICE_CLIPBOARD_TYPE)\n const html = clipboard.getData('text/html')\n const text = clipboard.getData('text/plain')\n const placeholders = () => `clipboard-${++placeholderIds}`\n // Slice first, then HTML, then plain text (§69). A payload this version\n // cannot read falls through rather than pasting nothing, and the Paste\n // command remints every identity, so these placeholders never reach the\n // document.\n const slice =\n (payload.length > 0 ? RichText.deserializeSlice(payload) : undefined) ??\n (html.length > 0\n ? {\n version: 1 as const,\n blocks: parseHtml(html, { kit: options.kit, mint: placeholders }).blocks,\n }\n : undefined) ??\n (text.length > 0 ? RichText.sliceFromText(text, placeholders) : undefined)\n if (slice === undefined || slice.blocks.length === 0) return\n event.preventDefault()\n reportSelection()\n options.onIntent({ type: 'Paste', slice })\n }\n // A click on an adornment that toggles a prop (a task's checkbox, a toggle's arrow) flips it in\n // the document; the browser's own change to a checkbox is refused, since the patch that\n // follows draws what the document says.\n const onClick = (event: MouseEvent): void => {\n const origin = event.target\n if (!(origin instanceof Element)) return\n const acting = origin.closest(`[${RichText.ACTION_ATTRIBUTE}]`)\n const actedOn = acting?.closest('[data-block]')?.getAttribute('data-block')\n if (\n acting !== null &&\n actedOn !== null &&\n actedOn !== undefined &&\n current.root.contains(acting)\n ) {\n event.preventDefault()\n options.onAction?.(\n RichText.NodeId.make(actedOn),\n acting.getAttribute(RichText.ACTION_ATTRIBUTE)!,\n )\n return\n }\n const adornment = origin.closest(`[${RichText.TOGGLES_ATTRIBUTE}]`)\n if (adornment === null || !current.root.contains(adornment)) return\n const prop = adornment.getAttribute(RichText.TOGGLES_ATTRIBUTE)!\n const id = adornment.closest('[data-block]')?.getAttribute('data-block')\n const block =\n id === null || id === undefined\n ? undefined\n : RichText.locateBlock(current.content, RichText.NodeId.make(id))?.block\n if (block?.type !== 'Node') return\n event.preventDefault()\n options.onIntent({\n type: 'SetProps',\n node: block.id,\n props: { [prop]: block.props[prop] !== true },\n })\n }\n /**\n * A press in the space below the last block writes after it, as below the last line of a\n * page: at the end of a paragraph or heading, or on a new paragraph after anything else (a\n * table, code, an image), which no caret could otherwise get below.\n */\n const pressBelow = (event: MouseEvent): boolean => {\n const last = current.content.children.at(-1)\n const element = last === undefined ? undefined : current.elements.get(last.id)\n if (last === undefined || element === undefined) return false\n if (event.clientY <= element.getBoundingClientRect().bottom) return false\n event.preventDefault()\n current.root.focus({ preventScroll: true })\n const run =\n last.type === 'Paragraph' || last.type === 'Heading' ? last.children.at(-1) : undefined\n if (run !== undefined) {\n const end = { node: run.id, offset: run.text.length, affinity: 'after' as const }\n select({ type: 'Range', anchor: end, focus: end })\n return true\n }\n options.onIntent({\n type: 'InsertBlock',\n block: {\n type: 'Paragraph',\n id: RichText.NodeId.make('paragraph'),\n children: [{ type: 'Text', id: RichText.NodeId.make('text'), text: '', marks: [] }],\n },\n to: { after: last.id },\n })\n return true\n }\n // A press on an atom (an image, a divider) selects it whole, as there is no text in it for\n // a caret, and keeps the browser from dragging it or putting the caret elsewhere.\n const onMouseDown = (event: MouseEvent): void => {\n const origin = event.target\n if (event.button !== 0 || !(origin instanceof Element)) return\n if (origin === current.root && pressBelow(event)) return\n // An adornment (a task's checkbox, a code block's Copy) acts on its click; the press keeps\n // the caret where it was, so writing goes on there after it.\n if (\n origin.closest(`[${RichText.ACTION_ATTRIBUTE}], [${RichText.TOGGLES_ATTRIBUTE}]`) !== null\n ) {\n event.preventDefault()\n return\n }\n if (origin.closest(INTERACTIVE) !== null) return\n const id = origin.closest('[data-block]')?.getAttribute('data-block')\n if (id === null || id === undefined || !current.root.contains(origin)) return\n const block = RichText.locateBlock(current.content, RichText.NodeId.make(id))?.block\n if (block === undefined) return\n // A third press selects its block's text. The browser's own selection runs on to the\n // start of the next block, so what is typed over it would swallow that block too.\n const first = block.children[0]\n const last = block.children[block.children.length - 1]\n if (event.detail === 3 && first !== undefined && last !== undefined) {\n event.preventDefault()\n select({\n type: 'Range',\n anchor: { node: first.id, offset: 0, affinity: 'after' },\n focus: { node: last.id, offset: last.text.length, affinity: 'after' },\n })\n return\n }\n if (!RichText.isAtom(options.nodes, block)) return\n event.preventDefault()\n current.root.focus({ preventScroll: true })\n select({ type: 'Node', node: block.id })\n }\n const target = dom.root\n // The caret belongs to the document, not the subtree: a click elsewhere on the\n // page, or an arrow key, moves it without an event the root would see.\n const ownerDocument = target.ownerDocument\n ownerDocument.addEventListener('selectionchange', reportSelection)\n target.addEventListener('beforeinput', onEvent)\n target.addEventListener('keydown', onEvent)\n target.addEventListener('compositionstart', onCompositionStart)\n target.addEventListener('compositionend', onCompositionEnd)\n target.addEventListener('copy', onCopy)\n target.addEventListener('cut', onCut)\n target.addEventListener('paste', onPaste)\n target.addEventListener('click', onClick)\n target.addEventListener('mousedown', onMouseDown)\n return {\n current: () => current,\n held: () => deferred?.state.document ?? current.content,\n composing: () => composing,\n redecorate: () => {\n if (composing) return\n const selection = readSelection(current)\n redraw(patchInto(current, current.content, unchanged, options.decorate?.(current.content)))\n if (selection !== null) restoreSelection(current, selection)\n },\n focus: selection => {\n current.root.focus({ preventScroll: true })\n restoreSelection(current, selection)\n if (selection.type !== 'Range') return\n const at = positionToRange(current, selection.focus)?.startContainer\n const element = at instanceof Element ? at : at?.parentElement\n const box = element?.getBoundingClientRect()\n const view = current.root.ownerDocument.defaultView\n if (box === undefined || view === null) return\n if (box.top < 0 || box.bottom > view.innerHeight) element?.scrollIntoView({ block: 'center' })\n },\n sync: (state, changeSet) => {\n if (!composing) return draw(state, changeSet)\n deferred = {\n state,\n changeSet:\n deferred === undefined\n ? changeSet\n : RichText.unionChangeSet(deferred.changeSet, changeSet),\n }\n },\n detach: () => {\n ownerDocument.removeEventListener('selectionchange', reportSelection)\n target.removeEventListener('beforeinput', onEvent)\n target.removeEventListener('keydown', onEvent)\n target.removeEventListener('compositionstart', onCompositionStart)\n target.removeEventListener('compositionend', onCompositionEnd)\n target.removeEventListener('copy', onCopy)\n target.removeEventListener('cut', onCut)\n target.removeEventListener('paste', onPaste)\n target.removeEventListener('click', onClick)\n target.removeEventListener('mousedown', onMouseDown)\n },\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"events.mjs","names":["patchInto"],"sources":["../src/events.ts"],"sourcesContent":["/**\n * Browser events as editor intent (§§30–31). The adapter owns the subtree, so\n * every event it understands is `preventDefault`ed: the browser must not mutate\n * the DOM behind the semantic document. An event the adapter does not\n * understand is left alone (the caller lets it through), and one it understands\n * but cannot yet honor is prevented without a command — a deliberate no-op\n * rather than a silent DOM divergence.\n */\nimport * as RichText from 'foldkit-richtext'\nimport {\n patch as patchInto,\n positionToRange,\n rangeToPosition,\n repair,\n type EditorDom,\n} from './index.js'\nimport { parseHtml } from './html.js'\n\nexport interface Intent {\n /** The semantic command to run, when the event maps to one. */\n readonly command?: RichText.Command\n /** A history intent: undo or redo, which are editor intents, not commands. */\n readonly history?: 'undo' | 'redo'\n readonly preventDefault: boolean\n}\n\n/**\n * One chord an application adds to the adapter's table, or overrides in it.\n * Modifiers match exactly and the key matches what the browser reports, so\n * `Mod-b` does not fire for Shift held, and a chord that types a symbol names the\n * symbol: Shift+8 is `Mod-Shift-*` on a US layout, not `Mod-Shift-8`.\n */\nexport interface KeyBinding {\n /** `Mod-b`, `Mod-Shift-b`, `Alt-ArrowUp`. `Mod` is Meta or Control, either one. */\n readonly chord: string\n readonly command: RichText.Command\n}\n\nconst chordMatches = (chord: string, event: KeyboardEvent): boolean => {\n const parts = chord.split('-')\n const name = parts[parts.length - 1] ?? ''\n const modifiers = parts.slice(0, -1)\n const wantsMod = modifiers.includes('Mod')\n const hasMod = event.metaKey || event.ctrlKey\n if (hasMod !== wantsMod) return false\n if (event.shiftKey !== modifiers.includes('Shift')) return false\n if (event.altKey !== modifiers.includes('Alt')) return false\n return name.length === 1 ? event.key.toLowerCase() === name.toLowerCase() : event.key === name\n}\n\nconst MOD = (event: KeyboardEvent): boolean => event.metaKey || event.ctrlKey\n\nconst historyChord = (event: KeyboardEvent): 'undo' | 'redo' | undefined => {\n if (!MOD(event)) return undefined\n const key = event.key.toLowerCase()\n if (key === 'z') return event.shiftKey ? 'redo' : 'undo'\n if (key === 'y') return 'redo'\n return undefined\n}\n\nconst markChord = (event: KeyboardEvent): string | undefined => {\n if (!MOD(event)) return undefined\n switch (event.key.toLowerCase()) {\n case 'b':\n return 'Bold'\n case 'i':\n return 'Italic'\n case 'e':\n return 'Code'\n // Without Shift, the browser's save.\n case 's':\n return event.shiftKey ? 'Strikethrough' : undefined\n default:\n return undefined\n }\n}\n\n/**\n * Reads one event as intent. `composing` tells the adapter that an IME owns the\n * interaction: the browser must keep its temporary text, so composition input\n * is passed through and committed on `compositionend` instead.\n *\n * `keymap` is checked before the built-in chords, so an application adds to the\n * table or overrides an entry without forking the adapter.\n */\nexport const intentFor = (\n event: Event,\n composing = false,\n keymap: ReadonlyArray<KeyBinding> = [],\n): Intent | undefined => {\n if (event.type === 'beforeinput') {\n const input = event as Event & { readonly inputType?: string; readonly data?: string | null }\n const inputType = input.inputType ?? ''\n if (composing || inputType === 'insertCompositionText') return { preventDefault: false }\n switch (inputType) {\n case 'insertText':\n return input.data == null || input.data.length === 0\n ? { preventDefault: true }\n : { preventDefault: true, command: { type: 'InsertText', text: input.data } }\n case 'insertParagraph':\n case 'insertLineBreak':\n return { preventDefault: true, command: { type: 'SplitBlock' } }\n case 'deleteContentBackward':\n return { preventDefault: true, command: { type: 'DeleteBackward' } }\n case 'deleteContentForward':\n return { preventDefault: true, command: { type: 'DeleteForward' } }\n case 'formatBold':\n return { preventDefault: true, command: { type: 'ToggleMark', mark: 'Bold' } }\n case 'formatItalic':\n return { preventDefault: true, command: { type: 'ToggleMark', mark: 'Italic' } }\n default:\n // Paste, word deletion, autocorrect, and anything else the browser\n // invents: prevented so the DOM cannot drift, until it is implemented.\n return inputType.length === 0 ? undefined : { preventDefault: true }\n }\n }\n if (event.type !== 'keydown') return undefined\n const key = event as KeyboardEvent\n if (composing) return { preventDefault: false }\n const bound = keymap.find(binding => chordMatches(binding.chord, key))\n if (bound !== undefined) return { preventDefault: true, command: bound.command }\n const history = historyChord(key)\n if (history !== undefined) return { preventDefault: true, history }\n const mark = markChord(key)\n if (mark !== undefined) return { preventDefault: true, command: { type: 'ToggleMark', mark } }\n switch (key.key) {\n case 'Enter':\n return { preventDefault: true, command: { type: 'SplitBlock' } }\n case 'Backspace':\n return { preventDefault: true, command: { type: 'DeleteBackward' } }\n case 'Delete':\n return { preventDefault: true, command: { type: 'DeleteForward' } }\n default:\n return undefined\n }\n}\n\n/**\n * The semantic selection the browser is currently showing, if it resolves: null when the\n * page's selection is outside the editor, which is not the editor's to read.\n */\n/**\n * The atom a range holds whole, as a node selection is drawn: an atom has no text for a caret,\n * so the browser is given the range around its element.\n */\nconst selectedAtom = (dom: EditorDom, selection: Selection): RichText.NodeId | undefined => {\n const parent = selection.anchorNode\n if (parent === null || parent !== selection.focusNode) return undefined\n if (Math.abs(selection.focusOffset - selection.anchorOffset) !== 1) return undefined\n const child = parent.childNodes[Math.min(selection.anchorOffset, selection.focusOffset)]\n const id = child instanceof Element ? child.getAttribute('data-block') : null\n if (id === null) return undefined\n const block = RichText.locateBlock(dom.content, RichText.NodeId.make(id))?.block\n return block !== undefined && RichText.isAtom(undefined, block) ? block.id : undefined\n}\n\nexport const readSelection = (dom: EditorDom): RichText.Selection | null => {\n const selection = dom.root.ownerDocument.defaultView?.getSelection()\n if (selection === null || selection === undefined || selection.rangeCount === 0) return null\n const selected = selectedAtom(dom, selection)\n if (selected !== undefined) return { type: 'Node', node: selected }\n const anchor = rangeToPosition(dom, selection.anchorNode ?? dom.root, selection.anchorOffset)\n const focus = rangeToPosition(dom, selection.focusNode ?? dom.root, selection.focusOffset)\n if (anchor === undefined || focus === undefined) return null\n return { type: 'Range', anchor, focus }\n}\n\n/**\n * Where a position is in the DOM: a text node and an offset into it, or, on an empty line, the\n * line's element and the index of its `<br>`, where a browser draws a caret.\n */\nconst endpointFor = (\n dom: EditorDom,\n position: RichText.Position,\n): { node: Node; offset: number } | undefined => {\n const range = positionToRange(dom, position)\n return range === undefined ? undefined : { node: range.startContainer, offset: range.startOffset }\n}\n\n/**\n * Puts the browser selection back where the semantic state says it is. Direction\n * is preserved through `setBaseAndExtent` when the environment has it; jsdom and\n * some engines only offer a range, which cannot express direction.\n */\nexport const restoreSelection = (dom: EditorDom, selection: RichText.Selection | null): void => {\n const window = dom.root.ownerDocument.defaultView\n const live = window?.getSelection()\n if (window === null || window === undefined || live === null || live === undefined) return\n live.removeAllRanges()\n if (selection === null) return\n if (selection.type === 'Node') {\n const element = dom.elements.get(selection.node)\n if (element === undefined || element.parentNode === null) return\n const range = dom.root.ownerDocument.createRange()\n range.selectNode(element)\n live.addRange(range)\n return\n }\n const anchor = endpointFor(dom, selection.anchor)\n const focus = endpointFor(dom, selection.focus)\n if (anchor === undefined || focus === undefined) return\n if (typeof live.setBaseAndExtent === 'function') {\n live.setBaseAndExtent(anchor.node, anchor.offset, focus.node, focus.offset)\n return\n }\n const range = dom.root.ownerDocument.createRange()\n range.setStart(anchor.node, anchor.offset)\n range.setEnd(focus.node, focus.offset)\n live.addRange(range)\n}\n\n/** The clipboard type a Foldkit RichText slice travels under. */\nexport const SLICE_CLIPBOARD_TYPE = 'application/x-foldkit-richtext+json'\n\ninterface ClipboardLike {\n getData: (type: string) => string\n setData: (type: string, value: string) => void\n}\n\nexport interface AttachOptions {\n /** Called with each semantic command the browser produced. */\n readonly onIntent: (command: RichText.Command) => void\n /** Called for undo and redo, which are editor intents rather than commands. */\n readonly onHistory?: (direction: 'undo' | 'redo') => void\n /**\n * Called when the caret or range moves to a position the application did not\n * just commit. Without it the editor never learns where the caret is; the\n * adapter cannot follow a selection it is not told about.\n */\n readonly onSelection?: (selection: RichText.Selection | null) => void\n /**\n * When given, imported HTML is constrained to this vocabulary: a node kind\n * the Kit does not declare is degraded to a paragraph, never kept.\n */\n readonly kit?: RichText.Kit | undefined\n /** Chords the application adds or overrides, checked before the built-ins. */\n readonly keymap?: ReadonlyArray<KeyBinding> | undefined\n /**\n * What is drawn over the document (§129), derived from it each time it is synced — code\n * highlighting, say. A pure read: it sees the document and nothing else.\n */\n readonly decorate?: Decorate | undefined\n /** What a blank document shows (`RichText.isBlank`): \"Write something…\". */\n readonly placeholder?: string | undefined\n /**\n * What the empty paragraph a collapsed caret is in shows, as `data-hint` on it, which a\n * stylesheet draws as it draws the placeholder (usually only while the editor has the focus).\n * A blank document's block shows the placeholder instead.\n */\n readonly lineHint?: string | undefined\n /**\n * The node vocabulary, which says where Tab means something (`RichText.takesTab`): code and\n * table cells take it as `Indent` and `Outdent`; anywhere else it moves focus as it always does.\n */\n readonly nodes?: RichText.NodeRegistry | undefined\n /** Called with a block and an adornment's `action` when that adornment is clicked. */\n readonly onAction?: ((node: RichText.NodeId, action: string) => void) | undefined\n}\n\n/** What is drawn over a document, derived from it on every render (§129). */\nexport type Decorate = (document: RichText.Document) => RichText.DecorationSet\n\n/** A change set that names nothing: what a redraw of the same document patches with. */\nconst unchanged: RichText.ChangeSet = {\n dirtyNodes: new Set(),\n insertedNodes: new Set(),\n removedNodes: new Set(),\n textChanged: new Set(),\n structureChanged: false,\n selectionChanged: false,\n}\n\nexport interface Attachment {\n /** The current subtree; replaced as patches are applied. */\n readonly current: () => EditorDom\n /**\n * Applies a committed state, patching and restoring the browser selection. While the\n * browser is composing, the latest state waits and is drawn when composition ends.\n */\n readonly sync: (state: RichText.EditorState, changeSet: RichText.ChangeSet) => void\n /**\n * Draws the decorations again over what is drawn, for when they changed and the document\n * did not, and puts the browser's selection back where it was. Nothing while composing:\n * the next state drawn brings them.\n */\n readonly redecorate: () => void\n /**\n * Takes the focus with the caret or range at `selection`, scrolled into view when it is out\n * of it: for a control outside the editor that sends the reader into the text, such as a table\n * of contents or a find bar closing on its match.\n */\n readonly focus: (selection: RichText.Selection) => void\n /**\n * The latest document synced: the one waiting on a composition, or else the one drawn. A\n * patch computed from here to the next state is what brings the subtree up to it.\n */\n readonly held: () => RichText.Document\n /** True between compositionstart and compositionend. */\n readonly composing: () => boolean\n /**\n * Tells it an edit it asked for is drawn, or refused: input that came after the ask was held\n * until then, and goes on when every ask is answered. The editor answers each, in order.\n */\n readonly answered: () => void\n readonly detach: () => void\n}\n\n/** A caret key's move, as `Selection.modify` makes it. */\ninterface CaretMove {\n readonly extend: boolean\n readonly direction: 'backward' | 'forward' | 'left' | 'right'\n readonly granularity: 'character' | 'line' | 'lineboundary'\n}\n\nconst CARET_KEYS: Readonly<Record<string, Omit<CaretMove, 'extend'>>> = {\n ArrowUp: { direction: 'backward', granularity: 'line' },\n ArrowDown: { direction: 'forward', granularity: 'line' },\n // Left and right, not backward and forward: the arrows go the way they point in any direction.\n ArrowLeft: { direction: 'left', granularity: 'character' },\n ArrowRight: { direction: 'right', granularity: 'character' },\n Home: { direction: 'backward', granularity: 'lineboundary' },\n End: { direction: 'forward', granularity: 'lineboundary' },\n}\n\n/** How long held input waits for an edit nothing answers. */\nconst UNANSWERED_MS = 1000\n\n/** The move a caret key makes, with Shift or none: other modifiers keep the browser's. */\nconst caretMoveOf = (event: KeyboardEvent): CaretMove | undefined => {\n if (event.altKey || event.ctrlKey || event.metaKey) return undefined\n const move = Object.hasOwn(CARET_KEYS, event.key) ? CARET_KEYS[event.key] : undefined\n return move === undefined ? undefined : { ...move, extend: event.shiftKey }\n}\n\nconst samePosition = (left: RichText.Position, right: RichText.Position): boolean =>\n left.node === right.node && left.offset === right.offset\n\nconst sameSelection = (\n left: RichText.Selection | null,\n right: RichText.Selection | null,\n): boolean => {\n if (left === null || right === null) return left === right\n if (left.type === 'Node' || right.type === 'Node') {\n return left.type === 'Node' && right.type === 'Node' && left.node === right.node\n }\n return samePosition(left.anchor, right.anchor) && samePosition(left.focus, right.focus)\n}\n\n/**\n * Marks the lone block of a blank document with `data-placeholder`, which a stylesheet draws\n * with `::before { content: attr(data-placeholder) }`, and clears it from any block that no\n * longer qualifies. It is an attribute rather than a text node, so it never enters the\n * content the adapter reads back or the caret mapping. The block, not the root, carries it,\n * so the text sits on the line the caret is on.\n */\nconst drawPlaceholder = (\n dom: EditorDom,\n placeholder: string,\n marked: Element | undefined,\n): Element | undefined => {\n dom.root.setAttribute('aria-placeholder', placeholder)\n const first = dom.content.children[0]\n const target =\n RichText.isBlank(dom.content) && first !== undefined ? dom.elements.get(first.id) : undefined\n // Only the element marked last can carry the attribute, so nothing is searched per patch.\n if (marked !== target) marked?.removeAttribute('data-placeholder')\n target?.setAttribute('data-placeholder', placeholder)\n return target\n}\n\n/** The empty paragraph a collapsed caret is in, in a document that is not blank. */\nconst emptyLineAt = (dom: EditorDom, selection: RichText.Selection | null): Element | undefined => {\n if (selection?.type !== 'Range' || !samePosition(selection.anchor, selection.focus)) {\n return undefined\n }\n if (RichText.isBlank(dom.content)) return undefined\n const run = RichText.locateRun(dom.content, selection.focus.node)\n const block = run === undefined ? undefined : RichText.blockAtPath(dom.content, run.path)\n return block?.type === 'Paragraph' && block.children.every(each => each.text === '')\n ? dom.elements.get(block.id)\n : undefined\n}\n\n/**\n * The caret in the nearest text before or after a block, as an arrow key leaves a selected\n * image: null when there is no text on that side.\n */\nconst caretBeside = (\n document: RichText.Document,\n node: RichText.NodeId,\n after: boolean,\n): RichText.Selection | null => {\n let seen = false\n let before: RichText.Block['children'][number] | undefined\n let next: RichText.Block['children'][number] | undefined\n RichText.eachBlock(document.children, block => {\n if (block.id === node) seen = true\n else if (!seen) before = block.children.at(-1) ?? before\n else next ??= block.children[0]\n })\n const run = after ? next : before\n if (!seen || run === undefined) return null\n const at = { node: run.id, offset: after ? 0 : run.text.length, affinity: 'after' as const }\n return { type: 'Range', anchor: at, focus: at }\n}\n\n/** What a press inside an atom belongs to rather than the editor: a control, a link. */\nconst INTERACTIVE = 'a, button, input, select, textarea, video, audio'\n\n/**\n * Wires the owned subtree to an application. The adapter never decides what an\n * edit means: it translates events into commands, hands them to `onIntent`, and\n * patches whatever the application commits.\n */\nexport const attach = (dom: EditorDom, options: AttachOptions): Attachment => {\n let current = dom\n // Every replacement of the subtree goes through here: a repair or a patch can render the\n // blank block fresh, without the attribute the last one carried.\n let marked: Element | undefined\n /**\n * The selection the application last committed, or the adapter last reported.\n * Restoring it fires `selectionchange`, so without this the adapter would\n * report the position it had just been told as if a person had moved there.\n * Affinity is ignored: it is derived from the range, not carried by it.\n */\n let lastSelection: RichText.Selection | null = null\n let hinted: Element | undefined\n let selectedBlock: Element | undefined\n /**\n * Marks the block a node selection holds with `data-selected`: the browser draws no\n * selection of its own for one, so this is the only sign of it.\n */\n const markSelected = (): void => {\n const target =\n lastSelection?.type === 'Node' ? current.elements.get(lastSelection.node) : undefined\n if (selectedBlock !== target) selectedBlock?.removeAttribute('data-selected')\n target?.setAttribute('data-selected', '')\n selectedBlock = target\n }\n /** Moves `data-hint` to the empty line the caret is on, or takes it away. */\n const hint = (): void => {\n if (options.lineHint === undefined) return\n const target = emptyLineAt(current, lastSelection)\n if (hinted !== target) hinted?.removeAttribute('data-hint')\n target?.setAttribute('data-hint', options.lineHint)\n hinted = target\n }\n const redraw = (next: EditorDom): void => {\n current = next\n if (options.placeholder !== undefined) {\n marked = drawPlaceholder(current, options.placeholder, marked)\n }\n hint()\n markSelected()\n }\n redraw(dom)\n let composing = false\n /**\n * Only a range that resolves inside the editor is news. A selection elsewhere on the page\n * (another editor, an input) leaves the editor's own where it was, and so does a live\n * selection that cannot show the Model's: the browser never holds a Node selection, so\n * reading its absence back would turn a selected block into no selection at all.\n */\n const reportSelection = (): void => {\n // While composing, the caret points into text the document does not have.\n if (composing) return\n const selection = readSelection(current)\n if (selection === null || sameSelection(selection, lastSelection)) return\n lastSelection = selection\n hint()\n markSelected()\n options.onSelection?.(selection)\n }\n /** Takes a selection the editor made itself, as a click on an image or an arrow key does. */\n const select = (selection: RichText.Selection): void => {\n restoreSelection(current, selection)\n if (sameSelection(selection, lastSelection)) return\n lastSelection = selection\n hint()\n markSelected()\n options.onSelection?.(selection)\n }\n /**\n * The browser's selection is the page's, not the editor's: the editor moves it only while\n * it is inside the editor, so a redraw never takes the caret from another editor or an input.\n * Read before a patch replaces the nodes it points into.\n */\n const ownsSelection = (): boolean => {\n const live = current.root.ownerDocument.defaultView?.getSelection()\n if (live === null || live === undefined || live.rangeCount === 0) return false\n return current.root.contains(live.anchorNode) && current.root.contains(live.focusNode)\n }\n let placeholderIds = 0\n /**\n * A state the application committed while the browser was composing: drawing it then\n * would rewrite the text under the IME, so it waits for the composition to end. Change\n * sets are unioned, which never under-invalidates.\n */\n let deferred: { state: RichText.EditorState; changeSet: RichText.ChangeSet } | undefined\n /**\n * Input held after asking for an edit until what is drawn holds it, in the order it came.\n * The browser moves the caret through what is drawn, and the edit is drawn by a Command\n * that a loaded machine runs after the next keys: three Enters and an ArrowUp went up from\n * the old last line, two lines too far. A caret key waits, and an edit asked behind it\n * waits too, so it lands where the move took the caret. The editor answers every edit once\n * it is drawn or refused (`answered`); when none is unanswered they are made in order, and\n * one that asks for another edit holds the rest again. An edit nothing answers (a command\n * the editor has no Message for) lets them go after `UNANSWERED_MS`.\n */\n let held: Array<() => void> = []\n let unanswered = 0\n let fallback: ReturnType<typeof setTimeout> | undefined\n const release = (): void => {\n while (held.length > 0 && unanswered === 0) held.shift()!()\n }\n const answered = (): void => {\n unanswered = Math.max(0, unanswered - 1)\n if (unanswered > 0) return\n clearTimeout(fallback)\n release()\n }\n const holdMove = (move: CaretMove): void => {\n held.push(() => {\n const live = current.root.ownerDocument.defaultView?.getSelection()\n if (live === null || live === undefined || !ownsSelection()) return\n live.modify(move.extend ? 'extend' : 'move', move.direction, move.granularity)\n reportSelection()\n })\n }\n const ask = (command: RichText.Command): void => {\n unanswered++\n clearTimeout(fallback)\n fallback = setTimeout(() => {\n unanswered = 0\n release()\n }, UNANSWERED_MS)\n options.onIntent(command)\n }\n const onIntent = (command: RichText.Command): void => {\n if (held.length === 0) return ask(command)\n held.push(() => {\n reportSelection()\n ask(command)\n })\n }\n const draw = (state: RichText.EditorState, changeSet: RichText.ChangeSet): void => {\n const owned = ownsSelection()\n lastSelection = state.selection\n redraw(patchInto(current, state.document, changeSet, options.decorate?.(state.document)))\n if (owned) restoreSelection(current, state.selection)\n }\n /** For Mod+A in a kind whose text keeps line breaks (code), its text, unless that is selected. */\n const codeToSelect = (event: KeyboardEvent): RichText.Selection | undefined => {\n if (event.key.toLowerCase() !== 'a' || !MOD(event) || event.shiftKey || event.altKey) {\n return undefined\n }\n const selection = readSelection(current)\n if (selection?.type !== 'Range') return undefined\n const found = RichText.locateRun(current.content, selection.focus.node)\n const block =\n found === undefined ? undefined : RichText.blockAtPath(current.content, found.path)\n if (block?.type !== 'Node') return undefined\n const declared = options.nodes?.definitionFor(block.kind)\n if (declared?.kind !== 'node' || declared.lineBreaks !== true) return undefined\n const first = block.children[0]\n const last = block.children[block.children.length - 1]\n if (first === undefined || last === undefined) return undefined\n const whole: RichText.Selection = {\n type: 'Range',\n anchor: { node: first.id, offset: 0, affinity: 'after' },\n focus: { node: last.id, offset: last.text.length, affinity: 'after' },\n }\n return sameSelection(selection, whole) ? undefined : whole\n }\n /**\n * The atom an arrow key reaches from a caret at the edge it leaves its block by: the end or\n * start for Right and Left, the last or first line for Down and Up, with an atom beside the\n * block in its container.\n */\n const atomReached = (event: KeyboardEvent): RichText.NodeId | undefined => {\n const key = event.key\n const after = key === 'ArrowRight' || key === 'ArrowDown'\n if (!after && key !== 'ArrowLeft' && key !== 'ArrowUp') return undefined\n // With Shift a range grows, and with the others a word or a line is skipped.\n if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return undefined\n // Read live: a click just before may not have been reported yet.\n const selection = readSelection(current)\n if (selection?.type !== 'Range' || !samePosition(selection.anchor, selection.focus)) {\n return undefined\n }\n const content = current.content\n const found = RichText.locateRun(content, selection.focus.node)\n if (found === undefined) return undefined\n const index = found.path[found.path.length - 1]!\n const holder =\n found.path.length > 1 ? RichText.blockAtPath(content, found.path.slice(0, -1)) : undefined\n const siblings = holder?.type === 'Node' ? (holder.blocks ?? []) : content.children\n const block = siblings[index]\n const neighbour = siblings[index + (after ? 1 : -1)]\n if (block === undefined || neighbour === undefined) return undefined\n if (!RichText.isAtom(options.nodes, neighbour)) return undefined\n const lead = block.children.slice(0, found.index).reduce((sum, run) => sum + run.text.length, 0)\n const offset = lead + selection.focus.offset\n const length = block.children.reduce((sum, run) => sum + run.text.length, 0)\n if (offset === (after ? length : 0)) return neighbour.id\n if (key === 'ArrowRight' || key === 'ArrowLeft') return undefined\n // Down and Up leave from the last or first line, which only the layout knows.\n const caret = positionToRange(current, selection.focus)?.getBoundingClientRect()\n const box = current.elements.get(block.id)?.getBoundingClientRect()\n if (caret === undefined || box === undefined || caret.height === 0) return undefined\n const onEdgeLine = after\n ? caret.bottom > box.bottom - caret.height\n : caret.top < box.top + caret.height\n return onEdgeLine ? neighbour.id : undefined\n }\n const onEvent = (event: Event): void => {\n if (unanswered > 0 && event.type === 'keydown' && !composing) {\n const move = caretMoveOf(event as KeyboardEvent)\n if (move !== undefined) {\n event.preventDefault()\n holdMove(move)\n return\n }\n }\n // From a selected atom, an arrow goes to the text beside it, where a browser would step\n // into the atom's own markup.\n // Read live, as a click just before may not have been reported yet.\n const live = event.type === 'keydown' && !composing ? readSelection(current) : null\n if (live?.type === 'Node') {\n const key = (event as KeyboardEvent).key\n const after = key === 'ArrowRight' || key === 'ArrowDown'\n if (after || key === 'ArrowLeft' || key === 'ArrowUp') {\n event.preventDefault()\n const beside = caretBeside(current.content, live.node, after)\n if (beside !== null) select(beside)\n return\n }\n }\n // In code, the first select-all selects the code, and a second the document.\n if (event.type === 'keydown' && !composing) {\n const code = codeToSelect(event as KeyboardEvent)\n if (code !== undefined) {\n event.preventDefault()\n select(code)\n return\n }\n }\n // An arrow leaving a block for an atom beside it selects the atom, which a browser would\n // step over.\n if (event.type === 'keydown' && !composing) {\n const reached = atomReached(event as KeyboardEvent)\n if (reached !== undefined) {\n event.preventDefault()\n select({ type: 'Node', node: reached })\n return\n }\n }\n // Tab is taken only where it means something, so it still leaves the editor elsewhere.\n if (event.type === 'keydown' && !composing) {\n const key = event as KeyboardEvent\n if (key.key === 'Tab' && !key.altKey && !key.ctrlKey && !key.metaKey) {\n if (!RichText.takesTab(current.content, readSelection(current), options.nodes)) return\n event.preventDefault()\n reportSelection()\n onIntent({ type: key.shiftKey ? 'Outdent' : 'Indent' })\n return\n }\n }\n const intent = intentFor(event, composing, options.keymap)\n if (intent === undefined) return\n if (intent.preventDefault) event.preventDefault()\n // `selectionchange` is asynchronous, so a click just before a keystroke may not have\n // been reported yet: report it first, or the edit lands at the caret before the click.\n if (intent.command !== undefined) {\n reportSelection()\n onIntent(intent.command)\n }\n if (intent.history !== undefined) options.onHistory?.(intent.history)\n }\n const onCompositionStart = (): void => {\n reportSelection()\n composing = true\n }\n const onCompositionEnd = (event: Event): void => {\n composing = false\n const data = (event as Event & { readonly data?: string | null }).data\n // The browser's temporary text is not in the document, whether the IME\n // committed or cancelled, so repair the subtree before anything else, then\n // put back the selection composition started from — not the caret the\n // browser moved into its own temporary text. Nothing is reported or synced\n // while composing, so that is still `lastSelection`; with none known (an end\n // without a start), the caret is left where the browser put it.\n redraw(repair(current, current.content))\n // What arrived during the composition is drawn now. Its selection is the\n // application's, which holds the caret composition started from.\n if (deferred !== undefined) draw(deferred.state, deferred.changeSet)\n else if (lastSelection !== null) restoreSelection(current, lastSelection)\n deferred = undefined\n if (data != null && data.length > 0) onIntent({ type: 'InsertText', text: data })\n }\n const writeClipboard = (clipboard: ClipboardLike, slice: RichText.Slice): void => {\n clipboard.setData(SLICE_CLIPBOARD_TYPE, RichText.serializeSlice(slice))\n // HTML for other applications; importing it back waits on a\n // kit-constrained parser, so paste still prefers the slice.\n clipboard.setData('text/html', RichText.toHtml(slice.blocks))\n clipboard.setData('text/plain', RichText.toText(slice.blocks))\n }\n const onCopy = (event: Event): void => {\n const clipboard = (event as Event & { readonly clipboardData?: ClipboardLike }).clipboardData\n const slice = RichText.sliceOf(current.content, readSelection(current))\n if (clipboard === undefined || slice === undefined) return\n event.preventDefault()\n writeClipboard(clipboard, slice)\n }\n const onCut = (event: Event): void => {\n // The delete below runs at the application's selection, which a click just before may\n // not have reached yet, as with a keystroke.\n reportSelection()\n const clipboard = (event as Event & { readonly clipboardData?: ClipboardLike }).clipboardData\n const selection = readSelection(current)\n const slice = RichText.sliceOf(current.content, selection)\n if (clipboard === undefined || slice === undefined) return\n event.preventDefault()\n writeClipboard(clipboard, slice)\n // A collapsed caret cuts nothing; a range is removed through the same\n // delete intent a Backspace would produce.\n const collapsed =\n selection?.type === 'Range' &&\n selection.anchor.node === selection.focus.node &&\n selection.anchor.offset === selection.focus.offset\n if (!collapsed) onIntent({ type: 'DeleteBackward' })\n }\n const onPaste = (event: Event): void => {\n const clipboard = (event as Event & { readonly clipboardData?: ClipboardLike }).clipboardData\n if (clipboard === undefined) return\n const payload = clipboard.getData(SLICE_CLIPBOARD_TYPE)\n const html = clipboard.getData('text/html')\n const text = clipboard.getData('text/plain')\n const placeholders = () => `clipboard-${++placeholderIds}`\n // Slice first, then HTML, then plain text (§69). A payload this version\n // cannot read falls through rather than pasting nothing, and the Paste\n // command remints every identity, so these placeholders never reach the\n // document.\n const slice =\n (payload.length > 0 ? RichText.deserializeSlice(payload) : undefined) ??\n (html.length > 0\n ? {\n version: 1 as const,\n blocks: parseHtml(html, { kit: options.kit, mint: placeholders }).blocks,\n }\n : undefined) ??\n (text.length > 0 ? RichText.sliceFromText(text, placeholders) : undefined)\n if (slice === undefined || slice.blocks.length === 0) return\n event.preventDefault()\n reportSelection()\n onIntent({ type: 'Paste', slice })\n }\n // A click on an adornment that toggles a prop (a task's checkbox, a toggle's arrow) flips it in\n // the document; the browser's own change to a checkbox is refused, since the patch that\n // follows draws what the document says.\n const onClick = (event: MouseEvent): void => {\n const origin = event.target\n if (!(origin instanceof Element)) return\n const acting = origin.closest(`[${RichText.ACTION_ATTRIBUTE}]`)\n const actedOn = acting?.closest('[data-block]')?.getAttribute('data-block')\n if (\n acting !== null &&\n actedOn !== null &&\n actedOn !== undefined &&\n current.root.contains(acting)\n ) {\n event.preventDefault()\n options.onAction?.(\n RichText.NodeId.make(actedOn),\n acting.getAttribute(RichText.ACTION_ATTRIBUTE)!,\n )\n return\n }\n const adornment = origin.closest(`[${RichText.TOGGLES_ATTRIBUTE}]`)\n if (adornment === null || !current.root.contains(adornment)) return\n const prop = adornment.getAttribute(RichText.TOGGLES_ATTRIBUTE)!\n const id = adornment.closest('[data-block]')?.getAttribute('data-block')\n const block =\n id === null || id === undefined\n ? undefined\n : RichText.locateBlock(current.content, RichText.NodeId.make(id))?.block\n if (block?.type !== 'Node') return\n event.preventDefault()\n onIntent({\n type: 'SetProps',\n node: block.id,\n props: { [prop]: block.props[prop] !== true },\n })\n }\n /**\n * A press in the space below the last block writes after it, as below the last line of a\n * page: at the end of a paragraph or heading, or on a new paragraph after anything else (a\n * table, code, an image), which no caret could otherwise get below.\n */\n const pressBelow = (event: MouseEvent): boolean => {\n const last = current.content.children.at(-1)\n const element = last === undefined ? undefined : current.elements.get(last.id)\n if (last === undefined || element === undefined) return false\n if (event.clientY <= element.getBoundingClientRect().bottom) return false\n event.preventDefault()\n current.root.focus({ preventScroll: true })\n const run =\n last.type === 'Paragraph' || last.type === 'Heading' ? last.children.at(-1) : undefined\n if (run !== undefined) {\n const end = { node: run.id, offset: run.text.length, affinity: 'after' as const }\n select({ type: 'Range', anchor: end, focus: end })\n return true\n }\n onIntent({\n type: 'InsertBlock',\n block: {\n type: 'Paragraph',\n id: RichText.NodeId.make('paragraph'),\n children: [{ type: 'Text', id: RichText.NodeId.make('text'), text: '', marks: [] }],\n },\n to: { after: last.id },\n })\n return true\n }\n // A press on an atom (an image, a divider) selects it whole, as there is no text in it for\n // a caret, and keeps the browser from dragging it or putting the caret elsewhere.\n const onMouseDown = (event: MouseEvent): void => {\n const origin = event.target\n if (event.button !== 0 || !(origin instanceof Element)) return\n if (origin === current.root && pressBelow(event)) return\n // An adornment (a task's checkbox, a code block's Copy) acts on its click; the press keeps\n // the caret where it was, so writing goes on there after it.\n if (\n origin.closest(`[${RichText.ACTION_ATTRIBUTE}], [${RichText.TOGGLES_ATTRIBUTE}]`) !== null\n ) {\n event.preventDefault()\n return\n }\n if (origin.closest(INTERACTIVE) !== null) return\n const id = origin.closest('[data-block]')?.getAttribute('data-block')\n if (id === null || id === undefined || !current.root.contains(origin)) return\n const block = RichText.locateBlock(current.content, RichText.NodeId.make(id))?.block\n if (block === undefined) return\n // A third press selects its block's text. The browser's own selection runs on to the\n // start of the next block, so what is typed over it would swallow that block too.\n const first = block.children[0]\n const last = block.children[block.children.length - 1]\n if (event.detail === 3 && first !== undefined && last !== undefined) {\n event.preventDefault()\n select({\n type: 'Range',\n anchor: { node: first.id, offset: 0, affinity: 'after' },\n focus: { node: last.id, offset: last.text.length, affinity: 'after' },\n })\n return\n }\n if (!RichText.isAtom(options.nodes, block)) return\n event.preventDefault()\n current.root.focus({ preventScroll: true })\n select({ type: 'Node', node: block.id })\n }\n const target = dom.root\n // The caret belongs to the document, not the subtree: a click elsewhere on the\n // page, or an arrow key, moves it without an event the root would see.\n const ownerDocument = target.ownerDocument\n ownerDocument.addEventListener('selectionchange', reportSelection)\n target.addEventListener('beforeinput', onEvent)\n target.addEventListener('keydown', onEvent)\n target.addEventListener('compositionstart', onCompositionStart)\n target.addEventListener('compositionend', onCompositionEnd)\n target.addEventListener('copy', onCopy)\n target.addEventListener('cut', onCut)\n target.addEventListener('paste', onPaste)\n target.addEventListener('click', onClick)\n target.addEventListener('mousedown', onMouseDown)\n return {\n current: () => current,\n held: () => deferred?.state.document ?? current.content,\n composing: () => composing,\n answered,\n redecorate: () => {\n if (composing) return\n const selection = readSelection(current)\n redraw(patchInto(current, current.content, unchanged, options.decorate?.(current.content)))\n if (selection !== null) restoreSelection(current, selection)\n },\n focus: selection => {\n current.root.focus({ preventScroll: true })\n restoreSelection(current, selection)\n if (selection.type !== 'Range') return\n const at = positionToRange(current, selection.focus)?.startContainer\n const element = at instanceof Element ? at : at?.parentElement\n const box = element?.getBoundingClientRect()\n const view = current.root.ownerDocument.defaultView\n if (box === undefined || view === null) return\n if (box.top < 0 || box.bottom > view.innerHeight) element?.scrollIntoView({ block: 'center' })\n },\n sync: (state, changeSet) => {\n if (!composing) return draw(state, changeSet)\n deferred = {\n state,\n changeSet:\n deferred === undefined\n ? changeSet\n : RichText.unionChangeSet(deferred.changeSet, changeSet),\n }\n },\n detach: () => {\n clearTimeout(fallback)\n ownerDocument.removeEventListener('selectionchange', reportSelection)\n target.removeEventListener('beforeinput', onEvent)\n target.removeEventListener('keydown', onEvent)\n target.removeEventListener('compositionstart', onCompositionStart)\n target.removeEventListener('compositionend', onCompositionEnd)\n target.removeEventListener('copy', onCopy)\n target.removeEventListener('cut', onCut)\n target.removeEventListener('paste', onPaste)\n target.removeEventListener('click', onClick)\n target.removeEventListener('mousedown', onMouseDown)\n },\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { a as KeyBinding, n as Attachment, r as Decorate, t as AttachOptions } from "./events-DLi6ecvD.mjs";
2
- import { i as SlashEntry, n as EditorEvent } from "./editor-gCZiXgG4.mjs";
1
+ import { a as KeyBinding, n as Attachment, r as Decorate, t as AttachOptions } from "./events-De4SvZil.mjs";
2
+ import { i as SlashEntry, n as EditorEvent } from "./editor-Cmo24yol.mjs";
3
3
  import * as RichText from "foldkit-richtext";
4
4
  //#region src/host.d.ts
5
5
  /**
@@ -102,4 +102,4 @@ declare const attachmentIn: (host: Element) => Attachment | undefined;
102
102
  declare const releaseMount: (host: Element) => void;
103
103
  //#endregion
104
104
  export { vocabularyFor as S, placeholdersFor as _, inputRulesFor as a, serverRenderedFor as b, placeDecorations as c, placeOverlay as d, placePlaceholders as f, placeVocabulary as g, placeSlashEntries as h, decorationsFor as i, placeInputRules as l, placeServerRendered as m, Vocabulary as n, keymapFor as o, placeRendering as p, attachmentIn as r, mountInto as s, Placeholders as t, placeKeymap as u, releaseMount as v, slashEntriesFor as x, renderingFor as y };
105
- //# sourceMappingURL=host-Cp2Zz-bO.d.mts.map
105
+ //# sourceMappingURL=host-BKvhyPj-.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"host-Cp2Zz-bO.d.mts","names":[],"sources":["../src/host.ts"],"mappings":";;;;;;;;;UA0BiB;WACN,QAAQ,SAAS;WACjB,QAAQ,SAAS;;;;;;;cAQf,kBAAe,gBAAkB,YAAc;;cAK/C,gBAAa,mBAAqB;;;;;cAQlC,mBAAgB,gBAAkB,UAAY;;;;;cAU9C,eAAY,gBAAkB,aAAe,SAAS;;;;;cAQtD,iBAAc,mBACP;;;;;cASP,sBAAmB,gBAAkB;;cAKrC,oBAAiB;;UAIb;;WAEN;;WAEA;;;;;;cASE,oBAAiB,gBAAkB,QAAU;;cAK7C,kBAAe,mBAAqB;;cAKpC,cAAW,gBAAkB,QAAU,cAAc;;cAKrD,YAAS,mBAAqB,cAAc;;;;;cAQ5C,oBAAiB,gBACd,SACL,cAAc,WAAW;;cAOvB,kBAAe,mBAEzB,cAAc,WAAW;;;;;;;cAUf,kBAAe,gBAAkB,OAAS,cAAc,SAAS;;cAKjE,gBAAa,mBAAqB,cAAc,SAAS;;;;;;;cASzD,iBAAc,gBAAkB,WAAa,SAAS;;cAKtD,eAAY,mBAAqB,SAAS;;;;;;;cAS1C,YAAS,MACd,SAAO,SACJ,SAAS,UAAQ,SACjB,eAAa,YACX,SAAS,cACnB;;cAgBU,eAAY,MAAU,YAAU;;;;;;;cAQhC,eAAY,MAAU"}
1
+ {"version":3,"file":"host-BKvhyPj-.d.mts","names":[],"sources":["../src/host.ts"],"mappings":";;;;;;;;;UA0BiB;WACN,QAAQ,SAAS;WACjB,QAAQ,SAAS;;;;;;;cAQf,kBAAe,gBAAkB,YAAc;;cAK/C,gBAAa,mBAAqB;;;;;cAQlC,mBAAgB,gBAAkB,UAAY;;;;;cAU9C,eAAY,gBAAkB,aAAe,SAAS;;;;;cAQtD,iBAAc,mBACP;;;;;cASP,sBAAmB,gBAAkB;;cAKrC,oBAAiB;;UAIb;;WAEN;;WAEA;;;;;;cASE,oBAAiB,gBAAkB,QAAU;;cAK7C,kBAAe,mBAAqB;;cAKpC,cAAW,gBAAkB,QAAU,cAAc;;cAKrD,YAAS,mBAAqB,cAAc;;;;;cAQ5C,oBAAiB,gBACd,SACL,cAAc,WAAW;;cAOvB,kBAAe,mBAEzB,cAAc,WAAW;;;;;;;cAUf,kBAAe,gBAAkB,OAAS,cAAc,SAAS;;cAKjE,gBAAa,mBAAqB,cAAc,SAAS;;;;;;;cASzD,iBAAc,gBAAkB,WAAa,SAAS;;cAKtD,eAAY,mBAAqB,SAAS;;;;;;;cAS1C,YAAS,MACd,SAAO,SACJ,SAAS,UAAQ,SACjB,eAAa,YACX,SAAS,cACnB;;cAgBU,eAAY,MAAU,YAAU;;;;;;;cAQhC,eAAY,MAAU"}
package/dist/host.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { S as vocabularyFor, _ as placeholdersFor, a as inputRulesFor, b as serverRenderedFor, c as placeDecorations, d as placeOverlay, f as placePlaceholders, g as placeVocabulary, h as placeSlashEntries, i as decorationsFor, l as placeInputRules, m as placeServerRendered, n as Vocabulary, o as keymapFor, p as placeRendering, r as attachmentIn, s as mountInto, t as Placeholders, u as placeKeymap, v as releaseMount, x as slashEntriesFor, y as renderingFor } from "./host-Cp2Zz-bO.mjs";
1
+ import { S as vocabularyFor, _ as placeholdersFor, a as inputRulesFor, b as serverRenderedFor, c as placeDecorations, d as placeOverlay, f as placePlaceholders, g as placeVocabulary, h as placeSlashEntries, i as decorationsFor, l as placeInputRules, m as placeServerRendered, n as Vocabulary, o as keymapFor, p as placeRendering, r as attachmentIn, s as mountInto, t as Placeholders, u as placeKeymap, v as releaseMount, x as slashEntriesFor, y as renderingFor } from "./host-BKvhyPj-.mjs";
2
2
  export { Placeholders, Vocabulary, attachmentIn, decorationsFor, inputRulesFor, keymapFor, mountInto, placeDecorations, placeInputRules, placeKeymap, placeOverlay, placePlaceholders, placeRendering, placeServerRendered, placeSlashEntries, placeVocabulary, placeholdersFor, releaseMount, renderingFor, serverRenderedFor, slashEntriesFor, vocabularyFor };
@@ -1 +1 @@
1
- {"version":3,"file":"toolbar.d.mts","names":[],"sources":["../src/floating.ts","../src/drag.ts","../src/toolbar.ts"],"mappings":";;;;;;qBAgPa,iBAAe,MAAA;;;;;;;;;qBAqBf,aAAW,MAAA;;;;;;;;;;;;;;;;qBCiBX,WAAS,MAAA;;;;;;;;;;;;;;;;;;;iBC1QL;WACN,UAAU,SAAS;WACnB,WAAW,SAAS;WACpB,aAAa;;iBAGP,eAAe;WACrB,OAAO;;WAEP,QAAQ;;WAER,YAAY,iBAAiB;;;;;;;qBAQ3B,aAAU,OAAW,cAAY;;qBAMjC,eACV,SAAO,SAAW,eAAe,cAAQ,GACtC,YAAY,aAAW"}
1
+ {"version":3,"file":"toolbar.d.mts","names":[],"sources":["../src/floating.ts","../src/drag.ts","../src/toolbar.ts"],"mappings":";;;;;;qBAgPa,iBAAe,MAAA;;;;;;;;;qBAqBf,aAAW,MAAA;;;;;;;;;;;;;;;;qBCgCX,WAAS,MAAA;;;;;;;;;;;;;;;;;;;iBCzRL;WACN,UAAU,SAAS;WACnB,WAAW,SAAS;WACpB,aAAa;;iBAGP,eAAe;WACrB,OAAO;;WAEP,QAAQ;;WAER,YAAY,iBAAiB;;;;;;;qBAQ3B,aAAU,OAAW,cAAY;;qBAMjC,eACV,SAAO,SAAW,eAAe,cAAQ,GACtC,YAAY,aAAW"}
package/dist/toolbar.mjs CHANGED
@@ -293,6 +293,8 @@ const dragBlock = (handle, hostId, node, dropped) => {
293
293
  let moving;
294
294
  let marked = false;
295
295
  let lastY = 0;
296
+ let startX = 0;
297
+ let startY = 0;
296
298
  const { style } = handle;
297
299
  const touchAction = style.getPropertyValue("touch-action");
298
300
  style.setProperty("touch-action", "none");
@@ -345,6 +347,9 @@ const dragBlock = (handle, hostId, node, dropped) => {
345
347
  if (!ours(pointed)) return;
346
348
  if (((pointed.buttons ?? 1) & 1) === 0) return end(false);
347
349
  if (!marked) {
350
+ const x = (pointed.clientX ?? startX) - startX;
351
+ const y = (pointed.clientY ?? startY) - startY;
352
+ if (Math.hypot(x, y) < 5) return;
348
353
  moving?.setAttribute("data-richtext-dragging", "");
349
354
  owner.documentElement.setAttribute("data-richtext-drag", "");
350
355
  marked = true;
@@ -384,6 +389,7 @@ const dragBlock = (handle, hostId, node, dropped) => {
384
389
  ["pointermove", moved],
385
390
  ["pointerup", (event) => {
386
391
  if (!ours(event)) return;
392
+ if (!marked) return end(false);
387
393
  place(event.clientY ?? lastY);
388
394
  end(true);
389
395
  }],
@@ -403,6 +409,8 @@ const dragBlock = (handle, hostId, node, dropped) => {
403
409
  pressed = true;
404
410
  pointer = pointed.pointerId;
405
411
  lastY = pointed.clientY ?? 0;
412
+ startX = pointed.clientX ?? 0;
413
+ startY = lastY;
406
414
  const host = owner.getElementById(hostId);
407
415
  const dom = host === null ? void 0 : attachmentIn(host)?.current();
408
416
  if (dom !== void 0) {