@aicayzer/inkkit 0.1.1 → 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.
@@ -31143,6 +31143,11 @@ function mermaidPreview(onError) {
31143
31143
  }));
31144
31144
  }
31145
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
31146
31151
  //#region node_modules/.pnpm/rope-sequence@1.3.4/node_modules/rope-sequence/dist/index.js
31147
31152
  var GOOD_LEAF_SIZE = 200;
31148
31153
  var RopeSequence = function RopeSequence() {};
@@ -39501,6 +39506,194 @@ var cursor = [
39501
39506
  })
39502
39507
  ].flat();
39503
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
39504
39697
  //#region src/text-input.ts
39505
39698
  var isInputRules = (plugin) => plugin.spec.isInputRules === true;
39506
39699
  /**
@@ -39530,12 +39723,12 @@ function insertTextChunk(view, from, to, text, deflt) {
39530
39723
  return true;
39531
39724
  }
39532
39725
  const rules = state.plugins.filter(isInputRules);
39533
- const prepared = EditorState.create({
39726
+ const prepared = chunkState(state, {
39534
39727
  schema: state.schema,
39535
39728
  doc: tr.doc,
39536
39729
  selection: TextSelection.create(tr.doc, at),
39537
39730
  storedMarks: state.storedMarks ?? (from === to ? $from.marks() : $from.marksAcross($to))
39538
- });
39731
+ }, rest);
39539
39732
  let ruled;
39540
39733
  const preparedView = Object.create(view, {
39541
39734
  state: { value: prepared },
@@ -39544,7 +39737,8 @@ function insertTextChunk(view, from, to, text, deflt) {
39544
39737
  } }
39545
39738
  });
39546
39739
  if (!rules.some((rule) => rule.props.handleTextInput?.call(rule, preparedView, at, at, last, () => prepared.tr.insertText(last, at)))) {
39547
- view.dispatch(deflt().scrollIntoView());
39740
+ const plain = deflt().scrollIntoView();
39741
+ view.dispatch(last === "-" ? insertedHyphen(plain) : plain);
39548
39742
  return true;
39549
39743
  }
39550
39744
  if (ruled) {
@@ -44241,7 +44435,7 @@ function lineBreakHTML(value) {
44241
44435
  ].includes(value.trim());
44242
44436
  }
44243
44437
  function restoreInlineBreaks(node) {
44244
- 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) ? {
44245
44439
  type: "break",
44246
44440
  data: { inkkitHTMLBreak: true },
44247
44441
  position: child.position
@@ -44499,6 +44693,39 @@ function relabel(text, offsets, marker) {
44499
44693
  }
44500
44694
  return text;
44501
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
+ }
44502
44729
  function authoredMarker(list, source) {
44503
44730
  const at = list.children?.[0]?.position?.start.offset;
44504
44731
  return at == null ? void 0 : markerAt(source, at)?.marker;
@@ -44740,11 +44967,18 @@ function runOffsets(nodes, text, from, to) {
44740
44967
  let decoded = "";
44741
44968
  const offsets = [from];
44742
44969
  const links = [];
44970
+ const breaks = /* @__PURE__ */ new Set();
44743
44971
  let at = from;
44972
+ let afterBreak = false;
44744
44973
  for (const node of nodes) {
44745
44974
  const start = node.position?.start.offset;
44746
44975
  const end = node.position?.end.offset;
44747
- 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;
44748
44982
  const raw = text.slice(start, end);
44749
44983
  if (node.type === "link") {
44750
44984
  const label = inlineText(node);
@@ -44758,13 +44992,19 @@ function runOffsets(nodes, text, from, to) {
44758
44992
  if (!read || read.decoded !== lf(node.value ?? "")) return void 0;
44759
44993
  decoded += read.decoded;
44760
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);
44761
44999
  } else return void 0;
45000
+ afterBreak = node.type === "break";
44762
45001
  at = end;
44763
45002
  }
44764
45003
  return at === to ? {
44765
45004
  decoded,
44766
45005
  offsets,
44767
- links
45006
+ links,
45007
+ breaks
44768
45008
  } : void 0;
44769
45009
  }
44770
45010
  function retainRun(before, after, source, canonical, [from, to], [newFrom, newTo]) {
@@ -44774,9 +45014,9 @@ function retainRun(before, after, source, canonical, [from, to], [newFrom, newTo
44774
45014
  const a = old.decoded;
44775
45015
  const b = now.decoded;
44776
45016
  let prefix = 0;
44777
- 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++;
44778
45018
  let suffix = 0;
44779
- 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++;
44780
45020
  for (let widened = true; widened;) {
44781
45021
  widened = false;
44782
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) {
@@ -44790,7 +45030,7 @@ function retainRun(before, after, source, canonical, [from, to], [newFrom, newTo
44790
45030
  }
44791
45031
  }
44792
45032
  }
44793
- 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));
44794
45034
  }
44795
45035
  function retainText(raw, before, after, inTable = false, written, beside = [false, false]) {
44796
45036
  before = lf(before);
@@ -44812,14 +45052,38 @@ function retainText(raw, before, after, inTable = false, written, beside = [fals
44812
45052
  }).join("");
44813
45053
  return raw.slice(0, offsets[prefix]) + inserted + raw.slice(offsets[before.length - suffix]);
44814
45054
  }
44815
- 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
+ }
44816
45062
  const token = "(?:[ \\t]|&#(?:[xX]0*(?:20|9)|0*(?:32|9));|&Tab;)";
44817
45063
  const decoded = (value) => (value.match(new RegExp(token, "g")) ?? []).map((value) => value === " " || /(?:9;|Tab;)$/.test(value) ? " " : " ").join("");
44818
45064
  const encode = (value) => value.replace(/[ \t]/g, (value) => value === " " ? "&#x9;" : "&#x20;");
44819
- 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
+ }
44820
45079
  const rawStart = new RegExp(`^${token}+`).exec(raw)?.[0];
44821
45080
  const encodedStart = new RegExp(`^${token}+`).exec(canonical)?.[0];
44822
- 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
+ }
44823
45087
  const end = /[ \t]+$/.exec(raw)?.[0];
