@prosekit/extensions 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/_tsup-dts-rollup.d.ts +87 -6
  2. package/dist/{chunk-DZAKXWWF.js → chunk-2JYT2MT7.js} +21 -41
  3. package/dist/{chunk-LVMTQOWG.js → chunk-PYT3MOTF.js} +14 -31
  4. package/dist/chunk-ZPEMHYTU.js +140 -0
  5. package/dist/list/style.css +4 -8
  6. package/dist/placeholder/style.css +1 -1
  7. package/dist/prosekit-extensions-autocomplete.js +33 -62
  8. package/dist/prosekit-extensions-blockquote.js +2 -2
  9. package/dist/prosekit-extensions-bold.js +3 -7
  10. package/dist/prosekit-extensions-code-block.js +61 -81
  11. package/dist/prosekit-extensions-code.js +1 -1
  12. package/dist/prosekit-extensions-enter-rule.js +1 -1
  13. package/dist/prosekit-extensions-heading.js +6 -13
  14. package/dist/prosekit-extensions-image.js +5 -14
  15. package/dist/prosekit-extensions-input-rule.js +1 -1
  16. package/dist/prosekit-extensions-italic.js +1 -1
  17. package/dist/prosekit-extensions-link.js +16 -31
  18. package/dist/prosekit-extensions-list.js +2 -4
  19. package/dist/prosekit-extensions-mark-rule.js +1 -1
  20. package/dist/prosekit-extensions-mention.js +4 -6
  21. package/dist/prosekit-extensions-placeholder.js +9 -20
  22. package/dist/prosekit-extensions-readonly.js +1 -1
  23. package/dist/prosekit-extensions-search.d.ts +3 -0
  24. package/dist/prosekit-extensions-search.js +49 -0
  25. package/dist/prosekit-extensions-strike.js +1 -1
  26. package/dist/prosekit-extensions-table.js +30 -68
  27. package/dist/prosekit-extensions-text-align.js +2 -4
  28. package/dist/prosekit-extensions-virtual-selection.js +6 -12
  29. package/dist/search/style.css +13 -0
  30. package/dist/shiki-import-UFUFVKJ2.js +5 -0
  31. package/package.json +19 -7
  32. package/dist/chunk-ZOBSD7ZH.js +0 -189
  33. package/dist/shiki-import-25BJYIO2.js +0 -5
@@ -9,7 +9,8 @@ import { Command } from '@prosekit/pm/state';
9
9
  import { DedentListOptions } from 'prosemirror-flat-list';
10
10
  import { EditorState } from '@prosekit/pm/state';
11
11
  import { Extension } from '@prosekit/core';
12
- import { getHighlighter } from 'shiki/bundle/full';
12
+ import { getSingletonHighlighter } from 'shiki/bundle/full';
13
+ import type { Highlighter } from 'shiki';
13
14
  import { IndentListOptions } from 'prosemirror-flat-list';
14
15
  import { InputRule } from '@prosekit/pm/inputrules';
15
16
  import { ListAttributes } from 'prosemirror-flat-list';
@@ -88,13 +89,14 @@ export { CodeBlockAttrs as CodeBlockAttrs_alias_1 }
88
89
  */
