@prosekit/extensions 0.4.2 → 0.4.3

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.
@@ -16,6 +16,7 @@ import { IndentListOptions } from 'prosemirror-flat-list';
16
16
  import { InputRule } from '@prosekit/pm/inputrules';
17
17
  import { ListAttributes } from 'prosemirror-flat-list';
18
18
  import { ListDOMSerializer } from 'prosemirror-flat-list';
19
+ import { MarkType } from '@prosekit/pm/model';
19
20
  import { NodeRange } from 'prosemirror-model';
20
21
  import { NodeType } from '@prosekit/pm/model';
21
22
  import { Options } from 'tsup';
@@ -28,6 +29,8 @@ import { ToggleCollapsedOptions } from 'prosemirror-flat-list';
28
29
  import { Transaction } from '@prosekit/pm/state';
29
30
  import { UnwrapListOptions } from 'prosemirror-flat-list';
30
31
 
32
+ export declare function applyMarkRules(rules: MarkRule[], transactions: readonly Transaction[], oldState: EditorState, newState: EditorState): Transaction | null;
33
+
31
34
  declare class AutocompleteRule {
32
35
  readonly regex: RegExp;
33
36
  readonly onMatch: MatchHandler;
@@ -412,10 +415,21 @@ expandLink: [];
412
415
  };
413
416
  }>;
414
417
 
418
+ /**
419
+ * Apply link marks after typing Enter.
420
+ */
415
421
  export declare function defineLinkEnterRule(): Extension<ExtensionTyping<string, string, CommandArgs>>;
416
422
 
423
+ /**
424
+ * Apply link marks after pressing Space.
425
+ */
417
426
  export declare function defineLinkInputRule(): Extension<ExtensionTyping<string, string, CommandArgs>>;
418
427
 
428
+ /**
429
+ * Apply and remove link marks to the text during typing.
430
+ */
431
+ export declare function defineLinkMarkRule(): Extension<ExtensionTyping<string, string, CommandArgs>>;
432
+
419
433
  export declare function defineLinkSpec(): Extension< {
420
434
  MARKS: "link";
421
435
  }>;
@@ -467,6 +481,12 @@ export declare function defineListSpec(): Extension<{
467
481
  NODES: "list";
468
482
  }>;
469
483
 
484
+ /**
485
+ * A mark rule is something that can automatically apply marks to text if it
486
+ * matches a certain pattern, and remove them if it doesn't match anymore.
487
+ */
488
+ export declare function defineMarkRule(options: MarkRuleOptions): Extension<ExtensionTyping<string, string, CommandArgs>>;
489
+
470
490
  /**
471
491
  * @public
472
492
  */