44824
45088
  const rawEnd = new RegExp(`${token}+$`).exec(raw)?.[0];
44825
45089
  const encodedEnd = new RegExp(`${token}+$`).exec(canonical)?.[0];
@@ -44982,7 +45246,7 @@ function markLiterals(raw, transformed, blocks = []) {
44982
45246
  };
44983
45247
  raw.children.forEach(mark);
44984
45248
  }
44985
- 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) {
44986
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);
44987
45251
  if (before.type === "list" && after.type === "list" && before.ordered && after.ordered && !numbered.has(before) && before.position?.start.offset != null) {
44988
45252
  const own = {
@@ -44990,7 +45254,7 @@ function retainSource(before, after, source, canonical, lineEnding, inTable = fa
44990
45254
  start: after.start
44991
45255
  };
44992
45256
  numbered.add(own);
44993
- 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);
44994
45258
  const at = before.position.start.offset;
44995
45259
  const prefix = source.slice(source.lastIndexOf("\n", at - 1) + 1, at).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
44996
45260
  let count = 0, previous = 0, number = 0;
@@ -45008,7 +45272,7 @@ function retainSource(before, after, source, canonical, lineEnding, inTable = fa
45008
45272
  const text = retainSource({
45009
45273
  ...before,
45010
45274
  checked: after.checked
45011
- }, after, source, canonical, lineEnding, inTable, width, beside, joined);
45275
+ }, after, source, canonical, lineEnding, inTable, width, beside, joined, reorder);
45012
45276
  const box = /^((?:[-*+]|\d{1,9}[.)])[ \t]+\[)[ xX](\])/.exec(text);
45013
45277
  if (box) return box[1] + (after.checked ? "x" : " ") + text.slice(box[1].length + 1);
45014
45278
  }
@@ -45073,8 +45337,8 @@ function retainSource(before, after, source, canonical, lineEnding, inTable = fa
45073
45337
  const to = first.position.end.offset - start;
45074
45338
  return retained.startsWith(source.slice(start, before.children[1].position.start.offset)) ? retained.slice(0, from) + "<br />" + retained.slice(to) : retained;
45075
45339
  }
45076
- 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));
45077
- 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;
45078
45342
  const marker = sameList(before, after) ? authoredMarker(before, source) : void 0;
45079
45343
  const located = (nodes) => nodes.every((node) => node.position?.start.offset != null);
