@inkandswitch/patchwork-pkg-base 0.0.125 → 0.0.127

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.
@@ -1269,23 +1269,23 @@ var FlattenedDoc = class _FlattenedDoc {
1269
1269
  get to() {
1270
1270
  return this.from + this.text.length;
1271
1271
  }
1272
- static get(doc, from, to) {
1273
- let cached = flattened.get(doc);
1272
+ static get(doc2, from, to) {
1273
+ let cached = flattened.get(doc2);
1274
1274
  if (!cached || cached.from >= to || cached.to <= from) {
1275
- let flat = new _FlattenedDoc(from, doc.sliceString(from, to));
1276
- flattened.set(doc, flat);
1275
+ let flat = new _FlattenedDoc(from, doc2.sliceString(from, to));
1276
+ flattened.set(doc2, flat);
1277
1277
  return flat;
1278
1278
  }
1279
1279
  if (cached.from == from && cached.to == to)
1280
1280
  return cached;
1281
1281
  let { text, from: cachedFrom } = cached;
1282
1282
  if (cachedFrom > from) {
1283
- text = doc.sliceString(from, cachedFrom) + text;
1283
+ text = doc2.sliceString(from, cachedFrom) + text;
1284
1284
  cachedFrom = from;
1285
1285
  }
1286
1286
  if (cached.to < to)
1287
- text += doc.sliceString(cached.to, to);
1288
- flattened.set(doc, new _FlattenedDoc(cachedFrom, text));
1287
+ text += doc2.sliceString(cached.to, to);
1288
+ flattened.set(doc2, new _FlattenedDoc(cachedFrom, text));
1289
1289
  return new _FlattenedDoc(from, text.slice(from - cachedFrom, to - cachedFrom));
1290
1290
  }
1291
1291
  };
@@ -1511,11 +1511,11 @@ function stringCursor(spec, state, from, to) {
1511
1511
  test = wrapStringTest(spec.test, state, test);
1512
1512
  return new SearchCursor(state.doc, spec.unquoted, from, to, spec.caseSensitive ? void 0 : (x) => x.toLowerCase(), test);
1513
1513
  }
1514
- function stringWordTest(doc, categorizer) {
1514
+ function stringWordTest(doc2, categorizer) {
1515
1515
  return (from, to, buf, bufPos) => {
1516
1516
  if (bufPos > from || bufPos + buf.length < to) {
1517
1517
  bufPos = Math.max(0, from - 2);
1518
- buf = doc.sliceString(bufPos, Math.min(doc.length, to + 2));
1518
+ buf = doc2.sliceString(bufPos, Math.min(doc2.length, to + 2));
1519
1519
  }
1520
1520
  return (categorizer(charBefore(buf, from - bufPos)) != CharCategory.Word || categorizer(charAfter(buf, from - bufPos)) != CharCategory.Word) && (categorizer(charAfter(buf, to - bufPos)) != CharCategory.Word || categorizer(charBefore(buf, to - bufPos)) != CharCategory.Word);
1521
1521
  };
@@ -2066,7 +2066,7 @@ var searchExtensions = [
2066
2066
  ];
2067
2067
 
2068
2068
  // src/extensions/markdown.ts
2069
- import { EditorView as EditorView6, keymap as keymap3 } from "@codemirror/view";
2069
+ import { EditorView as EditorView9, keymap as keymap5 } from "@codemirror/view";
2070
2070
 
2071
2071
  // src/extensions/formatting.ts
2072
2072
  import { EditorSelection as EditorSelection2 } from "@codemirror/state";
@@ -2189,14 +2189,14 @@ function toggle(view, from) {
2189
2189
  var doneMark = Decoration2.mark({ class: "cm-task-done" });
2190
2190
  function build(view) {
2191
2191
  const builder = new RangeSetBuilder2();
2192
- const { doc } = view.state;
2192
+ const { doc: doc2 } = view.state;
2193
2193
  for (const { from, to } of view.visibleRanges) {
2194
2194
  syntaxTree(view.state).iterate({
2195
2195
  from,
2196
2196
  to,
2197
2197
  enter: (node) => {
2198
2198
  if (node.name !== "TaskMarker") return;
2199
- const checked = doc.sliceString(node.from + 1, node.to - 1) !== " ";
2199
+ const checked = doc2.sliceString(node.from + 1, node.to - 1) !== " ";
2200
2200
  builder.add(
2201
2201
  node.from,
2202
2202
  node.to,
@@ -2234,8 +2234,6 @@ import {
2234
2234
  ViewPlugin as ViewPlugin3,
2235
2235
  WidgetType as WidgetType2
2236
2236
  } from "@codemirror/view";
2237
- var numberMark = Decoration3.mark({ class: "cm-md-hang cm-md-hang-number" });
2238
- var quoteMark = Decoration3.mark({ class: "cm-md-hang cm-md-hang-quote" });
2239
2237
  var hidden = Decoration3.replace({});
2240
2238
  var taskMark = Decoration3.mark({ class: "cm-md-hang", inclusive: true });
2241
2239
  var HeadingLabel = class extends WidgetType2 {
@@ -2301,15 +2299,6 @@ function renderedNumber(state, mark) {
2301
2299
  const start = firstMark ? parseInt(state.doc.sliceString(firstMark.from, firstMark.to), 10) : 1;
2302
2300
  return `${(Number.isNaN(start) ? 1 : start) + index}${delimiter}`;
2303
2301
  }
2304
- function caretLines(state) {
2305
- const lines = /* @__PURE__ */ new Set();
2306
- for (const r of state.selection.ranges) {
2307
- const first = state.doc.lineAt(r.from).number;
2308
- const last = state.doc.lineAt(r.to).number;
2309
- for (let n = first; n <= last; n++) lines.add(n);
2310
- }
2311
- return lines;
2312
- }
2313
2302
  function skipSpaces(state, pos) {
2314
2303
  const line = state.doc.lineAt(pos);
2315
2304
  while (pos < line.to && state.doc.sliceString(pos, pos + 1) === " ") pos++;
@@ -2317,8 +2306,7 @@ function skipSpaces(state, pos) {
2317
2306
  }
2318
2307
  function build2(view) {
2319
2308
  const { state } = view;
2320
- const { doc } = state;
2321
- const editing = caretLines(state);
2309
+ const { doc: doc2 } = state;
2322
2310
  const decos = [];
2323
2311
  for (const { from, to } of view.visibleRanges) {
2324
2312
  syntaxTree2(state).iterate({
@@ -2329,21 +2317,20 @@ function build2(view) {
2329
2317
  const heading = /^ATXHeading(\d)$/.exec(name);
2330
2318
  if (heading) {
2331
2319
  const mark = node.node.firstChild;
2332
- if (!mark || mark.name !== "HeaderMark" || mark.from !== node.from)
2333
- return;
2320
+ if (!mark || mark.name !== "HeaderMark") return;
2334
2321
  decos.push(
2335
2322
  Decoration3.replace({
2336
2323
  widget: new HeadingLabel(Number(heading[1]))
2337
- }).range(mark.from, skipSpaces(state, mark.to))
2324
+ }).range(doc2.lineAt(node.from).from, skipSpaces(state, mark.to))
2338
2325
  );
2339
2326
  return;
2340
2327
  }
2341
2328
  if (name === "ListMark") {
2342
2329
  const end = skipSpaces(state, node.to);
2343
- const char = doc.sliceString(node.from, node.to);
2330
+ const char = doc2.sliceString(node.from, node.to);
2344
2331
  const bullet = ["-", "+", "*"].includes(char);
2345
2332
  const task = node.node.nextSibling?.firstChild;
2346
- const isTask = bullet && task?.name === "TaskMarker" && doc.sliceString(end, task.from).trim() === "";
2333
+ const isTask = bullet && task?.name === "TaskMarker" && doc2.sliceString(end, task.from).trim() === "";
2347
2334
  if (isTask && task) {
2348
2335
  if (end > node.from) decos.push(hidden.range(node.from, end));
2349
2336
  decos.push(taskMark.range(task.from, skipSpaces(state, task.to)));
@@ -2353,8 +2340,6 @@ function build2(view) {
2353
2340
  decos.push(
2354
2341
  Decoration3.replace({ widget: new Bullet() }).range(node.from, end)
2355
2342
  );
2356
- } else if (editing.has(doc.lineAt(node.from).number)) {
2357
- decos.push(numberMark.range(node.from, end));
2358
2343
  } else {
2359
2344
  decos.push(
2360
2345
  Decoration3.replace({
@@ -2367,10 +2352,10 @@ function build2(view) {
2367
2352
  if (name === "Blockquote") {
2368
2353
  for (let p = node.node.parent; p; p = p.parent)
2369
2354
  if (p.name === "Blockquote") return;
2370
- const first = doc.lineAt(node.from).number;
2371
- const last = doc.lineAt(node.to).number;
2355
+ const first = doc2.lineAt(node.from).number;
2356
+ const last = doc2.lineAt(node.to).number;
2372
2357
  for (let n = first; n <= last; n++) {
2373
- const line = doc.line(n);
2358
+ const line = doc2.line(n);
2374
2359
  const classes = ["cm-md-quote"];
2375
2360
  if (n === first) classes.push("cm-md-quote-first");
2376
2361
  if (n === last) classes.push("cm-md-quote-last");
@@ -2381,22 +2366,17 @@ function build2(view) {
2381
2366
  return;
2382
2367
  }
2383
2368
  if (name === "QuoteMark") {
2384
- const line = doc.lineAt(node.from);
2385
- const before = doc.sliceString(line.from, node.from);
2369
+ const line = doc2.lineAt(node.from);
2370
+ const before = doc2.sliceString(line.from, node.from);
2386
2371
  if (before.trim() !== "") return;
2387
2372
  let end = node.to;
2388
2373
  for (; ; ) {
2389
2374
  const next = skipSpaces(state, end);
2390
- if (doc.sliceString(next, next + 1) !== ">") break;
2375
+ if (doc2.sliceString(next, next + 1) !== ">") break;
2391
2376
  end = next + 1;
2392
2377
  }
2393
2378
  end = skipSpaces(state, end);
2394
- decos.push(
2395
- (editing.has(line.number) ? quoteMark : hidden).range(
2396
- line.from,
2397
- end
2398
- )
2399
- );
2379
+ decos.push(hidden.range(line.from, end));
2400
2380
  }
2401
2381
  }
2402
2382
  });
@@ -2410,7 +2390,7 @@ var marginPlugin = ViewPlugin3.fromClass(
2410
2390
  this.decorations = build2(view);
2411
2391
  }
2412
2392
  update(update) {
2413
- if (update.docChanged || update.selectionSet || update.viewportChanged || syntaxTree2(update.startState) !== syntaxTree2(update.state)) {
2393
+ if (update.docChanged || update.viewportChanged || syntaxTree2(update.startState) !== syntaxTree2(update.state)) {
2414
2394
  this.decorations = build2(update.view);
2415
2395
  }
2416
2396
  }
@@ -2500,7 +2480,6 @@ var margin = [marginPlugin, marginTheme];
2500
2480
  import { syntaxTree as syntaxTree3 } from "@codemirror/language";
2501
2481
  import {
2502
2482
  EditorSelection as EditorSelection3,
2503
- EditorState as EditorState2,
2504
2483
  Prec as Prec2
2505
2484
  } from "@codemirror/state";
2506
2485
  import { keymap } from "@codemirror/view";
@@ -2517,31 +2496,6 @@ function headingPrefix(state, line) {
2517
2496
  }
2518
2497
  return null;
2519
2498
  }
2520
- var keepOutOfPrefix = EditorState2.transactionFilter.of((tr) => {
2521
- if (!tr.selection) return tr;
2522
- const { state } = tr;
2523
- const before = tr.startState.selection;
2524
- let moved = false;
2525
- const ranges = tr.selection.ranges.map((r, i) => {
2526
- if (!r.empty) return r;
2527
- const line = state.doc.lineAt(r.head);
2528
- if (r.head >= line.to && line.length > 0) return r;
2529
- const prefix = headingPrefix(state, line);
2530
- if (!prefix || r.head >= prefix.to) return r;
2531
- const old = before.ranges[i] ?? before.main;
2532
- const leaving = !tr.docChanged && old.empty && old.head === prefix.to && line.from > 0;
2533
- moved = true;
2534
- return EditorSelection3.cursor(leaving ? line.from - 1 : prefix.to);
2535
- });
2536
- if (!moved) return tr;
2537
- return [
2538
- tr,
2539
- {
2540
- selection: EditorSelection3.create(ranges, tr.selection.mainIndex),
2541
- sequential: true
2542
- }
2543
- ];
2544
- });
2545
2499
  function selectedLines(state) {
2546
2500
  const seen = /* @__PURE__ */ new Set();
2547
2501
  const lines = [];
@@ -2556,12 +2510,12 @@ function selectedLines(state) {
2556
2510
  }
2557
2511
  var setHeading = (level) => ({ state, dispatch }) => {
2558
2512
  const lines = selectedLines(state);
2559
- const prefixes = lines.map((line) => headingPrefix(state, line));
2560
- const already = level > 0 && prefixes.every((p) => p && p.level === level);
2513
+ const prefixes2 = lines.map((line) => headingPrefix(state, line));
2514
+ const already = level > 0 && prefixes2.every((p) => p && p.level === level);
2561
2515
  const target = already ? 0 : level;
2562
2516
  const insert = target ? "#".repeat(target) + " " : "";
2563
2517
  const changes = lines.map((line, i) => {
2564
- const p = prefixes[i];
2518
+ const p = prefixes2[i];
2565
2519
  return { from: line.from, to: p ? p.to : line.from, insert };
2566
2520
  });
2567
2521
  dispatch(
@@ -2580,18 +2534,6 @@ function atHeadingStart(state) {
2580
2534
  const prefix = headingPrefix(state, line);
2581
2535
  return prefix && sel.main.head === prefix.to ? prefix : null;
2582
2536
  }
2583
- var backspace = ({ state, dispatch }) => {
2584
- const prefix = atHeadingStart(state);
2585
- if (!prefix) return false;
2586
- dispatch(
2587
- state.update({
2588
- changes: { from: prefix.from, to: prefix.to },
2589
- selection: EditorSelection3.cursor(prefix.from),
2590
- userEvent: "delete.backward"
2591
- })
2592
- );
2593
- return true;
2594
- };
2595
2537
  var enter = ({ state, dispatch }) => {
2596
2538
  const prefix = atHeadingStart(state);
2597
2539
  if (!prefix) return false;
@@ -2607,12 +2549,420 @@ var enter = ({ state, dispatch }) => {
2607
2549
  );
2608
2550
  return true;
2609
2551
  };
2552
+ var headings = [
2553
+ // above lang-markdown's own (Prec.high) Enter.
2554
+ Prec2.highest(
2555
+ keymap.of([
2556
+ { key: "Enter", run: enter },
2557
+ { key: "Mod-Shift-h", run: setHeading(2), preventDefault: true },
2558
+ { key: "Mod-Shift-j", run: setHeading(3), preventDefault: true },
2559
+ { key: "Mod-Shift-b", run: setHeading(0), preventDefault: true },
2560
+ { key: "Mod-Alt-1", run: setHeading(1), preventDefault: true },
2561
+ { key: "Mod-Alt-2", run: setHeading(2), preventDefault: true },
2562
+ { key: "Mod-Alt-3", run: setHeading(3), preventDefault: true },
2563
+ { key: "Mod-Alt-0", run: setHeading(0), preventDefault: true }
2564
+ ])
2565
+ )
2566
+ ];
2567
+
2568
+ // src/extensions/lists.ts
2569
+ import { syntaxTree as syntaxTree4 } from "@codemirror/language";
2570
+ import {
2571
+ StateEffect as StateEffect2,
2572
+ StateField as StateField2
2573
+ } from "@codemirror/state";
2574
+ import {
2575
+ Decoration as Decoration4,
2576
+ EditorView as EditorView4,
2577
+ ViewPlugin as ViewPlugin4,
2578
+ WidgetType as WidgetType3
2579
+ } from "@codemirror/view";
2580
+ function listAt(state, pos) {
2581
+ for (let n = syntaxTree4(state).resolveInner(pos, 1); n; n = n.parent) {
2582
+ if (n.name === "BulletList" || n.name === "OrderedList") return n;
2583
+ }
2584
+ return null;
2585
+ }
2586
+ var items = (list) => list.getChildren("ListItem");
2587
+ function done(state, item) {
2588
+ const marker = item.getChild("Task")?.getChild("TaskMarker");
2589
+ if (!marker) return null;
2590
+ return state.doc.sliceString(marker.from + 1, marker.to - 1).trim() !== "";
2591
+ }
2592
+ var startOf = (state, item) => state.doc.lineAt(item.from).from;
2593
+ var textOf = (state, item) => state.doc.sliceString(startOf(state, item), item.to);
2594
+ function deleteItems(state, all, gone) {
2595
+ const changes = [];
2596
+ for (let i = 0; i < all.length; ) {
2597
+ if (!gone.has(i)) {
2598
+ i++;
2599
+ continue;
2600
+ }
2601
+ let j = i;
2602
+ while (j + 1 < all.length && gone.has(j + 1)) j++;
2603
+ if (j + 1 < all.length) {
2604
+ changes.push({ from: startOf(state, all[i]), to: startOf(state, all[j + 1]) });
2605
+ } else if (i > 0) {
2606
+ changes.push({ from: all[i - 1].to, to: all[j].to });
2607
+ } else {
2608
+ changes.push({ from: startOf(state, all[i]), to: all[j].to });
2609
+ }
2610
+ i = j + 1;
2611
+ }
2612
+ return changes;
2613
+ }
2614
+ function renumber(state, list) {
2615
+ const changes = [];
2616
+ const all = items(list);
2617
+ const firstMark = all[0]?.getChild("ListMark");
2618
+ if (!firstMark) return changes;
2619
+ const start = parseInt(state.doc.sliceString(firstMark.from, firstMark.to), 10) || 0;
2620
+ all.forEach((item, i) => {
2621
+ const mark = item.getChild("ListMark");
2622
+ if (!mark) return;
2623
+ const text = state.doc.sliceString(mark.from, mark.to);
2624
+ const want = `${start + i}${text.slice(-1)}`;
2625
+ if (want === text) return;
2626
+ changes.push({ from: mark.from, to: mark.to, insert: want });
2627
+ const shift = want.length - text.length;
2628
+ const first = state.doc.lineAt(item.from).number;
2629
+ const last = state.doc.lineAt(item.to).number;
2630
+ for (let n = first + 1; n <= last; n++) {
2631
+ const line = state.doc.line(n);
2632
+ if (shift > 0) {
2633
+ if (line.length) changes.push({ from: line.from, insert: " ".repeat(shift) });
2634
+ } else {
2635
+ const spaces = /^ */.exec(line.text)[0].length;
2636
+ const cut = Math.min(spaces, -shift);
2637
+ if (cut) changes.push({ from: line.from, to: line.from + cut });
2638
+ }
2639
+ }
2640
+ });
2641
+ return changes;
2642
+ }
2643
+ function needsRenumber(state, list) {
2644
+ return list.name === "OrderedList" && renumber(state, list).length > 0;
2645
+ }
2646
+ function clearDone(state, list) {
2647
+ const all = items(list);
2648
+ const gone = new Set(all.flatMap((it, i) => done(state, it) ? [i] : []));
2649
+ return deleteItems(state, all, gone);
2650
+ }
2651
+ function moveDoneDown(state, list) {
2652
+ const all = items(list);
2653
+ let lastOpen = -1;
2654
+ all.forEach((it, i) => {
2655
+ if (done(state, it) === false || done(state, it) === null) lastOpen = i;
2656
+ });
2657
+ const moving = /* @__PURE__ */ new Set();
2658
+ for (let i = 0; i < lastOpen; i++) if (done(state, all[i])) moving.add(i);
2659
+ if (!moving.size) return [];
2660
+ const texts = [...moving].map((i) => textOf(state, all[i]));
2661
+ return [
2662
+ ...deleteItems(state, all, moving),
2663
+ { from: all[lastOpen].to, insert: "\n" + texts.join("\n") }
2664
+ ];
2665
+ }
2666
+ var countDone = (state, list) => items(list).filter((it) => done(state, it)).length;
2667
+ var setHideDone = StateEffect2.define();
2668
+ var HiddenDone = class extends WidgetType3 {
2669
+ constructor(count) {
2670
+ super();
2671
+ this.count = count;
2672
+ }
2673
+ eq(other) {
2674
+ return other.count === this.count;
2675
+ }
2676
+ toDOM(view) {
2677
+ const button = document.createElement("button");
2678
+ button.type = "button";
2679
+ button.className = "cm-md-hidden-done";
2680
+ button.textContent = `${this.count} done`;
2681
+ button.title = "Show done tasks";
2682
+ button.addEventListener("mousedown", (event) => {
2683
+ event.preventDefault();
2684
+ view.dispatch({ effects: setHideDone.of(false) });
2685
+ });
2686
+ return button;
2687
+ }
2688
+ ignoreEvent() {
2689
+ return true;
2690
+ }
2691
+ };
2692
+ function hiddenDone(state) {
2693
+ const decos = [];
2694
+ const hide = Decoration4.replace({ block: true });
2695
+ let hiddenTo = -1;
2696
+ syntaxTree4(state).iterate({
2697
+ enter: (node) => {
2698
+ if (node.from < hiddenTo) return false;
2699
+ if (node.name !== "BulletList" && node.name !== "OrderedList") return;
2700
+ const all = items(node.node);
2701
+ const total = all.filter((it) => done(state, it)).length;
2702
+ if (!total) return;
2703
+ const button = new HiddenDone(total);
2704
+ for (let i = 0; i < all.length; ) {
2705
+ if (!done(state, all[i])) {
2706
+ i++;
2707
+ continue;
2708
+ }
2709
+ let j = i;
2710
+ while (j + 1 < all.length && done(state, all[j + 1])) j++;
2711
+ const last = j === all.length - 1;
2712
+ decos.push(
2713
+ (last ? Decoration4.replace({ block: true, widget: button }) : hide).range(
2714
+ startOf(state, all[i]),
2715
+ all[j].to
2716
+ )
2717
+ );
2718
+ hiddenTo = Math.max(hiddenTo, all[j].to);
2719
+ i = j + 1;
2720
+ }
2721
+ const end = all[all.length - 1];
2722
+ if (!done(state, end)) {
2723
+ decos.push(
2724
+ Decoration4.widget({ block: true, side: 1, widget: button }).range(end.to)
2725
+ );
2726
+ }
2727
+ }
2728
+ });
2729
+ return Decoration4.set(decos, true);
2730
+ }
2731
+ var hideDoneField = StateField2.define({
2732
+ create: () => ({ on: false, decos: Decoration4.none }),
2733
+ update(value, tr) {
2734
+ let on = value.on;
2735
+ for (const e of tr.effects) if (e.is(setHideDone)) on = e.value;
2736
+ if (!on) return { on, decos: Decoration4.none };
2737
+ if (on === value.on && !tr.docChanged && syntaxTree4(tr.startState) === syntaxTree4(tr.state))
2738
+ return value;
2739
+ return { on, decos: hiddenDone(tr.state) };
2740
+ },
2741
+ provide: (f) => [
2742
+ EditorView4.decorations.from(f, (v) => v.decos),
2743
+ EditorView4.atomicRanges.from(f, (v) => () => v.decos)
2744
+ ]
2745
+ });
2746
+ var isHidingDone = (state) => state.field(hideDoneField, false)?.on ?? false;
2747
+ function menuItems(view, pos) {
2748
+ const { state } = view;
2749
+ const list = listAt(state, pos);
2750
+ if (!list) return [];
2751
+ const apply = (changes, userEvent) => () => view.dispatch({ changes, userEvent });
2752
+ const out = [];
2753
+ if (needsRenumber(state, list)) {
2754
+ out.push({ label: "Fix numbering", run: apply(renumber(state, list), "input.renumber") });
2755
+ }
2756
+ const hasTasks = items(list).some((it) => done(state, it) !== null);
2757
+ if (hasTasks) {
2758
+ const n = countDone(state, list);
2759
+ if (moveDoneDown(state, list).length)
2760
+ out.push({ label: "Move done to bottom", run: apply(moveDoneDown(state, list), "input.move") });
2761
+ if (n)
2762
+ out.push({ label: `Clear done (${n})`, run: apply(clearDone(state, list), "delete.clear-done") });
2763
+ const hiding = isHidingDone(state);
2764
+ out.push({
2765
+ label: hiding ? "Show done" : "Hide done",
2766
+ run: () => view.dispatch({ effects: setHideDone.of(!hiding) })
2767
+ });
2768
+ }
2769
+ return out;
2770
+ }
2771
+ function openMenu(view, x, y, entries) {
2772
+ const menu = document.createElement("div");
2773
+ menu.className = "cm-md-menu";
2774
+ menu.setAttribute("role", "menu");
2775
+ menu.style.left = `${x}px`;
2776
+ menu.style.top = `${y}px`;
2777
+ const buttons = entries.map(({ label, run }) => {
2778
+ const b = document.createElement("button");
2779
+ b.type = "button";
2780
+ b.className = "cm-md-menu-item";
2781
+ b.setAttribute("role", "menuitem");
2782
+ b.textContent = label;
2783
+ b.addEventListener("click", () => {
2784
+ close();
2785
+ run();
2786
+ view.focus();
2787
+ });
2788
+ return b;
2789
+ });
2790
+ menu.append(...buttons);
2791
+ const outside = (e) => {
2792
+ if (!menu.contains(e.target)) close();
2793
+ };
2794
+ const keys = (e) => {
2795
+ const i = buttons.indexOf(document.activeElement);
2796
+ if (e.key === "Escape") {
2797
+ e.preventDefault();
2798
+ close();
2799
+ view.focus();
2800
+ } else if (e.key === "ArrowDown" || e.key === "ArrowUp") {
2801
+ e.preventDefault();
2802
+ const step2 = e.key === "ArrowDown" ? 1 : -1;
2803
+ buttons[(i + step2 + buttons.length) % buttons.length]?.focus();
2804
+ }
2805
+ };
2806
+ function close() {
2807
+ menu.remove();
2808
+ window.removeEventListener("pointerdown", outside, true);
2809
+ window.removeEventListener("scroll", close, true);
2810
+ window.removeEventListener("blur", close);
2811
+ menu.removeEventListener("keydown", keys);
2812
+ }
2813
+ window.addEventListener("pointerdown", outside, true);
2814
+ window.addEventListener("scroll", close, true);
2815
+ window.addEventListener("blur", close);
2816
+ menu.addEventListener("keydown", keys);
2817
+ view.dom.appendChild(menu);
2818
+ const r = menu.getBoundingClientRect();
2819
+ if (r.right > window.innerWidth) menu.style.left = `${Math.max(0, x - r.width)}px`;
2820
+ if (r.bottom > window.innerHeight) menu.style.top = `${Math.max(0, y - r.height)}px`;
2821
+ buttons[0]?.focus();
2822
+ return close;
2823
+ }
2824
+ var menuPlugin = ViewPlugin4.fromClass(
2825
+ class {
2826
+ constructor(view) {
2827
+ this.view = view;
2828
+ view.contentDOM.addEventListener("contextmenu", this.open);
2829
+ }
2830
+ close = null;
2831
+ open = (event) => {
2832
+ const { view } = this;
2833
+ const target = event.target;
2834
+ const marker = target?.closest(".cm-md-hang, .cm-md-hidden-done");
2835
+ if (!marker || !view.contentDOM.contains(marker)) return;
2836
+ const entries = menuItems(view, view.posAtDOM(marker));
2837
+ if (!entries.length) return;
2838
+ event.preventDefault();
2839
+ this.close?.();
2840
+ this.close = openMenu(view, event.clientX, event.clientY, entries);
2841
+ };
2842
+ destroy() {
2843
+ this.view.contentDOM.removeEventListener("contextmenu", this.open);
2844
+ this.close?.();
2845
+ }
2846
+ }
2847
+ );
2848
+ var listsTheme = EditorView4.baseTheme({
2849
+ ".cm-md-menu": {
2850
+ position: "fixed",
2851
+ zIndex: "var(--studio-layer-popover, 4000)",
2852
+ display: "flex",
2853
+ flexDirection: "column",
2854
+ minWidth: "12rem",
2855
+ padding: "4px",
2856
+ borderRadius: "6px",
2857
+ background: "var(--studio-fill, white)",
2858
+ color: "var(--studio-line, black)",
2859
+ border: "var(--studio-border-width, 1px) var(--studio-border-style, solid) var(--studio-fill-offset-30, #ddd)",
2860
+ boxShadow: "var(--studio-shadow-md, 0 4px 6px -1px rgb(0 0 0 / 0.1))",
2861
+ fontFamily: "var(--studio-family-ui, system-ui, sans-serif)",
2862
+ fontSize: "0.8rem"
2863
+ },
2864
+ ".cm-md-menu-item": {
2865
+ all: "unset",
2866
+ padding: "8px 12px",
2867
+ borderRadius: "4px",
2868
+ cursor: "var(--studio-cursor-default, default)"
2869
+ },
2870
+ ".cm-md-menu-item:hover, .cm-md-menu-item:focus-visible": {
2871
+ background: "var(--studio-fill-offset-20, #eee)"
2872
+ },
2873
+ ".cm-md-hidden-done": {
2874
+ all: "unset",
2875
+ display: "block",
2876
+ paddingInline: "6px 2px",
2877
+ fontSize: "0.8rem",
2878
+ color: "var(--text-editor-line-offset-40, #999)",
2879
+ cursor: "var(--studio-cursor-pointer, pointer)"
2880
+ },
2881
+ ".cm-md-hidden-done:hover": {
2882
+ textDecoration: "underline"
2883
+ }
2884
+ });
2885
+ var lists = [hideDoneField, menuPlugin, listsTheme];
2886
+
2887
+ // src/extensions/prefix.ts
2888
+ import { syntaxTree as syntaxTree5 } from "@codemirror/language";
2889
+ import {
2890
+ EditorSelection as EditorSelection4,
2891
+ EditorState as EditorState2,
2892
+ Prec as Prec3
2893
+ } from "@codemirror/state";
2894
+ import { EditorView as EditorView5, keymap as keymap2 } from "@codemirror/view";
2895
+ var MARKS2 = /* @__PURE__ */ new Set(["QuoteMark", "ListMark", "HeaderMark", "TaskMarker"]);
2896
+ function skipSpaces2(state, pos, end) {
2897
+ while (pos < end && /[ \t]/.test(state.doc.sliceString(pos, pos + 1))) pos++;
2898
+ return pos;
2899
+ }
2900
+ function blockPrefix(state, line) {
2901
+ const tree = syntaxTree5(state);
2902
+ const marks = [];
2903
+ const starts = [];
2904
+ let pos = line.from;
2905
+ for (; ; ) {
2906
+ const at = skipSpaces2(state, pos, line.to);
2907
+ const node = tree.resolveInner(at, 1);
2908
+ if (!MARKS2.has(node.name) || node.from !== at || node.to <= at) break;
2909
+ if (doc(state, pos, at).trim() !== "") break;
2910
+ marks.push(node.name);
2911
+ starts.push(at);
2912
+ pos = node.to;
2913
+ }
2914
+ if (!marks.length) return null;
2915
+ return { from: line.from, to: skipSpaces2(state, pos, line.to), marks, starts };
2916
+ }
2917
+ var doc = (state, from, to) => state.doc.sliceString(from, to);
2918
+ var keepOut = EditorState2.transactionFilter.of((tr) => {
2919
+ if (!tr.selection) return tr;
2920
+ const { state } = tr;
2921
+ const before = tr.startState.selection;
2922
+ let moved = false;
2923
+ const ranges = tr.selection.ranges.map((r, i) => {
2924
+ if (!r.empty) return r;
2925
+ const line = state.doc.lineAt(r.head);
2926
+ const prefix = blockPrefix(state, line);
2927
+ if (!prefix || r.head >= prefix.to) return r;
2928
+ const old = before.ranges[i] ?? before.main;
2929
+ const leaving = !tr.docChanged && old.empty && old.head === prefix.to && line.from > 0;
2930
+ moved = true;
2931
+ return EditorSelection4.cursor(leaving ? line.from - 1 : prefix.to);
2932
+ });
2933
+ if (!moved) return tr;
2934
+ return [
2935
+ tr,
2936
+ {
2937
+ selection: EditorSelection4.create(ranges, tr.selection.mainIndex),
2938
+ sequential: true
2939
+ }
2940
+ ];
2941
+ });
2942
+ function atTextStart(state) {
2943
+ const sel = state.selection;
2944
+ if (sel.ranges.length !== 1 || !sel.main.empty) return null;
2945
+ const prefix = blockPrefix(state, state.doc.lineAt(sel.main.head));
2946
+ return prefix && sel.main.head === prefix.to ? prefix : null;
2947
+ }
2948
+ var backspace = ({ state, dispatch }) => {
2949
+ const prefix = atTextStart(state);
2950
+ if (!prefix) return false;
2951
+ dispatch(
2952
+ state.update({
2953
+ changes: { from: prefix.starts.at(-1), to: prefix.to },
2954
+ selection: EditorSelection4.cursor(prefix.starts.at(-1)),
2955
+ userEvent: "delete.backward"
2956
+ })
2957
+ );
2958
+ return true;
2959
+ };
2610
2960
  var deleteForward = ({ state, dispatch }) => {
2611
2961
  const sel = state.selection;
2612
2962
  if (sel.ranges.length !== 1 || !sel.main.empty) return false;
2613
2963
  const line = state.doc.lineAt(sel.main.head);
2614
2964
  if (sel.main.head !== line.to || line.number >= state.doc.lines) return false;
2615
- const prefix = headingPrefix(state, state.doc.line(line.number + 1));
2965
+ const prefix = blockPrefix(state, state.doc.line(line.number + 1));
2616
2966
  if (!prefix) return false;
2617
2967
  dispatch(
2618
2968
  state.update({
@@ -2622,28 +2972,93 @@ var deleteForward = ({ state, dispatch }) => {
2622
2972
  );
2623
2973
  return true;
2624
2974
  };
2625
- var headings = [
2626
- keepOutOfPrefix,
2627
- // above lang-markdown's own (Prec.high) Enter and Backspace.
2628
- Prec2.highest(
2629
- keymap.of([
2975
+ var inCode = (state, pos) => {
2976
+ for (let n = syntaxTree5(state).resolveInner(pos, -1); n; n = n.parent) {
2977
+ if (/^(FencedCode|CodeBlock|CodeText|InlineCode)$/.test(n.name)) return true;
2978
+ }
2979
+ return false;
2980
+ };
2981
+ var LIST_OR_HEADING = /^(\d{1,9}[.)]|[-*+]|#{1,6})$/;
2982
+ var HEADING = /^#{1,6}$/;
2983
+ var replaceMarker = EditorView5.inputHandler.of((view, from, to, text) => {
2984
+ if (text !== " " || from !== to) return false;
2985
+ const { state } = view;
2986
+ const line = state.doc.lineAt(from);
2987
+ const prefix = blockPrefix(state, line);
2988
+ if (!prefix || inCode(state, from)) return false;
2989
+ const { marks, starts } = prefix;
2990
+ let typed = doc(state, prefix.to, from);
2991
+ let mine = marks.length - 1;
2992
+ if (!typed && prefix.to === from && marks.length > 1) {
2993
+ typed = doc(state, starts[mine], from);
2994
+ mine--;
2995
+ }
2996
+ if (!typed || marks[mine] === "QuoteMark") return false;
2997
+ if (marks[mine] === "TaskMarker" && marks[mine - 1] === "ListMark") mine--;
2998
+ const ok = marks[mine] === "HeaderMark" ? HEADING : LIST_OR_HEADING;
2999
+ if (!ok.test(typed)) return false;
3000
+ view.dispatch({
3001
+ changes: { from: starts[mine], to: from, insert: typed + " " },
3002
+ selection: EditorSelection4.cursor(starts[mine] + typed.length + 1),
3003
+ userEvent: "input.type"
3004
+ });
3005
+ return true;
3006
+ });
3007
+ var prefixes = [
3008
+ keepOut,
3009
+ replaceMarker,
3010
+ // above lang-markdown's own (Prec.high) Backspace.
3011
+ Prec3.highest(
3012
+ keymap2.of([
2630
3013
  { key: "Backspace", run: backspace },
2631
- { key: "Enter", run: enter },
2632
- { key: "Delete", run: deleteForward },
2633
- { key: "Mod-Shift-h", run: setHeading(2), preventDefault: true },
2634
- { key: "Mod-Shift-j", run: setHeading(3), preventDefault: true },
2635
- { key: "Mod-Shift-b", run: setHeading(0), preventDefault: true },
2636
- { key: "Mod-Alt-1", run: setHeading(1), preventDefault: true },
2637
- { key: "Mod-Alt-2", run: setHeading(2), preventDefault: true },
2638
- { key: "Mod-Alt-3", run: setHeading(3), preventDefault: true },
2639
- { key: "Mod-Alt-0", run: setHeading(0), preventDefault: true }
3014
+ { key: "Delete", run: deleteForward }
2640
3015
  ])
2641
3016
  )
2642
3017
  ];
2643
3018
 
3019
+ // src/extensions/vertical.ts
3020
+ import { EditorSelection as EditorSelection5, Prec as Prec4 } from "@codemirror/state";
3021
+ import { cursorLineDown, cursorLineUp } from "@codemirror/commands";
3022
+ import { keymap as keymap3 } from "@codemirror/view";
3023
+ var streaks = /* @__PURE__ */ new WeakMap();
3024
+ var vertical = (move) => (view) => {
3025
+ const { state } = view;
3026
+ const sel = state.selection;
3027
+ if (sel.ranges.length !== 1 || !sel.main.empty) return move(view);
3028
+ let streak = streaks.get(view);
3029
+ if (!streak || streak.head !== sel.main.head || streak.doc !== state.doc) {
3030
+ streak = { visited: /* @__PURE__ */ new Map(), head: sel.main.head, doc: state.doc };
3031
+ }
3032
+ const from = state.doc.lineAt(sel.main.head).number;
3033
+ if (!streak.visited.has(from)) streak.visited.set(from, sel.main.head);
3034
+ if (!move(view)) return false;
3035
+ const now = view.state.selection.main;
3036
+ const to = view.state.doc.lineAt(now.head).number;
3037
+ const back = streak.visited.get(to);
3038
+ if (back !== void 0 && back !== now.head && view.state.doc === streak.doc) {
3039
+ view.dispatch({
3040
+ selection: EditorSelection5.cursor(back),
3041
+ scrollIntoView: true,
3042
+ userEvent: "select"
3043
+ });
3044
+ }
3045
+ streak.head = view.state.selection.main.head;
3046
+ streak.doc = view.state.doc;
3047
+ streaks.set(view, streak);
3048
+ return true;
3049
+ };
3050
+ var verticalMemory = Prec4.high(
3051
+ keymap3.of([
3052
+ { key: "ArrowUp", run: vertical(cursorLineUp) },
3053
+ { key: "ArrowDown", run: vertical(cursorLineDown) },
3054
+ { mac: "Ctrl-p", run: vertical(cursorLineUp) },
3055
+ { mac: "Ctrl-n", run: vertical(cursorLineDown) }
3056
+ ])
3057
+ );
3058
+
2644
3059
  // src/extensions/zoom.ts
2645
- import { Prec as Prec3 } from "@codemirror/state";
2646
- import { keymap as keymap2 } from "@codemirror/view";
3060
+ import { Prec as Prec5 } from "@codemirror/state";
3061
+ import { keymap as keymap4 } from "@codemirror/view";
2647
3062
  var MIN_FONT_SIZE = 6;
2648
3063
  var FALLBACK_FONT_SIZE = 16;
2649
3064
  var px = (value) => parseFloat(value) || FALLBACK_FONT_SIZE;
@@ -2672,8 +3087,8 @@ function step(view, by) {
2672
3087
  var zoomIn = (view) => step(view, 1);
2673
3088
  var zoomOut = (view) => step(view, -1);
2674
3089
  var zoomReset = (view) => step(view, 0);
2675
- var markdownZoom = Prec3.high(
2676
- keymap2.of([
3090
+ var markdownZoom = Prec5.high(
3091
+ keymap4.of([
2677
3092
  // `+` and `_` are the shifted keys, for keyboards that need Shift.
2678
3093
  { key: "Mod-=", run: zoomIn, preventDefault: true },
2679
3094
  { key: "Mod-+", run: zoomIn, preventDefault: true },
@@ -2685,7 +3100,7 @@ var markdownZoom = Prec3.high(
2685
3100
 
2686
3101
  // src/themes/markdown.ts
2687
3102
  import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
2688
- import { EditorView as EditorView5 } from "@codemirror/view";
3103
+ import { EditorView as EditorView8 } from "@codemirror/view";
2689
3104
  var bodyFont = (style) => style == "serif" ? 'var(--text-editor-family-serif, "Merriweather", serif)' : "var(--text-editor-family-sans)";
2690
3105
  var zoomed2 = (length) => `calc(${length} * var(--markdown-zoom, 1))`;
2691
3106
  var markdownStyles = (style) => ({
@@ -2900,7 +3315,7 @@ var markdownSyntaxHighlighting = (style) => HighlightStyle.define([
2900
3315
  { tag: tags.monospace, ...baseCodeStyles }
2901
3316
  ]);
2902
3317
  var theme = (style) => [
2903
- EditorView5.theme(markdownStyles(style)),
3318
+ EditorView8.theme(markdownStyles(style)),
2904
3319
  syntaxHighlighting(markdownSyntaxHighlighting(style))
2905
3320
  ];
2906
3321
 
@@ -2910,7 +3325,7 @@ function markdownExtensions() {
2910
3325
  ...theme("sans"),
2911
3326
  indentOnInput(),
2912
3327
  markdownZoom,
2913
- keymap3.of([
3328
+ keymap5.of([
2914
3329
  ...markdownFormattingKeymap,
2915
3330
  ...defaultKeymap,
2916
3331
  ...searchKeymap,
@@ -2918,7 +3333,7 @@ function markdownExtensions() {
2918
3333
  ...completionKeymap,
2919
3334
  indentWithTab
2920
3335
  ]),
2921
- EditorView6.lineWrapping,
3336
+ EditorView9.lineWrapping,
2922
3337
  // GFM base: task lists, tables, strikethrough, autolinks.
2923
3338
  markdown({
2924
3339
  base: markdownLanguage,
@@ -2927,7 +3342,10 @@ function markdownExtensions() {
2927
3342
  }),
2928
3343
  taskCheckboxes,
2929
3344
  margin,
3345
+ prefixes,
3346
+ verticalMemory,
2930
3347
  headings,
3348
+ lists,
2931
3349
  indentUnit.of(" ")
2932
3350
  ];
2933
3351
  }