@@ -758,6 +778,8 @@ export declare type EnterRuleOptions = {
758
778
  */
759
779
  export declare const exitTable: Command;
760
780
 
781
+ export declare function getCheckRanges(transactions: readonly Transaction[], oldState: EditorState, newState: EditorState): Array<[number, number]>;
782
+
761
783
  export { getHighlighter }
762
784
 
763
785
  export declare function getPluginState(state: EditorState): PredictionPluginState | undefined;
@@ -799,9 +821,11 @@ export declare function insertTable({ row, col, header, }: {
799
821
  header: boolean;
800
822
  }): Command;
801
823
 
802
- export declare const LINK_RE: RegExp;
824
+ export declare const LINK_ENTER_RE: RegExp;
803
825
 
804
- export declare const LINK_SPACE_RE: RegExp;
826
+ export declare const LINK_INPUT_RE: RegExp;
827
+
828
+ export declare const LINK_MARK_RE: RegExp;
805
829
 
806
830
  /**
807
831
  * @public
@@ -812,6 +836,42 @@ export declare interface LinkAttrs {
812
836
 
813
837
  export { ListDOMSerializer }
814
838
 
839
+ /**
840
+ * @internal
841
+ */
842
+ export declare class MarkRule {
843
+ readonly regex: RegExp;
844
+ readonly type: string | MarkType;
845
+ readonly getAttrs: (match: RegExpMatchArray) => Attrs | null;
846
+ constructor({ regex, type, attrs }: MarkRuleOptions);
847
+ }
848
+
849
+ /**
850
+ * The options for {@link defineMarkRule}.
851
+ *
852
+ * @public
853
+ */
854
+ declare interface MarkRuleOptions {
855
+ /**
856
+ * The regular expression to match against. It must has a `g` flag to match
857
+ * all instances of the mark.
858
+ */
859
+ regex: RegExp;
860
+ /**
861
+ * The mark type to apply to the matched text.
862
+ */
863
+ type: string | MarkType;
864
+ /**
865
+ * Attributes to set on the mark. If a function is provided, it will be called
866
+ * with the matched result from the regular expression.
867
+ *
868
+ * @default null
869
+ */
870
+ attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null);
871
+ }
872
+ export { MarkRuleOptions }
873
+ export { MarkRuleOptions as MarkRuleOptions_alias_1 }
874
+
815
875
  declare type MatchHandler = (options: {
816
876
  state: EditorState;
817
877
  match: RegExpExecArray;
@@ -0,0 +1,185 @@
1
+ // src/mark-rule/index.ts
2
+ import { Facet, pluginFacet } from "@prosekit/core";
3
+ import { ProseMirrorPlugin } from "@prosekit/pm/state";
4
+
5
+ // src/mark-rule/apply.ts
6
+ import { OBJECT_REPLACEMENT_CHARACTER, getMarkType } from "@prosekit/core";
7
+ import "@prosekit/pm/model";
8
+ import "@prosekit/pm/state";
9
+
10
+ // src/mark-rule/range.ts
11
+ import "@prosekit/pm/state";
12
+ function getSpanTextRanges($from, $to) {
13
+ const nodeRange = $from.blockRange($to);
14
+ if (!nodeRange) {
15
+ return [];
16
+ }
17
+ const stack = [];
18
+ let start = nodeRange.start;
19
+ for (let i = nodeRange.startIndex; i < nodeRange.endIndex; i++) {
20
+ const child = nodeRange.parent.child(i);
21
+ stack.push([start, child]);
22
+ start += child.nodeSize;
23
+ }
24
+ const ranges = [];
25
+ while (stack.length > 0) {
26
+ const [start2, node] = stack.pop();
27
+ if (node.type.spec.code) {
28
+ continue;
29
+ }
30
+ if (node.type.isTextblock) {
31
+ ranges.push([start2 + 1, start2 + 1 + node.content.size]);
32
+ continue;
33
+ }
34
+ node.forEach((child, offset) => {
35
+ stack.push([start2 + offset + 1, child]);
36
+ });
37
+ }
38
+ return ranges;
39
+ }
40
+ function getInlineTextRange($from, $to) {
41
+ return [$from.start(), $to.end()];
42
+ }
43
+ function getTextRanges(doc, from, to) {
44
+ const $from = doc.resolve(from);
45
+ const $to = doc.resolve(to);
46
+ if ($from.sameParent($to) && $from.parent.isTextblock) {
47
+ return [getInlineTextRange($from, $to)];
48
+ } else {
49
+ const nodeRange = $from.blockRange($to);
50
+ if (!nodeRange) {
51
+ return [];
52
+ }
53
+ return getSpanTextRanges($from, $to);
54
+ }
55
+ }
56
+ function getMapRange(transactions, oldState, newState) {
57
+ let lo = oldState.selection.from;
58
+ let hi = oldState.selection.to;
59
+ for (const tr of transactions) {
60
+ for (const map of tr.mapping.maps) {
61
+ lo = map.map(lo);
62
+ hi = map.map(hi);
63
+ map.forEach((_oldStart, _oldEnd, newStart, newEnd) => {
64
+ lo = Math.min(lo, hi, newStart);
65
+ hi = Math.max(hi, hi, newEnd);
66
+ });
67
+ }
68
+ }
69
+ lo = Math.min(lo, hi, newState.selection.from);
70
+ hi = Math.min(lo, hi, newState.selection.to);
71
+ return [lo, hi];
72
+ }
73
+ function getCheckRanges(transactions, oldState, newState) {
74
+ const [from, to] = getMapRange(transactions, oldState, newState);
75
+ return getTextRanges(newState.doc, from, to);
76
+ }
77
+
78
+ // src/mark-rule/apply.ts
79
+ function getExpectedMarkings(rules, doc, from, to) {
80
+ const text = doc.textBetween(from, to, OBJECT_REPLACEMENT_CHARACTER);
81
+ const result = [];
82
+ for (const rule of rules) {
83
+ rule.regex.lastIndex = 0;
84
+ const matches = text.matchAll(rule.regex);
85
+ const markType = getMarkType(doc.type.schema, rule.type);
86
+ for (const match of matches) {
87
+ const index = match.index;
88
+ if (index == null)
89
+ continue;
90
+ const attrs = rule.getAttrs(match);
91
+ const mark = markType.create(attrs);
92
+ result.push([mark, from + index, from + index + match[0].length]);
93
+ }
94
+ }
95
+ return result;
96
+ }
97
+ function getReceivedMarkings(rules, doc, from, to) {
98
+ const result = [];
99
+ const schema = doc.type.schema;
100
+ const markTypes = rules.map((rule) => getMarkType(schema, rule.type));
101
+ doc.nodesBetween(from, to, (node, pos) => {
102
+ if (!node.isInline) {
103
+ return;
104
+ }
105
+ for (const markType of markTypes) {
106
+ const mark = node.marks.find((mark2) => mark2.type === markType);
107
+ if (mark) {
108
+ result.push([mark, pos, pos + node.nodeSize]);
109
+ }
110
+ }
111
+ });
112
+ return result;
113
+ }
114
+ function markingEquals(a, b) {
115
+ return a[1] === b[1] && a[2] === b[2] && a[0].eq(b[0]);
116
+ }
117
+ function markingDiffs(a, b) {
118
+ return a.filter((x) => !b.some((y) => markingEquals(x, y)));
119
+ }
120
+ function applyMarkRules(rules, transactions, oldState, newState) {
121
+ if (transactions.length === 0 || transactions.every((tr2) => !tr2.docChanged)) {
122
+ return null;
123
+ }
124
+ const ranges = getCheckRanges(transactions, oldState, newState);
125
+ const toRemove = [];
126
+ const toCreate = [];
127
+ for (const [from, to] of ranges) {
128
+ const expected = getExpectedMarkings(rules, newState.doc, from, to);
129
+ const received = getReceivedMarkings(rules, newState.doc, from, to);
130
+ toRemove.push(...markingDiffs(received, expected));
131
+ toCreate.push(...markingDiffs(expected, received));
132
+ }
133
+ if (toCreate.length === 0 && toRemove.length === 0) {
134
+ return null;
135
+ }
136
+ const tr = newState.tr;
137
+ for (const [mark, from, to] of toRemove) {
138
+ tr.removeMark(from, to, mark);
139
+ }
140
+ for (const [mark, from, to] of toCreate) {
141
+ tr.addMark(from, to, mark);
142
+ }
143
+ return tr;
144
+ }
145
+
146
+ // src/mark-rule/rule.ts
147
+ import "@prosekit/pm/model";
148
+ var MarkRule = class {
149
+ constructor({ regex, type, attrs = null }) {
150
+ this.regex = regex;
151
+ this.type = type;
152
+ this.getAttrs = typeof attrs === "function" ? attrs : () => attrs;
153
+ }
154
+ };
155
+
156
+ // src/mark-rule/index.ts
157
+ function defineMarkRule(options) {
158
+ return markRuleFacet.extension([new MarkRule(options)]);
159
+ }
160
+ var markRuleFacet = Facet.define({
161
+ converter: () => {
162
+ let rules = [];
163
+ const plugin = new ProseMirrorPlugin({
164
+ appendTransaction: (transactions, oldState, newState) => {
165
+ return applyMarkRules(rules, transactions, oldState, newState);
166
+ }
167
+ });
168
+ const pluginFunc = () => [plugin];
169
+ return {
170
+ create: (inputs) => {
171
+ rules = inputs;
172
+ return pluginFunc;
173
+ },
174
+ update: (inputs) => {
175
+ rules = inputs;
176
+ return null;
177
+ }
178
+ };
179
+ },
180
+ next: pluginFacet
181
+ });
182
+
183
+ export {
184
+ defineMarkRule
185
+ };
@@ -1,4 +1,4 @@
1
- /* ../../node_modules/.pnpm/prosemirror-flat-list@0.4.6/node_modules/prosemirror-flat-list/dist/style.css */
1
+ /* ../../node_modules/.pnpm/prosemirror-flat-list@0.5.0/node_modules/prosemirror-flat-list/dist/style.css */
2
2
  .prosemirror-flat-list {
3
3
  padding: 0;
4
4
  margin-top: 0;
@@ -1,9 +1,9 @@
1
- import {
2
- defineTextBlockInputRule
3
- } from "./chunk-DYFRBXUX.js";
4
1
  import {
5
2
  defineTextBlockEnterRule
6
3
  } from "./chunk-ASTUC4KT.js";
4
+ import {
5
+ defineTextBlockInputRule
6
+ } from "./chunk-DYFRBXUX.js";
7
7
 
8
8
  // src/code-block/code-block.ts
9
9
  import { union } from "@prosekit/core";
@@ -2,5 +2,6 @@ export { defineLinkSpec } from './_tsup-dts-rollup';
2
2
  export { defineLinkCommands } from './_tsup-dts-rollup';
3
3
  export { defineLinkInputRule } from './_tsup-dts-rollup';
4
4
  export { defineLinkEnterRule } from './_tsup-dts-rollup';
5
+ export { defineLinkMarkRule } from './_tsup-dts-rollup';
5
6
  export { defineLink } from './_tsup-dts-rollup';
6
7
  export { LinkAttrs } from './_tsup-dts-rollup';
@@ -1,9 +1,12 @@
1
1
  import {
2
- defineInputRule
3
- } from "./chunk-DYFRBXUX.js";
2
+ defineMarkRule
3
+ } from "./chunk-TXF4SPMB.js";
4
4
  import {
5
5
  defineEnterRule
6
6
  } from "./chunk-ASTUC4KT.js";
7
+ import {
8
+ defineInputRule
9
+ } from "./chunk-DYFRBXUX.js";
7
10
 
8
11
  // src/link/index.ts
9
12
  import {
@@ -19,15 +22,19 @@ import { InputRule } from "@prosekit/pm/inputrules";
19
22
 
20
23
  // src/link/link-regex.ts
21
24
  var TLD_RE_PATTERN = "a(?:a(?:a|rp)|b(?:arth|b(?:ott|vie)?|c|le|ogado|udhabi)|c(?:ademy|c(?:enture|ountants?)|o|tor)?|d(?:s|ult)?|e(?:g|ro|tna)?|f(?:l|rica)?|g(?:akhan|ency)?|i(?:g|r(?:bus|force|tel))?|kdn|l(?:faromeo|i(?:baba|pay)|l(?:finanz|state|y)|s(?:ace|tom))?|m(?:azon|e(?:rican(?:express|family)|x)|fam|ica|sterdam)?|n(?:alytics|droid|quan|z)|ol?|p(?:artments|p(?:le)?)|q(?:uarelle)?|r(?:a(?:b|mco)|chi|my|pa|te?)?|s(?:da|ia|sociates)?|t(?:hleta|torney)?|u(?:ction|di(?:ble|o)?|spost|t(?:hor|os?))?|vianca|ws?|xa?|z(?:ure)?)|b(?:a(?:by|idu|n(?:a(?:mex|narepublic)|d|k)|r(?:c(?:elona|lay(?:card|s))|efoot|gains)?|s(?:eball|ketball)|uhaus|yern)?|b(?:c|t|va)?|c[gn]|d|e(?:a(?:ts|uty)|er|ntley|rlin|st(?:buy)?|t)?|f|g|h(?:arti)?|i(?:ble|d|ke|ngo?|o|z)?|j|l(?:ack(?:friday)?|o(?:ckbuster|g|omberg)|ue)|m[sw]?|n(?:pparibas)?|o(?:ats|ehringer|fa|m|nd|o(?:k(?:ing)?)?|s(?:ch|t(?:ik|on))|t|utique|x)?|r(?:adesco|idgestone|o(?:adway|ker|ther)|ussels)?|s|t|u(?:ild(?:ers)?|siness|y|zz)|v|w|y|zh?)|c(?:a(?:b|fe|l(?:l|vinklein)?|m(?:era|p)?|non|p(?:etown|ital(?:one)?)|r(?:avan|ds|e(?:ers?)?|s)?|s(?:a|e|h|ino)|t(?:ering|holic)?)?|b(?:a|n|re|s)|c|d|e(?:nter|o|rn)|f[ad]?|g|h(?:a(?:n(?:el|nel)|rity|se|t)|eap|intai|r(?:istmas|ome)|urch)?|i(?:priani|rcle|sco|t(?:adel|ic?|y(?:eats)?))?|k|l(?:aims|eaning|i(?:ck|ni(?:c|que))|o(?:thing|ud)|ub(?:med)?)?|m|n|o(?:ach|des|ffee|l(?:lege|ogne)|m(?:cast|m(?:bank|unity)|p(?:a(?:ny|re)|uter)|sec)?|n(?:dos|s(?:truction|ulting)|t(?:act|ractors))|o(?:king(?:channel)?|l|p)|rsica|u(?:ntry|pons?|rses))?|pa|r(?:edit(?:card|union)?|icket|own|s|uises?)?|u(?:isinella)?|v|w|x|y(?:mru|ou)?|z)|d(?:a(?:bur|d|nce|t(?:a|e|ing|sun)|y)|clk|ds|e(?:al(?:er|s)?|gree|l(?:ivery|l|oitte|ta)|mocrat|nt(?:al|ist)|si(?:gn)?|v)?|hl|i(?:amonds|et|gital|rect(?:ory)?|s(?:co(?:unt|ver)|h)|y)|j|k|m|np|o(?:c(?:s|tor)|g|mains|t|wnload)?|rive|tv|u(?:bai|nlop|pont|rban)|v(?:ag|r)|z)|e(?:a(?:rth|t)|co?|d(?:eka|u(?:cation)?)|e|g|m(?:ail|erck)|n(?:ergy|gineer(?:ing)?|terprises)|pson|quipment|r(?:icsson|ni)?|s(?:q|tate)?|t(?:isalat)?|u(?:rovision|s)?|vents|x(?:change|p(?:ert|osed|ress)|traspace))|f(?:a(?:ge|i(?:l|rwinds|th)|mily|ns?|rm(?:ers)?|s(?:hion|t))|e(?:dex|edback|rr(?:ari|ero))|i(?:at|d(?:elity|o)|lm|na(?:l|nc(?:e|ial))|r(?:e(?:stone)?|mdale)|sh(?:ing)?|t(?:ness)?)?|j|k|l(?:i(?:ckr|ghts|r)|o(?:rist|wers)|y)|m|o(?:o(?:d(?:network)?|tball)?|r(?:d|ex|sale|um)|undation|x)?|r(?:e(?:e|senius)|l|o(?:gans|nt(?:door|ier)))?|tr|u(?:jitsu|nd?|rniture|tbol)|yi)|g(?:a(?:l(?:l(?:ery|o|up))?|mes?|p|rden|y)?|b(?:iz)?|dn?|e(?:a|nt(?:ing)?|orge)?|f|g(?:ee)?|h|i(?:fts?|v(?:es|ing))?|l(?:ass|e|ob(?:al|o))?|m(?:ail|bh|o|x)?|n|o(?:daddy|l(?:d(?:point)?|f)|o(?:dyear|g(?:le)?)?|p|t|v)|p|q|r(?:a(?:inger|phics|tis)|een|ipe|o(?:cery|up))?|s|t|u(?:ardian|cci|ge|i(?:de|tars)|ru)?|w|y)|h(?:a(?:ir|mburg|ngout|us)|bo|dfc(?:bank)?|e(?:alth(?:care)?|l(?:p|sinki)|r(?:e|mes))|gtv|i(?:phop|samitsu|tachi|v)|kt?|m|n|o(?:ckey|l(?:dings|iday)|me(?:depot|goods|s(?:ense)?)|nda|rse|s(?:pital|t(?:ing)?)|t(?:el(?:es|s)|mail)?|use|w)|r|sbc|t|u(?:ghes)?|y(?:att|undai))|i(?:bm|c(?:bc|e|u)|d|e(?:ee)?|fm|kano|l|m(?:amat|db|mo(?:bilien)?)?|n(?:c|dustries|f(?:initi|o)|g|k|s(?:titute|ur(?:ance|e))|t(?:ernational|uit)?|vestments)?|o|piranga|q|r(?:ish)?|s(?:maili|t(?:anbul)?)?|t(?:au|v)?)|j(?:a(?:guar|va)|cb|e(?:ep|tzt|welry)?|io|ll|mp?|nj|o(?:b(?:s|urg)|t|y)?|p(?:morgan|rs)?|u(?:egos|niper))|k(?:aufen|ddi|e(?:rry(?:hotels|logistics|properties))?|fh|g|h|i(?:a|ds|m|nd(?:er|le)|tchen|wi)?|m|n|o(?:eln|matsu|sher)|p(?:mg|n)?|r(?:d|ed)?|uokgroup|w|y(?:oto)?|z)|l(?:a(?:caixa|m(?:borghini|er)|n(?:c(?:aster|ia)|d(?:rover)?|xess)|salle|t(?:ino|robe)?|w(?:yer)?)?|b|c|ds|e(?:ase|clerc|frak|g(?:al|o)|xus)|gbt|i(?:dl|fe(?:insurance|style)?|ghting|ke|lly|m(?:ited|o)|n(?:coln|de|k)|psy|v(?:e|ing))?|k|l[cp]|o(?:ans?|c(?:ker|us)|l|ndon|tt[eo]|ve)|pl(?:financial)?|r|s|t(?:da?)?|u(?:ndbeck|x(?:e|ury))?|v|y)|m(?:a(?:cys|drid|i(?:f|son)|keup|n(?:agement|go)?|p|r(?:ket(?:ing|s)?|riott|shalls)|serati|ttel)?|ba|c(?:kinsey)?|d|e(?:d(?:ia)?|et|lbourne|m(?:e|orial)|nu?|rckmsd)?|g|h|i(?:ami|crosoft|l|n[it]|t(?:subishi)?)|k|l[bs]?|ma?|n|o(?:bi(?:le)?|da|e|i|m|n(?:ash|ey|ster)|r(?:mon|tgage)|scow|to(?:rcycles)?|v(?:ie)?)?|p|q|r|sd?|t[nr]?|u(?:s(?:eum|ic)|tual)?|v|w|x|y|z)|n(?:a(?:b|goya|me|tura|vy)?|ba|c|e(?:c|t(?:bank|flix|work)?|ustar|ws?|x(?:t(?:direct)?|us))?|fl?|go?|hk|i(?:co|k(?:e|on)|nja|ssa[ny])?|l|o(?:kia|rt(?:hwesternmutual|on)|w(?:ruz|tv)?)?|p|r[aw]?|tt|u|yc|z)|o(?:b(?:i|server)|ffice|kinawa|l(?:ayan(?:group)?|dnavy|lo)|m(?:ega)?|n(?:e|g|l(?:ine)?)|oo|pen|r(?:a(?:cle|nge)|g(?:anic)?|igins)|saka|t(?:suka|t)|vh)|p(?:a(?:ge|nasonic|r(?:is|s|t(?:ners|s|y))|ssagens|y)?|ccw|et?|f(?:izer)?|g|h(?:armacy|d|ilips|o(?:ne|to(?:graphy|s)?)|ysio)?|i(?:c(?:s|t(?:et|ures))|d|n[gk]?|oneer|zza)|k|l(?:a(?:ce|y(?:station)?)|u(?:mbing|s))?|m|nc?|o(?:hl|ker|litie|rn|st)|r(?:a(?:merica|xi)|ess|ime|o(?:d(?:uctions)?|f|gressive|mo|pert(?:ies|y)|tection)?|u(?:dential)?)?|s|t|ub|wc?|y)|q(?:a|pon|ue(?:bec|st))|r(?:a(?:cing|dio)|e(?:a(?:d|l(?:estate|t(?:or|y)))|cipes|d(?:stone|umbrella)?|hab|i(?:sen?|t)|liance|n(?:t(?:als)?)?|p(?:air|ort|ublican)|st(?:aurant)?|views?|xroth)?|i(?:c(?:h(?:ardli)?|oh)|l|o|p)|o(?:c(?:her|ks)|deo|gers|om)?|s(?:vp)?|u(?:gby|hr|n)?|we?|yukyu)|s(?:a(?:arland|fe(?:ty)?|kura|l(?:e|on)|ms(?:club|ung)|n(?:dvik(?:coromant)?|ofi)|p|rl|s|ve|xo)?|b[is]?|c(?:a|b|h(?:aeffler|midt|o(?:larships|ol)|ule|warz)|ience|ot)?|d|e(?:a(?:rch|t)|cur(?:e|ity)|ek|lect|ner|rvices|ven|w|xy?)?|fr|g|h(?:a(?:ngrila|rp|w)|ell|i(?:a|ksha)|o(?:es|p(?:ping)?|uji|w(?:time)?))?|i(?:lk|n(?:a|gles)|te)?|j|k(?:in?|y(?:pe)?)?|l(?:ing)?|m(?:art|ile)?|n(?:cf)?|o(?:c(?:cer|ial)|ft(?:bank|ware)|hu|l(?:ar|utions)|n[gy]|y)?|p(?:a(?:ce)?|o(?:rt|t))|rl?|s|t(?:a(?:da|ples|r|te(?:bank|farm))|c(?:group)?|o(?:ckholm|r(?:age|e))|ream|ud(?:io|y)|yle)?|u(?:cks|pp(?:l(?:ies|y)|ort)|r(?:f|gery)|zuki)?|v|w(?:atch|iss)|x|y(?:dney|stems)?|z)|t(?:a(?:b|ipei|lk|obao|rget|t(?:a(?:motors|r)|too)|xi?)|ci?|dk?|e(?:am|ch(?:nology)?|l|masek|nnis|va)|f|g|h(?:d|eat(?:er|re))?|i(?:aa|ckets|enda|ffany|ps|r(?:es|ol))|j(?:maxx|x)?|k(?:maxx)?|l|m(?:all)?|n|o(?:day|kyo|ols|p|ray|shiba|tal|urs|wn|y(?:ota|s))?|r(?:a(?:d(?:e|ing)|ining|vel(?:channel|ers(?:insurance)?)?)|ust|v)?|t|u(?:be|i|nes|shu)|vs?|w|z)|u(?:a|b(?:ank|s)|g|k|n(?:i(?:com|versity)|o)|ol|ps|s|y|z)|v(?:a(?:cations|n(?:a|guard))?|c|e(?:gas|ntures|r(?:isign|sicherung)|t)?|g|i(?:ajes|deo|g|king|llas|n|p|rgin|s(?:a|ion)|v[ao])?|laanderen|n|o(?:dka|l(?:kswagen|vo)|t(?:e|ing|o)|yage)|u(?:elos)?)|w(?:a(?:l(?:es|mart|ter)|ng(?:gou)?|tch(?:es)?)|e(?:ather(?:channel)?|b(?:cam|er|site)|d(?:ding)?|i(?:bo|r))|f|hoswho|i(?:en|ki|lliamhill|n(?:dows|e|ners)?)|me|o(?:lterskluwer|odside|r(?:ks?|ld)|w)|s|t[cf])|x(?:box|erox|finity|i(?:huan|n)|xx|yz)|y(?:a(?:chts|hoo|maxun|ndex)|e|o(?:dobashi|ga|kohama|u(?:tube)?)|t|un)|z(?:a(?:ppos|ra)?|ero|ip|m|one|uerich|w)";
25
+ var PUNCTUATION_CHAR_PATTERN = "\\.\\,\\;\\!\\?";
26
+ var STOP_CHAR_PATTERN = "[" + PUNCTUATION_CHAR_PATTERN + "]";
27
+ var END_CHAR_PATTERN = "[^\\s" + PUNCTUATION_CHAR_PATTERN + "]";
22
28
  var LINK_RE_BASE_PATTERN = (
23
29
  // start of the link group
24
- "((?:(?:(?:https?:)?\\/\\/)?(?:(?:[a-z0-9\\u00a1-\\uffff][a-z0-9\\u00a1-\\uffff_-]{0,62})?[a-z0-9\\u00a1-\\uffff]\\.)+(?:" + TLD_RE_PATTERN + "))(?::\\d{2,5})?(?:[/?#]\\S*)?)"
30
+ "((?:(?:(?:https?:)?\\/\\/)?(?:(?:[a-z0-9\\u00a1-\\uffff][a-z0-9\\u00a1-\\uffff_-]{0,62})?[a-z0-9\\u00a1-\\uffff]\\.)+(?:" + TLD_RE_PATTERN + "))(?::\\d{2,5})?(?:/(?:\\S*" + END_CHAR_PATTERN + ")?)?(?:\\?(?:\\S*" + END_CHAR_PATTERN + "))?(?:\\#(?:\\S*" + END_CHAR_PATTERN + ")?)?)"
25
31
  );
26
- var LINK_STOP_PATTERN = "(?:\\.|\\,|\\;\\!)?";
27
- var LINK_RE_PATTERN = LINK_RE_BASE_PATTERN + LINK_STOP_PATTERN + "$";
28
- var LINK_SPACE_RE_PATTERN = LINK_RE_BASE_PATTERN + LINK_STOP_PATTERN + "\\s$";
29
- var LINK_RE = new RegExp(LINK_RE_PATTERN, "gi");
30
- var LINK_SPACE_RE = new RegExp(LINK_SPACE_RE_PATTERN, "gi");
32
+ var LINK_ENTER_PATTERN = LINK_RE_BASE_PATTERN + STOP_CHAR_PATTERN + "?$";
33
+ var LINK_INPUT_PATTERN = LINK_RE_BASE_PATTERN + STOP_CHAR_PATTERN + "?\\s$";
34
+ var LINK_MARK_PATTERN = LINK_RE_BASE_PATTERN + "(?=" + STOP_CHAR_PATTERN + "|\\s|$)";
35
+ var LINK_ENTER_RE = new RegExp(LINK_ENTER_PATTERN, "gi");
36
+ var LINK_INPUT_RE = new RegExp(LINK_INPUT_PATTERN, "gi");
37
+ var LINK_MARK_RE = new RegExp(LINK_MARK_PATTERN, "gi");
31
38
 
32
39
  // src/link/index.ts
33
40
  function defineLinkSpec() {
@@ -63,7 +70,7 @@ function defineLinkCommands() {
63
70
  }
64
71
  function defineLinkInputRule() {
65
72
  return defineInputRule(
66
- new InputRule(LINK_SPACE_RE, (state, match, from) => {
73
+ new InputRule(LINK_INPUT_RE, (state, match, from) => {
67
74
  const href = match[1];
68
75
  if (!href)
69
76
  return null;
@@ -74,7 +81,7 @@ function defineLinkInputRule() {
74
81
  }
75
82
  function defineLinkEnterRule() {
76
83
  return defineEnterRule({
77
- regex: LINK_RE,
84
+ regex: LINK_ENTER_RE,
78
85
  handler: ({ state, from, match }) => {
79
86
  const href = match[1];
80
87
  if (!href)
@@ -85,6 +92,13 @@ function defineLinkEnterRule() {
85
92
  }
86
93
  });
87
94
  }
95
+ function defineLinkMarkRule() {
96
+ return defineMarkRule({
97
+ regex: LINK_MARK_RE,
98
+ type: "link",
99
+ attrs: (match) => ({ href: match[1] })
100
+ });
101
+ }
88
102
  function defineLink() {
89
103
  return union([
90
104
  defineLinkSpec(),
@@ -98,5 +112,6 @@ export {
98
112
  defineLinkCommands,
99
113
  defineLinkEnterRule,
100
114
  defineLinkInputRule,
115
+ defineLinkMarkRule,
101
116
  defineLinkSpec
102
117
  };
@@ -0,0 +1,2 @@
1
+ export { defineMarkRule } from './_tsup-dts-rollup';
2
+ export { MarkRuleOptions } from './_tsup-dts-rollup';
@@ -0,0 +1,6 @@
1
+ import {
2
+ defineMarkRule
3
+ } from "./chunk-TXF4SPMB.js";
4
+ export {
5
+ defineMarkRule
6
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@prosekit/extensions",
3
3
  "type": "module",
4
- "version": "0.4.2",
4
+ "version": "0.4.3",
5
5
  "private": false,
6
6
  "author": {
7
7
  "name": "ocavue",
@@ -30,6 +30,11 @@
30
30
  "import": "./dist/prosekit-extensions.js",
31
31
  "default": "./dist/prosekit-extensions.js"
32
32
  },
33
+ "./mark-rule": {
34
+ "types": "./dist/prosekit-extensions-mark-rule.d.ts",
35
+ "import": "./dist/prosekit-extensions-mark-rule.js",
36
+ "default": "./dist/prosekit-extensions-mark-rule.js"
37
+ },
33
38
  "./autocomplete": {
34
39
  "types": "./dist/prosekit-extensions-autocomplete.d.ts",
35
40
  "import": "./dist/prosekit-extensions-autocomplete.js",
@@ -150,16 +155,16 @@
150
155
  "@prosekit/core": "^0.4.0",
151
156
  "@prosekit/pm": "^0.1.2",
152
157
  "prosemirror-dropcursor": "^1.8.1",
153
- "prosemirror-flat-list": "^0.4.6",
158
+ "prosemirror-flat-list": "^0.5.0",
154
159
  "prosemirror-highlight": "^0.5.0",
155
160
  "prosemirror-tables": "^1.3.7",
156
- "shiki": "^1.1.7"
161
+ "shiki": "^1.3.0"
157
162
  },
158
163
  "devDependencies": {
159
164
  "@prosekit/dev": "*",
160
165
  "tsup": "^8.0.2",
161
- "typescript": "^5.3.3",
162
- "vitest": "^1.3.1"
166
+ "typescript": "^5.4.5",
167
+ "vitest": "^1.5.2"
163
168
  },
164
169
  "scripts": {
165
170
  "build:tsup": "tsup",
@@ -171,6 +176,9 @@
171
176
  ".": [
172
177
  "./dist/prosekit-extensions.d.ts"
173
178
  ],
179
+ "mark-rule": [
180
+ "./dist/prosekit-extensions-mark-rule.d.ts"
181
+ ],
174
182
  "autocomplete": [
175
183
  "./dist/prosekit-extensions-autocomplete.d.ts"
176
184
  ],