@inkandswitch/patchwork-pkg-base 0.0.125 → 0.0.126

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.
@@ -1 +1 @@
1
- automerge:Wh4456nTsRj7WssD8ChKeeRFqa9
1
+ automerge:3LxJsuExcL8S2E8j8758gWkK1Hdp
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inkandswitch/patchwork-pkg-base",
3
- "version": "0.0.125",
3
+ "version": "0.0.126",
4
4
  "type": "module",
5
5
  "description": "Base packages for the Patchwork system",
6
6
  "repository": {
@@ -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 EditorView7, keymap as keymap3 } from "@codemirror/view";
2070
2070
 
2071
2071
  // src/extensions/formatting.ts
2072
2072
  import { EditorSelection as EditorSelection2 } from "@codemirror/state";
@@ -2641,6 +2641,325 @@ var headings = [
2641
2641
  )
2642
2642
  ];
2643
2643
 
2644
+ // src/extensions/lists.ts
2645
+ import { syntaxTree as syntaxTree4 } from "@codemirror/language";
2646
+ import {
2647
+ StateEffect as StateEffect2,
2648
+ StateField as StateField2
2649
+ } from "@codemirror/state";
2650
+ import {
2651
+ Decoration as Decoration4,
2652
+ EditorView as EditorView4,
2653
+ ViewPlugin as ViewPlugin4,
2654
+ WidgetType as WidgetType3
2655
+ } from "@codemirror/view";
2656
+ function listAt(state, pos) {
2657
+ for (let n = syntaxTree4(state).resolveInner(pos, 1); n; n = n.parent) {
2658
+ if (n.name === "BulletList" || n.name === "OrderedList") return n;
2659
+ }
2660
+ return null;
2661
+ }
2662
+ var items = (list) => list.getChildren("ListItem");
2663
+ function done(state, item) {
2664
+ const marker = item.getChild("Task")?.getChild("TaskMarker");
2665
+ if (!marker) return null;
2666
+ return state.doc.sliceString(marker.from + 1, marker.to - 1).trim() !== "";
2667
+ }
2668
+ var startOf = (state, item) => state.doc.lineAt(item.from).from;
2669
+ var textOf = (state, item) => state.doc.sliceString(startOf(state, item), item.to);
2670
+ function deleteItems(state, all, gone) {
2671
+ const changes = [];
2672
+ for (let i = 0; i < all.length; ) {
2673
+ if (!gone.has(i)) {
2674
+ i++;
2675
+ continue;
2676
+ }
2677
+ let j = i;
2678
+ while (j + 1 < all.length && gone.has(j + 1)) j++;
2679
+ if (j + 1 < all.length) {
2680
+ changes.push({ from: startOf(state, all[i]), to: startOf(state, all[j + 1]) });
2681
+ } else if (i > 0) {
2682
+ changes.push({ from: all[i - 1].to, to: all[j].to });
2683
+ } else {
2684
+ changes.push({ from: startOf(state, all[i]), to: all[j].to });
2685
+ }
2686
+ i = j + 1;
2687
+ }
2688
+ return changes;
2689
+ }
2690
+ function renumber(state, list) {
2691
+ const changes = [];
2692
+ const all = items(list);
2693
+ const firstMark = all[0]?.getChild("ListMark");
2694
+ if (!firstMark) return changes;
2695
+ const start = parseInt(state.doc.sliceString(firstMark.from, firstMark.to), 10) || 0;
2696
+ all.forEach((item, i) => {
2697
+ const mark = item.getChild("ListMark");
2698
+ if (!mark) return;
2699
+ const text = state.doc.sliceString(mark.from, mark.to);
2700
+ const want = `${start + i}${text.slice(-1)}`;
2701
+ if (want === text) return;
2702
+ changes.push({ from: mark.from, to: mark.to, insert: want });
2703
+ const shift = want.length - text.length;
2704
+ const first = state.doc.lineAt(item.from).number;
2705
+ const last = state.doc.lineAt(item.to).number;
2706
+ for (let n = first + 1; n <= last; n++) {
2707
+ const line = state.doc.line(n);
2708
+ if (shift > 0) {
2709
+ if (line.length) changes.push({ from: line.from, insert: " ".repeat(shift) });
2710
+ } else {
2711
+ const spaces = /^ */.exec(line.text)[0].length;
2712
+ const cut = Math.min(spaces, -shift);
2713
+ if (cut) changes.push({ from: line.from, to: line.from + cut });
2714
+ }
2715
+ }
2716
+ });
2717
+ return changes;
2718
+ }
2719
+ function needsRenumber(state, list) {
2720
+ return list.name === "OrderedList" && renumber(state, list).length > 0;
2721
+ }
2722
+ function clearDone(state, list) {
2723
+ const all = items(list);
2724
+ const gone = new Set(all.flatMap((it, i) => done(state, it) ? [i] : []));
2725
+ return deleteItems(state, all, gone);
2726
+ }
2727
+ function moveDoneDown(state, list) {
2728
+ const all = items(list);
2729
+ let lastOpen = -1;
2730
+ all.forEach((it, i) => {
2731
+ if (done(state, it) === false || done(state, it) === null) lastOpen = i;
2732
+ });
2733
+ const moving = /* @__PURE__ */ new Set();
2734
+ for (let i = 0; i < lastOpen; i++) if (done(state, all[i])) moving.add(i);
2735
+ if (!moving.size) return [];
2736
+ const texts = [...moving].map((i) => textOf(state, all[i]));
2737
+ return [
2738
+ ...deleteItems(state, all, moving),
2739
+ { from: all[lastOpen].to, insert: "\n" + texts.join("\n") }
2740
+ ];
2741
+ }
2742
+ var countDone = (state, list) => items(list).filter((it) => done(state, it)).length;
2743
+ var setHideDone = StateEffect2.define();
2744
+ var HiddenDone = class extends WidgetType3 {
2745
+ constructor(count) {
2746
+ super();
2747
+ this.count = count;
2748
+ }
2749
+ eq(other) {
2750
+ return other.count === this.count;
2751
+ }
2752
+ toDOM(view) {
2753
+ const button = document.createElement("button");
2754
+ button.type = "button";
2755
+ button.className = "cm-md-hidden-done";
2756
+ button.textContent = `${this.count} done`;
2757
+ button.title = "Show done tasks";
2758
+ button.addEventListener("mousedown", (event) => {
2759
+ event.preventDefault();
2760
+ view.dispatch({ effects: setHideDone.of(false) });
2761
+ });
2762
+ return button;
2763
+ }
2764
+ ignoreEvent() {
2765
+ return true;
2766
+ }
2767
+ };
2768
+ function hiddenDone(state) {
2769
+ const decos = [];
2770
+ const hide = Decoration4.replace({ block: true });
2771
+ let hiddenTo = -1;
2772
+ syntaxTree4(state).iterate({
2773
+ enter: (node) => {
2774
+ if (node.from < hiddenTo) return false;
2775
+ if (node.name !== "BulletList" && node.name !== "OrderedList") return;
2776
+ const all = items(node.node);
2777
+ const total = all.filter((it) => done(state, it)).length;
2778
+ if (!total) return;
2779
+ const button = new HiddenDone(total);
2780
+ for (let i = 0; i < all.length; ) {
2781
+ if (!done(state, all[i])) {
2782
+ i++;
2783
+ continue;
2784
+ }
2785
+ let j = i;
2786
+ while (j + 1 < all.length && done(state, all[j + 1])) j++;
2787
+ const last = j === all.length - 1;
2788
+ decos.push(
2789
+ (last ? Decoration4.replace({ block: true, widget: button }) : hide).range(
2790
+ startOf(state, all[i]),
2791
+ all[j].to
2792
+ )
2793
+ );
2794
+ hiddenTo = Math.max(hiddenTo, all[j].to);
2795
+ i = j + 1;
2796
+ }
2797
+ const end = all[all.length - 1];
2798
+ if (!done(state, end)) {
2799
+ decos.push(
2800
+ Decoration4.widget({ block: true, side: 1, widget: button }).range(end.to)
2801
+ );
2802
+ }
2803
+ }
2804
+ });
2805
+ return Decoration4.set(decos, true);
2806
+ }
2807
+ var hideDoneField = StateField2.define({
2808
+ create: () => ({ on: false, decos: Decoration4.none }),
2809
+ update(value, tr) {
2810
+ let on = value.on;
2811
+ for (const e of tr.effects) if (e.is(setHideDone)) on = e.value;
2812
+ if (!on) return { on, decos: Decoration4.none };
2813
+ if (on === value.on && !tr.docChanged && syntaxTree4(tr.startState) === syntaxTree4(tr.state))
2814
+ return value;
2815
+ return { on, decos: hiddenDone(tr.state) };
2816
+ },
2817
+ provide: (f) => [
2818
+ EditorView4.decorations.from(f, (v) => v.decos),
2819
+ EditorView4.atomicRanges.from(f, (v) => () => v.decos)
2820
+ ]
2821
+ });
2822
+ var isHidingDone = (state) => state.field(hideDoneField, false)?.on ?? false;
2823
+ function menuItems(view, pos) {
2824
+ const { state } = view;
2825
+ const list = listAt(state, pos);
2826
+ if (!list) return [];
2827
+ const apply = (changes, userEvent) => () => view.dispatch({ changes, userEvent });
2828
+ const out = [];
2829
+ if (needsRenumber(state, list)) {
2830
+ out.push({ label: "Fix numbering", run: apply(renumber(state, list), "input.renumber") });
2831
+ }
2832
+ const hasTasks = items(list).some((it) => done(state, it) !== null);
2833
+ if (hasTasks) {
2834
+ const n = countDone(state, list);
2835
+ if (moveDoneDown(state, list).length)
2836
+ out.push({ label: "Move done to bottom", run: apply(moveDoneDown(state, list), "input.move") });
2837
+ if (n)
2838
+ out.push({ label: `Clear done (${n})`, run: apply(clearDone(state, list), "delete.clear-done") });
2839
+ const hiding = isHidingDone(state);
2840
+ out.push({
2841
+ label: hiding ? "Show done" : "Hide done",
2842
+ run: () => view.dispatch({ effects: setHideDone.of(!hiding) })
2843
+ });
2844
+ }
2845
+ return out;
2846
+ }
2847
+ function openMenu(view, x, y, entries) {
2848
+ const menu = document.createElement("div");
2849
+ menu.className = "cm-md-menu";
2850
+ menu.setAttribute("role", "menu");
2851
+ menu.style.left = `${x}px`;
2852
+ menu.style.top = `${y}px`;
2853
+ const buttons = entries.map(({ label, run }) => {
2854
+ const b = document.createElement("button");
2855
+ b.type = "button";
2856
+ b.className = "cm-md-menu-item";
2857
+ b.setAttribute("role", "menuitem");
2858
+ b.textContent = label;
2859
+ b.addEventListener("click", () => {
2860
+ close();
2861
+ run();
2862
+ view.focus();
2863
+ });
2864
+ return b;
2865
+ });
2866
+ menu.append(...buttons);
2867
+ const outside = (e) => {
2868
+ if (!menu.contains(e.target)) close();
2869
+ };
2870
+ const keys = (e) => {
2871
+ const i = buttons.indexOf(document.activeElement);
2872
+ if (e.key === "Escape") {
2873
+ e.preventDefault();
2874
+ close();
2875
+ view.focus();
2876
+ } else if (e.key === "ArrowDown" || e.key === "ArrowUp") {
2877
+ e.preventDefault();
2878
+ const step2 = e.key === "ArrowDown" ? 1 : -1;
2879
+ buttons[(i + step2 + buttons.length) % buttons.length]?.focus();
2880
+ }
2881
+ };
2882
+ function close() {
2883
+ menu.remove();
2884
+ window.removeEventListener("pointerdown", outside, true);
2885
+ window.removeEventListener("scroll", close, true);
2886
+ window.removeEventListener("blur", close);
2887
+ menu.removeEventListener("keydown", keys);
2888
+ }
2889
+ window.addEventListener("pointerdown", outside, true);
2890
+ window.addEventListener("scroll", close, true);
2891
+ window.addEventListener("blur", close);
2892
+ menu.addEventListener("keydown", keys);
2893
+ view.dom.appendChild(menu);
2894
+ const r = menu.getBoundingClientRect();
2895
+ if (r.right > window.innerWidth) menu.style.left = `${Math.max(0, x - r.width)}px`;
2896
+ if (r.bottom > window.innerHeight) menu.style.top = `${Math.max(0, y - r.height)}px`;
2897
+ buttons[0]?.focus();
2898
+ return close;
2899
+ }
2900
+ var menuPlugin = ViewPlugin4.fromClass(
2901
+ class {
2902
+ constructor(view) {
2903
+ this.view = view;
2904
+ view.contentDOM.addEventListener("contextmenu", this.open);
2905
+ }
2906
+ close = null;
2907
+ open = (event) => {
2908
+ const { view } = this;
2909
+ const target = event.target;
2910
+ const marker = target?.closest(".cm-md-hang, .cm-md-hidden-done");
2911
+ if (!marker || !view.contentDOM.contains(marker)) return;
2912
+ const entries = menuItems(view, view.posAtDOM(marker));
2913
+ if (!entries.length) return;
2914
+ event.preventDefault();
2915
+ this.close?.();
2916
+ this.close = openMenu(view, event.clientX, event.clientY, entries);
2917
+ };
2918
+ destroy() {
2919
+ this.view.contentDOM.removeEventListener("contextmenu", this.open);
2920
+ this.close?.();
2921
+ }
2922
+ }
2923
+ );
2924
+ var listsTheme = EditorView4.baseTheme({
2925
+ ".cm-md-menu": {
2926
+ position: "fixed",
2927
+ zIndex: "var(--studio-layer-popover, 4000)",
2928
+ display: "flex",
2929
+ flexDirection: "column",
2930
+ minWidth: "12rem",
2931
+ padding: "4px",
2932
+ borderRadius: "6px",
2933
+ background: "var(--studio-fill, white)",
2934
+ color: "var(--studio-line, black)",
2935
+ border: "var(--studio-border-width, 1px) var(--studio-border-style, solid) var(--studio-fill-offset-30, #ddd)",
2936
+ boxShadow: "var(--studio-shadow-md, 0 4px 6px -1px rgb(0 0 0 / 0.1))",
2937
+ fontFamily: "var(--studio-family-ui, system-ui, sans-serif)",
2938
+ fontSize: "0.8rem"
2939
+ },
2940
+ ".cm-md-menu-item": {
2941
+ all: "unset",
2942
+ padding: "8px 12px",
2943
+ borderRadius: "4px",
2944
+ cursor: "var(--studio-cursor-default, default)"
2945
+ },
2946
+ ".cm-md-menu-item:hover, .cm-md-menu-item:focus-visible": {
2947
+ background: "var(--studio-fill-offset-20, #eee)"
2948
+ },
2949
+ ".cm-md-hidden-done": {
2950
+ all: "unset",
2951
+ display: "block",
2952
+ paddingInline: "6px 2px",
2953
+ fontSize: "0.8rem",
2954
+ color: "var(--text-editor-line-offset-40, #999)",
2955
+ cursor: "var(--studio-cursor-pointer, pointer)"
2956
+ },
2957
+ ".cm-md-hidden-done:hover": {
2958
+ textDecoration: "underline"
2959
+ }
2960
+ });
2961
+ var lists = [hideDoneField, menuPlugin, listsTheme];
2962
+
2644
2963
  // src/extensions/zoom.ts