89
90
  export declare interface CodeBlockShikiOptions {
90
91
  /**
91
- * Theme registation
92
+ * A list of shiki themes to pre-load. The first theme in the list will be
93
+ * used to render the code block.
92
94
  *
93
95
  * @default ['one-dark-pro']
94
96
  */
95
97
  themes?: BundledTheme[];
96
98
  /**
97
- * Language registation
99
+ * A list of shiki languages to pre-load.
98
100
  *
99
101
  * @default ['text']
100
102
  */
@@ -583,6 +585,33 @@ export declare function definePlaceholder(options: PlaceholderOptions): Extensio
583
585
  */
584
586
  export declare function defineReadonly(): Extension<any>;
585
587
 
588
+ /**
589
+ * Defines commands for search and replace.
590
+ *
591
+ * @public
592
+ */
593
+ export declare function defineSearchCommands(): Extension< {
594
+ Commands: {
595
+ findNext: [];
596
+ findPrev: [];
597
+ findNextNoWrap: [];
598
+ findPrevNoWrap: [];
599
+ replaceNext: [];
600
+ replaceNextNoWrap: [];
601
+ replaceCurrent: [];
602
+ replaceAll: [];
603
+ };
604
+ Nodes: never;
605
+ Marks: never;
606
+ }>;
607
+
608
+ /**
609
+ * Defines an extension that stores a current search query and replace string.
610
+ *
611
+ * @public
612
+ */
613
+ export declare function defineSearchQuery(options: SearchQueryOptions): Extension<any>;
614
+
586
615
  /**
587
616
  * @public
588
617
  */
@@ -892,22 +921,30 @@ export declare const exitTable: Command;
892
921
 
893
922
  export declare function getCheckRanges(transactions: readonly Transaction[], oldState: EditorState, newState: EditorState): Array<[number, number]>;
894
923
 
895
- export { getHighlighter }
896
-
897
924
  export declare function getPluginState(state: EditorState): PredictionPluginState | undefined;
898
925
 
926
+ export { getSingletonHighlighter }
927
+
899
928
  export declare function getTrMeta(tr: Transaction): PredictionPluginState;
900
929
 
901
930
  export declare interface HeadingAttrs {
902
931
  level: number;
903
932
  }
904
933
 
905
- declare type HighlighterOptions = {
934
+ export declare type HighlighterOptions = {
906
935
  themes: BundledTheme[];
907
936
  langs: (BundledLanguage | SpecialLanguage)[];
908
937
  langAlias?: Record<string, BundledLanguage>;
909
938
  };
910
939
 
940
+ declare type HighlighterResult = {
941
+ highlighter: Highlighter;
942
+ promise?: undefined;
943
+ } | {
944
+ highlighter?: undefined;
945
+ promise: Promise<void>;
946
+ };
947
+
911
948
  /**
912
949
  * @public
913
950
  *
@@ -1041,6 +1078,50 @@ export declare interface PredictionPluginState {
1041
1078
  } | null;
1042
1079
  }
1043
1080
 
1081
+ export declare function prepareHighlighter(options: HighlighterOptions): HighlighterResult;
1082
+
1083
+ /**
1084
+ * Options for {@link defineSearchQuery}
1085
+ *
1086
+ * @public
1087
+ */
1088
+ export declare interface SearchQueryOptions {
1089
+ /**
1090
+ * The search string (or regular expression).
1091
+ */
1092
+ search: string;
1093
+ /**
1094
+ * The replace text.
1095
+ */
1096
+ replace?: string;
1097
+ /**
1098
+ * Indicates whether the search is case-sensitive
1099
+ *
1100
+ * @default false
1101
+ */
1102
+ caseSensitive?: boolean;
1103
+ /**
1104
+ * By default, string search will replace `\n`, `\r`, and `\t` in the query
1105
+ * with newline, return, and tab characters. When this is set to true, that
1106
+ * behavior is disabled.
1107
+ *
1108
+ * @default false
1109
+ */
1110
+ literal?: boolean;
1111
+ /**
1112
+ * When true, the search string is interpreted as a regular expression.
1113
+ *
1114
+ * @default false
1115
+ */
1116
+ regexp?: boolean;
1117
+ /**
1118
+ * Enable whole-word matching.
1119
+ *
1120
+ * @default false
1121
+ */
1122
+ wholeWord?: boolean;
1123
+ }
1124
+
1044
1125
  /**
1045
1126
  * @internal
1046
1127
  */
@@ -16,26 +16,24 @@ import {
16
16
  function defineEnterRule({
17
17
  regex,
18
18
  handler,
19
- stop = false
19
+ stop = !1
20
20
  }) {
21
- const rule = new EnterRule(regex, handler, stop);
21
+ let rule = new EnterRule(regex, handler, stop);
22
22
  return defineFacetPayload(enterRule, [rule]);
23
23
  }
24
24
  function defineTextBlockEnterRule({
25
25
  regex,
26
26
  type,
27
27
  attrs,
28
- stop = true
28
+ stop = !0
29
29
  }) {
30
30
  return defineEnterRule({
31
31
  regex,
32
32
  handler: ({ state, from, to, match }) => {
33
- const nodeType = getNodeType(state.schema, type);
34
- const $start = state.doc.resolve(from);
35
- if (!$start.node(-1).canReplaceWith($start.index(-1), $start.indexAfter(-1), nodeType)) {
33
+ let nodeType = getNodeType(state.schema, type), $start = state.doc.resolve(from);
34
+ if (!$start.node(-1).canReplaceWith($start.index(-1), $start.indexAfter(-1), nodeType))
36
35
  return null;
37
- }
38
- const nodeAttrs = maybeRun(attrs, match);
36
+ let nodeAttrs = maybeRun(attrs, match);
39
37
  return state.tr.delete(from, to).setBlockType(from, from, nodeType, nodeAttrs);
40
38
  },
41
39
  stop
@@ -47,60 +45,42 @@ var EnterRule = class {
47
45
  this.handler = handler;
48
46
  this.stop = stop;
49
47
  }
50
- };
51
- var enterRule = defineFacet({
48
+ }, enterRule = defineFacet({
52
49
  reduce: () => {
53
- let rules = [];
54
- const command = (state, dispatch, view) => {
55
- if (!view)
56
- return false;
57
- return execRules(view, rules, dispatch);
58
- };
59
- const handler = keydownHandler({ Enter: command });
60
- const plugin = new ProseMirrorPlugin({
50
+ let rules = [], handler = keydownHandler({ Enter: (state, dispatch, view) => view ? execRules(view, rules, dispatch) : !1 }), plugin = new ProseMirrorPlugin({
61
51
  key: new PluginKey("prosekit-enter-rule"),
62
52
  props: { handleKeyDown: handler }
63
53
  });
64
- return function reducer(inputs) {
65
- rules = inputs;
66
- return plugin;
54
+ return function(inputs) {
55
+ return rules = inputs, plugin;
67
56
  };
68
57
  },
69
58
  parent: pluginFacet
70
59
  });
71
60
  function execRules(view, rules, dispatch) {
72
- if (view.composing)
73
- return false;
74
- const state = view.state;
75
- const selection = state.selection;
76
- if (!isTextSelection(selection))
77
- return false;
78
- const $cursor = selection.$cursor;
79
- if (!$cursor || $cursor.parent.type.spec.code)
80
- return false;
81
- const textBefore = $cursor.parent.textBetween(
61
+ if (view.composing) return !1;
62
+ let state = view.state, selection = state.selection;
63
+ if (!isTextSelection(selection)) return !1;
64
+ let $cursor = selection.$cursor;
65
+ if (!$cursor || $cursor.parent.type.spec.code) return !1;
66
+ let textBefore = $cursor.parent.textBetween(
82
67
  Math.max(0, $cursor.parentOffset - MAX_MATCH),
83
68
  $cursor.parentOffset,
84
69
  null,
85
70
  OBJECT_REPLACEMENT_CHARACTER
86
71
  );
87
- for (const rule of rules) {
72
+ for (let rule of rules) {
88
73
  rule.regex.lastIndex = 0;
89
- const match = rule.regex.exec(textBefore);
90
- const tr = match && rule.handler({
74
+ let match = rule.regex.exec(textBefore), tr = match && rule.handler({
91
75
  state,
92
76
  from: $cursor.pos - match[0].length,
93
77
  to: $cursor.pos,
94
78
  match
95
79
  });
96
- if (!tr)
97
- continue;
98
- dispatch == null ? void 0 : dispatch(tr);
99
- if (rule.stop) {
100
- return true;
101
- }
80
+ if (tr && (dispatch == null || dispatch(tr), rule.stop))
81
+ return !0;
102
82
  }
103
- return false;
83
+ return !1;
104
84
  }
105
85
  var MAX_MATCH = 200;
106
86
 
@@ -24,35 +24,20 @@ function createMarkInputRule({
24
24
  type,
25
25
  attrs = null
26
26
  }) {
27
- const rule = new InputRule(regex, (state, match, start, end) => {
27
+ return new InputRule(regex, (state, match, start, end) => {
28
28
  var _a;
29
- const { tr, schema } = state;
30
- const [fullText, markText] = match;
31
- if (!markText) {
29
+ let { tr, schema } = state, [fullText, markText] = match;
30
+ if (!markText)
32
31
  return null;
33
- }
34
- const markStart = start + fullText.indexOf(markText);
35
- const markEnd = markStart + markText.length;
36
- if (!(start <= markStart && markStart < markEnd && markEnd <= end)) {
32
+ let markStart = start + fullText.indexOf(markText), markEnd = markStart + markText.length;
33
+ if (!(start <= markStart && markStart < markEnd && markEnd <= end))
37
34
  return null;
38
- }
39
- const markType = getMarkType(schema, type);
40
- const mark = markType.create(maybeRun(attrs, match));
41
- if (!isMarkAbsent(tr.doc, markStart, markEnd, markType, attrs)) {
35
+ let markType = getMarkType(schema, type), mark = markType.create(maybeRun(attrs, match));
36
+ if (!isMarkAbsent(tr.doc, markStart, markEnd, markType, attrs))
42
37
  return null;
43
- }
44
- const initialStoredMarks = (_a = tr.storedMarks) != null ? _a : [];
45
- tr.addMark(markStart, markEnd, mark);
46
- if (markEnd < end) {
47
- tr.delete(markEnd, end);
48
- }
49
- if (start < markStart) {
50
- tr.delete(start, markStart);
51
- }
52
- tr.setStoredMarks(initialStoredMarks);
53
- return tr;
38
+ let initialStoredMarks = (_a = tr.storedMarks) != null ? _a : [];
39
+ return tr.addMark(markStart, markEnd, mark), markEnd < end && tr.delete(markEnd, end), start < markStart && tr.delete(start, markStart), tr.setStoredMarks(initialStoredMarks), tr;
54
40
  });
55
- return rule;
56
41
  }
57
42
  function defineMarkInputRule(options) {
58
43
  return defineInputRule(createMarkInputRule(options));
@@ -64,7 +49,7 @@ function defineTextBlockInputRule({
64
49
  }) {
65
50
  return defineFacetPayload(inputRuleFacet, [
66
51
  ({ schema }) => {
67
- const nodeType = getNodeType(schema, type);
52
+ let nodeType = getNodeType(schema, type);
68
53
  return textblockTypeInputRule(regex, nodeType, attrs);
69
54
  }
70
55
  ]);
@@ -77,17 +62,15 @@ function defineWrappingInputRule({
77
62
  }) {
78
63
  return defineFacetPayload(inputRuleFacet, [
79
64
  ({ schema }) => {
80
- const nodeType = getNodeType(schema, type);
65
+ let nodeType = getNodeType(schema, type);
81
66
  return wrappingInputRule(regex, nodeType, attrs, join);
82
67
  }
83
68
  ]);
84
69
  }
85
70
  var inputRuleFacet = defineFacet({
86
- reducer: (inputs) => {
87
- return (context) => {
88
- const rules = inputs.flatMap((callback) => callback(context));
89
- return [inputRules({ rules })];
90
- };
71
+ reducer: (inputs) => (context) => {
72
+ let rules = inputs.flatMap((callback) => callback(context));
73
+ return [inputRules({ rules })];
91
74
  },
92
75
  parent: pluginFacet
93
76
  });
@@ -0,0 +1,140 @@
1
+ // src/mark-rule/extension.ts
2
+ import {
3
+ defineFacet,
4
+ defineFacetPayload,
5
+ pluginFacet
6
+ } from "@prosekit/core";
7
+ import {
8
+ PluginKey,
9
+ ProseMirrorPlugin
10
+ } from "@prosekit/pm/state";
11
+
12
+ // src/mark-rule/apply.ts
13
+ import {
14
+ OBJECT_REPLACEMENT_CHARACTER,
15
+ getMarkType,
16
+ maybeRun
17
+ } from "@prosekit/core";
18
+ import "@prosekit/pm/model";
19
+ import "@prosekit/pm/state";
20
+
21
+ // src/mark-rule/range.ts
22
+ import "@prosekit/pm/state";
23
+ function getSpanTextRanges($from, $to) {
24
+ let nodeRange = $from.blockRange($to);
25
+ if (!nodeRange)
26
+ return [];
27
+ let stack = [], start = nodeRange.start;
28
+ for (let i = nodeRange.startIndex; i < nodeRange.endIndex; i++) {
29
+ let child = nodeRange.parent.child(i);
30
+ stack.push([start, child]), start += child.nodeSize;
31
+ }
32
+ let ranges = [];
33
+ for (; stack.length > 0; ) {
34
+ let [start2, node] = stack.pop();
35
+ if (!node.type.spec.code) {
36
+ if (node.type.isTextblock) {
37
+ ranges.push([start2 + 1, start2 + 1 + node.content.size]);
38
+ continue;
39
+ }
40
+ node.forEach((child, offset) => {
41
+ stack.push([start2 + offset + 1, child]);
42
+ });
43
+ }
44
+ }
45
+ return ranges;
46
+ }
47
+ function getInlineTextRange($from, $to) {
48
+ return [$from.start(), $to.end()];
49
+ }
50
+ function getTextRanges(doc, from, to) {
51
+ let $from = doc.resolve(from), $to = doc.resolve(to);
52
+ return $from.sameParent($to) && $from.parent.isTextblock ? [getInlineTextRange($from, $to)] : $from.blockRange($to) ? getSpanTextRanges($from, $to) : [];
53
+ }
54
+ function getMapRange(transactions, oldState, newState) {
55
+ let lo = oldState.selection.from, hi = oldState.selection.to;
56
+ for (let tr of transactions)
57
+ for (let map of tr.mapping.maps)
58
+ lo = map.map(lo), hi = map.map(hi), map.forEach((_oldStart, _oldEnd, newStart, newEnd) => {
59
+ lo = Math.min(lo, hi, newStart), hi = Math.max(lo, hi, newEnd);
60
+ });
61
+ return lo = Math.min(lo, hi, newState.selection.from), hi = Math.min(lo, hi, newState.selection.to), [lo, hi];
62
+ }
63
+ function getCheckRanges(transactions, oldState, newState) {
64
+ let [from, to] = getMapRange(transactions, oldState, newState);
65
+ return getTextRanges(newState.doc, from, to);
66
+ }
67
+
68
+ // src/mark-rule/apply.ts
69
+ function getExpectedMarkings(rules, doc, from, to) {
70
+ let text = doc.textBetween(from, to, null, OBJECT_REPLACEMENT_CHARACTER), ranges = [];
71
+ for (let rule of rules) {
72
+ rule.regex.lastIndex = 0;
73
+ let matches = text.matchAll(rule.regex), markType = getMarkType(doc.type.schema, rule.type);
74
+ for (let match of matches) {
75
+ let index = match.index;
76
+ if (index == null) continue;
77
+ let attrs = maybeRun(rule.attrs, match), mark = markType.create(attrs);
78
+ ranges.push([from + index, from + index + match[0].length, mark]);
79
+ }
80
+ }
81
+ ranges.sort((a, b) => a[0] - b[0] || b[1] - a[1]);
82
+ let result = [], freeIndex = 0;
83
+ for (let range of ranges)
84
+ range[0] >= freeIndex && (result.push(range), freeIndex = range[1]);
85
+ return result;
86
+ }
87
+ function getReceivedMarkings(rules, doc, from, to) {
88
+ let result = [], schema = doc.type.schema, markTypes = rules.map((rule) => getMarkType(schema, rule.type));
89
+ return doc.nodesBetween(from, to, (node, pos) => {
90
+ if (node.isInline)
91
+ for (let markType of markTypes) {
92
+ let mark = node.marks.find((mark2) => mark2.type === markType);
93
+ mark && result.push([pos, pos + node.nodeSize, mark]);
94
+ }
95
+ }), result;
96
+ }
97
+ function markRangeEquals(a, b) {
98
+ return a[0] === b[0] && a[1] === b[1] && a[2].eq(b[2]);
99
+ }
100
+ function markRangeDiffs(a, b) {
101
+ return a.filter((x) => !b.some((y) => markRangeEquals(x, y)));
102
+ }
103
+ function applyMarkRules(rules, transactions, oldState, newState) {
104
+ if (transactions.length === 0 || transactions.every((tr2) => !tr2.docChanged))
105
+ return null;
106
+ let ranges = getCheckRanges(transactions, oldState, newState), toRemove = [], toCreate = [];
107
+ for (let [from, to] of ranges) {
108
+ let expected = getExpectedMarkings(rules, newState.doc, from, to), received = getReceivedMarkings(rules, newState.doc, from, to);
109
+ toRemove.push(...markRangeDiffs(received, expected)), toCreate.push(...markRangeDiffs(expected, received));
110
+ }
111
+ if (toCreate.length === 0 && toRemove.length === 0)
112
+ return null;
113
+ let tr = newState.tr;
114
+ for (let [from, to, mark] of toRemove)
115
+ tr.removeMark(from, to, mark);
116
+ for (let [from, to, mark] of toCreate)
117
+ tr.addMark(from, to, mark);
118
+ return tr;
119
+ }
120
+
121
+ // src/mark-rule/extension.ts
122
+ function defineMarkRule(options) {
123
+ return defineFacetPayload(markRuleFacet, [options]);
124
+ }
125
+ var markRuleFacet = defineFacet({
126
+ reduce: () => {
127
+ let rules = [], plugin = new ProseMirrorPlugin({
128
+ key: new PluginKey("prosekit-mark-rule"),
129
+ appendTransaction: (transactions, oldState, newState) => applyMarkRules(rules, transactions, oldState, newState)
130
+ });
131
+ return function(input) {
132
+ return rules = input, plugin;
133
+ };
134
+ },
135
+ parent: pluginFacet
136
+ });
137
+
138
+ export {
139
+ defineMarkRule
140
+ };
@@ -2,7 +2,6 @@
2
2
  .prosemirror-flat-list {
3
3
  padding: 0;
4
4
  margin-top: 0;
5
- margin-bottom: 0;
6
5
  margin-left: 32px;
7
6
  margin-bottom: 0;
8
7
  position: relative;
@@ -15,10 +14,7 @@
15
14
  .prosemirror-flat-list.ProseMirror-selectednode:after {
16
15
  content: "";
17
16
  position: absolute;
18
- left: -32px;
19
- right: -2px;
20
- top: -2px;
21
- bottom: -2px;
17
+ inset: -2px -2px -2px -32px;
22
18
  border: 2px solid #8cf;
23
19
  pointer-events: none;
24
20
  }
@@ -31,7 +27,7 @@
31
27
  .prosemirror-flat-list[data-list-kind=ordered] > * {
32
28
  contain: style;
33
29
  }
34
- .prosemirror-flat-list[data-list-kind=ordered]::before {
30
+ .prosemirror-flat-list[data-list-kind=ordered]:before {
35
31
  position: absolute;
36
32
  right: 100%;
37
33
  font-variant-numeric: tabular-nums;
@@ -69,10 +65,10 @@
69
65
  width: 1.5em;
70
66
  width: 1lh;
71
67
  }
72
- .prosemirror-flat-list[data-list-kind=toggle] > .list-marker::before {
68
+ .prosemirror-flat-list[data-list-kind=toggle] > .list-marker:before {
73
69
  content: "\23f7";
74
70
  }
75
- .prosemirror-flat-list[data-list-kind=toggle][data-list-collapsable][data-list-collapsed] > .list-marker::before {
71
+ .prosemirror-flat-list[data-list-kind=toggle][data-list-collapsable][data-list-collapsed] > .list-marker:before {
76
72
  content: "\23f5";
77
73
  }
78
74
  .prosemirror-flat-list[data-list-kind=toggle][data-list-collapsable] > .list-marker {
@@ -1,5 +1,5 @@
1
1
  /* src/placeholder/style.css */
2
- .prosekit-placeholder::before {
2
+ .prosekit-placeholder:before {
3
3
  position: absolute;
4
4
  opacity: 30%;
5
5
  pointer-events: none;