@aicayzer/inkkit 0.1.0 → 0.1.2

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.
@@ -22734,7 +22734,7 @@ var SerializerState = class extends Stack {
22734
22734
  #closeMark;
22735
22735
  };
22736
22736
  //#endregion
22737
- //#region node_modules/.pnpm/prosemirror-view@1.42.6/node_modules/prosemirror-view/dist/index.js
22737
+ //#region node_modules/.pnpm/prosemirror-view@1.42.6_patch_hash=a3414754d6cb6cd32517617a4affee5f7895ca0b3b976186951d43106adf18b3/node_modules/prosemirror-view/dist/index.js
22738
22738
  var domIndex = function(node) {
22739
22739
  for (var index = 0;; index++) {
22740
22740
  node = node.previousSibling;
@@ -25155,6 +25155,7 @@ var InputState = class {
25155
25155
  this.lastIOSEnter = 0;
25156
25156
  this.lastIOSEnterFallbackTimeout = -1;
25157
25157
  this.lastFocus = 0;
25158
+ this.focusTimeout = -1;
25158
25159
  this.lastTouch = 0;
25159
25160
  this.lastChromeDelete = 0;
25160
25161
  this.composing = false;
@@ -25185,6 +25186,7 @@ function setSelectionOrigin(view, origin) {
25185
25186
  view.input.lastSelectionTime = Date.now();
25186
25187
  }
25187
25188
  function destroyInput(view) {
25189
+ clearTimeout(view.input.focusTimeout);
25188
25190
  if (view.input.mouseDown) view.input.mouseDown.done();
25189
25191
  view.domObserver.stop();
25190
25192
  for (let type in view.input.eventHandlers) view.dom.removeEventListener(type, view.input.eventHandlers[type]);
@@ -25213,6 +25215,17 @@ function dispatchEvent$1(view, event) {
25213
25215
  }
25214
25216
  editHandlers.keydown = (view, _event) => {
25215
25217
  let event = _event;
25218
+ if (![
25219
+ 16,
25220
+ 17,
25221
+ 18,
25222
+ 91,
25223
+ 92,
25224
+ 93
25225
+ ].includes(event.keyCode)) {
25226
+ clearTimeout(view.input.focusTimeout);
25227
+ view.input.focusTimeout = -1;
25228
+ }
25216
25229
  view.input.shiftKey = event.keyCode == 16 || event.shiftKey;
25217
25230
  if (inOrNearComposition(view)) return;
25218
25231
  view.input.lastKeyCode = event.keyCode;
@@ -25755,12 +25768,15 @@ handlers.focus = (view) => {
25755
25768
  view.dom.classList.add("ProseMirror-focused");
25756
25769
  view.domObserver.start();
25757
25770
  view.focused = true;
25758
- setTimeout(() => {
25771
+ view.input.focusTimeout = setTimeout(() => {
25772
+ view.input.focusTimeout = -1;
25759
25773
  if (view.docView && view.hasFocus() && !view.domObserver.currentSelection.eq(view.domSelectionRange())) selectionToDOM(view);
25760
25774
  }, 20);
25761
25775
  }
25762
25776
  };
25763
25777
  handlers.blur = (view, _event) => {
25778
+ clearTimeout(view.input.focusTimeout);
25779
+ view.input.focusTimeout = -1;
25764
25780
  let event = _event;
25765
25781
  if (view.focused) {
25766
25782
  view.domObserver.stop();
@@ -31127,6 +31143,11 @@ function mermaidPreview(onError) {
31127
31143
  }));
31128
31144
  }
31129
31145
  //#endregion
31146
+ //#region src/hard-break.ts
31147
+ function htmlBreakAt(position) {
31148
+ return position.parent.type.name === "heading" && position.parent.attrs.form !== "setext" || position.depth > 1 && /^table_(cell|header)$/.test(position.node(-1).type.name);
31149
+ }
31150
+ //#endregion
31130
31151
  //#region node_modules/.pnpm/rope-sequence@1.3.4/node_modules/rope-sequence/dist/index.js
31131
31152
  var GOOD_LEAF_SIZE = 200;
31132
31153
  var RopeSequence = function RopeSequence() {};
@@ -39485,6 +39506,194 @@ var cursor = [
39485
39506
  })
39486
39507
  ].flat();
39487
39508
  //#endregion
39509
+ //#region src/smart-punctuation.ts
39510
+ /**
39511
+ * Smart punctuation: the quote or dash being typed becomes a curly quote or a dash.
39512
+ *
39513
+ * - `"` and `'` open after the start of a block, a line break, whitespace, an opening bracket, a
39514
+ * dash or another opening quote, and close everywhere else. A `'` between letters is an
39515
+ * apostrophe, which is the closing form.
39516
+ * - `--` gives an en dash and `---` an em dash: the third hyphen turns the en dash that the second
39517
+ * one made into an em dash. Only a hyphen that text input has just inserted counts as the first
39518
+ * of the two, so a hyphen already in the note, or typed before the caret moved, stays one.
39519
+ * - A quote that a dead key made arrives as a composition. When the composition commits exactly
39520
+ * that quote at the caret, it follows the same rules.
39521
+ *
39522
+ * These are input rules, so only the character being typed changes, Backspace and Undo straight
39523
+ * after restore what was typed, and nothing in a note is rewritten. The editor answers
39524
+ * `smartPunctuationCtx` with the current option, so turning it on or off needs no new editor.
39525
+ */
39526
+ var smartPunctuationCtx = $ctx(() => false, "inkkitSmartPunctuation");
39527
+ /** Set on the transaction of each rule that this file makes. */
39528
+ var smartRule = "inkkitSmartPunctuation";
39529
+ var enDash = "–";
39530
+ var emDash = "—";
39531
+ /** What may come before a quote for it to open. */
39532
+ var opensAfter = /[\s([{<\u2013\u2014\u2018\u201C]/u;
39533
+ /**
39534
+ * Markdown that a person is typing out in a line of text, not words: a code span, a link
39535
+ * destination, a web address, a wiki link or footnote label, an HTML tag or comment and a
39536
+ * reference definition. Smart punctuation leaves a line alone while one is open, because the
39537
+ * curly character would change what the Markdown means.
39538
+ */
39539
+ var typingSyntax = [
39540
+ (before) => (before.match(/`+/g)?.length ?? 0) % 2 === 1,
39541
+ (before) => /\]\([^)]*$/.test(before),
39542
+ (before) => /\[\[[^\]]*$/.test(before),
39543
+ (before) => /\[\^[^\]]*$/.test(before),
39544
+ (before) => /(?:^|[^\w])(?:[a-z][a-z0-9+.-]*:\/\/|www\.|mailto:)\S*$/i.test(before),
39545
+ (before) => /(?:^|[^\w])<[A-Za-z!/?][^>]*$/.test(before),
39546
+ (before) => /^\s*\[[^\]]+\]:/.test(before)
39547
+ ];
39548
+ /**
39549
+ * The text of the block before `pos`, with each code span and each non-text node as one
39550
+ * placeholder, so that a backtick or `<` inside code is not syntax being typed.
39551
+ */
39552
+ function textBefore(state, pos) {
39553
+ const $pos = state.doc.resolve(pos);
39554
+ let text = "";
39555
+ $pos.parent.forEach((child, offset) => {
39556
+ if (offset >= $pos.parentOffset) return;
39557
+ text += child.isText && !child.marks.some((mark) => mark.type.spec.code) ? child.text.slice(0, $pos.parentOffset - offset) : "";
39558
+ });
39559
+ return text;
39560
+ }
39561
+ var input = new PluginKey("inkkitSmartPunctuationInput");
39562
+ /** The text a transaction inserted, when it is one step that inserts text at `at` and nothing else. */
39563
+ function insertedAt(tr, at) {
39564
+ const [step] = tr.steps;
39565
+ if (tr.steps.length !== 1 || !(step instanceof ReplaceStep)) return void 0;
39566
+ if (step.from !== at || step.slice.openStart || step.slice.openEnd) return void 0;
39567
+ return step.slice.content.textBetween(0, step.slice.content.size);
39568
+ }
39569
+ var inputTracking = $prose(() => new Plugin({
39570
+ key: input,
39571
+ state: {
39572
+ init: () => ({
39573
+ pending: null,
39574
+ hyphen: null,
39575
+ composition: null,
39576
+ composedQuote: null
39577
+ }),
39578
+ apply(tr, value) {
39579
+ const { pending, composition } = value;
39580
+ value.pending = value.hyphen = value.composedQuote = null;
39581
+ if (tr.getMeta(hyphenReceived)) value.hyphen = tr.selection.from;
39582
+ if (!tr.docChanged) return value;
39583
+ if (pending !== null && insertedAt(tr, pending) === "-") value.hyphen = pending + 1;
39584
+ if (composition) {
39585
+ value.composition = null;
39586
+ const text = insertedAt(tr, composition.pos);
39587
+ if (tr.getMeta("composition") !== void 0 && (text === "\"" || text === "'") && tr.doc.content.size === composition.size + 1) value.composedQuote = composition.pos + 1;
39588
+ }
39589
+ return value;
39590
+ }
39591
+ },
39592
+ props: {
39593
+ handleTextInput(view, from, _to, text) {
39594
+ input.getState(view.state).pending = text === "-" ? from : null;
39595
+ return false;
39596
+ },
39597
+ handleDOMEvents: { compositionstart(view) {
39598
+ const { from, empty } = view.state.selection;
39599
+ const tracked = input.getState(view.state);
39600
+ tracked.composedQuote = null;
39601
+ tracked.composition = empty ? {
39602
+ pos: from,
39603
+ size: view.state.doc.content.size
39604
+ } : null;
39605
+ return false;
39606
+ } }
39607
+ }
39608
+ }));
39609
+ /** Set on a transaction that inserted text ending in a hyphen, whatever else came with it. */
39610
+ var hyphenReceived = "inkkitHyphenReceived";
39611
+ /** Marks a transaction that inserted text ending in a hyphen that no rule changed. */
39612
+ function insertedHyphen(tr) {
39613
+ return tr.setMeta(hyphenReceived, true);
39614
+ }
39615
+ /**
39616
+ * The state that input rules see for text that arrived in one piece, which holds only what the
39617
+ * rules need: it follows the document, with the tracking plugin when the editor has one. When
39618
+ * `rest` ends in a hyphen, the last character follows a hyphen that came in the same piece.
39619
+ */
39620
+ function chunkState(state, config, rest) {
39621
+ const tracker = input.get(state);
39622
+ const prepared = EditorState.create({
39623
+ ...config,
39624
+ plugins: tracker ? [tracker] : []
39625
+ });
39626
+ return tracker && rest.endsWith("-") ? prepared.apply(insertedHyphen(prepared.tr)) : prepared;
39627
+ }
39628
+ /**
39629
+ * Whether a rule may change what `match` found at `pos`: the option is on, the last character of
39630
+ * the match is the one being typed, which is not in the document yet, and the text is ordinary,
39631
+ * with no Markdown being typed. The input rules plugin also runs every rule when a composition
39632
+ * ends, over text that is all in the document; that is not typing, and is left alone.
39633
+ */
39634
+ function applies(on, state, match, pos) {
39635
+ if (!on()) return false;
39636
+ const typed = pos + match[0].length - 1 === state.selection.from;
39637
+ const composed = !typed && match[0].length === 1 && input.getState(state)?.composedQuote === state.selection.from && pos === state.selection.from - 1;
39638
+ if (!typed && !composed) return false;
39639
+ const $pos = state.doc.resolve(pos);
39640
+ if ($pos.parent.type.spec.code || !$pos.parent.inlineContent) return false;
39641
+ if ((state.storedMarks ?? $pos.marks()).some((mark) => mark.type.spec.code)) return false;
39642
+ const before = textBefore(state, pos);
39643
+ return !typingSyntax.some((open) => open(before));
39644
+ }
39645
+ function opensHere(state, pos) {
39646
+ const node = state.doc.resolve(pos).nodeBefore;
39647
+ if (!node) return true;
39648
+ if (node.isText) {
39649
+ const text = node.text ?? "";
39650
+ return opensAfter.test(String.fromCodePoint(text.codePointAt(text.length - 1)));
39651
+ }
39652
+ return node.type.name === "hardbreak";
39653
+ }
39654
+ /** The input rule that ran at the last change, when nothing has happened since. */
39655
+ function lastRule(state) {
39656
+ for (const plugin of state.plugins) if (plugin.spec.isInputRules) {
39657
+ const stored = plugin.getState(state);
39658
+ if (stored) return stored;
39659
+ }
39660
+ }
39661
+ var quotes$1 = {
39662
+ "\"": ["“", "”"],
39663
+ "'": ["‘", "’"]
39664
+ };
39665
+ var smartPunctuation = [
39666
+ smartPunctuationCtx,
39667
+ inputTracking,
39668
+ $inputRule((ctx) => new InputRule(/["']$/, (state, match, start, end) => {
39669
+ if (!applies(ctx.get(smartPunctuationCtx.key), state, match, start)) return null;
39670
+ const [opening, closing] = quotes$1[match[0]];
39671
+ return state.tr.insertText(opensHere(state, start) ? opening : closing, start, end).setMeta(smartRule, "quote");
39672
+ }, { inCodeMark: false })),
39673
+ $inputRule((ctx) => new InputRule(/--$/, (state, match, start, end) => {
39674
+ if (!applies(ctx.get(smartPunctuationCtx.key), state, match, start)) return null;
39675
+ if (input.getState(state)?.hyphen !== state.selection.from) return null;
39676
+ const before = textBefore(state, start);
39677
+ if (!before.trim() || before.endsWith("-") || /^\s*\|/.test(before)) return null;
39678
+ return state.tr.insertText(enDash, start, end).setMeta(smartRule, "en");
39679
+ }, { inCodeMark: false })),
39680
+ $inputRule((ctx) => new InputRule(/\u2013-$/, (state, match, start, end) => {
39681
+ if (!applies(ctx.get(smartPunctuationCtx.key), state, match, start)) return null;
39682
+ if (lastRule(state)?.transform.getMeta(smartRule) !== "en") return null;
39683
+ return state.tr.insertText(emDash, start, end).setMeta(smartRule, "em");
39684
+ }, { inCodeMark: false }))
39685
+ ].flat();
39686
+ /**
39687
+ * Undo straight after a smart quote or dash puts back the character that was typed, as Backspace
39688
+ * does for any input rule. The replacement joins the typing's history step, so the next Undo
39689
+ * removes the typing and not the curly character. Returns whether it applied.
39690
+ */
39691
+ var undoSmartPunctuation = (state, dispatch) => {
39692
+ const rule = lastRule(state);
39693
+ if (!rule?.transform.getMeta(smartRule)) return false;
39694
+ return undoInputRule(state, dispatch && ((tr) => dispatch(tr.setMeta("appendedTransaction", rule.transform))));
39695
+ };
39696
+ //#endregion
39488
39697
  //#region src/text-input.ts
39489
39698
  var isInputRules = (plugin) => plugin.spec.isInputRules === true;
39490
39699
  /**
@@ -39514,12 +39723,12 @@ function insertTextChunk(view, from, to, text, deflt) {
39514
39723
  return true;
39515
39724
  }
39516
39725
  const rules = state.plugins.filter(isInputRules);
39517
- const prepared = EditorState.create({
39726
+ const prepared = chunkState(state, {
39518
39727
  schema: state.schema,
39519
39728
  doc: tr.doc,
39520
39729
  selection: TextSelection.create(tr.doc, at),
39521
39730
  storedMarks: state.storedMarks ?? (from === to ? $from.marks() : $from.marksAcross($to))
39522
- });
39731
+ }, rest);
39523
39732
  let ruled;
39524
39733
  const preparedView = Object.create(view, {
39525
39734
  state: { value: prepared },
@@ -39528,7 +39737,8 @@ function insertTextChunk(view, from, to, text, deflt) {
39528
39737
  } }
39529
39738
  });
39530
39739
  if (!rules.some((rule) => rule.props.handleTextInput?.call(rule, preparedView, at, at, last, () => prepared.tr.insertText(last, at)))) {
39531
- view.dispatch(deflt().scrollIntoView());
39740
+ const plain = deflt().scrollIntoView();
39741
+ view.dispatch(last === "-" ? insertedHyphen(plain) : plain);
39532
39742
  return true;
39533
39743
  }
39534
39744
  if (ruled) {
@@ -44225,7 +44435,7 @@ function lineBreakHTML(value) {
44225
44435
  ].includes(value.trim());
44226
44436
  }
44227
44437
  function restoreInlineBreaks(node) {
44228
- if ((node.type === "paragraph" || node.type === "heading") && node.children.length > 1 || node.type === "tableCell") node.children = node.children.map((child) => child.type === "html" && lineBreakHTML(child.value) ? {
44438
+ if (node.type === "paragraph" && node.children.length > 1 || node.type === "heading" || node.type === "tableCell") node.children = node.children.map((child) => child.type === "html" && lineBreakHTML(child.value) ? {
44229
44439
  type: "break",
44230
44440
  data: { inkkitHTMLBreak: true },
44231
44441
  position: child.position
@@ -44483,6 +44693,39 @@ function relabel(text, offsets, marker) {
44483
44693
  }
44484
44694
  return text;
44485
44695
  }
44696
+ var parentKey = (node) => `${node.type}:${node.position.start.offset}`;
44697
+ function reorderedWithin(node, reorder) {
44698
+ if (!reorder) return false;
44699
+ const start = node.position?.start.offset;
44700
+ const end = node.position?.end.offset;
44701
+ if (start == null || end == null) return false;
44702
+ for (const key of reorder.keys()) {
44703
+ const at = Number(key.slice(key.lastIndexOf(":") + 1));
44704
+ if (at >= start && at < end) return true;
44705
+ }
44706
+ return false;
44707
+ }
44708
+ /**
44709
+ * An item of an ordered list moved from `original`'s place to `place`'s, which
44710
+ * takes the number written there, so the numbers keep their order. Lines after
44711
+ * the first that are indented to the item's content move with it when the
44712
+ * number's width changes.
44713
+ */
44714
+ function renumberItem(text, source, original, place) {
44715
+ const own = /^\d{1,9}/.exec(text)?.[0];
44716
+ const number = /^\d{1,9}/.exec(source.slice(place.position.start.offset))?.[0];
44717
+ if (!own || !number || own === number) return text;
44718
+ const start = original.position.start.offset;
44719
+ const content = original.children?.[0]?.position?.start.offset;
44720
+ const lines = text.split(/(?<=\n)/);
44721
+ const prefix = source.slice(source.lastIndexOf("\n", start - 1) + 1, start);
44722
+ const indent = content == null ? -1 : content - start;
44723
+ const first = lines[0];
44724
+ if (indent < 0 || first.slice(0, indent).includes("\n")) return text;
44725
+ const from = prefix + " ".repeat(indent);
44726
+ const to = prefix + " ".repeat(indent + number.length - own.length);
44727
+ return [number + first.slice(own.length), ...lines.slice(1).map((line) => line.startsWith(from) ? to + line.slice(from.length) : line)].join("");
44728
+ }
44486
44729
  function authoredMarker(list, source) {
44487
44730
  const at = list.children?.[0]?.position?.start.offset;
44488
44731
  return at == null ? void 0 : markerAt(source, at)?.marker;
@@ -44724,11 +44967,18 @@ function runOffsets(nodes, text, from, to) {
44724
44967
  let decoded = "";
44725
44968
  const offsets = [from];
44726
44969
  const links = [];
44970
+ const breaks = /* @__PURE__ */ new Set();
44727
44971
  let at = from;
44972
+ let afterBreak = false;
44728
44973
  for (const node of nodes) {
44729
44974
  const start = node.position?.start.offset;
44730
44975
  const end = node.position?.end.offset;
44731
- if (start !== at || end == null) return void 0;
44976
+ if (start == null || end == null) return void 0;
44977
+ if (afterBreak && start >= at && /^[ \t>]*$/.test(text.slice(at, start))) {
44978
+ at = start;
44979
+ offsets[offsets.length - 1] = start;
44980
+ }
44981
+ if (start !== at) return void 0;
44732
44982
  const raw = text.slice(start, end);
44733
44983
  if (node.type === "link") {
44734
44984
  const label = inlineText(node);
@@ -44742,13 +44992,19 @@ function runOffsets(nodes, text, from, to) {
44742
44992
  if (!read || read.decoded !== lf(node.value ?? "")) return void 0;
44743
44993
  decoded += read.decoded;
44744
44994
  offsets.push(...read.offsets.slice(1).map((offset) => start + offset));
44995
+ } else if (node.type === "break") {
44996
+ breaks.add(decoded.length);
44997
+ decoded += "\n";
44998
+ offsets.push(end);
44745
44999
  } else return void 0;
45000
+ afterBreak = node.type === "break";
44746
45001
  at = end;
44747
45002
  }
44748
45003
  return at === to ? {
44749
45004
  decoded,
44750
45005
  offsets,
44751
- links
45006
+ links,
45007
+ breaks
44752
45008
  } : void 0;
44753
45009
  }
44754
45010
  function retainRun(before, after, source, canonical, [from, to], [newFrom, newTo]) {
@@ -44758,9 +45014,9 @@ function retainRun(before, after, source, canonical, [from, to], [newFrom, newTo
44758
45014
  const a = old.decoded;
44759
45015
  const b = now.decoded;
44760
45016
  let prefix = 0;
44761
- while (prefix < a.length && prefix < b.length && a[prefix] === b[prefix]) prefix++;
45017
+ while (prefix < a.length && prefix < b.length && a[prefix] === b[prefix] && old.breaks.has(prefix) === now.breaks.has(prefix)) prefix++;
44762
45018
  let suffix = 0;
44763
- while (suffix < a.length - prefix && suffix < b.length - prefix && a[a.length - 1 - suffix] === b[b.length - 1 - suffix]) suffix++;
45019
+ while (suffix < a.length - prefix && suffix < b.length - prefix && a[a.length - 1 - suffix] === b[b.length - 1 - suffix] && old.breaks.has(a.length - 1 - suffix) === now.breaks.has(b.length - 1 - suffix)) suffix++;
44764
45020
  for (let widened = true; widened;) {
44765
45021
  widened = false;
44766
45022
  for (const [links, length] of [[old.links, a.length], [now.links, b.length]]) for (const [start, end] of links) if (start <= length - suffix && end >= prefix) {
@@ -44774,7 +45030,7 @@ function retainRun(before, after, source, canonical, [from, to], [newFrom, newTo
44774
45030
  }
44775
45031
  }
44776
45032
  }
44777
- return source.slice(from, old.offsets[prefix]) + canonical.slice(now.offsets[prefix], now.offsets[b.length - suffix]) + source.slice(old.offsets[a.length - suffix], to);
45033
+ return source.slice(from, old.offsets[prefix]) + canonical.slice(now.offsets[prefix], now.offsets[b.length - suffix]) + (now.breaks.has(b.length - suffix - 1) ? retainBoundaryText(source.slice(old.offsets[a.length - suffix], to), canonical.slice(now.offsets[b.length - suffix], newTo), true) : source.slice(old.offsets[a.length - suffix], to));
44778
45034
  }
44779
45035
  function retainText(raw, before, after, inTable = false, written, beside = [false, false]) {
44780
45036
  before = lf(before);
@@ -44796,14 +45052,38 @@ function retainText(raw, before, after, inTable = false, written, beside = [fals
44796
45052
  }).join("");
44797
45053
  return raw.slice(0, offsets[prefix]) + inserted + raw.slice(offsets[before.length - suffix]);
44798
45054
  }
44799
- function retainBoundaryWhitespace(raw, canonical) {
45055
+ function retainBoundaryText(raw, canonical, afterBreak = false) {
45056
+ if (afterBreak) {
45057
+ const opening = /^(?:\d+)?(?:\\.)+/.exec(canonical)?.[0];
45058
+ const written = opening ? decodedOffsets(opening) : void 0;
45059
+ const original = written ? decodedOffsets(raw, true) : void 0;
45060
+ if (written && original?.decoded.startsWith(written.decoded)) raw = opening + raw.slice(original.offsets[written.decoded.length]);
45061
+ }
44800
45062
  const token = "(?:[ \\t]|&#(?:[xX]0*(?:20|9)|0*(?:32|9));|&Tab;)";
44801
45063
  const decoded = (value) => (value.match(new RegExp(token, "g")) ?? []).map((value) => value === " " || /(?:9;|Tab;)$/.test(value) ? " " : " ").join("");
44802
45064
  const encode = (value) => value.replace(/[ \t]/g, (value) => value === " " ? "&#x9;" : "&#x20;");
44803
- const start = /^[ \t]+/.exec(raw)?.[0];
45065
+ if (raw.includes("\n")) {
45066
+ const lines = raw.split(/(\r?\n)/);
45067
+ const written = canonical.split(/\r?\n/);
45068
+ if (lines.length === written.length * 2 - 1) return lines.map((line, index) => {
45069
+ if (index % 2) return line;
45070
+ const canonicalLine = written[index / 2];
45071
+ const gutter = (value) => /^(?:[ \t]*>)*/.exec(value)?.[0] ?? "";
45072
+ const prefix = gutter(line);
45073
+ const canonicalPrefix = gutter(canonicalLine);
45074
+ if (prefix.replace(/[ \t]/g, "") !== canonicalPrefix.replace(/[ \t]/g, "")) return line;
45075
+ const canonicalBody = canonicalLine.slice(canonicalPrefix.length);
45076
+ return prefix + retainBoundaryText(line.slice(prefix.length), canonicalBody);
45077
+ }).join("");
45078
+ }
44804
45079
  const rawStart = new RegExp(`^${token}+`).exec(raw)?.[0];
44805
45080
  const encodedStart = new RegExp(`^${token}+`).exec(canonical)?.[0];
44806
- if (start && rawStart && encodedStart?.includes("&") && decoded(rawStart) === decoded(encodedStart)) raw = encode(start) + raw.slice(start.length);
45081
+ if (rawStart && encodedStart?.includes("&")) {
45082
+ const content = decoded(encodedStart.slice(encodedStart.indexOf("&")));
45083
+ const parts = rawStart.match(new RegExp(token, "g"));
45084
+ const from = parts.length - content.length;
45085
+ if (from >= 0 && decoded(parts.slice(from).join("")) === content) raw = parts.map((part, index) => index < from ? part : encode(part)).join("") + raw.slice(rawStart.length);
45086
+ }
44807
45087
  const end = /[ \t]+$/.exec(raw)?.[0];
44808
45088
  const rawEnd = new RegExp(`${token}+$`).exec(raw)?.[0];
44809
45089
  const encodedEnd = new RegExp(`${token}+$`).exec(canonical)?.[0];
@@ -44966,7 +45246,7 @@ function markLiterals(raw, transformed, blocks = []) {
44966
45246
  };
44967
45247
  raw.children.forEach(mark);
44968
45248
  }
44969
- function retainSource(before, after, source, canonical, lineEnding, inTable = false, width, beside = [false, false], joined = []) {
45249
+ function retainSource(before, after, source, canonical, lineEnding, inTable = false, width, beside = [false, false], joined = [], reorder, afterBreak = false) {
44970
45250
  if (literals$1.has(after)) return withEnding(retainLiteral(source.slice(before.position.start.offset, before.position.end.offset), literalForms.get(before), lf(canonical.slice(after.position.start.offset, after.position.end.offset))), lineEnding);
44971
45251
  if (before.type === "list" && after.type === "list" && before.ordered && after.ordered && !numbered.has(before) && before.position?.start.offset != null) {
44972
45252
  const own = {
@@ -44974,7 +45254,7 @@ function retainSource(before, after, source, canonical, lineEnding, inTable = fa
44974
45254
  start: after.start
44975
45255
  };
44976
45256
  numbered.add(own);
44977
- const text = retainSource(own, after, source, canonical, lineEnding, inTable, width, beside, joined);
45257
+ const text = retainSource(own, after, source, canonical, lineEnding, inTable, width, beside, joined, reorder);
44978
45258
  const at = before.position.start.offset;
44979
45259
  const prefix = source.slice(source.lastIndexOf("\n", at - 1) + 1, at).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
44980
45260
  let count = 0, previous = 0, number = 0;
@@ -44992,7 +45272,7 @@ function retainSource(before, after, source, canonical, lineEnding, inTable = fa
44992
45272
  const text = retainSource({
44993
45273
  ...before,
44994
45274
  checked: after.checked
44995
- }, after, source, canonical, lineEnding, inTable, width, beside, joined);
45275
+ }, after, source, canonical, lineEnding, inTable, width, beside, joined, reorder);
44996
45276
  const box = /^((?:[-*+]|\d{1,9}[.)])[ \t]+\[)[ xX](\])/.exec(text);
44997
45277
  if (box) return box[1] + (after.checked ? "x" : " ") + text.slice(box[1].length + 1);
44998
45278
  }
@@ -45057,8 +45337,8 @@ function retainSource(before, after, source, canonical, lineEnding, inTable = fa
45057
45337
  const to = first.position.end.offset - start;
45058
45338
  return retained.startsWith(source.slice(start, before.children[1].position.start.offset)) ? retained.slice(0, from) + "<br />" + retained.slice(to) : retained;
45059
45339
  }
45060
- if (before.type === "text" && after.type === "text") return retainBoundaryWhitespace(withEnding(retainText(raw, before.value, after.value, inTable, canonical.slice(newStart, newEnd), beside), lineEnding), canonical.slice(newStart, newEnd));
45061
- if (signature(before) === signature(after) && ownShape(before) === ownShape(after) && !holdsLiteral(after)) return raw;
45340
+ if (before.type === "text" && after.type === "text") return retainBoundaryText(withEnding(retainText(raw, before.value, after.value, inTable, canonical.slice(newStart, newEnd), beside), lineEnding), canonical.slice(newStart, newEnd), afterBreak);
45341
+ if (signature(before) === signature(after) && ownShape(before) === ownShape(after) && !holdsLiteral(after) && !reorderedWithin(before, reorder)) return raw;
45062
45342
  const marker = sameList(before, after) ? authoredMarker(before, source) : void 0;
45063
45343
  const located = (nodes) => nodes.every((node) => node.position?.start.offset != null);
45064
45344
  if (before.type === after.type && ownShape(before) === ownShape(after) && before.children && after.children && before.children.length === after.children.length && located(before.children) && located(after.children)) {
@@ -45066,15 +45346,17 @@ function retainSource(before, after, source, canonical, lineEnding, inTable = fa
45066
45346
  let previous;
45067
45347
  const same = (candidate, incoming) => signature(candidate) === signature(incoming) && ownShape(candidate) === ownShape(incoming);
45068
45348
  const reorderable = before.type === "table" || before.type === "tableRow";
45349
+ const moves = reorder?.get(parentKey(before));
45069
45350
  const retained = new Set(reorderable ? before.children.flatMap((child, index) => same(child, after.children[index]) ? [index] : []) : []);
45070
45351
  before.children.forEach((child, index) => {
45071
45352
  const incoming = after.children[index];
45072
- const movedIndex = !reorderable ? -1 : same(child, incoming) ? index : before.children.findIndex((candidate, candidateIndex) => !retained.has(candidateIndex) && same(candidate, incoming));
45353
+ const carried = moves?.get(index);
45354
+ const movedIndex = !reorderable ? carried !== void 0 && before.children[carried] !== void 0 && same(before.children[carried], incoming) ? carried : -1 : same(child, incoming) ? index : before.children.findIndex((candidate, candidateIndex) => !retained.has(candidateIndex) && same(candidate, incoming));
45073
45355
  const original = movedIndex < 0 ? child : before.children[movedIndex];
45074
45356
  if (movedIndex >= 0) retained.add(movedIndex);
45075
45357
  const childStart = child.position?.start.offset, childEnd = child.position?.end.offset;
45076
45358
  if (childStart == null || childEnd == null) throw new PreservationError("A source token has no location");
45077
- let retainedSource = rewrittenAddress(before.type, before.children, movedIndex < 0 ? index : movedIndex, after.children, index, source, canonical) ? canonical.slice(incoming.position.start.offset, incoming.position.end.offset) : incoming.type === "list" && !sameList(original, incoming) ? withEnding(canonicalSlice(void 0, [incoming], canonical, incoming.position.start.offset, incoming.position.end.offset, previous, authoredNeighbour(before.children[index + 1], after.children[index + 1], source)), lineEnding) : retainSource(original, incoming, source, canonical, lineEnding, inTable || before.type === "tableCell", before.type === "table" ? before.children[0]?.children?.length : void 0, [after.children[index - 1]?.type === "link", after.children[index + 1]?.type === "link"]);
45359
+ let retainedSource = rewrittenAddress(before.type, before.children, movedIndex < 0 ? index : movedIndex, after.children, index, source, canonical) ? canonical.slice(incoming.position.start.offset, incoming.position.end.offset) : incoming.type === "list" && !sameList(original, incoming) ? withEnding(canonicalSlice(void 0, [incoming], canonical, incoming.position.start.offset, incoming.position.end.offset, previous, authoredNeighbour(before.children[index + 1], after.children[index + 1], source)), lineEnding) : retainSource(original, incoming, source, canonical, lineEnding, inTable || before.type === "tableCell", before.type === "table" ? before.children[0]?.children?.length : void 0, [after.children[index - 1]?.type === "link", after.children[index + 1]?.type === "link"], [], reorder, after.children[index - 1]?.type === "break");
45078
45360
  if (before.type === "tableRow" && movedIndex >= 0 && movedIndex !== index) {
45079
45361
  const originalFrom = original.children?.[0]?.position?.start.offset;
45080
45362
  const originalTo = original.children?.at(-1)?.position?.end.offset;
@@ -45082,6 +45364,7 @@ function retainSource(before, after, source, canonical, lineEnding, inTable = fa
45082
45364
  const to = child.children?.at(-1)?.position?.end.offset;
45083
45365
  retainedSource = originalFrom != null && originalTo != null && from != null && to != null ? source.slice(childStart, from) + source.slice(originalFrom, originalTo) + source.slice(to, childEnd) : withEnding(canonical.slice(incoming.position.start.offset, incoming.position.end.offset), lineEnding);
45084
45366
  }
45367
+ if (!reorderable && before.ordered && movedIndex >= 0 && movedIndex !== index) retainedSource = renumberItem(retainedSource, source, original, child);
45085
45368
  if (marker) retainedSource = relabel(retainedSource, [0], marker);
45086
45369
  previous = incoming.type !== "list" ? void 0 : authoredNeighbour(original, incoming, source) ?? {
45087
45370
  ordered: incoming.ordered,
@@ -45098,11 +45381,11 @@ function retainSource(before, after, source, canonical, lineEnding, inTable = fa
45098
45381
  while (suffix < before.children.length - prefix && suffix < after.children.length - prefix && signature(before.children[before.children.length - 1 - suffix]) === signature(after.children[after.children.length - 1 - suffix])) suffix++;
45099
45382
  if (prefix && rewrittenAddress(before.type, before.children, prefix - 1, after.children, prefix - 1, source, canonical)) prefix--;
45100
45383
  if (suffix && rewrittenAddress(before.type, before.children, before.children.length - suffix, after.children, after.children.length - suffix, source, canonical)) suffix--;
45101
- const cell = before.type === "tableCell" && before.children.length > 0 && after.children.length > 0;
45102
- const first = cell ? before.children[0].position?.start.offset : start;
45103
- const last = cell ? before.children.at(-1).position?.end.offset : end;
45104
- const newFirst = cell ? after.children[0].position?.start.offset : newStart;
45105
- const newLast = cell ? after.children.at(-1).position?.end.offset : newEnd;
45384
+ const wrapped = (before.type === "tableCell" || formatting.has(before.type)) && before.children.length > 0 && after.children.length > 0;
45385
+ const first = wrapped ? before.children[0].position?.start.offset : start;
45386
+ const last = wrapped ? before.children.at(-1).position?.end.offset : end;
45387
+ const newFirst = wrapped ? after.children[0].position?.start.offset : newStart;
45388
+ const newLast = wrapped ? after.children.at(-1).position?.end.offset : newEnd;
45106
45389
  const ending = (node) => {
45107
45390
  const last = node.children?.at(-1);
45108
45391
  return last && (node.type === "list" || node.type === "listItem" && last.type === "list") ? ending(last) : node;
@@ -45117,7 +45400,7 @@ function retainSource(before, after, source, canonical, lineEnding, inTable = fa
45117
45400
  const changes = after.children.slice(prefix, after.children.length - suffix);
45118
45401
  const previous = authoredNeighbour(before.children[prefix - 1], after.children[prefix - 1], source);
45119
45402
  const next = authoredNeighbour(before.children[before.children.length - suffix], after.children[after.children.length - suffix], source);
45120
- return source.slice(start, first) + retainBoundaryWhitespace(source.slice(first, from) + withEnding((inlineParents.has(before.type) ? retainRun(changed, changes, source, canonical, [from, to], [newFrom, newTo]) : retainParts(marker, [...changed, ...joined], changes, source, canonical, newFrom, newTo, lineEnding, previous, next)) ?? canonicalSlice(marker, changes, canonical, newFrom, newTo, previous, next), lineEnding) + source.slice(to, last), canonical.slice(newFirst, newLast)) + source.slice(last, end);
45403
+ return source.slice(start, first) + retainBoundaryText(source.slice(first, from) + withEnding((inlineParents.has(before.type) ? retainRun(changed, changes, source, canonical, [from, to], [newFrom, newTo]) : retainParts(marker, [...changed, ...joined], changes, source, canonical, newFrom, newTo, lineEnding, previous, next)) ?? canonicalSlice(marker, changes, canonical, newFrom, newTo, previous, next), lineEnding) + (inlineParents.has(before.type) ? retainBoundaryText(source.slice(to, last), canonical.slice(newTo, newLast), after.children[after.children.length - suffix - 1]?.type === "break") : source.slice(to, last)), canonical.slice(newFirst, newLast)) + source.slice(last, end);
45121
45404
  }
45122
45405
  }
45123
45406
  const lastAfter = after.children?.at(-1)?.position?.end.offset;
@@ -45213,7 +45496,13 @@ function semanticSignature(doc) {
45213
45496
  const content = value.content;
45214
45497
  if (!content) return value;
45215
45498
  const tokens = [];
45216
- for (const child of content) {
45499
+ for (const [index, child] of content.entries()) {
45500
+ const attrs = child.attrs;
45501
+ const next = content[index + 1];
45502
+ if (child.type === "hardbreak" && attrs?.isInline === false && next?.type === "comment_inline" && next.attrs?.syntax === "html") child.attrs = {
45503
+ ...attrs,
45504
+ isHTML: true
45505
+ };
45217
45506
  if (child.type !== "text") {
45218
45507
  tokens.push(clean(child));
45219
45508
  continue;
@@ -45425,6 +45714,11 @@ function similarity(left, right) {
45425
45714
  while (suffix < limit - prefix && left[left.length - 1 - suffix] === right[right.length - 1 - suffix]) suffix++;
45426
45715
  return prefix + suffix;
45427
45716
  }
45717
+ var nothingCarried = {
45718
+ moved: /* @__PURE__ */ new Map(),
45719
+ children: false,
45720
+ within: /* @__PURE__ */ new Set()
45721
+ };
45428
45722
  /** Retains the loaded source of blocks the edit leaves alone. */
45429
45723
  var Preservation = class Preservation {
45430
45724
  ctx;
@@ -45440,6 +45734,8 @@ var Preservation = class Preservation {
45440
45734
  itemEdits = /* @__PURE__ */ new WeakMap();
45441
45735
  regionEdits = /* @__PURE__ */ new WeakMap();
45442
45736
  definitionNodes = /* @__PURE__ */ new Map();
45737
+ moves;
45738
+ origins;
45443
45739
  nestedLiterals = /* @__PURE__ */ new Set();
45444
45740
  literalsRead = /* @__PURE__ */ new WeakSet();
45445
45741
  definitionSources;
@@ -45505,12 +45801,12 @@ var Preservation = class Preservation {
45505
45801
  throw new PreservationError("Cannot read Markdown for preservation", { cause });
45506
45802
  }
45507
45803
  }
45508
- retainBlock(block, after, canonical, joined = []) {
45804
+ retainBlock(block, after, canonical, joined = [], reorder) {
45509
45805
  const offset = this.source.startsWith("") ? 1 : 0;
45510
45806
  const start = block.ast.position?.start.offset;
45511
45807
  if (start == null) throw new PreservationError("A source block has no location");
45512
45808
  this.readNestedLiterals(block);
45513
- return this.source.slice(block.start, start + offset) + retainSource(block.ast, after, this.source.slice(offset), canonical, this.lineEnding, false, void 0, void 0, joined);
45809
+ return this.source.slice(block.start, start + offset) + retainSource(block.ast, after, this.source.slice(offset), canonical, this.lineEnding, false, void 0, void 0, joined, reorder);
45514
45810
  }
45515
45811
  readNestedLiterals(block) {
45516
45812
  if (!this.nestedLiterals.size || this.literalsRead.has(block)) return;
@@ -45551,7 +45847,8 @@ var Preservation = class Preservation {
45551
45847
  for (let index = 0; index <= doc.childCount; index++) {
45552
45848
  const node = index < doc.childCount ? doc.child(index) : void 0;
45553
45849
  const before = node && this.baseline.child(index);
45554
- if (!node || node === before || node.eq(before)) {
45850
+ if (node && node !== before && this.moves?.has(node)) return void 0;
45851
+ if (!node || node === before || node.eq(before) && !this.carries(node)) {
45555
45852
  if (run.length > 1 && !this.pairedInPlace(doc, run)) return void 0;
45556
45853
  run = [];
45557
45854
  continue;
@@ -45697,19 +45994,24 @@ var Preservation = class Preservation {
45697
45994
  if (other !== index && candidate.type === node.type && candidate.childCount === node.childCount && candidate.textContent === node.textContent) return void 0;
45698
45995
  }
45699
45996
  const marker = authoredMarker(list, source);
45997
+ const reorder = this.moves ? this.carriedChildren(node, index) : void 0;
45998
+ const carried = reorder?.get(parentKey(list));
45700
45999
  let output = "";
45701
46000
  let previous = block.start;
45702
46001
  for (let at = 0; at < node.childCount; at++) {
45703
46002
  const item = node.child(at);
45704
46003
  const original = before.child(at);
45705
- if (item === original || item.eq(original)) continue;
46004
+ if (item === original || item.eq(original) && !this.carries(item)) continue;
46005
+ const moved = carried?.get(at);
46006
+ const from = moved !== void 0 && list.children[moved] ? moved : void 0;
45706
46007
  let retained = this.itemEdits.get(item);
45707
- if (retained?.index !== index || retained.item !== at) {
45708
- const text = this.localItem(node, item, before.child(at), list.children[at], source, marker);
46008
+ if (retained?.index !== index || retained.item !== at || retained.from !== from) {
46009
+ const text = from === void 0 ? this.localItem(node, item, before.child(at), list.children[at], source, marker, reorder) : this.movedItem(node, item, list, list.children[from], list.children[at], source, marker);
45709
46010
  if (text === void 0) return void 0;
45710
46011
  retained = {
45711
46012
  index,
45712
46013
  item: at,
46014
+ from,
45713
46015
  text
45714
46016
  };
45715
46017
  this.itemEdits.set(item, retained);
@@ -45720,18 +46022,17 @@ var Preservation = class Preservation {
45720
46022
  }
45721
46023
  return output + this.source.slice(previous, block.end);
45722
46024
  }
45723
- localItem(list, item, before, ast, source, marker) {
45724
- const start = ast.position.start.offset;
45725
- const end = ast.position.end.offset;
45726
- if (!item.sameMarkup(before) || !sameBlocks(item, before) || !Preservation.plainInline(item) || !Preservation.plainInline(before) || !this.inContext(source.slice(start, end))) return void 0;
45727
- const local = this.canonicalTree(form(write(this.ctx, list.type.schema.topNodeType.create(null, [list.type.create(list.attrs, [item])]))));
45728
- const after = local?.node.children?.[0];
45729
- if (!local || local.node.children.length !== 1 || !after) return void 0;
45730
- let text = retainSource(ast, {
45731
- ...after,
45732
- spread: ast.spread
45733
- }, source, local.canonical, this.lineEnding);
46025
+ movedItem(node, item, list, original, place, source, marker) {
46026
+ const start = original.position.start.offset;
46027
+ const end = original.position.end.offset;
46028
+ if (!Preservation.plainInline(item) || !this.inContext(source.slice(start, end))) return void 0;
46029
+ let text = source.slice(start, end);
46030
+ if (list.ordered) text = renumberItem(text, source, original, place);
45734
46031
  if (marker) text = relabel(text, [0], marker);
46032
+ return this.readsAsItem(node, item, text, place, source);
46033
+ }
46034
+ readsAsItem(list, item, text, ast, source) {
46035
+ const start = ast.position.start.offset;
45735
46036
  const content = ast.children?.[0]?.position?.start.offset;
45736
46037
  if (content == null || !text.startsWith(source.slice(start, content))) return void 0;
45737
46038
  if (/^(?:[-*+]|\d{1,9}[.)])[ \t]*(?:\r?\n|$)/.test(text)) return void 0;
@@ -45744,6 +46045,20 @@ var Preservation = class Preservation {
45744
46045
  const { label: _l, spread: _s, ...expected } = item.attrs;
45745
46046
  return reread.type === item.type && JSON.stringify(attrs) === JSON.stringify(expected) && reread.content.eq(item.content) ? text : void 0;
45746
46047
  }
46048
+ localItem(list, item, before, ast, source, marker, reorder) {
46049
+ const start = ast.position.start.offset;
46050
+ const end = ast.position.end.offset;
46051
+ if (!item.sameMarkup(before) || !sameBlocks(item, before) || !Preservation.plainInline(item) || !Preservation.plainInline(before) || !this.inContext(source.slice(start, end))) return void 0;
46052
+ const local = this.canonicalTree(form(write(this.ctx, list.type.schema.topNodeType.create(null, [list.type.create(list.attrs, [item])]))));
46053
+ const after = local?.node.children?.[0];
46054
+ if (!local || local.node.children.length !== 1 || !after) return void 0;
46055
+ let text = retainSource(ast, {
46056
+ ...after,
46057
+ spread: ast.spread
46058
+ }, source, local.canonical, this.lineEnding, false, void 0, void 0, [], reorder);
46059
+ if (marker) text = relabel(text, [0], marker);
46060
+ return this.readsAsItem(list, item, text, ast, source);
46061
+ }
45747
46062
  separateLists(doc, pieces, kept, positions, current) {
45748
46063
  const kind = (index) => {
45749
46064
  const name = doc.maybeChild(index)?.type.name;
@@ -45763,7 +46078,7 @@ var Preservation = class Preservation {
45763
46078
  return true;
45764
46079
  };
45765
46080
  const cost = (index) => {
45766
- if (kept[index] === "canonical") return 1;
46081
+ if (kept[index] === "canonical" || kept[index] === "moved") return 1;
45767
46082
  if (kept[index] === "edited") return 10;
45768
46083
  const own = marker(index);
45769
46084
  const sides = [-1, 1].map((step) => inPlace(index, step));
@@ -45826,7 +46141,18 @@ var Preservation = class Preservation {
45826
46141
  const reread = read(relabelled);
45827
46142
  return reread.length === 1 && signature(reread[0]) === signature(list) && markerAt(relabelled, list.children[0].position.start.offset)?.marker === marker ? relabelled : void 0;
45828
46143
  }
45829
- serialize(doc) {
46144
+ /** `moved` holds the nodes block moves carried. A moved node that is still a
46145
+ * node of the loaded document keeps that node's source where it reads the
46146
+ * same; every other edit is saved without it. */
46147
+ serialize(doc, moved) {
46148
+ this.moves = moved;
46149
+ try {
46150
+ return this.serializeWith(doc);
46151
+ } finally {
46152
+ this.moves = void 0;
46153
+ }
46154
+ }
46155
+ serializeWith(doc) {
45830
46156
  if (this.unchanged(doc)) return this.source;
45831
46157
  let local;
45832
46158
  try {
@@ -45848,10 +46174,20 @@ var Preservation = class Preservation {
45848
46174
  for (let index = 0; index < doc.childCount; index++) {
45849
46175
  const node = doc.child(index);
45850
46176
  const before = this.baseline.child(index);
45851
- if (node !== before && !node.eq(before)) return false;
46177
+ if (node !== before && (!node.eq(before) || this.carries(node))) return false;
45852
46178
  }
45853
46179
  return true;
45854
46180
  }
46181
+ carries(node) {
46182
+ const moves = this.moves;
46183
+ if (!moves) return false;
46184
+ let found = moves.has(node);
46185
+ node.descendants((child) => {
46186
+ found ||= moves.has(child);
46187
+ return !found && !child.isTextblock;
46188
+ });
46189
+ return found;
46190
+ }
45855
46191
  checked(doc, text) {
45856
46192
  if (!crossCheck.enabled) return text;
45857
46193
  crossCheck.compared++;
@@ -45884,7 +46220,7 @@ var Preservation = class Preservation {
45884
46220
  const previous = positions[index - 1];
45885
46221
  return previous != null && position === previous + 1 && block ? this.source.slice(this.blocks[previous].end, block.start) : this.lineEnding.repeat(2);
45886
46222
  }
45887
- build(doc, positions, current, from, to, canonical, tree) {
46223
+ build(doc, positions, current, carried, from, to, canonical, tree) {
45888
46224
  const transformed = (index) => tree.transformed.children[index - from];
45889
46225
  if (tree) {
45890
46226
  const failed = [];
@@ -45912,12 +46248,13 @@ var Preservation = class Preservation {
45912
46248
  for (let index = from; index < to; index++) {
45913
46249
  const text = current[index];
45914
46250
  const position = positions[index];
45915
- const block = position == null ? void 0 : this.blocks[position];
46251
+ const moved = carried.moved.get(index);
46252
+ const block = position == null ? moved === void 0 ? void 0 : this.blocks[moved] : this.blocks[position];
45916
46253
  gaps.push(this.gap(index, positions));
45917
- const kind = block && block.form === text ? "unchanged" : block && tree ? "edited" : "canonical";
46254
+ const kind = block && block.form === text && !carried.within.has(index) ? position == null ? "moved" : "unchanged" : block && tree && position != null ? "edited" : "canonical";
45918
46255
  kept.push(kind);
45919
46256
  const node = tree ? positionalNode(tree.raw, transformed(index)) : void 0;
45920
- pieces.push(kind === "unchanged" ? this.source.slice(block.start, block.end) : kind === "edited" ? this.retainBlock(block, node, canonical, this.around(positions, index).filter((other) => !paired.has(other) && other.ast.type === block.ast.type).flatMap((other) => other.ast.children ?? [])) : (node && this.retainPart(positions, index, node, canonical, taken)) ?? restoreCodeEndings(this.ctx, doc.type.create(null, [doc.child(index)]), withEnding(text, this.lineEnding)));
46257
+ pieces.push(kind === "unchanged" || kind === "moved" ? this.source.slice(block.start, block.end) : kind === "edited" ? this.retainBlock(block, node, canonical, this.around(positions, index).filter((other) => !paired.has(other) && other.ast.type === block.ast.type).flatMap((other) => other.ast.children ?? []), carried.children ? this.carriedChildren(doc.child(index), position) : void 0) : (node && this.retainPart(positions, index, node, canonical, taken)) ?? restoreCodeEndings(this.ctx, doc.type.create(null, [doc.child(index)]), withEnding(text, this.lineEnding)));
45921
46258
  }
45922
46259
  return {
45923
46260
  gaps,
@@ -45961,10 +46298,10 @@ var Preservation = class Preservation {
45961
46298
  extra
45962
46299
  };
45963
46300
  }
45964
- runs(positions, current) {
46301
+ runs(positions, current, within = nothingCarried.within) {
45965
46302
  const unchanged = (index) => {
45966
46303
  const position = positions[index];
45967
- return position != null && this.blocks[position].form === current[index];
46304
+ return position != null && this.blocks[position].form === current[index] && !within.has(index);
45968
46305
  };
45969
46306
  const consecutive = (index) => index === 0 ? positions[0] === 0 : positions[index] != null && positions[index - 1] != null && positions[index] === positions[index - 1] + 1;
45970
46307
  const runs = [];
@@ -45994,7 +46331,7 @@ var Preservation = class Preservation {
45994
46331
  return true;
45995
46332
  });
45996
46333
  }
45997
- runPieces(doc, positions, current, run) {
46334
+ runPieces(doc, positions, current, carried, run) {
45998
46335
  const from = run.previous === void 0 ? 0 : run.previous + 1;
45999
46336
  const to = run.next ?? doc.childCount;
46000
46337
  if (from === to) return {
@@ -46005,26 +46342,27 @@ var Preservation = class Preservation {
46005
46342
  const nodes = [];
46006
46343
  for (let index = from; index < to; index++) nodes.push(doc.child(index));
46007
46344
  const { canonical, tree } = from === 0 && to === doc.childCount ? this.canonicalDocument(doc) : this.canonicalBlocks(doc, nodes);
46008
- return this.build(doc, positions, current, from, to, canonical, tree);
46345
+ return this.build(doc, positions, current, carried, from, to, canonical, tree);
46009
46346
  }
46010
46347
  regions(doc) {
46011
46348
  if (!doc.childCount || this.baseline.childCount !== this.blocks.length || !doc.sameMarkup(this.baseline) || this.definitionSources === void 0) return void 0;
46012
46349
  const current = [];
46013
46350
  doc.forEach((node) => current.push(this.formOf(node)));
46014
46351
  let positions;
46352
+ let carried;
46015
46353
  try {
46016
- positions = correspondence(this.blockForms, current);
46354
+ ({positions, carried} = this.pairing(doc, current));
46017
46355
  } catch {
46018
46356
  return;
46019
46357
  }
46020
- const runs = this.runs(positions, current);
46358
+ const runs = this.runs(positions, current, carried.within);
46021
46359
  if (runs.some((run) => run.previous === void 0 && run.next === void 0) || !this.readAlone(doc, positions, runs)) return void 0;
46022
46360
  let output = "";
46023
46361
  let at = 0;
46024
46362
  for (const run of runs) {
46025
46363
  const from = run.previous === void 0 ? 0 : this.blocks[positions[run.previous]].end;
46026
46364
  const to = run.next === void 0 ? this.source.length : this.blocks[positions[run.next]].start;
46027
- const text = this.region(doc, positions, current, run);
46365
+ const text = this.region(doc, positions, current, carried, run);
46028
46366
  if (text === void 0) return void 0;
46029
46367
  output += this.source.slice(at, from) + text;
46030
46368
  at = to;
@@ -46035,7 +46373,7 @@ var Preservation = class Preservation {
46035
46373
  if (frontmatter !== sourceFrontmatter(bom(this.source)) || doc.firstChild?.type.name === "hr" && frontmatter?.startsWith("---")) return void 0;
46036
46374
  return output;
46037
46375
  }
46038
- region(doc, positions, current, run) {
46376
+ region(doc, positions, current, carried, run) {
46039
46377
  const from = run.previous === void 0 ? 0 : run.previous + 1;
46040
46378
  const to = run.next ?? doc.childCount;
46041
46379
  const nodes = [];
@@ -46046,7 +46384,7 @@ var Preservation = class Preservation {
46046
46384
  const identity = [
46047
46385
  original(run.previous),
46048
46386
  original(run.next),
46049
- ...positions.slice(from, to)
46387
+ ...Array.from({ length: to - from }, (_, at) => positions[from + at] ?? `m${carried.moved.get(from + at) ?? ""}`)
46050
46388
  ].join();
46051
46389
  const cached = this.regionEdits.get(key);
46052
46390
  if (cached && cached.identity === identity && cached.nodes.length === nodes.length + neighbours.length && [...nodes, ...neighbours].every((node, index) => cached.nodes[index] === node)) return cached.text;
@@ -46055,10 +46393,10 @@ var Preservation = class Preservation {
46055
46393
  return name === "bullet_list" || name === "ordered_list" ? name : void 0;
46056
46394
  };
46057
46395
  for (let index = Math.max(1, from); index <= Math.min(to, doc.childCount - 1); index++) if (list(index) && list(index) === list(index - 1)) return void 0;
46058
- const { gaps, pieces } = this.runPieces(doc, positions, current, run);
46396
+ const { gaps, pieces } = this.runPieces(doc, positions, current, carried, run);
46059
46397
  let text = pieces.map((piece, index) => gaps[index] + piece).join("");
46060
46398
  if (run.next !== void 0) text += this.gap(run.next, positions);
46061
- else text += positions[doc.childCount - 1] === this.blocks.length - 1 ? this.source.slice(this.blocks.at(-1).end) : finalCodeEnding(doc) ?? this.lineEnding;
46399
+ else text += this.endsAsAuthored(positions, carried) ? this.source.slice(this.blocks.at(-1).end) : finalCodeEnding(doc) ?? this.lineEnding;
46062
46400
  const slice = (index) => {
46063
46401
  if (index === void 0) return "";
46064
46402
  const block = this.blocks[positions[index]];
@@ -46118,26 +46456,21 @@ var Preservation = class Preservation {
46118
46456
  if (canonical === "") return this.source.startsWith("") ? "" : "";
46119
46457
  const current = [];
46120
46458
  doc.forEach((node) => current.push(this.formOf(node)));
46121
- const positions = correspondence(this.blockForms, current);
46122
- const gaps = current.map((_, index) => this.gap(index, positions));
46123
- const pieces = positions.map((position) => position == null ? "" : this.source.slice(this.blocks[position].start, this.blocks[position].end));
46124
- const kept = current.map(() => "unchanged");
46125
- const runs = this.runs(positions, current);
46126
- for (const run of this.readAlone(doc, positions, runs) ? runs : [{}]) {
46127
- const from = run.previous === void 0 ? 0 : run.previous + 1;
46128
- const written = this.runPieces(doc, positions, current, run);
46129
- written.pieces.forEach((piece, index) => {
46130
- pieces[from + index] = piece;
46131
- kept[from + index] = written.kept[index];
46132
- });
46459
+ const pairing = this.pairing(doc, current);
46460
+ const carries = pairing.carried.moved.size > 0 || pairing.carried.children;
46461
+ if (carries) {
46462
+ let output;
46463
+ try {
46464
+ output = this.writeDocument(doc, current, pairing);
46465
+ } catch {
46466
+ output = void 0;
46467
+ }
46468
+ if (output !== void 0 && this.reopens(doc, output, canonical)) return output;
46133
46469
  }
46134
- const previous = positions.at(-1);
46135
- this.separateLists(doc, pieces, kept, positions, current);
46136
- let output = pieces.map((piece, index) => gaps[index] + piece).join("");
46137
- if (previous === this.blocks.length - 1 && this.blocks.length) output += this.source.slice(this.blocks[previous].end);
46138
- else if (output) output += finalCodeEnding(doc) ?? this.lineEnding;
46139
- const bom = output.startsWith("") ? 1 : 0;
46140
- if (doc.firstChild?.type.name === "hr" && sourceFrontmatter(output.slice(bom))?.startsWith("---")) output = output.slice(0, bom) + "***" + output.slice(bom + 3);
46470
+ const output = this.writeDocument(doc, current, carries ? {
46471
+ positions: correspondence(this.blockForms, current),
46472
+ carried: nothingCarried
46473
+ } : pairing);
46141
46474
  if (this.reopens(doc, output, canonical)) return output;
46142
46475
  throw new PreservationError("The edited Markdown cannot be reopened without changing its content");
46143
46476
  } catch (cause) {
@@ -46145,6 +46478,157 @@ var Preservation = class Preservation {
46145
46478
  throw new PreservationError("Cannot preserve the edited Markdown", { cause });
46146
46479
  }
46147
46480
  }
46481
+ writeDocument(doc, current, { positions, carried }) {
46482
+ const gaps = current.map((_, index) => this.gap(index, positions));
46483
+ const pieces = positions.map((position) => position == null ? "" : this.source.slice(this.blocks[position].start, this.blocks[position].end));
46484
+ const kept = current.map(() => "unchanged");
46485
+ const runs = this.runs(positions, current, carried.within);
46486
+ for (const run of this.readAlone(doc, positions, runs) ? runs : [{}]) {
46487
+ const from = run.previous === void 0 ? 0 : run.previous + 1;
46488
+ const written = this.runPieces(doc, positions, current, carried, run);
46489
+ written.pieces.forEach((piece, index) => {
46490
+ pieces[from + index] = piece;
46491
+ kept[from + index] = written.kept[index];
46492
+ });
46493
+ }
46494
+ positions.at(-1);
46495
+ this.separateLists(doc, pieces, kept, positions, current);
46496
+ let output = pieces.map((piece, index) => gaps[index] + piece).join("");
46497
+ if (this.endsAsAuthored(positions, carried)) output += this.source.slice(this.blocks.at(-1).end);
46498
+ else if (output) output += finalCodeEnding(doc) ?? this.lineEnding;
46499
+ const bom = output.startsWith("") ? 1 : 0;
46500
+ if (doc.firstChild?.type.name === "hr" && sourceFrontmatter(output.slice(bom))?.startsWith("---")) output = output.slice(0, bom) + "***" + output.slice(bom + 3);
46501
+ return output;
46502
+ }
46503
+ originIndex() {
46504
+ if (this.origins) return this.origins;
46505
+ const origins = /* @__PURE__ */ new WeakMap();
46506
+ const visit = (node, path) => node.forEach((child, _, index) => {
46507
+ const at = [...path, index];
46508
+ origins.set(child, at);
46509
+ if (!child.isTextblock && !child.isLeaf && child.type.name !== "table") visit(child, at);
46510
+ });
46511
+ visit(this.baseline, []);
46512
+ return this.origins = origins;
46513
+ }
46514
+ pairing(doc, current) {
46515
+ const moves = this.moves;
46516
+ if (!moves || this.baseline.childCount !== this.blocks.length) return {
46517
+ positions: correspondence(this.blockForms, current),
46518
+ carried: nothingCarried
46519
+ };
46520
+ const origins = this.originIndex();
46521
+ const pinned = /* @__PURE__ */ new Map();
46522
+ doc.forEach((node, _, index) => {
46523
+ if (moves.has(node)) {
46524
+ const origin = origins.get(node);
46525
+ if (origin?.length === 1) pinned.set(index, origin[0]);
46526
+ }
46527
+ });
46528
+ if (!pinned.size) {
46529
+ const positions = correspondence(this.blockForms, current);
46530
+ return {
46531
+ positions,
46532
+ carried: {
46533
+ moved: pinned,
46534
+ children: true,
46535
+ within: this.within(doc, positions, current)
46536
+ }
46537
+ };
46538
+ }
46539
+ const taken = new Set(pinned.values());
46540
+ const before = this.blocks.flatMap((_, index) => taken.has(index) ? [] : [index]);
46541
+ const after = current.flatMap((_, index) => pinned.has(index) ? [] : [index]);
46542
+ const present = /* @__PURE__ */ new Map();
46543
+ for (const index of after) {
46544
+ const origin = origins.get(doc.child(index));
46545
+ if (origin?.length === 1) present.set(index, origin[0]);
46546
+ }
46547
+ const kept = new Set(present.values());
46548
+ const matched = correspondence(before.map((index) => kept.has(index) ? `${this.blockForms[index]}\u0000${index}` : this.blockForms[index]), after.map((index) => present.has(index) ? `${current[index]}\u0000${present.get(index)}` : current[index]));
46549
+ const positions = new Array(current.length);
46550
+ after.forEach((index, at) => {
46551
+ const match = matched[at];
46552
+ if (match !== void 0) positions[index] = before[match];
46553
+ });
46554
+ const next = new Array(current.length);
46555
+ let following = this.blocks.length;
46556
+ for (let index = current.length - 1; index >= 0; index--) {
46557
+ next[index] = following;
46558
+ if (positions[index] !== void 0) following = positions[index];
46559
+ }
46560
+ const own = (index) => {
46561
+ const origin = origins.get(doc.child(index));
46562
+ return origin?.length === 1 ? origin[0] : void 0;
46563
+ };
46564
+ const above = new Array(current.length);
46565
+ let upper = this.blocks.length;
46566
+ for (let index = current.length - 1; index >= 0; index--) {
46567
+ above[index] = upper;
46568
+ upper = own(index) ?? upper;
46569
+ }
46570
+ const moved = /* @__PURE__ */ new Map();
46571
+ let last = -1;
46572
+ let lower = -1;
46573
+ for (let index = 0; index < current.length; index++) {
46574
+ const origin = pinned.get(index);
46575
+ const inOrder = origin > lower && origin < above[index];
46576
+ lower = own(index) ?? lower;
46577
+ if (origin === void 0) {
46578
+ if (positions[index] !== void 0) last = positions[index];
46579
+ } else if (origin > last && origin < next[index] && inOrder) {
46580
+ positions[index] = origin;
46581
+ last = origin;
46582
+ } else moved.set(index, origin);
46583
+ }
46584
+ return {
46585
+ positions,
46586
+ carried: {
46587
+ moved,
46588
+ children: true,
46589
+ within: this.within(doc, positions, current)
46590
+ }
46591
+ };
46592
+ }
46593
+ endsAsAuthored(positions, carried) {
46594
+ const end = this.blocks.length - 1;
46595
+ if (end < 0) return false;
46596
+ if (positions.at(-1) === end) return true;
46597
+ return carried.moved.has(positions.length - 1) && [...carried.moved.values()].includes(end);
46598
+ }
46599
+ within(doc, positions, current) {
46600
+ const within = /* @__PURE__ */ new Set();
46601
+ positions.forEach((position, index) => {
46602
+ const node = doc.child(index);
46603
+ if (position !== void 0 && this.blocks[position].form === current[index] && node !== this.baseline.child(position) && this.carries(node)) within.add(index);
46604
+ });
46605
+ return within;
46606
+ }
46607
+ carriedChildren(node, original) {
46608
+ const moves = this.moves;
46609
+ if (!moves || this.baseline.childCount !== this.blocks.length) return;
46610
+ const origins = this.originIndex();
46611
+ let reorder;
46612
+ const walk = (current, path, ast) => {
46613
+ if (!ast?.children || ast.position?.start.offset == null) return;
46614
+ current.forEach((child, _, index) => {
46615
+ let from = index;
46616
+ const origin = moves.has(child) ? origins.get(child) : void 0;
46617
+ if (origin?.length === path.length + 1 && origin.every((step, at) => at === path.length || step === path[at])) {
46618
+ from = origin.at(-1);
46619
+ if (from !== index) {
46620
+ reorder ??= /* @__PURE__ */ new Map();
46621
+ const key = parentKey(ast);
46622
+ if (!reorder.has(key)) reorder.set(key, /* @__PURE__ */ new Map());
46623
+ reorder.get(key).set(index, from);
46624
+ }
46625
+ }
46626
+ if (!child.isTextblock && !child.isLeaf && child.type.name !== "table") walk(child, [...path, from], ast.children[from]);
46627
+ });
46628
+ };
46629
+ walk(node, [original], this.blocks[original].ast);
46630
+ return reorder;
46631
+ }
46148
46632
  };
46149
46633
  //#endregion
46150
46634
  //#region src/autolink.ts
@@ -46194,14 +46678,8 @@ function read(ctx, sources, texts) {
46194
46678
  function literals(ctx, texts) {
46195
46679
  const possible = texts.map((text) => /www\.|https?:\/\/|@/i.test(text));
46196
46680
  const candidates = texts.filter((_, index) => possible[index]);
46197
- const escaped = candidates.map((text) => text.replace(/[\\`[\]<>]/g, "\\$&"));
46198
- const raw = read(ctx, candidates, candidates);
46199
- const changed = candidates.flatMap((text, index) => escaped[index] === text ? [] : [index]);
46200
- const written = read(ctx, changed.map((index) => escaped[index]), changed.map((index) => candidates[index]));
46201
- changed.forEach((index, at) => {
46202
- if (JSON.stringify(written[at]) !== JSON.stringify(raw[index])) raw[index] = void 0;
46203
- });
46204
- return texts.map((_, index) => possible[index] ? raw.shift() : []);
46681
+ const found = read(ctx, candidates.map((text) => text.replace(/[\\`[\]<>]/g, "\\$&")), candidates);
46682
+ return texts.map((_, index) => possible[index] ? found.shift() : []);
46205
46683
  }
46206
46684
  function angleHref(text) {
46207
46685
  if (/^[a-zA-Z][a-zA-Z0-9+.-]{1,31}:[^\s<>\u0000-\u001f]*$/.test(text)) return text;
@@ -46233,7 +46711,7 @@ function wantedLinks(ctx, chars, from, to) {
46233
46711
  }
46234
46712
  const free = (index) => {
46235
46713
  const char = chars[index];
46236
- return char !== void 0 && (released.has(index) || char.form !== "angle" && !(char.link && !char.form));
46714
+ return char !== void 0 && !char.marks.some((mark) => mark.type.spec.code) && (released.has(index) || char.form !== "angle" && !(char.link && !char.form));
46237
46715
  };
46238
46716
  const spans = [];
46239
46717
  for (let index = 0; index < chars.length;) {
@@ -46403,9 +46881,96 @@ var autolinkLiterals = $prose((ctx) => new Plugin({
46403
46881
  }
46404
46882
  }));
46405
46883
  //#endregion
46884
+ //#region src/inline-code.ts
46885
+ var held = new PluginKey("storedInlineCode");
46886
+ function finish(state, pending, code) {
46887
+ const { selection } = state;
46888
+ if (state.storedMarks || !selection.empty || selection.from !== pending.to) return null;
46889
+ const tr = state.tr;
46890
+ if (pending.from < pending.to) tr.addMark(pending.from, pending.to, code.create());
46891
+ return tr.setStoredMarks(pending.marks);
46892
+ }
46893
+ var storedInlineCode = $prose((ctx) => {
46894
+ const type = () => inlineCodeSchema.type(ctx);
46895
+ let composing;
46896
+ return new Plugin({
46897
+ key: held,
46898
+ state: {
46899
+ init: () => null,
46900
+ apply(tr, value, old) {
46901
+ const id = tr.getMeta("composition");
46902
+ if (id === void 0) return tr.docChanged || tr.selectionSet || tr.storedMarksSet ? null : value;
46903
+ const marks = old.storedMarks;
46904
+ const start = value?.id === id ? value : marks && type().isInSet(marks) ? {
46905
+ marks,
46906
+ id,
46907
+ from: old.selection.from,
46908
+ to: 0
46909
+ } : null;
46910
+ if (!start) return null;
46911
+ let to = value?.id === id ? tr.mapping.map(start.to, 1) : start.from;
46912
+ tr.mapping.maps.forEach((map, index) => map.forEach((_from, _to, _start, end) => {
46913
+ to = Math.max(to, tr.mapping.slice(index + 1).map(end, 1));
46914
+ }));
46915
+ return {
46916
+ ...start,
46917
+ to
46918
+ };
46919
+ }
46920
+ },
46921
+ view: (view) => {
46922
+ composing = view;
46923
+ return { destroy() {
46924
+ composing = void 0;
46925
+ } };
46926
+ },
46927
+ props: { handleDOMEvents: { compositionend(view) {
46928
+ Promise.resolve().then(() => void 0).then(() => {
46929
+ if (view.isDestroyed || view.composing) return;
46930
+ const pending = held.getState(view.state);
46931
+ const tr = pending && finish(view.state, pending, type());
46932
+ if (tr) view.dispatch(tr.setMeta("appendedTransaction", tr));
46933
+ });
46934
+ return false;
46935
+ } } },
46936
+ appendTransaction(transactions, oldState, newState) {
46937
+ const code = type();
46938
+ const stored = oldState.storedMarks;
46939
+ if (!stored || !code.isInSet(stored) || composing?.composing || transactions.some((tr) => tr.getMeta("composition"))) return null;
46940
+ const { $from } = newState.selection;
46941
+ if ($from.start() !== oldState.selection.$from.start()) return newState.storedMarks && code.isInSet(newState.storedMarks) ? newState.tr.setStoredMarks(code.removeFromSet(newState.storedMarks)) : null;
46942
+ const before = $from.nodeBefore;
46943
+ if (newState.storedMarks || !transactions.some((tr) => tr.docChanged) || !newState.selection.empty || !before?.isText || !code.isInSet(before.marks)) return null;
46944
+ return newState.tr.setStoredMarks(stored);
46945
+ }
46946
+ });
46947
+ });
46948
+ //#endregion
46406
46949
  //#region src/source.ts
46407
46950
  var sourceAttribute = "inkkitSource";
46951
+ var completedLines = /* @__PURE__ */ new WeakMap();
46952
+ function completedSource(node) {
46953
+ const cached = completedLines.get(node);
46954
+ if (cached) return cached;
46955
+ let result = node;
46956
+ if (node.type.name === "paragraph" || node.type.name === "heading" && node.attrs.form === "setext") {
46957
+ let end = node.content.size;
46958
+ for (let index = node.childCount - 1; index >= 0; index--) {
46959
+ const child = node.child(index);
46960
+ if (child.type.name !== "hardbreak" || child.attrs.isInline || child.attrs.isHTML) break;
46961
+ end -= child.nodeSize;
46962
+ }
46963
+ if (end !== node.content.size) result = node.copy(node.content.cut(0, end));
46964
+ } else if (!node.isLeaf && !node.isTextblock) {
46965
+ const children = [];
46966
+ node.forEach((child) => children.push(completedSource(child)));
46967
+ if (children.some((child, index) => child !== node.child(index))) result = node.copy(Fragment.fromArray(children));
46968
+ }
46969
+ completedLines.set(node, result);
46970
+ return result;
46971
+ }
46408
46972
  function cleanSourceDoc(doc) {
46973
+ doc = completedSource(doc);
46409
46974
  return doc.attrs["inkkitSource"] == null ? doc : doc.type.create({
46410
46975
  ...doc.attrs,
46411
46976
  [sourceAttribute]: null
@@ -48548,6 +49113,7 @@ function writesBare(node, index, ancestors) {
48548
49113
  const follows = (parent, text) => {
48549
49114
  if (text.type !== "text") return false;
48550
49115
  const run = /^\S*/.exec(text.value)[0];
49116
+ if (!node.url.startsWith("mailto:") && blocks$2.has(parent.type) && parent.children.at(-1) === text && /^[ \t]+$/.test(text.value.slice(run.length))) return false;
48551
49117
  return trailing.test(run) && (run.length < text.value.length || blocks$2.has(parent.type) && parent.children.at(-1) === text);
48552
49118
  };
48553
49119
  const side = (step) => {
@@ -48707,6 +49273,10 @@ function remarkDialect() {
48707
49273
  ]
48708
49274
  }],
48709
49275
  handlers: {
49276
+ break(node, parent, state, info) {
49277
+ const next = parent?.children[parent.children.indexOf(node) + 1];
49278
+ return next?.type === "inkkitCommentInline" && next.syntax === "html" ? "<br />" : handle.break(node, parent, state, info);
49279
+ },
48710
49280
  text(node, parent, state, info) {
48711
49281
  const value = handle.text(node, parent, state, info);
48712
49282
  if (!state.stack.includes("tableCell") || parent?.type !== "paragraph" && parent?.type !== "tableCell") return value;
@@ -48738,12 +49308,11 @@ function padShortRows(node) {
48738
49308
  if ("children" in node) node.children.forEach(padShortRows);
48739
49309
  }
48740
49310
  var remarkDialectPlugin = $remark("remarkDialect", () => remarkDialect);
48741
- var commonmarkWithLiteralReferences = commonmark.filter((plugin) => !remarkInlineLinkPlugin.includes(plugin) && plugin !== hardbreakClearMarkPlugin && plugin !== syncHeadingIdPlugin && plugin !== createCodeBlockInputRule);
49311
+ var commonmarkWithLiteralReferences = commonmark.filter((plugin) => !remarkInlineLinkPlugin.includes(plugin) && plugin !== hardbreakClearMarkPlugin && !hardbreakKeymap.some((part) => part === plugin) && plugin !== syncHeadingIdPlugin && plugin !== createCodeBlockInputRule);
48742
49312
  var breaksWithoutMarks = $prose((ctx) => new Plugin({
48743
49313
  key: new PluginKey("inkkitBreaksWithoutMarks"),
48744
49314
  appendTransaction(transactions, oldState, newState) {
48745
49315
  if (!transactions.some((tr) => tr.docChanged)) return null;
48746
- const inserted = transactions.some((tr) => tr.getMeta("hardbreak"));
48747
49316
  const start = oldState.doc.content.findDiffStart(newState.doc.content);
48748
49317
  if (start == null) return null;
48749
49318
  const end = oldState.doc.content.findDiffEnd(newState.doc.content).b;
@@ -48751,7 +49320,7 @@ var breaksWithoutMarks = $prose((ctx) => new Plugin({
48751
49320
  const tr = newState.tr;
48752
49321
  newState.doc.nodesBetween(start, Math.max(start, end), (node, pos) => {
48753
49322
  if (node.type !== type) return;
48754
- for (const mark of node.marks) if (inserted || mark.type.spec.code) tr.removeMark(pos, pos + 1, mark.type);
49323
+ for (const mark of node.marks) if (mark.type.spec.code) tr.removeMark(pos, pos + 1, mark.type);
48755
49324
  });
48756
49325
  return tr.steps.length ? tr : null;
48757
49326
  }
@@ -48785,6 +49354,36 @@ var preserveSpacerParagraphs = paragraphSchema.extendSchema((base) => (ctx) => {
48785
49354
  }
48786
49355
  };
48787
49356
  });
49357
+ var headingsWithBreaks = headingSchema.extendSchema((base) => (ctx) => {
49358
+ const schema = base(ctx);
49359
+ return {
49360
+ ...schema,
49361
+ attrs: {
49362
+ ...schema.attrs,
49363
+ form: { default: "atx" }
49364
+ },
49365
+ parseMarkdown: {
49366
+ ...schema.parseMarkdown,
49367
+ runner(state, node, type) {
49368
+ const position = node.position;
49369
+ state.openNode(type, {
49370
+ level: node.depth,
49371
+ form: position && position.end.line > position.start.line ? "setext" : "atx"
49372
+ });
49373
+ state.next(node.children);
49374
+ state.closeNode();
49375
+ }
49376
+ },
49377
+ toMarkdown: {
49378
+ ...schema.toMarkdown,
49379
+ runner(state, node) {
49380
+ state.openNode("heading", void 0, { depth: node.attrs.level });
49381
+ state.next(node.content);
49382
+ state.closeNode();
49383
+ }
49384
+ }
49385
+ };
49386
+ });
48788
49387
  var visibleSoftbreaks = hardbreakSchema.extendSchema((base) => (ctx) => {
48789
49388
  const schema = base(ctx);
48790
49389
  return {
@@ -48881,10 +49480,14 @@ function cellToMarkdown(base) {
48881
49480
  }
48882
49481
  };
48883
49482
  }
48884
- var cellBreakKeymap = $prose(() => keymap$3({ "Shift-Enter": (state, dispatch) => {
49483
+ var breakKeymap = $prose(() => keymap$3({ "Shift-Enter": (state, dispatch, view) => {
48885
49484
  const { $from } = state.selection;
48886
- if (!/^table_(cell|header)$/.test($from.node(-1)?.type.name ?? "")) return false;
48887
- dispatch?.(state.tr.replaceSelectionWith(state.schema.nodes.hardbreak.create({ isHTML: true }), false).scrollIntoView());
49485
+ if (!(state.selection instanceof TextSelection) || $from.parent.type.spec.code) return false;
49486
+ const isHTML = htmlBreakAt($from);
49487
+ if (dispatch) {
49488
+ dispatch(closeHistory(state.tr).replaceSelectionWith(state.schema.nodes.hardbreak.create({ isHTML }), false).scrollIntoView());
49489
+ if (view) dispatch(closeHistory(view.state.tr).setMeta("addToHistory", false));
49490
+ }
48888
49491
  return true;
48889
49492
  } }));
48890
49493
  var listItems = extendListItemSchemaForTask.extendSchema((previous) => (ctx) => {
@@ -48970,7 +49573,7 @@ var tables = [
48970
49573
  ].flat();
48971
49574
  function createDialect(images, wikiLinks = false, files = false) {
48972
49575
  return [
48973
- cellBreakKeymap,
49576
+ breakKeymap,
48974
49577
  remarkCodeFences,
48975
49578
  remarkReferencesPlugin,
48976
49579
  remarkLinkFormsPlugin,
@@ -48998,9 +49601,11 @@ function createDialect(images, wikiLinks = false, files = false) {
48998
49601
  joinKeepingSourceKeymap,
48999
49602
  preserveSpacerParagraphs,
49000
49603
  visibleSoftbreaks,
49604
+ headingsWithBreaks,
49001
49605
  normalizedImages,
49002
49606
  imageBlocks,
49003
49607
  autolinkLiterals,
49608
+ storedInlineCode,
49004
49609
  breaksWithoutMarks,
49005
49610
  listItems,
49006
49611
  strikethroughAttr,
@@ -49009,6 +49614,7 @@ function createDialect(images, wikiLinks = false, files = false) {
49009
49614
  strikethroughKeymap,
49010
49615
  toggleStrikethroughCommand,
49011
49616
  wrapInTaskListInputRule,
49617
+ smartPunctuation,
49012
49618
  remarkDialectPlugin,
49013
49619
  tables,
49014
49620
  literalBlock,
@@ -49289,7 +49895,7 @@ function selectionContent(doc, content) {
49289
49895
  return content.append(Fragment.fromArray(extra));
49290
49896
  }
49291
49897
  function selectionMarkdown(ctx, doc, content) {
49292
- return serialize(ctx, doc.type.create(null, selectionContent(doc, content)));
49898
+ return serialize(ctx, cleanSourceDoc(doc.type.create(null, selectionContent(doc, content))));
49293
49899
  }
49294
49900
  function referenceMetadata(ctx, doc, content) {
49295
49901
  doc = doc.copy(shareableFragment(doc.content));
@@ -49883,9 +50489,116 @@ function tableAvailability(ctx) {
49883
50489
  available.sortRows = [...body].sort((a, b) => compareCells(a.key, b.key, "text", "ascending") || a.index - b.index).some((row, index) => row !== body[index]);
49884
50490
  return available;
49885
50491
  }
50492
+ /** Trims the whitespace at both ends of a cell's inline content. */
50493
+ function trimCell(nodes) {
50494
+ const trimmed = [...nodes];
50495
+ for (const [edge, trim] of [[0, (text) => text.trimStart()], [-1, (text) => text.trimEnd()]]) for (;;) {
50496
+ const node = trimmed.at(edge);
50497
+ if (!node?.text) break;
50498
+ const text = trim(node.text);
50499
+ if (text) {
50500
+ trimmed.splice(edge < 0 ? trimmed.length - 1 : 0, 1, node.type.schema.text(text, node.marks));
50501
+ break;
50502
+ }
50503
+ trimmed.splice(edge < 0 ? trimmed.length - 1 : 0, 1);
50504
+ }
50505
+ return trimmed;
50506
+ }
50507
+ /**
50508
+ * The cells of a paragraph that is one table row typed as Markdown, such as `| Name | Date |`: it
50509
+ * starts and ends with a pipe and holds at least two cells. A pipe written `\|` or inside inline
50510
+ * code belongs to its cell. Returns nothing for any other paragraph, including one with a line
50511
+ * break or an inline node, which a cell could not hold the same way.
50512
+ */
50513
+ function typedRowCells(paragraph) {
50514
+ const rows = [[]];
50515
+ for (let index = 0; index < paragraph.childCount; index++) {
50516
+ const child = paragraph.child(index);
50517
+ if (!child.isText || /[\r\n]/.test(child.text)) return void 0;
50518
+ if (child.marks.some((mark) => mark.type.spec.code)) {
50519
+ rows.at(-1).push(child);
50520
+ continue;
50521
+ }
50522
+ let piece = "", slashes = 0;
50523
+ const flush = () => {
50524
+ if (piece) rows.at(-1).push(child.type.schema.text(piece, child.marks));
50525
+ piece = "";
50526
+ };
50527
+ for (const character of child.text) if (character !== "|") {
50528
+ slashes = character === "\\" ? slashes + 1 : 0;
50529
+ piece += character;
50530
+ } else if (slashes % 2) {
50531
+ piece = piece.slice(0, -1) + character;
50532
+ slashes = 0;
50533
+ } else {
50534
+ flush();
50535
+ rows.push([]);
50536
+ slashes = 0;
50537
+ }
50538
+ flush();
50539
+ }
50540
+ const blank = (cell) => cell.every((node) => node.isText && !node.text.trim());
50541
+ if (rows.length < 4 || !blank(rows[0]) || !blank(rows.at(-1))) return void 0;
50542
+ const cells = rows.slice(1, -1);
50543
+ return cells.length <= 100 ? cells.map(trimCell) : void 0;
50544
+ }
50545
+ /**
50546
+ * Turns the paragraph holding the caret at its end into a table when it is a typed table row: the
50547
+ * typed cells are the header, one empty row follows, and the caret is in its first cell. It builds
50548
+ * the table `table('insert')` does and replaces the paragraph in one undo step of its own, so Undo
50549
+ * restores the typed line with the caret at its end.
50550
+ */
50551
+ function typedRowToTable(ctx) {
50552
+ return (state, dispatch) => {
50553
+ const view = ctx.get(editorViewCtx);
50554
+ const { $cursor } = state.selection;
50555
+ if (!$cursor || !view.editable || view.composing || $cursor.parent.type.name !== "paragraph" || $cursor.parentOffset !== $cursor.parent.content.size || $cursor.depth < 1) return false;
50556
+ const cells = typedRowCells($cursor.parent);
50557
+ if (!cells) return false;
50558
+ const template = createTable(ctx, 2, cells.length);
50559
+ const [head, ...rest] = [template.child(0), template.child(1)];
50560
+ const paragraph = state.schema.nodes.paragraph;
50561
+ let table;
50562
+ try {
50563
+ table = template.copy(Fragment.from([head.copy(Fragment.fromArray(cells.map((cell, column) => {
50564
+ return head.child(column).copy(Fragment.from(paragraph.create(null, cell)));
50565
+ }))), ...rest]));
50566
+ table.check();
50567
+ } catch {
50568
+ return false;
50569
+ }
50570
+ const index = $cursor.index(-1), parent = $cursor.node(-1);
50571
+ const replaces = parent.canReplaceWith(index, index + 1, table.type);
50572
+ if (!replaces && (parent.attrs.checked != null || !parent.canReplaceWith(index + 1, index + 1, table.type))) return false;
50573
+ if (dispatch) {
50574
+ const tr = state.tr;
50575
+ if (replaces) tr.replaceWith($cursor.before(), $cursor.after(), table);
50576
+ else {
50577
+ tr.delete($cursor.start(), $cursor.end());
50578
+ tr.insert($cursor.start() + 1, table);
50579
+ }
50580
+ const body = (replaces ? $cursor.before() : $cursor.start() + 1) + 1 + table.child(0).nodeSize;
50581
+ tr.setSelection(Selection.findFrom(tr.doc.resolve(body), 1, true));
50582
+ dispatchTableOperation(view, tr);
50583
+ }
50584
+ return true;
50585
+ };
50586
+ }
49886
50587
  var tablePlugins = [
49887
50588
  moveRowCommand,
49888
50589
  moveColCommand,
50590
+ $shortcut((ctx) => ({
50591
+ Enter: {
50592
+ key: "Enter",
50593
+ onRun: () => typedRowToTable(ctx),
50594
+ priority: 60
50595
+ },
50596
+ Tab: {
50597
+ key: "Tab",
50598
+ onRun: () => typedRowToTable(ctx),
50599
+ priority: 60
50600
+ }
50601
+ })),
49889
50602
  $prose(() => keymap$3({
49890
50603
  Tab: goToNextCell(1),
49891
50604
  "Shift-Tab": goToNextCell(-1)
@@ -50954,6 +51667,101 @@ function literalVisibleRanges(plain, viewport) {
50954
51667
  }) ?? [];
50955
51668
  }
50956
51669
  //#endregion
51670
+ //#region src/block-move.ts
51671
+ var containers = /* @__PURE__ */ new Set([
51672
+ "doc",
51673
+ "blockquote",
51674
+ "inkkit_callout",
51675
+ "bullet_list",
51676
+ "ordered_list",
51677
+ "footnote_definition"
51678
+ ]);
51679
+ var tableParts = /* @__PURE__ */ new Set([
51680
+ "table",
51681
+ "table_row",
51682
+ "table_header_row",
51683
+ "table_cell",
51684
+ "table_header"
51685
+ ]);
51686
+ /** On a move's transaction, the blocks it carried, so their source can go
51687
+ * with them. */
51688
+ var carriedBlocks = new PluginKey("carriedBlocks");
51689
+ var frontMatter = (node) => node.type.name === "literal_markdown" && /^(?:---|\+\+\+)[ \t]*\r?\n/.test(node.textContent);
51690
+ /**
51691
+ * The move a selection makes, or `'boundary'` when its blocks are already
51692
+ * first or last in their container, or `undefined` when it has no blocks to
51693
+ * move, such as in a table, where the table's own row moves apply.
51694
+ */
51695
+ function plan(state, direction) {
51696
+ const { $from, $to } = state.selection;
51697
+ for (const $pos of [$from, $to]) for (let depth = $pos.depth; depth > 0; depth--) if (tableParts.has($pos.node(depth).type.name)) return void 0;
51698
+ const range = $from.blockRange($to, (node) => containers.has(node.type.name));
51699
+ if (!range) return void 0;
51700
+ const container = range.parent;
51701
+ const first = range.depth === 0 && frontMatter(container.child(0)) ? 1 : 0;
51702
+ if (range.startIndex < first) return void 0;
51703
+ const hidden = (index) => !commentsVisible(state) && container.child(index).type.name === "comment_block";
51704
+ const up = direction === "up";
51705
+ let index = up ? range.startIndex - 1 : range.endIndex;
51706
+ const step = up ? -1 : 1;
51707
+ const inside = (index) => up ? index >= first : index < container.childCount;
51708
+ while (inside(index) && hidden(index)) index += step;
51709
+ if (!inside(index)) return "boundary";
51710
+ const past = up ? [index, range.startIndex] : [range.endIndex, index + 1];
51711
+ return {
51712
+ depth: range.depth,
51713
+ start: range.startIndex,
51714
+ end: range.endIndex,
51715
+ past
51716
+ };
51717
+ }
51718
+ /** Whether `moveBlock` would move the blocks at the selection. */
51719
+ function canMoveBlock(state, direction) {
51720
+ return typeof plan(state, direction) === "object";
51721
+ }
51722
+ /**
51723
+ * `'move'` when `moveBlock` would move the blocks at the selection,
51724
+ * `'boundary'` when they are already first or last in their container, and
51725
+ * undefined when there are none to move, as in a table.
51726
+ */
51727
+ function blockMove(state, direction) {
51728
+ const move = plan(state, direction);
51729
+ return typeof move === "object" ? "move" : move;
51730
+ }
51731
+ /**
51732
+ * Moves the blocks at the selection past their neighbour in `direction`, as
51733
+ * one step that keeps the moved and the passed nodes as they are, so their
51734
+ * authored source travels with them. Returns undefined when nothing moves.
51735
+ */
51736
+ function moveBlockTransaction(state, direction) {
51737
+ const move = plan(state, direction);
51738
+ if (typeof move !== "object") return void 0;
51739
+ const { depth, start, end, past } = move;
51740
+ const $at = state.selection.$from;
51741
+ const container = $at.node(depth);
51742
+ const offset = (index) => $at.posAtIndex(index, depth);
51743
+ const children = (from, to) => {
51744
+ const nodes = [];
51745
+ for (let index = from; index < to; index++) nodes.push(container.child(index));
51746
+ return nodes;
51747
+ };
51748
+ const moved = children(start, end);
51749
+ const passed = children(past[0], past[1]);
51750
+ const up = direction === "up";
51751
+ const from = offset(up ? past[0] : start);
51752
+ const to = offset(up ? end : past[1]);
51753
+ const reordered = up ? [...moved, ...passed] : [...passed, ...moved];
51754
+ const tr = state.tr.replaceWith(from, to, Fragment.fromArray(reordered)).setMeta(carriedBlocks, reordered);
51755
+ const shift = up ? -passed.reduce((size, node) => size + node.nodeSize, 0) : passed.reduce((size, node) => size + node.nodeSize, 0);
51756
+ tr.setSelection(shifted(state.selection, tr.doc, shift));
51757
+ return tr.scrollIntoView();
51758
+ }
51759
+ function shifted(selection, doc, shift) {
51760
+ if (selection instanceof NodeSelection) return NodeSelection.create(doc, selection.from + shift);
51761
+ if (selection instanceof TextSelection) return TextSelection.create(doc, selection.anchor + shift, selection.head + shift);
51762
+ return Selection.near(doc.resolve(selection.head + shift));
51763
+ }
51764
+ //#endregion
50957
51765
  //#region src/scroll.ts
50958
51766
  /** Rounds a distance away from zero, to a whole number of pixels. */
50959
51767
  var scrollOutward = (distance) => distance > 0 ? Math.ceil(distance) : Math.floor(distance);
@@ -53090,9 +53898,10 @@ function replacementSlice(state, from, to, value) {
53090
53898
  if (start.parent.type.spec.code) {
53091
53899
  if (value) nodes.push(state.schema.text(value, marks));
53092
53900
  } else {
53901
+ const isHTML = htmlBreakAt(start);
53093
53902
  const lines = value.split("\n");
53094
53903
  lines.forEach((line, index) => {
53095
- if (index) nodes.push(state.schema.nodes.hardbreak.create({ isHTML: index === lines.length - 1 && line === "" }, null, marks));
53904
+ if (index) nodes.push(state.schema.nodes.hardbreak.create({ isHTML }, null, index === lines.length - 1 && line === "" ? [] : marks));
53096
53905
  if (line) nodes.push(state.schema.text(line, marks));
53097
53906
  });
53098
53907
  }
@@ -53138,8 +53947,8 @@ function replaceFormattedRange(view, from, to, replacement, preflight) {
53138
53947
  const start = view.state.doc.resolve(from);
53139
53948
  const end = view.state.doc.resolve(to);
53140
53949
  let textOnly = start.parent === end.parent && start.parent.inlineContent;
53141
- view.state.doc.nodesBetween(from, to, (node) => {
53142
- if (!node.isText && !node.isTextblock) textOnly = false;
53950
+ if (textOnly) start.parent.nodesBetween(start.parentOffset, end.parentOffset, (node) => {
53951
+ if (!node.isText) textOnly = false;
53143
53952
  });
53144
53953
  if (!textOnly) throw new InkKitError("invalid-range", "Replacement requires literal text within one block");
53145
53954
  const value = replacement.replace(/\r\n?/g, "\n");
@@ -53688,6 +54497,8 @@ var shortcutNames = [
53688
54497
  ...formatShortcuts,
53689
54498
  "undo",
53690
54499
  "redo",
54500
+ "moveBlockUp",
54501
+ "moveBlockDown",
53691
54502
  ...tableCommands.map(tableShortcut)
53692
54503
  ];
53693
54504
  /** Checks a keymap and drops omitted entries, so a `null` binding keeps its default. */
@@ -53716,11 +54527,13 @@ function textInputPreferences(value, name) {
53716
54527
  const record = expectRecord(value, name, [
53717
54528
  "spellcheck",
53718
54529
  "autocorrect",
53719
- "autocapitalize"
54530
+ "autocapitalize",
54531
+ "smartPunctuation"
53720
54532
  ]);
53721
54533
  return {
53722
54534
  spellcheck: optionalBoolean(record.spellcheck, `${name}.spellcheck`),
53723
54535
  autocorrect: optionalBoolean(record.autocorrect, `${name}.autocorrect`),
54536
+ smartPunctuation: optionalBoolean(record.smartPunctuation, `${name}.smartPunctuation`),
53724
54537
  autocapitalize: isOmitted(record.autocapitalize) ? void 0 : expectOneOf(record.autocapitalize, autocapitalizeValues, `${name}.autocapitalize`)
53725
54538
  };
53726
54539
  }
@@ -54134,6 +54947,7 @@ var InkKitEditor = class InkKitEditor {
54134
54947
  ready = false;
54135
54948
  destroyed = false;
54136
54949
  preservation;
54950
+ carried;
54137
54951
  pasteController;
54138
54952
  plain;
54139
54953
  root;
@@ -54164,7 +54978,7 @@ var InkKitEditor = class InkKitEditor {
54164
54978
  changePlugin = $prose(() => new Plugin({
54165
54979
  key: new PluginKey("documentChanges"),
54166
54980
  view: () => ({ update: (view, previous) => {
54167
- if (this.loading || view.state.doc.eq(previous.doc)) return;
54981
+ if (this.loading || this.sameDocument(view.state.doc, previous.doc)) return;
54168
54982
  if (this.syncingSource) {
54169
54983
  this.mediaPolicy();
54170
54984
  return;
@@ -54426,7 +55240,7 @@ var InkKitEditor = class InkKitEditor {
54426
55240
  }
54427
55241
  this.publishCommandState();
54428
55242
  }
54429
- /** Replaces the input hints. A field left out leaves the browser's behavior. Throws `composition`
55243
+ /** Replaces the input preferences. A field left out takes its default. Throws `composition`
54430
55244
  * during text composition. */
54431
55245
  setTextInputPreferences(preferences) {
54432
55246
  this.assertAlive();
@@ -54526,7 +55340,9 @@ var InkKitEditor = class InkKitEditor {
54526
55340
  insertFootnote: formatted,
54527
55341
  editReferenceDefinition: formatted && referenceDefinitions(state.doc).size > 0,
54528
55342
  format,
54529
- table: tables
55343
+ table: tables,
55344
+ moveBlockUp: formatted && canMoveBlock(state, "up"),
55345
+ moveBlockDown: formatted && canMoveBlock(state, "down")
54530
55346
  }
54531
55347
  };
54532
55348
  }
@@ -54548,6 +55364,17 @@ var InkKitEditor = class InkKitEditor {
54548
55364
  range = new NodeRange(state.doc.resolve($from.before(depth)), state.doc.resolve($from.after(depth)), depth - 1);
54549
55365
  return !!findWrapping(range, type);
54550
55366
  }
55367
+ historyInputPlugin = $prose(() => new Plugin({
55368
+ key: new PluginKey("inkkitHistoryInput"),
55369
+ props: { handleDOMEvents: { beforeinput: (_view, event) => {
55370
+ if (!this.writable) return false;
55371
+ const name = event.inputType === "historyUndo" ? "undo" : event.inputType === "historyRedo" ? "redo" : void 0;
55372
+ if (!name) return false;
55373
+ event.preventDefault();
55374
+ if (this.ready && this.commandState().commands[name]) this.forPerson(() => this[name](), true);
55375
+ return true;
55376
+ } } }
55377
+ }));
54551
55378
  policyPlugin = $prose(() => new Plugin({
54552
55379
  key: new PluginKey("inkkitInputPolicy"),
54553
55380
  filterTransaction: (tr) => this.loading || this.writable || this.syncingSource || !tr.docChanged && !tr.storedMarksSet,
@@ -54602,7 +55429,7 @@ var InkKitEditor = class InkKitEditor {
54602
55429
  this.sourcePreservations.set(provenance, preservation);
54603
55430
  }
54604
55431
  }
54605
- text = preservation.serialize(cleanSourceDoc(doc));
55432
+ text = preservation.serialize(cleanSourceDoc(doc), this.carried);
54606
55433
  } catch (error) {
54607
55434
  throw coded(error, "preservation");
54608
55435
  }
@@ -54818,7 +55645,7 @@ var InkKitEditor = class InkKitEditor {
54818
55645
  this.assertMutation(expectedGeneration);
54819
55646
  if (this.literalSurface && this.stepTypedSource("undo")) return true;
54820
55647
  const view = this.editor.ctx.get(editorViewCtx);
54821
- const undone = undo(view.state, view.dispatch);
55648
+ const undone = !this.literalSurface && undoSmartPunctuation(view.state, view.dispatch) || undo(view.state, view.dispatch);
54822
55649
  if (undone) {
54823
55650
  if (this.literalSurface && this.undoneSteps.length) this.formattedUndone++;
54824
55651
  else this.undoneSteps = [];
@@ -54984,10 +55811,11 @@ var InkKitEditor = class InkKitEditor {
54984
55811
  const { schema } = view.state;
54985
55812
  const marks = view.state.storedMarks ?? (view.state.selection.empty ? view.state.selection.$from.marks() : view.state.selection.$from.marksAcross(view.state.selection.$to)) ?? [];
54986
55813
  const children = [];
55814
+ const isHTML = !inHiddenComment && htmlBreakAt(view.state.selection.$from);
54987
55815
  const lines = text.replace(/\r\n?/g, "\n").split("\n");
54988
55816
  lines.forEach((line, index) => {
54989
55817
  const finalBreak = index === lines.length - 1 && line === "";
54990
- if (index) children.push(schema.nodes.hardbreak.create({ isHTML: finalBreak }, null, finalBreak ? [] : marks));
55818
+ if (index) children.push(schema.nodes.hardbreak.create({ isHTML }, null, finalBreak ? [] : marks));
54991
55819
  if (line) children.push(schema.text(line, marks));
54992
55820
  });
54993
55821
  if (!children.length) {
@@ -54997,8 +55825,9 @@ var InkKitEditor = class InkKitEditor {
54997
55825
  const paragraph = schema.nodes.paragraph.create(null, children);
54998
55826
  const transaction = inHiddenComment ? insertAfterHiddenComment(view.state, Fragment.from(paragraph)) : view.state.tr.replaceSelection(new Slice(Fragment.from(paragraph), 1, 1)).scrollIntoView();
54999
55827
  if (!transaction) return;
55000
- this.currentText(transaction.doc);
55001
- view.dispatch(transaction);
55828
+ const isolated = lines.length > 1;
55829
+ view.dispatch(isolated ? closeHistory(transaction) : transaction);
55830
+ if (isolated) view.dispatch(closeHistory(view.state.tr).setMeta("addToHistory", false));
55002
55831
  }
55003
55832
  }
55004
55833
  }
@@ -55011,6 +55840,21 @@ var InkKitEditor = class InkKitEditor {
55011
55840
  if (this.literalSurface) return false;
55012
55841
  return tableCommand(this.editor.ctx, command, options);
55013
55842
  }
55843
+ /** Moves the blocks at the selection one place up or down in their list, quote, callout, footnote
55844
+ * or note, as one undo step. Returns whether they moved: false in source and TXT modes, in a
55845
+ * table, and when they are already first or last. */
55846
+ moveBlock(direction, expectedGeneration) {
55847
+ this.assertAlive();
55848
+ expectOneOf(direction, ["up", "down"], "direction");
55849
+ this.assertMutation(expectedGeneration);
55850
+ if (this.literalSurface) return false;
55851
+ const view = this.editor.ctx.get(editorViewCtx);
55852
+ const tr = moveBlockTransaction(view.state, direction);
55853
+ if (!tr) return false;
55854
+ view.dispatch(closeHistory(tr));
55855
+ view.dispatch(closeHistory(view.state.tr).setMeta("addToHistory", false));
55856
+ return true;
55857
+ }
55014
55858
  /** Cancels pending work and releases the editor. It never rejects. Afterwards every other member
55015
55859
  * throws `destroyed`. */
55016
55860
  async destroy() {
@@ -55106,6 +55950,7 @@ var InkKitEditor = class InkKitEditor {
55106
55950
  instance.editor = await Editor.make().config((ctx) => {
55107
55951
  ctx.set(rootCtx, root);
55108
55952
  ctx.set(authoredSourcesCtx.key, (doc, positions) => instance.authoredSources(doc, positions));
55953
+ ctx.set(smartPunctuationCtx.key, () => instance.inputPreferences.smartPunctuation === true);
55109
55954
  ctx.update(labelsCtx.key, () => ({
55110
55955
  ...defaultLabels,
55111
55956
  ...options.labels
@@ -55143,6 +55988,7 @@ var InkKitEditor = class InkKitEditor {
55143
55988
  ...options,
55144
55989
  dispatchTransaction: function(tr) {
55145
55990
  if (instance.call?.refusal && tr.docChanged) return;
55991
+ for (const node of tr.getMeta(carriedBlocks) ?? []) (instance.carried ??= /* @__PURE__ */ new WeakSet()).add(node);
55146
55992
  const applied = this.state.applyTransaction(tr);
55147
55993
  let next = applied.state;
55148
55994
  if (!instance.loading) {
@@ -55171,7 +56017,7 @@ var InkKitEditor = class InkKitEditor {
55171
56017
  return true;
55172
56018
  }
55173
56019
  }));
55174
- }).use(labelsCtx).use(instance.pasteController.plugin).use(visibleClipboard).use(instance.keymapPlugin).use(createDialect(Boolean(options.images || options.files), Boolean(options.wikiLinks), Boolean(options.files))).use(tablePlugins).use(tableBlock).use(instance.changePlugin).use(history).use(clipboard).use(cursor).use(taskListPlugin).use(quoteBackspace).use(calloutEnter).use(literalEnter).use(literalReread).use(listItemBackspace).use(listItemShiftTab).use(nestedItemEnter).use(rejoinLists).use(movedListItems).use(textInputPolicy).use(events.copy ? codeCopyPlugin((text) => events.copy?.(text)) : []).use(placeholderPlugin).use(options.rendering?.codeHighlighting === false ? [] : (await import("./highlight-Daj2fv_u.js")).highlightPlugin).use(options.rendering?.diagramPreview === false || !diagramsAvailable() ? [] : mermaidPreview((error) => instance.report(error))).use(selectionPlugin).use($prose(() => search())).use(instance.policyPlugin).use(options.wikiLinks ? wikiLinkView(options.wikiLinks, {
56020
+ }).use(labelsCtx).use(instance.pasteController.plugin).use(visibleClipboard).use(instance.keymapPlugin).use(createDialect(Boolean(options.images || options.files), Boolean(options.wikiLinks), Boolean(options.files))).use(tablePlugins).use(tableBlock).use(instance.changePlugin).use(instance.historyInputPlugin).use(history).use(clipboard).use(cursor).use(taskListPlugin).use(quoteBackspace).use(calloutEnter).use(literalEnter).use(literalReread).use(listItemBackspace).use(listItemShiftTab).use(nestedItemEnter).use(rejoinLists).use(movedListItems).use(textInputPolicy).use(events.copy ? codeCopyPlugin((text) => events.copy?.(text)) : []).use(placeholderPlugin).use(options.rendering?.codeHighlighting === false ? [] : (await import("./highlight-DQC71L-N.js")).highlightPlugin).use(options.rendering?.diagramPreview === false || !diagramsAvailable() ? [] : mermaidPreview((error) => instance.report(error))).use(selectionPlugin).use($prose(() => search())).use(instance.policyPlugin).use(options.wikiLinks ? wikiLinkView(options.wikiLinks, {
55175
56021
  capture: () => instance.adapterCapture(),
55176
56022
  active: () => instance.ready && !instance.destroyed && !instance.literalSurface,
55177
56023
  labels: {
@@ -55586,12 +56432,24 @@ var InkKitEditor = class InkKitEditor {
55586
56432
  if (reveal) this.revealTextRange(range);
55587
56433
  this.publishCommandState();
55588
56434
  }
56435
+ /** Whether two documents are the same note. A move of blocks that read alike, such as a setext
56436
+ * heading and the same heading written with `#`, gives an equal document with other source. */
56437
+ sameDocument(after, before) {
56438
+ if (after === before) return true;
56439
+ if (!after.eq(before)) return false;
56440
+ if (!this.carried || !this.preservation) return true;
56441
+ try {
56442
+ return this.currentText(after) === this.currentText(before);
56443
+ } catch {
56444
+ return false;
56445
+ }
56446
+ }
55589
56447
  /** Throws when a document reached by an edit, after every appended normalisation, cannot be
55590
56448
  * saved as preserved Markdown. Dispatch and the replacement commands share this check.
55591
56449
  * `written` is false for history steps and source replacement, which restore or supply a
55592
56450
  * document that may hold comment text the formatted editor would refuse to accept typed. */
55593
56451
  assertPreserved(before, after, written = true) {
55594
- if (this.literalSurface || after.eq(before)) return;
56452
+ if (this.literalSurface || this.sameDocument(after, before)) return;
55595
56453
  if (written) assertCommentsEndWhereWritten(before, after);
55596
56454
  if (this.preservation) this.currentText(after);
55597
56455
  }
@@ -55768,6 +56626,7 @@ var InkKitEditor = class InkKitEditor {
55768
56626
  this.loading = false;
55769
56627
  }
55770
56628
  this.preservation = preservation;
56629
+ this.carried = void 0;
55771
56630
  this.lastMarkdown = markdown;
55772
56631
  const { doc } = view.state;
55773
56632
  const $end = doc.resolve(doc.content.size);
@@ -55959,14 +56818,17 @@ var InkKitEditor = class InkKitEditor {
55959
56818
  this.releaseFocus();
55960
56819
  }
55961
56820
  /** Replaces every binding. Keys are in ProseMirror's notation. A key given to two shortcuts goes
55962
- * to the later one, in the order history, formatting, tables, so it lands the same way every
55963
- * time. */
56821
+ * to the later one, in the order history, formatting, block moves, tables, so it lands the same
56822
+ * way every time. A table binding passes a key it shares with a block move to the move outside
56823
+ * a table. */
55964
56824
  setKeymap(keymap) {
55965
56825
  this.assertAlive();
55966
56826
  keymap = keymapBindings(keymap, "keymap");
55967
56827
  this.configuredKeys = {
55968
56828
  undo: ["Mod-z"],
55969
56829
  redo: ["Mod-y", "Shift-Mod-z"],
56830
+ moveBlockUp: ["Alt-ArrowUp"],
56831
+ moveBlockDown: ["Alt-ArrowDown"],
55970
56832
  tableExit: ["Mod-Enter", "Enter"],
55971
56833
  ...keymap
55972
56834
  };
@@ -55986,11 +56848,19 @@ var InkKitEditor = class InkKitEditor {
55986
56848
  this.forPerson(() => this.format(name), false);
55987
56849
  return true;
55988
56850
  };
55989
- for (const command of tableCommands) for (const key of this.configuredKeys[tableShortcut(command)] ?? []) bindings[key] = () => {
55990
- if (!this.ready || !this.commandState().commands.table[command]) return false;
56851
+ const moves = {};
56852
+ for (const [name, direction] of [["moveBlockUp", "up"], ["moveBlockDown", "down"]]) for (const key of this.configuredKeys[name] ?? []) moves[key] = bindings[key] = (state) => {
56853
+ if (!this.ready || !this.writable || this.literalSurface) return false;
56854
+ const move = blockMove(state, direction);
56855
+ if (!move) return false;
56856
+ if (move === "move" && !this.composing && !this.pasteController.pending) this.forPerson(() => this.moveBlock(direction), false);
56857
+ return true;
56858
+ };
56859
+ for (const command of tableCommands) for (const key of this.configuredKeys[tableShortcut(command)] ?? []) bindings[key] = (state, dispatch, view) => {
56860
+ if (!this.ready || !this.commandState().commands.table[command]) return moves[key]?.(state, dispatch, view) ?? false;
55991
56861
  return this.forPerson(() => this.table(command), true);
55992
56862
  };
55993
- this.readOnlyKeys = keydownHandler(Object.fromEntries(Object.keys(bindings).map((key) => [key, () => {
56863
+ this.readOnlyKeys = keydownHandler(Object.fromEntries(Object.keys(bindings).filter((key) => !moves[key]).map((key) => [key, () => {
55994
56864
  throw new InkKitError("read-only", "The editor is read-only");
55995
56865
  }])));
55996
56866
  this.keys = keydownHandler(bindings);
@@ -56448,6 +57318,7 @@ var InkKitEditor = class InkKitEditor {
56448
57318
  "replaceAll",
56449
57319
  "format",
56450
57320
  "table",
57321
+ "moveBlock",
56451
57322
  "insertText",
56452
57323
  "keyDown",
56453
57324
  "insertPaths",