2645
2964
  import { Prec as Prec3 } from "@codemirror/state";
2646
2965
  import { keymap as keymap2 } from "@codemirror/view";
@@ -2685,7 +3004,7 @@ var markdownZoom = Prec3.high(
2685
3004
 
2686
3005
  // src/themes/markdown.ts
2687
3006
  import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
2688
- import { EditorView as EditorView5 } from "@codemirror/view";
3007
+ import { EditorView as EditorView6 } from "@codemirror/view";
2689
3008
  var bodyFont = (style) => style == "serif" ? 'var(--text-editor-family-serif, "Merriweather", serif)' : "var(--text-editor-family-sans)";
2690
3009
  var zoomed2 = (length) => `calc(${length} * var(--markdown-zoom, 1))`;
2691
3010
  var markdownStyles = (style) => ({
@@ -2900,7 +3219,7 @@ var markdownSyntaxHighlighting = (style) => HighlightStyle.define([
2900
3219
  { tag: tags.monospace, ...baseCodeStyles }
2901
3220
  ]);
2902
3221
  var theme = (style) => [
2903
- EditorView5.theme(markdownStyles(style)),
3222
+ EditorView6.theme(markdownStyles(style)),
2904
3223
  syntaxHighlighting(markdownSyntaxHighlighting(style))
2905
3224
  ];
2906
3225
 
@@ -2918,7 +3237,7 @@ function markdownExtensions() {
2918
3237
  ...completionKeymap,
2919
3238
  indentWithTab
2920
3239
  ]),
2921
- EditorView6.lineWrapping,
3240
+ EditorView7.lineWrapping,
2922
3241
  // GFM base: task lists, tables, strikethrough, autolinks.
2923
3242
  markdown({
2924
3243
  base: markdownLanguage,
@@ -2928,6 +3247,7 @@ function markdownExtensions() {
2928
3247
  taskCheckboxes,
2929
3248
  margin,
2930
3249
  headings,
3250
+ lists,
2931
3251
  indentUnit.of(" ")
2932
3252
  ];
2933
3253
  }