45080
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)) {
@@ -45082,15 +45346,17 @@ function retainSource(before, after, source, canonical, lineEnding, inTable = fa
45082
45346
  let previous;
45083
45347
  const same = (candidate, incoming) => signature(candidate) === signature(incoming) && ownShape(candidate) === ownShape(incoming);
45084
45348
  const reorderable = before.type === "table" || before.type === "tableRow";
45349
+ const moves = reorder?.get(parentKey(before));
45085
45350
  const retained = new Set(reorderable ? before.children.flatMap((child, index) => same(child, after.children[index]) ? [index] : []) : []);
45086
45351
  before.children.forEach((child, index) => {
45087
45352
  const incoming = after.children[index];
45088
- 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));
45089
45355
  const original = movedIndex < 0 ? child : before.children[movedIndex];
45090
45356
  if (movedIndex >= 0) retained.add(movedIndex);
45091
45357
  const childStart = child.position?.start.offset, childEnd = child.position?.end.offset;
45092
45358
  if (childStart == null || childEnd == null) throw new PreservationError("A source token has no location");
45093
- 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");
45094
45360
  if (before.type === "tableRow" && movedIndex >= 0 && movedIndex !== index) {
45095
45361
  const originalFrom = original.children?.[0]?.position?.start.offset;
45096
45362
  const originalTo = original.children?.at(-1)?.position?.end.offset;
@@ -45098,6 +45364,7 @@ function retainSource(before, after, source, canonical, lineEnding, inTable = fa
45098
45364
  const to = child.children?.at(-1)?.position?.end.offset;
45099
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);
45100
45366
  }
45367
+ if (!reorderable && before.ordered && movedIndex >= 0 && movedIndex !== index) retainedSource = renumberItem(retainedSource, source, original, child);
45101
45368
  if (marker) retainedSource = relabel(retainedSource, [0], marker);
45102
45369
  previous = incoming.type !== "list" ? void 0 : authoredNeighbour(original, incoming, source) ?? {
45103
45370
  ordered: incoming.ordered,
@@ -45114,11 +45381,11 @@ function retainSource(before, after, source, canonical, lineEnding, inTable = fa
45114
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++;
45115
45382
  if (prefix && rewrittenAddress(before.type, before.children, prefix - 1, after.children, prefix - 1, source, canonical)) prefix--;
45116
45383
  if (suffix && rewrittenAddress(before.type, before.children, before.children.length - suffix, after.children, after.children.length - suffix, source, canonical)) suffix--;
45117
- const cell = before.type === "tableCell" && before.children.length > 0 && after.children.length > 0;
45118
- const first = cell ? before.children[0].position?.start.offset : start;
45119
- const last = cell ? before.children.at(-1).position?.end.offset : end;
45120
- const newFirst = cell ? after.children[0].position?.start.offset : newStart;
45121
- 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;
45122
45389
  const ending = (node) => {
45123
45390
  const last = node.children?.at(-1);
45124
45391
  return last && (node.type === "list" || node.type === "listItem" && last.type === "list") ? ending(last) : node;
@@ -45133,7 +45400,7 @@ function retainSource(before, after, source, canonical, lineEnding, inTable = fa
45133
45400
  const changes = after.children.slice(prefix, after.children.length - suffix);
45134
45401
  const previous = authoredNeighbour(before.children[prefix - 1], after.children[prefix - 1], source);
45135
45402
  const next = authoredNeighbour(before.children[before.children.length - suffix], after.children[after.children.length - suffix], source);
45136
- 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);
45137
45404
  }
45138
45405
  }
45139
45406
  const lastAfter = after.children?.at(-1)?.position?.end.offset;
@@ -45229,7 +45496,13 @@ function semanticSignature(doc) {
45229
45496
  const content = value.content;
45230
45497
  if (!content) return value;
45231
45498
  const tokens = [];
45232
- 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
+ };
45233
45506
  if (child.type !== "text") {
45234
45507
  tokens.push(clean(child));
45235
45508
  continue;
@@ -45441,6 +45714,11 @@ function similarity(left, right) {
45441
45714
  while (suffix < limit - prefix && left[left.length - 1 - suffix] === right[right.length - 1 - suffix]) suffix++;
45442
45715
  return prefix + suffix;
45443
45716
  }
45717
+ var nothingCarried = {
45718
+ moved: /* @__PURE__ */ new Map(),
45719
+ children: false,
45720
+ within: /* @__PURE__ */ new Set()
45721
+ };
45444
45722
  /** Retains the loaded source of blocks the edit leaves alone. */
45445
45723
  var Preservation = class Preservation {
45446
45724
  ctx;
@@ -45456,6 +45734,8 @@ var Preservation = class Preservation {
45456
45734
  itemEdits = /* @__PURE__ */ new WeakMap();
45457
45735
  regionEdits = /* @__PURE__ */ new WeakMap();
45458
45736
  definitionNodes = /* @__PURE__ */ new Map();
45737
+ moves;
45738
+ origins;
45459
45739
  nestedLiterals = /* @__PURE__ */ new Set();
45460
45740
  literalsRead = /* @__PURE__ */ new WeakSet();
45461
45741
  definitionSources;
@@ -45521,12 +45801,12 @@ var Preservation = class Preservation {
45521
45801
  throw new PreservationError("Cannot read Markdown for preservation", { cause });
45522
45802
  }
45523
45803
  }
45524
- retainBlock(block, after, canonical, joined = []) {
45804
+ retainBlock(block, after, canonical, joined = [], reorder) {
45525
45805
  const offset = this.source.startsWith("") ? 1 : 0;
45526
45806
  const start = block.ast.position?.start.offset;
45527
45807
  if (start == null) throw new PreservationError("A source block has no location");
45528
45808
  this.readNestedLiterals(block);
45529
- 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);
45530
45810
  }
45531
45811
  readNestedLiterals(block) {
45532
45812
  if (!this.nestedLiterals.size || this.literalsRead.has(block)) return;
@@ -45567,7 +45847,8 @@ var Preservation = class Preservation {
45567
45847
  for (let index = 0; index <= doc.childCount; index++) {
45568
45848
  const node = index < doc.childCount ? doc.child(index) : void 0;
45569
45849
  const before = node && this.baseline.child(index);
45570
- 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)) {
45571
45852
  if (run.length > 1 && !this.pairedInPlace(doc, run)) return void 0;
45572
45853
  run = [];
45573
45854
  continue;
@@ -45713,19 +45994,24 @@ var Preservation = class Preservation {
45713
45994
  if (other !== index && candidate.type === node.type && candidate.childCount === node.childCount && candidate.textContent === node.textContent) return void 0;
45714
45995
  }
45715
45996
  const marker = authoredMarker(list, source);
45997
+ const reorder = this.moves ? this.carriedChildren(node, index) : void 0;
45998
+ const carried = reorder?.get(parentKey(list));
45716
45999
  let output = "";
45717
46000
  let previous = block.start;
45718
46001
  for (let at = 0; at < node.childCount; at++) {
45719
46002
  const item = node.child(at);
45720
46003
  const original = before.child(at);
45721
- 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;
45722
46007
  let retained = this.itemEdits.get(item);
45723
- if (retained?.index !== index || retained.item !== at) {
45724
- 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);
45725
46010
  if (text === void 0) return void 0;
45726
46011
  retained = {
45727
46012
  index,
45728
46013
  item: at,
46014
+ from,
45729
46015
  text
45730
46016
  };
45731
46017
  this.itemEdits.set(item, retained);
@@ -45736,18 +46022,17 @@ var Preservation = class Preservation {
45736
46022
  }
45737
46023
  return output + this.source.slice(previous, block.end);
45738
46024
  }
45739
- localItem(list, item, before, ast, source, marker) {
45740
- const start = ast.position.start.offset;
45741
- const end = ast.position.end.offset;
45742
- if (!item.sameMarkup(before) || !sameBlocks(item, before) || !Preservation.plainInline(item) || !Preservation.plainInline(before) || !this.inContext(source.slice(start, end))) return void 0;
45743
- const local = this.canonicalTree(form(write(this.ctx, list.type.schema.topNodeType.create(null, [list.type.create(list.attrs, [item])]))));
45744
- const after = local?.node.children?.[0];
45745
- if (!local || local.node.children.length !== 1 || !after) return void 0;
45746
- let text = retainSource(ast, {
45747
- ...after,
45748
- spread: ast.spread
45749
- }, 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);
45750
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;
45751
46036
  const content = ast.children?.[0]?.position?.start.offset;
45752
46037
  if (content == null || !text.startsWith(source.slice(start, content))) return void 0;
45753
46038
  if (/^(?:[-*+]|\d{1,9}[.)])[ \t]*(?:\r?\n|$)/.test(text)) return void 0;
@@ -45760,6 +46045,20 @@ var Preservation = class Preservation {
45760
46045
  const { label: _l, spread: _s, ...expected } = item.attrs;
45761
46046
  return reread.type === item.type && JSON.stringify(attrs) === JSON.stringify(expected) && reread.content.eq(item.content) ? text : void 0;
45762
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
+ }
45763
46062
  separateLists(doc, pieces, kept, positions, current) {
45764
46063
  const kind = (index) => {
45765
46064
  const name = doc.maybeChild(index)?.type.name;
@@ -45779,7 +46078,7 @@ var Preservation = class Preservation {
45779
46078
  return true;
45780
46079
  };
45781
46080
  const cost = (index) => {
45782
- if (kept[index] === "canonical") return 1;
46081
+ if (kept[index] === "canonical" || kept[index] === "moved") return 1;
45783
46082
  if (kept[index] === "edited") return 10;
45784
46083
  const own = marker(index);
45785
46084
  const sides = [-1, 1].map((step) => inPlace(index, step));
@@ -45842,7 +46141,18 @@ var Preservation = class Preservation {
45842
46141
  const reread = read(relabelled);
45843
46142
  return reread.length === 1 && signature(reread[0]) === signature(list) && markerAt(relabelled, list.children[0].position.start.offset)?.marker === marker ? relabelled : void 0;
45844
46143
  }
45845
- 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) {
45846
46156
  if (this.unchanged(doc)) return this.source;
45847
46157
  let local;
45848
46158
  try {
@@ -45864,10 +46174,20 @@ var Preservation = class Preservation {
45864
46174
  for (let index = 0; index < doc.childCount; index++) {
45865
46175
  const node = doc.child(index);
45866
46176
  const before = this.baseline.child(index);
45867
- if (node !== before && !node.eq(before)) return false;
46177
+ if (node !== before && (!node.eq(before) || this.carries(node))) return false;
45868
46178
  }
45869
46179
  return true;
45870
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
+ }
45871
46191
  checked(doc, text) {
45872
46192
  if (!crossCheck.enabled) return text;
45873
46193
  crossCheck.compared++;
@@ -45900,7 +46220,7 @@ var Preservation = class Preservation {
45900
46220
  const previous = positions[index - 1];
45901
46221
  return previous != null && position === previous + 1 && block ? this.source.slice(this.blocks[previous].end, block.start) : this.lineEnding.repeat(2);
45902
46222
  }
45903
- build(doc, positions, current, from, to, canonical, tree) {
46223
+ build(doc, positions, current, carried, from, to, canonical, tree) {
45904
46224
  const transformed = (index) => tree.transformed.children[index - from];
45905
46225
  if (tree) {
45906
46226
  const failed = [];
@@ -45928,12 +46248,13 @@ var Preservation = class Preservation {
45928
46248
  for (let index = from; index < to; index++) {
45929
46249
  const text = current[index];
45930
46250
  const position = positions[index];
45931
- 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];
45932
46253
  gaps.push(this.gap(index, positions));
45933
- 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";
45934
46255
  kept.push(kind);
45935
46256
  const node = tree ? positionalNode(tree.raw, transformed(index)) : void 0;
45936
- 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)));
45937
46258
  }
45938
46259
  return {
45939
46260
  gaps,
@@ -45977,10 +46298,10 @@ var Preservation = class Preservation {
45977
46298
  extra
45978
46299
  };
45979
46300
  }
45980
- runs(positions, current) {
46301
+ runs(positions, current, within = nothingCarried.within) {
45981
46302
  const unchanged = (index) => {
45982
46303
  const position = positions[index];
45983
- return position != null && this.blocks[position].form === current[index];
46304
+ return position != null && this.blocks[position].form === current[index] && !within.has(index);
45984
46305
  };
45985
46306
  const consecutive = (index) => index === 0 ? positions[0] === 0 : positions[index] != null && positions[index - 1] != null && positions[index] === positions[index - 1] + 1;
45986
46307
  const runs = [];
@@ -46010,7 +46331,7 @@ var Preservation = class Preservation {
46010
46331
  return true;
46011
46332
  });
46012
46333
  }
46013
- runPieces(doc, positions, current, run) {
46334
+ runPieces(doc, positions, current, carried, run) {
46014
46335
  const from = run.previous === void 0 ? 0 : run.previous + 1;
46015
46336
  const to = run.next ?? doc.childCount;
46016
46337
  if (from === to) return {
@@ -46021,26 +46342,27 @@ var Preservation = class Preservation {
46021
46342
  const nodes = [];
46022
46343
  for (let index = from; index < to; index++) nodes.push(doc.child(index));
46023
46344
  const { canonical, tree } = from === 0 && to === doc.childCount ? this.canonicalDocument(doc) : this.canonicalBlocks(doc, nodes);
46024
- return this.build(doc, positions, current, from, to, canonical, tree);
46345
+ return this.build(doc, positions, current, carried, from, to, canonical, tree);
46025
46346
  }
46026
46347
  regions(doc) {
46027
46348
  if (!doc.childCount || this.baseline.childCount !== this.blocks.length || !doc.sameMarkup(this.baseline) || this.definitionSources === void 0) return void 0;
46028
46349
  const current = [];
46029
46350
  doc.forEach((node) => current.push(this.formOf(node)));
46030
46351
  let positions;
46352
+ let carried;
46031
46353
  try {
46032
- positions = correspondence(this.blockForms, current);
46354
+ ({positions, carried} = this.pairing(doc, current));
46033
46355
  } catch {
46034
46356
  return;
46035
46357
  }
46036
- const runs = this.runs(positions, current);
46358
+ const runs = this.runs(positions, current, carried.within);
46037
46359
  if (runs.some((run) => run.previous === void 0 && run.next === void 0) || !this.readAlone(doc, positions, runs)) return void 0;
46038
46360
  let output = "";
46039
46361
  let at = 0;
46040
46362
  for (const run of runs) {
46041
46363
  const from = run.previous === void 0 ? 0 : this.blocks[positions[run.previous]].end;
46042
46364
  const to = run.next === void 0 ? this.source.length : this.blocks[positions[run.next]].start;
46043
- const text = this.region(doc, positions, current, run);
46365
+ const text = this.region(doc, positions, current, carried, run);
46044
46366
  if (text === void 0) return void 0;
46045
46367
  output += this.source.slice(at, from) + text;
46046
46368
  at = to;
@@ -46051,7 +46373,7 @@ var Preservation = class Preservation {
46051
46373
  if (frontmatter !== sourceFrontmatter(bom(this.source)) || doc.firstChild?.type.name === "hr" && frontmatter?.startsWith("---")) return void 0;
46052
46374
  return output;
46053
46375
  }
46054
- region(doc, positions, current, run) {
46376
+ region(doc, positions, current, carried, run) {
46055
46377
  const from = run.previous === void 0 ? 0 : run.previous + 1;
46056
46378
  const to = run.next ?? doc.childCount;
46057
46379
  const nodes = [];
@@ -46062,7 +46384,7 @@ var Preservation = class Preservation {
46062
46384
  const identity = [
46063
46385
  original(run.previous),
46064
46386
  original(run.next),
46065
- ...positions.slice(from, to)
46387
+ ...Array.from({ length: to - from }, (_, at) => positions[from + at] ?? `m${carried.moved.get(from + at) ?? ""}`)
46066
46388
  ].join();
46067
46389
  const cached = this.regionEdits.get(key);
46068
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;
@@ -46071,10 +46393,10 @@ var Preservation = class Preservation {
46071
46393
  return name === "bullet_list" || name === "ordered_list" ? name : void 0;
46072
46394
  };
46073
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;
46074
- const { gaps, pieces } = this.runPieces(doc, positions, current, run);
46396
+ const { gaps, pieces } = this.runPieces(doc, positions, current, carried, run);
46075
46397
  let text = pieces.map((piece, index) => gaps[index] + piece).join("");
46076
46398
  if (run.next !== void 0) text += this.gap(run.next, positions);
46077
- 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;
46078
46400
  const slice = (index) => {
46079
46401
  if (index === void 0) return "";
46080
46402
  const block = this.blocks[positions[index]];
@@ -46134,26 +46456,21 @@ var Preservation = class Preservation {
46134
46456
  if (canonical === "") return this.source.startsWith("") ? "" : "";
46135
46457
  const current = [];
46136
46458
  doc.forEach((node) => current.push(this.formOf(node)));
46137
- const positions = correspondence(this.blockForms, current);
46138
- const gaps = current.map((_, index) => this.gap(index, positions));
46139
- const pieces = positions.map((position) => position == null ? "" : this.source.slice(this.blocks[position].start, this.blocks[position].end));
46140
- const kept = current.map(() => "unchanged");
46141
- const runs = this.runs(positions, current);
46142
- for (const run of this.readAlone(doc, positions, runs) ? runs : [{}]) {
46143
- const from = run.previous === void 0 ? 0 : run.previous + 1;
46144
- const written = this.runPieces(doc, positions, current, run);
46145
- written.pieces.forEach((piece, index) => {
46146
- pieces[from + index] = piece;
46147
- kept[from + index] = written.kept[index];
46148
- });
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;
46149
46469
  }
46150
- const previous = positions.at(-1);
46151
- this.separateLists(doc, pieces, kept, positions, current);
46152
- let output = pieces.map((piece, index) => gaps[index] + piece).join("");
46153
- if (previous === this.blocks.length - 1 && this.blocks.length) output += this.source.slice(this.blocks[previous].end);
46154
- else if (output) output += finalCodeEnding(doc) ?? this.lineEnding;
46155
- const bom = output.startsWith("") ? 1 : 0;
46156
- 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);
46157
46474
  if (this.reopens(doc, output, canonical)) return output;
46158
46475
  throw new PreservationError("The edited Markdown cannot be reopened without changing its content");
46159
46476
  } catch (cause) {
@@ -46161,6 +46478,157 @@ var Preservation = class Preservation {
46161
46478
  throw new PreservationError("Cannot preserve the edited Markdown", { cause });
46162
46479
  }
46163
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
+ }
46164
46632
  };
46165
46633
  //#endregion
46166
46634
  //#region src/autolink.ts
@@ -46210,14 +46678,8 @@ function read(ctx, sources, texts) {
46210
46678
  function literals(ctx, texts) {
46211
46679
  const possible = texts.map((text) => /www\.|https?:\/\/|@/i.test(text));
46212
46680
  const candidates = texts.filter((_, index) => possible[index]);
46213
- const escaped = candidates.map((text) => text.replace(/[\\`[\]<>]/g, "\\$&"));
46214
- const raw = read(ctx, candidates, candidates);
46215
- const changed = candidates.flatMap((text, index) => escaped[index] === text ? [] : [index]);
46216
- const written = read(ctx, changed.map((index) => escaped[index]), changed.map((index) => candidates[index]));
46217
- changed.forEach((index, at) => {
46218
- if (JSON.stringify(written[at]) !== JSON.stringify(raw[index])) raw[index] = void 0;
46219
- });
46220
- 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() : []);
46221
46683
  }
46222
46684
  function angleHref(text) {
46223
46685
  if (/^[a-zA-Z][a-zA-Z0-9+.-]{1,31}:[^\s<>\u0000-\u001f]*$/.test(text)) return text;
@@ -46249,7 +46711,7 @@ function wantedLinks(ctx, chars, from, to) {
46249
46711
  }
46250
46712
  const free = (index) => {
46251
46713
  const char = chars[index];
46252
- 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));
46253
46715
  };
46254
46716
  const spans = [];
46255
46717
  for (let index = 0; index < chars.length;) {
@@ -46419,9 +46881,96 @@ var autolinkLiterals = $prose((ctx) => new Plugin({
46419
46881
  }
46420
46882
  }));
46421
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
46422
46949
  //#region src/source.ts
46423
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
+ }
46424
46972
  function cleanSourceDoc(doc) {
46973
+ doc = completedSource(doc);
46425
46974
  return doc.attrs["inkkitSource"] == null ? doc : doc.type.create({
46426
46975
  ...doc.attrs,
46427
46976
  [sourceAttribute]: null
@@ -48564,6 +49113,7 @@ function writesBare(node, index, ancestors) {
48564
49113
  const follows = (parent, text) => {
48565
49114
  if (text.type !== "text") return false;
48566
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;
48567
49117
  return trailing.test(run) && (run.length < text.value.length || blocks$2.has(parent.type) && parent.children.at(-1) === text);
48568
49118
  };
48569
49119
  const side = (step) => {
@@ -48723,6 +49273,10 @@ function remarkDialect() {
48723
49273
  ]
48724
49274
  }],
48725
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
+ },
48726
49280
  text(node, parent, state, info) {
48727
49281
  const value = handle.text(node, parent, state, info);
48728
49282
  if (!state.stack.includes("tableCell") || parent?.type !== "paragraph" && parent?.type !== "tableCell") return value;
@@ -48754,12 +49308,11 @@ function padShortRows(node) {
48754
49308
  if ("children" in node) node.children.forEach(padShortRows);
48755
49309
  }
48756
49310
  var remarkDialectPlugin = $remark("remarkDialect", () => remarkDialect);
48757
- 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);
48758
49312
  var breaksWithoutMarks = $prose((ctx) => new Plugin({
48759
49313
  key: new PluginKey("inkkitBreaksWithoutMarks"),
48760
49314
  appendTransaction(transactions, oldState, newState) {
48761
49315
  if (!transactions.some((tr) => tr.docChanged)) return null;
48762
- const inserted = transactions.some((tr) => tr.getMeta("hardbreak"));
48763
49316
  const start = oldState.doc.content.findDiffStart(newState.doc.content);
48764
49317
  if (start == null) return null;
48765
49318
  const end = oldState.doc.content.findDiffEnd(newState.doc.content).b;
@@ -48767,7 +49320,7 @@ var breaksWithoutMarks = $prose((ctx) => new Plugin({
48767
49320
  const tr = newState.tr;
48768
49321
  newState.doc.nodesBetween(start, Math.max(start, end), (node, pos) => {
48769
49322
  if (node.type !== type) return;
48770
- 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);
48771
49324
  });
48772
49325
  return tr.steps.length ? tr : null;
48773
49326
  }
@@ -48801,6 +49354,36 @@ var preserveSpacerParagraphs = paragraphSchema.extendSchema((base) => (ctx) => {
48801
49354
  }
48802
49355
  };
48803
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
+ });
48804
49387
  var visibleSoftbreaks = hardbreakSchema.extendSchema((base) => (ctx) => {
48805
49388
  const schema = base(ctx);
48806
49389
  return {
@@ -48897,10 +49480,14 @@ function cellToMarkdown(base) {
48897
49480
  }
48898
49481
  };
48899
49482
  }
48900
- var cellBreakKeymap = $prose(() => keymap$3({ "Shift-Enter": (state, dispatch) => {
49483
+ var breakKeymap = $prose(() => keymap$3({ "Shift-Enter": (state, dispatch, view) => {
48901
49484
  const { $from } = state.selection;
48902
- if (!/^table_(cell|header)$/.test($from.node(-1)?.type.name ?? "")) return false;
48903
- 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
+ }
48904
49491
  return true;
48905
49492
  } }));
48906
49493
  var listItems = extendListItemSchemaForTask.extendSchema((previous) => (ctx) => {
@@ -48986,7 +49573,7 @@ var tables = [
48986
49573
  ].flat();
48987
49574
  function createDialect(images, wikiLinks = false, files = false) {
48988
49575
  return [
48989
- cellBreakKeymap,
49576
+ breakKeymap,
48990
49577
  remarkCodeFences,
48991
49578
  remarkReferencesPlugin,
48992
49579
  remarkLinkFormsPlugin,
@@ -49014,9 +49601,11 @@ function createDialect(images, wikiLinks = false, files = false) {
49014
49601
  joinKeepingSourceKeymap,
49015
49602
  preserveSpacerParagraphs,
49016
49603
  visibleSoftbreaks,
49604
+ headingsWithBreaks,
49017
49605
  normalizedImages,
49018
49606
  imageBlocks,
49019
49607
  autolinkLiterals,
49608
+ storedInlineCode,
49020
49609
  breaksWithoutMarks,
49021
49610
  listItems,
49022
49611
  strikethroughAttr,
@@ -49025,6 +49614,7 @@ function createDialect(images, wikiLinks = false, files = false) {
49025
49614
  strikethroughKeymap,
49026
49615
  toggleStrikethroughCommand,
49027
49616
  wrapInTaskListInputRule,
49617
+ smartPunctuation,
49028
49618
  remarkDialectPlugin,
49029
49619
  tables,
49030
49620
  literalBlock,
@@ -49305,7 +49895,7 @@ function selectionContent(doc, content) {
49305
49895
  return content.append(Fragment.fromArray(extra));
49306
49896
  }
49307
49897
  function selectionMarkdown(ctx, doc, content) {
49308
- return serialize(ctx, doc.type.create(null, selectionContent(doc, content)));
49898
+ return serialize(ctx, cleanSourceDoc(doc.type.create(null, selectionContent(doc, content))));
49309
49899
  }
49310
49900
  function referenceMetadata(ctx, doc, content) {
49311
49901
  doc = doc.copy(shareableFragment(doc.content));
@@ -49899,9 +50489,116 @@ function tableAvailability(ctx) {
49899
50489
  available.sortRows = [...body].sort((a, b) => compareCells(a.key, b.key, "text", "ascending") || a.index - b.index).some((row, index) => row !== body[index]);
49900
50490
  return available;
49901
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
+ }
49902
50587
  var tablePlugins = [
49903
50588
  moveRowCommand,
49904
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
+ })),
49905
50602
  $prose(() => keymap$3({
49906
50603
  Tab: goToNextCell(1),
49907
50604
  "Shift-Tab": goToNextCell(-1)
@@ -50970,6 +51667,101 @@ function literalVisibleRanges(plain, viewport) {
50970
51667
  }) ?? [];
50971
51668
  }
50972
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
50973
51765
  //#region src/scroll.ts
50974
51766
  /** Rounds a distance away from zero, to a whole number of pixels. */
50975
51767
  var scrollOutward = (distance) => distance > 0 ? Math.ceil(distance) : Math.floor(distance);
@@ -53106,9 +53898,10 @@ function replacementSlice(state, from, to, value) {
53106
53898
  if (start.parent.type.spec.code) {
53107
53899
  if (value) nodes.push(state.schema.text(value, marks));
53108
53900
  } else {
53901
+ const isHTML = htmlBreakAt(start);
53109
53902
  const lines = value.split("\n");
53110
53903
  lines.forEach((line, index) => {
53111
- 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));
53112
53905
  if (line) nodes.push(state.schema.text(line, marks));
53113
53906
  });
53114
53907
  }
@@ -53154,8 +53947,8 @@ function replaceFormattedRange(view, from, to, replacement, preflight) {
53154
53947
  const start = view.state.doc.resolve(from);
53155
53948
  const end = view.state.doc.resolve(to);
53156
53949
  let textOnly = start.parent === end.parent && start.parent.inlineContent;
53157
- view.state.doc.nodesBetween(from, to, (node) => {
53158
- if (!node.isText && !node.isTextblock) textOnly = false;
53950
+ if (textOnly) start.parent.nodesBetween(start.parentOffset, end.parentOffset, (node) => {
53951
+ if (!node.isText) textOnly = false;
53159
53952
  });
53160
53953
  if (!textOnly) throw new InkKitError("invalid-range", "Replacement requires literal text within one block");
53161
53954
  const value = replacement.replace(/\r\n?/g, "\n");
@@ -53704,6 +54497,8 @@ var shortcutNames = [
53704
54497
  ...formatShortcuts,
53705
54498
  "undo",
53706
54499
  "redo",
54500
+ "moveBlockUp",
54501
+ "moveBlockDown",
53707
54502
  ...tableCommands.map(tableShortcut)
53708
54503
  ];
53709
54504
  /** Checks a keymap and drops omitted entries, so a `null` binding keeps its default. */
@@ -53732,11 +54527,13 @@ function textInputPreferences(value, name) {
53732
54527
  const record = expectRecord(value, name, [
53733
54528
  "spellcheck",
53734
54529
  "autocorrect",
53735
- "autocapitalize"
54530
+ "autocapitalize",
54531
+ "smartPunctuation"
53736
54532
  ]);
53737
54533
  return {
53738
54534
  spellcheck: optionalBoolean(record.spellcheck, `${name}.spellcheck`),
53739
54535
  autocorrect: optionalBoolean(record.autocorrect, `${name}.autocorrect`),
54536
+ smartPunctuation: optionalBoolean(record.smartPunctuation, `${name}.smartPunctuation`),
53740
54537
  autocapitalize: isOmitted(record.autocapitalize) ? void 0 : expectOneOf(record.autocapitalize, autocapitalizeValues, `${name}.autocapitalize`)
53741
54538
  };
53742
54539
  }
@@ -54150,6 +54947,7 @@ var InkKitEditor = class InkKitEditor {
54150
54947
  ready = false;
54151
54948
  destroyed = false;
54152
54949
  preservation;
54950
+ carried;
54153
54951
  pasteController;
54154
54952
  plain;
54155
54953
  root;
@@ -54180,7 +54978,7 @@ var InkKitEditor = class InkKitEditor {
54180
54978
  changePlugin = $prose(() => new Plugin({
54181
54979
  key: new PluginKey("documentChanges"),
54182
54980
  view: () => ({ update: (view, previous) => {
54183
- if (this.loading || view.state.doc.eq(previous.doc)) return;
54981
+ if (this.loading || this.sameDocument(view.state.doc, previous.doc)) return;
54184
54982
  if (this.syncingSource) {
54185
54983
  this.mediaPolicy();
54186
54984
  return;
@@ -54442,7 +55240,7 @@ var InkKitEditor = class InkKitEditor {
54442
55240
  }
54443
55241
  this.publishCommandState();
54444
55242
  }
54445
- /** 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`
54446
55244
  * during text composition. */
54447
55245
  setTextInputPreferences(preferences) {
54448
55246
  this.assertAlive();
@@ -54542,7 +55340,9 @@ var InkKitEditor = class InkKitEditor {
54542
55340
  insertFootnote: formatted,
54543
55341
  editReferenceDefinition: formatted && referenceDefinitions(state.doc).size > 0,
54544
55342
  format,
54545
- table: tables
55343
+ table: tables,
55344
+ moveBlockUp: formatted && canMoveBlock(state, "up"),
55345
+ moveBlockDown: formatted && canMoveBlock(state, "down")
54546
55346
  }
54547
55347
  };
54548
55348
  }
@@ -54564,6 +55364,17 @@ var InkKitEditor = class InkKitEditor {
54564
55364
  range = new NodeRange(state.doc.resolve($from.before(depth)), state.doc.resolve($from.after(depth)), depth - 1);
54565
55365
  return !!findWrapping(range, type);
54566
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
+ }));
54567
55378
  policyPlugin = $prose(() => new Plugin({
54568
55379
  key: new PluginKey("inkkitInputPolicy"),
54569
55380
  filterTransaction: (tr) => this.loading || this.writable || this.syncingSource || !tr.docChanged && !tr.storedMarksSet,
@@ -54618,7 +55429,7 @@ var InkKitEditor = class InkKitEditor {
54618
55429
  this.sourcePreservations.set(provenance, preservation);
54619
55430
  }
54620
55431
  }
54621
- text = preservation.serialize(cleanSourceDoc(doc));
55432
+ text = preservation.serialize(cleanSourceDoc(doc), this.carried);
54622
55433
  } catch (error) {
54623
55434
  throw coded(error, "preservation");
54624
55435
  }
@@ -54834,7 +55645,7 @@ var InkKitEditor = class InkKitEditor {
54834
55645
  this.assertMutation(expectedGeneration);
54835
55646
  if (this.literalSurface && this.stepTypedSource("undo")) return true;
54836
55647
  const view = this.editor.ctx.get(editorViewCtx);
54837
- const undone = undo(view.state, view.dispatch);
55648
+ const undone = !this.literalSurface && undoSmartPunctuation(view.state, view.dispatch) || undo(view.state, view.dispatch);
54838
55649
  if (undone) {
54839
55650
  if (this.literalSurface && this.undoneSteps.length) this.formattedUndone++;
54840
55651
  else this.undoneSteps = [];
@@ -55000,10 +55811,11 @@ var InkKitEditor = class InkKitEditor {
55000
55811
  const { schema } = view.state;
55001
55812
  const marks = view.state.storedMarks ?? (view.state.selection.empty ? view.state.selection.$from.marks() : view.state.selection.$from.marksAcross(view.state.selection.$to)) ?? [];
55002
55813
  const children = [];
55814
+ const isHTML = !inHiddenComment && htmlBreakAt(view.state.selection.$from);
55003
55815
  const lines = text.replace(/\r\n?/g, "\n").split("\n");
55004
55816
  lines.forEach((line, index) => {
55005
55817
  const finalBreak = index === lines.length - 1 && line === "";
55006
- 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));
55007
55819
  if (line) children.push(schema.text(line, marks));
55008
55820
  });
55009
55821
  if (!children.length) {
@@ -55013,8 +55825,9 @@ var InkKitEditor = class InkKitEditor {
55013
55825
  const paragraph = schema.nodes.paragraph.create(null, children);
55014
55826
  const transaction = inHiddenComment ? insertAfterHiddenComment(view.state, Fragment.from(paragraph)) : view.state.tr.replaceSelection(new Slice(Fragment.from(paragraph), 1, 1)).scrollIntoView();
55015
55827
  if (!transaction) return;
55016
- this.currentText(transaction.doc);
55017
- 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));
55018
55831
  }
55019
55832
  }
55020
55833
  }
@@ -55027,6 +55840,21 @@ var InkKitEditor = class InkKitEditor {
55027
55840
  if (this.literalSurface) return false;
55028
55841
  return tableCommand(this.editor.ctx, command, options);
55029
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
+ }
55030
55858
  /** Cancels pending work and releases the editor. It never rejects. Afterwards every other member
55031
55859
  * throws `destroyed`. */
55032
55860
  async destroy() {
@@ -55122,6 +55950,7 @@ var InkKitEditor = class InkKitEditor {
55122
55950
  instance.editor = await Editor.make().config((ctx) => {
55123
55951
  ctx.set(rootCtx, root);
55124
55952
  ctx.set(authoredSourcesCtx.key, (doc, positions) => instance.authoredSources(doc, positions));
55953
+ ctx.set(smartPunctuationCtx.key, () => instance.inputPreferences.smartPunctuation === true);
55125
55954
  ctx.update(labelsCtx.key, () => ({
55126
55955
  ...defaultLabels,
55127
55956
  ...options.labels
@@ -55159,6 +55988,7 @@ var InkKitEditor = class InkKitEditor {
55159
55988
  ...options,
55160
55989
  dispatchTransaction: function(tr) {
55161
55990
  if (instance.call?.refusal && tr.docChanged) return;
55991
+ for (const node of tr.getMeta(carriedBlocks) ?? []) (instance.carried ??= /* @__PURE__ */ new WeakSet()).add(node);
55162
55992
  const applied = this.state.applyTransaction(tr);
55163
55993
  let next = applied.state;
55164
55994
  if (!instance.loading) {
@@ -55187,7 +56017,7 @@ var InkKitEditor = class InkKitEditor {
55187
56017
  return true;
55188
56018
  }
55189
56019
  }));
55190
- }).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-BW9W8PtS.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, {
55191
56021
  capture: () => instance.adapterCapture(),
55192
56022
  active: () => instance.ready && !instance.destroyed && !instance.literalSurface,
55193
56023
  labels: {
@@ -55602,12 +56432,24 @@ var InkKitEditor = class InkKitEditor {
55602
56432
  if (reveal) this.revealTextRange(range);
55603
56433
  this.publishCommandState();
55604
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
+ }
55605
56447
  /** Throws when a document reached by an edit, after every appended normalisation, cannot be
55606
56448
  * saved as preserved Markdown. Dispatch and the replacement commands share this check.
55607
56449
  * `written` is false for history steps and source replacement, which restore or supply a
55608
56450
  * document that may hold comment text the formatted editor would refuse to accept typed. */
55609
56451
  assertPreserved(before, after, written = true) {
55610
- if (this.literalSurface || after.eq(before)) return;
56452
+ if (this.literalSurface || this.sameDocument(after, before)) return;
55611
56453
  if (written) assertCommentsEndWhereWritten(before, after);
55612
56454
  if (this.preservation) this.currentText(after);
55613
56455
  }
@@ -55784,6 +56626,7 @@ var InkKitEditor = class InkKitEditor {
55784
56626
  this.loading = false;
55785
56627
  }
55786
56628
  this.preservation = preservation;
56629
+ this.carried = void 0;
55787
56630
  this.lastMarkdown = markdown;
55788
56631
  const { doc } = view.state;
55789
56632
  const $end = doc.resolve(doc.content.size);
@@ -55975,14 +56818,17 @@ var InkKitEditor = class InkKitEditor {
55975
56818
  this.releaseFocus();
55976
56819
  }
55977
56820
  /** Replaces every binding. Keys are in ProseMirror's notation. A key given to two shortcuts goes
55978
- * to the later one, in the order history, formatting, tables, so it lands the same way every
55979
- * 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. */
55980
56824
  setKeymap(keymap) {
55981
56825
  this.assertAlive();
55982
56826
  keymap = keymapBindings(keymap, "keymap");
55983
56827
  this.configuredKeys = {
55984
56828
  undo: ["Mod-z"],
55985
56829
  redo: ["Mod-y", "Shift-Mod-z"],
56830
+ moveBlockUp: ["Alt-ArrowUp"],
56831
+ moveBlockDown: ["Alt-ArrowDown"],
55986
56832
  tableExit: ["Mod-Enter", "Enter"],
55987
56833
  ...keymap
55988
56834
  };
@@ -56002,11 +56848,19 @@ var InkKitEditor = class InkKitEditor {
56002
56848
  this.forPerson(() => this.format(name), false);
56003
56849
  return true;
56004
56850
  };
56005
- for (const command of tableCommands) for (const key of this.configuredKeys[tableShortcut(command)] ?? []) bindings[key] = () => {
56006
- 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;
56007
56861
  return this.forPerson(() => this.table(command), true);
56008
56862
  };
56009
- 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, () => {
56010
56864
  throw new InkKitError("read-only", "The editor is read-only");
56011
56865
  }])));
56012
56866
  this.keys = keydownHandler(bindings);
@@ -56464,6 +57318,7 @@ var InkKitEditor = class InkKitEditor {
56464
57318
  "replaceAll",
56465
57319
  "format",
56466
57320
  "table",
57321
+ "moveBlock",
56467
57322
  "insertText",
56468
57323
  "keyDown",
56469
57324
  "insertPaths",