@prosekit/extensions 0.2.0 → 0.2.1

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,15 +1,22 @@
1
+ import { Attrs } from '@prosekit/pm/model';
2
+ import type { BundledTheme } from 'shikiji';
1
3
  import { CommandArgs } from '@prosekit/core';
4
+ import type { ContentMatch } from '@prosekit/pm/model';
2
5
  import { DedentListOptions } from 'prosemirror-flat-list';
3
6
  import { EditorState } from '@prosekit/pm/state';
4
7
  import { Extension } from '@prosekit/core';
5
8
  import { ExtensionTyping } from '@prosekit/core';
6
9
  import { IndentListOptions } from 'prosemirror-flat-list';
10
+ import { InputRule } from '@prosekit/pm/inputrules';
7
11
  import { ListAttributes } from 'prosemirror-flat-list';
8
12
  import { NodeRange } from 'prosemirror-model';
13
+ import { NodeType } from '@prosekit/pm/model';
14
+ import { NodeType as NodeType_2 } from 'prosemirror-model';
9
15
  import { Options } from 'tsup';
10
16
  import { Parser } from 'prosemirror-highlight';
11
17
  import { Plugin as Plugin_2 } from '@prosekit/pm/state';
12
18
  import { PluginKey } from '@prosekit/pm/state';
19
+ import { ProseMirrorNode } from '@prosekit/pm/model';
13
20
  import { ToggleCollapsedOptions } from 'prosemirror-flat-list';
14
21
  import { Transaction } from '@prosekit/pm/state';
15
22
  import { UnwrapListOptions } from 'prosemirror-flat-list';
@@ -56,6 +63,8 @@ export declare const default_alias_1: {
56
63
  };
57
64
  };
58
65
 
66
+ export declare function defaultBlockAt(match: ContentMatch): NodeType_2 | null;
67
+
59
68
  export declare function defaultCanMatch({ state }: {
60
69
  state: EditorState;
61
70
  }): boolean;
@@ -110,6 +119,8 @@ toggleCode: [];
110
119
  *
111
120
  * - {@link defineCodeBlockSpec}
112
121
  * - {@link defineCodeBlockInputRule}
122
+ * - {@link defineCodeBlockEnterRule}
123
+ * - {@link defineCodeBlockKeymap}
113
124
  * - {@link defineCodeBlockCommands}.
114
125
  *
115
126
  * @public
@@ -134,6 +145,15 @@ setCodeBlockLanguage: [language: string];
134
145
  export { defineCodeBlockCommands }
135
146
  export { defineCodeBlockCommands as defineCodeBlockCommands_alias_1 }
136
147
 
148
+ /**
149
+ * Adds enter rules for `codeBlock` nodes.
150
+ *
151
+ * @public
152
+ */
153
+ declare function defineCodeBlockEnterRule(): Extension<ExtensionTyping<string, string, CommandArgs>>;
154
+ export { defineCodeBlockEnterRule }
155
+ export { defineCodeBlockEnterRule as defineCodeBlockEnterRule_alias_1 }
156
+
137
157
  /**
138
158
  * Adds syntax highlighting to code blocks. This function requires a `Parser`
139
159
  * instance from the `prosemirror-highlight` package. See the
@@ -157,6 +177,27 @@ declare function defineCodeBlockInputRule(): Extension<ExtensionTyping<string, s
157
177
  export { defineCodeBlockInputRule }
158
178
  export { defineCodeBlockInputRule as defineCodeBlockInputRule_alias_1 }
159
179
 
180
+ /**
181
+ * Defines the keymap for code blocks.
182
+ */
183
+ export declare function defineCodeBlockKeymap(): Extension<ExtensionTyping<string, string, CommandArgs>>;
184
+
185
+ /**
186
+ * Adds syntax highlighting to code blocks using the [shikiji](https://github.com/antfu/shikiji) package.
187
+ *
188
+ * @public
189
+ */
190
+ declare function defineCodeBlockShikiji(options?: {
191
+ /**
192
+ * The shikiji theme to use.
193
+ *
194
+ * @default 'github-light'
195
+ */
196
+ theme?: BundledTheme;
197
+ }): Extension;
198
+ export { defineCodeBlockShikiji }
199
+ export { defineCodeBlockShikiji as defineCodeBlockShikiji_alias_1 }
200
+
160
201
  /**
161
202
  * Defines the `codeBlock` node spec.
162
203
  *
@@ -181,6 +222,15 @@ export declare function defineCodeSpec(): Extension< {
181
222
  MARKS: "code";
182
223
  }>;
183
224
 
225
+ /**
226
+ * Defines an enter rule. An enter rule applies when the text directly in front of
227
+ * the cursor matches `regex` and user presses Enter. The `regex` should end
228
+ * with `$`.
229
+ *
230
+ * @public
231
+ */
232
+ export declare function defineEnterRule({ regex, handler, }: EnterRuleOptions): Extension;
233
+
184
234
  /**
185
235
  * @public
186
236
  */
@@ -229,6 +279,15 @@ export declare function defineImageSpec(): Extension< {
229
279
  NODES: "image";
230
280
  }>;
231
281
 
282
+ /**
283
+ * Defines an input rule extension.
284
+ *
285
+ * @param rule - The ProseMirror input rule to add.
286
+ *
287
+ * @public
288
+ */
289
+ export declare function defineInputRule(rule: InputRule): Extension;
290
+
232
291
  /**
233
292
  * @public
234
293
  */
@@ -278,36 +337,36 @@ MARKS: "link";
278
337
  /**
279
338
  * @public
280
339
  */
281
- export declare function defineList(): Extension< {
282
- NODES: "list";
283
- COMMAND_ARGS: {
284
- dedentList: [options?: DedentListOptions | undefined];
285
- indentList: [options?: IndentListOptions | undefined];
286
- moveList: [direction: "up" | "down"];
287
- splitList: [];
288
- toggleCollapsed: [(ToggleCollapsedOptions | undefined)?];
289
- toggleList: [attrs: ListAttributes];
290
- unwrapList: [options?: UnwrapListOptions | undefined];
291
- wrapInList: [getAttrs: ListAttributes | ((range: NodeRange) => ListAttributes | null)];
292
- insertList: [attrs?: ListAttributes | undefined];
293
- };
340
+ export declare function defineList(): Extension<{
341
+ NODES: "list";
342
+ COMMAND_ARGS: {
343
+ dedentList: [options?: DedentListOptions | undefined];
344
+ indentList: [options?: IndentListOptions | undefined];
345
+ moveList: [direction: "up" | "down"];
346
+ splitList: [];
347
+ toggleCollapsed: [(ToggleCollapsedOptions | undefined)?];
348
+ toggleList: [attrs: ListAttributes];
349
+ unwrapList: [options?: UnwrapListOptions | undefined];
350
+ wrapInList: [getAttrs: ListAttributes | ((range: NodeRange) => ListAttributes | null)];
351
+ insertList: [attrs?: ListAttributes | undefined];
352
+ };
294
353
  }>;
295
354
 
296
- export declare function defineListCommands(): Extension< {
297
- COMMAND_ARGS: {
298
- dedentList: [options?: DedentListOptions | undefined];
299
- indentList: [options?: IndentListOptions | undefined];
300
- moveList: [direction: "up" | "down"];
301
- splitList: [];
302
- toggleCollapsed: [(ToggleCollapsedOptions | undefined)?];
303
- toggleList: [attrs: ListAttributes];
304
- unwrapList: [options?: UnwrapListOptions | undefined];
305
- wrapInList: [getAttrs: ListAttributes | ((range: NodeRange) => ListAttributes | null)];
306
- insertList: [attrs?: ListAttributes | undefined];
307
- };
355
+ export declare function defineListCommands(): Extension<{
356
+ COMMAND_ARGS: {
357
+ dedentList: [options?: DedentListOptions | undefined];
358
+ indentList: [options?: IndentListOptions | undefined];
359
+ moveList: [direction: "up" | "down"];
360
+ splitList: [];
361
+ toggleCollapsed: [(ToggleCollapsedOptions | undefined)?];
362
+ toggleList: [attrs: ListAttributes];
363
+ unwrapList: [options?: UnwrapListOptions | undefined];
364
+ wrapInList: [getAttrs: ListAttributes | ((range: NodeRange) => ListAttributes | null)];
365
+ insertList: [attrs?: ListAttributes | undefined];
366
+ };
308
367
  }>;
309
368
 
310
- export declare function defineListInputRules(): Extension<ExtensionTyping<string, string, CommandArgs>>;
369
+ export declare function defineListInputRules(): Extension;
311
370
 
312
371
  /**
313
372
  * Returns a extension that adds key bindings for list.
@@ -318,8 +377,8 @@ export declare function defineListKeymap(): Extension<ExtensionTyping<string, st
318
377
 
319
378
  export declare function defineListPlugins(): Extension<ExtensionTyping<string, string, CommandArgs>>;
320
379
 
321
- export declare function defineListSpec(): Extension< {
322
- NODES: "list";
380
+ export declare function defineListSpec(): Extension<{
381
+ NODES: "list";
323
382
  }>;
324
383
 
325
384
  /**
@@ -378,6 +437,37 @@ export declare function defineStrikeSpec(): Extension< {
378
437
  MARKS: "strike";
379
438
  }>;
380
439
 
440
+ /**
441
+ * Defines an enter rule that replaces the matched text with a block node.
442
+ *
443
+ * See also {@link defineEnterRule}.
444
+ *
445
+ * @public
446
+ */
447
+ export declare function defineTextBlockEnterRule({ regex, type, attrs, }: TextBlockEnterRuleOptions): Extension;
448
+
449
+ /**
450
+ * Defines an input rule that changes the type of a textblock when the matched
451
+ * text is typed into it.
452
+ *
453
+ * See also [textblockTypeInputRule](https://prosemirror.net/docs/ref/#inputrules.textblockTypeInputRule)
454
+ */
455
+ export declare function defineTextBlockInputRule({ regex, type, attrs, }: {
456
+ /**
457
+ * The regular expression to match against. You'll usually want to start it
458
+ * with `^` to that it is only matched at the start of a textblock.
459
+ */
460
+ regex: RegExp;
461
+ /**
462
+ * The node type to replace the matched text with.
463
+ */
464
+ type: string | NodeType;
465
+ /**
466
+ * Attributes to set on the node.
467
+ */
468
+ attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null);
469
+ }): Extension;
470
+
381
471
  /**
382
472
  * @public
383
473
  */
@@ -400,6 +490,63 @@ export declare function defineUnderlineSpec(): Extension< {
400
490
  MARKS: "underline";
401
491
  }>;
402
492
 
493
+ /**
494
+ * Defines an input rule for automatically wrapping a textblock when a given
495
+ * string is typed.
496
+ *
497
+ * See also [wrappingInputRule](https://prosemirror.net/docs/ref/#inputrules.wrappingInputRule)
498
+ */
499
+ export declare function defineWrappingInputRule({ regex, type, attrs, join, }: {
500
+ /**
501
+ * The regular expression to match against. You'll usually want to start it
502
+ * with `^` to that it is only matched at the start of a textblock.
503
+ */
504
+ regex: RegExp;
505
+ /**
506
+ * The type of node to wrap in.
507
+ */
508
+ type: string | NodeType;
509
+ /**
510
+ * Attributes to set on the node.
511
+ */
512
+ attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null);
513
+ /**
514
+ * By default, if there's a node with the same type above the newly wrapped
515
+ * node, the rule will try to
516
+ * [join](https://prosemirror.net/docs/ref/#transform.Transform.join) those
517
+ * two nodes. You can pass a join predicate, which takes a regular expression
518
+ * match and the node before the wrapped node, and can return a boolean to
519
+ * indicate whether a join should happen.
520
+ */
521
+ join?: (match: RegExpMatchArray, node: ProseMirrorNode) => boolean;
522
+ }): Extension;
523
+
524
+ /**
525
+ * @public
526
+ */
527
+ export declare type EnterRuleHandler = (options: {
528
+ state: EditorState;
529
+ from: number;
530
+ to: number;
531
+ match: RegExpExecArray;
532
+ }) => Transaction | null;
533
+
534
+ /**
535
+ * Options for {@link createEnterRule}.
536
+ *
537
+ * @public
538
+ */
539
+ export declare type EnterRuleOptions = {
540
+ /**
541
+ * The regular expression to match against. It should end with `$`.
542
+ */
543
+ regex: RegExp;
544
+ /**
545
+ * A handler function to be called when an enter rule is triggered.
546
+ */
547
+ handler: EnterRuleHandler;
548
+ };
549
+
403
550
  export declare function getPluginState(state: EditorState): PredictionPluginState | undefined;
404
551
 
405
552
  export declare function getTrMeta(tr: Transaction): PredictionPluginState;
@@ -445,7 +592,9 @@ export declare interface MentionAttrs {
445
592
  value: string;
446
593
  }
447
594
 
448
- export declare const OBJECT_REPLACEMENT = "\uFFFC";
595
+ export declare const NO_BREAK_SPACE = "\u00A0";
596
+
597
+ export declare const OBJECT_REPLACEMENT_CHARACTER = "\uFFFC";
449
598
 
450
599
  export declare interface PlaceholderOptions {
451
600
  /**
@@ -477,4 +626,24 @@ export declare interface PredictionPluginState {
477
626
 
478
627
  export declare function setTrMeta(tr: Transaction, meta: PredictionPluginState): Transaction;
479
628
 
629
+ /**
630
+ * Options for {@link createTextBlockEnterRule}.
631
+ *
632
+ * @public
633
+ */
634
+ export declare type TextBlockEnterRuleOptions = {
635
+ /**
636
+ * The regular expression to match against. It should end with `$`.
637
+ */
638
+ regex: RegExp;
639
+ /**
640
+ * The node type to replace the matched text with.
641
+ */
642
+ type: string | NodeType;
643
+ /**
644
+ * Attributes to set on the node.
645
+ */
646
+ attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null);
647
+ };
648
+
480
649
  export { }
@@ -0,0 +1,56 @@
1
+ // src/input-rule/index.ts
2
+ import {
3
+ Facet,
4
+ getNodeType,
5
+ pluginFacet
6
+ } from "@prosekit/core";
7
+ import {
8
+ inputRules,
9
+ textblockTypeInputRule,
10
+ wrappingInputRule
11
+ } from "@prosekit/pm/inputrules";
12
+ import "@prosekit/pm/model";
13
+ import "@prosekit/pm/state";
14
+ function defineInputRule(rule) {
15
+ return inputRuleFacet.extension([() => rule]);
16
+ }
17
+ function defineTextBlockInputRule({
18
+ regex,
19
+ type,
20
+ attrs
21
+ }) {
22
+ return inputRuleFacet.extension([
23
+ ({ schema }) => {
24
+ const nodeType = getNodeType(schema, type);
25
+ return textblockTypeInputRule(regex, nodeType, attrs);
26
+ }
27
+ ]);
28
+ }
29
+ function defineWrappingInputRule({
30
+ regex,
31
+ type,
32
+ attrs,
33
+ join
34
+ }) {
35
+ return inputRuleFacet.extension([
36
+ ({ schema }) => {
37
+ const nodeType = getNodeType(schema, type);
38
+ return wrappingInputRule(regex, nodeType, attrs, join);
39
+ }
40
+ ]);
41
+ }
42
+ var inputRuleFacet = Facet.define({
43
+ convert: (inputs) => {
44
+ return (context) => {
45
+ const rules = inputs.flatMap((callback) => callback(context));
46
+ return [inputRules({ rules })];
47
+ };
48
+ },
49
+ next: pluginFacet
50
+ });
51
+
52
+ export {
53
+ defineInputRule,
54
+ defineTextBlockInputRule,
55
+ defineWrappingInputRule
56
+ };
@@ -0,0 +1,6 @@
1
+ // src/utils/unicode.ts
2
+ var OBJECT_REPLACEMENT_CHARACTER = "\uFFFC";
3
+
4
+ export {
5
+ OBJECT_REPLACEMENT_CHARACTER
6
+ };
@@ -1,3 +1,7 @@
1
+ import {
2
+ OBJECT_REPLACEMENT_CHARACTER
3
+ } from "./chunk-HQZORKGY.js";
4
+
1
5
  // src/autocomplete/index.ts
2
6
  import {
3
7
  Facet,
@@ -22,7 +26,6 @@ function isInsideCode($pos) {
22
26
  }
23
27
  return $pos.marks().some((mark) => mark.type.name === "code");
24
28
  }
25
- var OBJECT_REPLACEMENT = "\uFFFC";
26
29
  function getPluginState(state) {
27
30
  return pluginKey.getState(state);
28
31
  }
@@ -75,10 +78,14 @@ function createAutocompletePlugin({
75
78
  const textContent = view.state.doc.textBetween(
76
79
  from,
77
80
  to,
78
- OBJECT_REPLACEMENT
81
+ OBJECT_REPLACEMENT_CHARACTER
79
82
  );
80
83
  const deleteMatch = () => {
81
- if (view.state.doc.textBetween(from, to, OBJECT_REPLACEMENT) === textContent) {
84
+ if (view.state.doc.textBetween(
85
+ from,
86
+ to,
87
+ OBJECT_REPLACEMENT_CHARACTER
88
+ ) === textContent) {
82
89
  view.dispatch(view.state.tr.delete(from, to));
83
90
  }
84
91
  };
@@ -125,7 +132,7 @@ function calcPluginState(state, rules) {
125
132
  Math.max(0, parentOffset - MAX_MATCH),
126
133
  parentOffset,
127
134
  null,
128
- OBJECT_REPLACEMENT
135
+ OBJECT_REPLACEMENT_CHARACTER
129
136
  );
130
137
  for (const rule of rules) {
131
138
  if (!rule.canMatch({ state })) {
@@ -1,7 +1,9 @@
1
1
  export { defineCodeBlock } from './_tsup-dts-rollup';
2
2
  export { defineCodeBlockCommands_alias_1 as defineCodeBlockCommands } from './_tsup-dts-rollup';
3
+ export { defineCodeBlockEnterRule_alias_1 as defineCodeBlockEnterRule } from './_tsup-dts-rollup';
3
4
  export { defineCodeBlockHighlight_alias_1 as defineCodeBlockHighlight } from './_tsup-dts-rollup';
4
5
  export { defineCodeBlockInputRule_alias_1 as defineCodeBlockInputRule } from './_tsup-dts-rollup';
6
+ export { defineCodeBlockShikiji_alias_1 as defineCodeBlockShikiji } from './_tsup-dts-rollup';
5
7
  export { defineCodeBlockSpec_alias_1 as defineCodeBlockSpec } from './_tsup-dts-rollup';
6
8
  export { CodeBlockAttrs_alias_1 as CodeBlockAttrs } from './_tsup-dts-rollup';
7
9
  export { HighlightParser_alias_1 as HighlightParser } from './_tsup-dts-rollup';
@@ -1,3 +1,10 @@
1
+ import {
2
+ OBJECT_REPLACEMENT_CHARACTER
3
+ } from "./chunk-HQZORKGY.js";
4
+ import {
5
+ defineTextBlockInputRule
6
+ } from "./chunk-DYFRBXUX.js";
7
+
1
8
  // src/code-block/index.ts
2
9
  import { union } from "@prosekit/core";
3
10
 
@@ -30,18 +37,216 @@ function defineCodeBlockHighlight({
30
37
  );
31
38
  }
32
39
 
40
+ // src/enter-rule/index.ts
41
+ import {
42
+ Facet,
43
+ getNodeType,
44
+ isTextSelection,
45
+ keymapFacet
46
+ } from "@prosekit/core";
47
+ function defineEnterRule({
48
+ regex,
49
+ handler
50
+ }) {
51
+ const rule = new EnterRule(regex, handler);
52
+ return inputRuleFacet.extension([rule]);
53
+ }
54
+ function defineTextBlockEnterRule({
55
+ regex,
56
+ type,
57
+ attrs
58
+ }) {
59
+ return defineEnterRule({
60
+ regex,
61
+ handler: ({ state, from, to, match }) => {
62
+ const nodeType = getNodeType(state.schema, type);
63
+ const $start = state.doc.resolve(from);
64
+ if (!$start.node(-1).canReplaceWith($start.index(-1), $start.indexAfter(-1), nodeType)) {
65
+ return null;
66
+ }
67
+ const nodeAttrs = attrs && typeof attrs === "function" ? attrs(match) : attrs;
68
+ return state.tr.delete(from, to).setBlockType(from, from, nodeType, nodeAttrs);
69
+ }
70
+ });
71
+ }
72
+ var EnterRule = class {
73
+ constructor(regex, handler) {
74
+ this.regex = regex;
75
+ this.handler = handler;
76
+ }
77
+ };
78
+ var inputRuleFacet = Facet.define({
79
+ convert: (inputs) => {
80
+ return {
81
+ Enter: (state, dispatch, view) => {
82
+ if (!view)
83
+ return false;
84
+ return execRules(view, inputs, dispatch);
85
+ }
86
+ };
87
+ },
88
+ next: keymapFacet
89
+ });
90
+ function execRules(view, rules, dispatch) {
91
+ if (view.composing)
92
+ return false;
93
+ const state = view.state;
94
+ const selection = state.selection;
95
+ if (!isTextSelection(selection))
96
+ return false;
97
+ const $cursor = selection.$cursor;
98
+ if (!$cursor || $cursor.parent.type.spec.code)
99
+ return false;
100
+ const textBefore = $cursor.parent.textBetween(
101
+ Math.max(0, $cursor.parentOffset - MAX_MATCH),
102
+ $cursor.parentOffset,
103
+ null,
104
+ OBJECT_REPLACEMENT_CHARACTER
105
+ );
106
+ for (const rule of rules) {
107
+ rule.regex.lastIndex = 0;
108
+ const match = rule.regex.exec(textBefore);
109
+ const tr = match && rule.handler({
110
+ state,
111
+ from: $cursor.pos - match[0].length,
112
+ to: $cursor.pos,
113
+ match
114
+ });
115
+ if (!tr)
116
+ continue;
117
+ dispatch == null ? void 0 : dispatch(tr);
118
+ return true;
119
+ }
120
+ return false;
121
+ }
122
+ var MAX_MATCH = 200;
123
+
33
124
  // src/code-block/code-block-input-rule.ts
34
- import { defineInputRule, getNodeType } from "@prosekit/core";
35
- import { textblockTypeInputRule } from "@prosekit/pm/inputrules";
36
125
  function defineCodeBlockInputRule() {
37
- return defineInputRule(({ schema }) => {
38
- const nodeType = getNodeType(schema, "codeBlock");
39
- const getAttrs = (match) => {
40
- return { language: match[1] || "" };
41
- };
42
- return textblockTypeInputRule(/^```(\S*)\s$/, nodeType, getAttrs);
126
+ return defineTextBlockInputRule({
127
+ regex: /^```(\S*)\s$/,
128
+ type: "codeBlock",
129
+ attrs: getAttrs
43
130
  });
44
131
  }
132
+ function defineCodeBlockEnterRule() {
133
+ return defineTextBlockEnterRule({
134
+ regex: /^```(\S*)$/,
135
+ type: "codeBlock",
136
+ attrs: getAttrs
137
+ });
138
+ }
139
+ function getAttrs(match) {
140
+ return { language: match[1] || "" };
141
+ }
142
+
143
+ // src/code-block/code-block-keymap.ts
144
+ import { defineKeymap } from "@prosekit/core";
145
+ import { TextSelection } from "@prosekit/pm/state";
146
+
147
+ // src/utils/default-block-at.ts
148
+ function defaultBlockAt(match) {
149
+ for (let i = 0; i < match.edgeCount; i++) {
150
+ const { type } = match.edge(i);
151
+ if (type.isTextblock && !type.hasRequiredAttrs())
152
+ return type;
153
+ }
154
+ return null;
155
+ }
156
+
157
+ // src/code-block/code-block-keymap.ts
158
+ function defineCodeBlockKeymap() {
159
+ return defineKeymap({
160
+ Enter: existCodeBlock
161
+ });
162
+ }
163
+ var existCodeBlock = (state, dispatch) => {
164
+ if (!state.selection.empty) {
165
+ return false;
166
+ }
167
+ const { $head } = state.selection;
168
+ const parent = $head.parent;
169
+ if (parent.isTextblock && parent.type.spec.code && $head.parentOffset === parent.content.size && parent.textContent.endsWith("\n\n")) {
170
+ const grandParent = $head.node(-1);
171
+ const insertIndex = $head.indexAfter(-1);
172
+ const type = defaultBlockAt(grandParent.contentMatchAt(insertIndex));
173
+ if (!type || !grandParent.canReplaceWith(insertIndex, insertIndex, type)) {
174
+ return false;
175
+ }
176
+ if (dispatch) {
177
+ const { tr } = state;
178
+ tr.delete($head.pos - 2, $head.pos);
179
+ const pos = tr.selection.$head.after();
180
+ const node = type.createAndFill();
181
+ if (node) {
182
+ tr.replaceWith(pos, pos, node);
183
+ tr.setSelection(TextSelection.near(tr.doc.resolve(pos), 1));
184
+ dispatch(tr.scrollIntoView());
185
+ }
186
+ }
187
+ return true;
188
+ }
189
+ return false;
190
+ };
191
+
192
+ // src/code-block/code-block-shikiji.ts
193
+ import { createParser } from "prosemirror-highlight/shikiji";
194
+ function createHighlighterLoader() {
195
+ let shikijiImport;
196
+ let highlighter;
197
+ const languages = /* @__PURE__ */ new Set();
198
+ const themes = /* @__PURE__ */ new Set();
199
+ return function highlighterLoader(lang, theme) {
200
+ if (!shikijiImport) {
201
+ shikijiImport = import("shikiji").then(async ({ getHighlighter }) => {
202
+ const fallbackLang = "md";
203
+ highlighter = await getHighlighter({
204
+ langs: [fallbackLang],
205
+ themes: []
206
+ });
207
+ });
208
+ return { promise: shikijiImport };
209
+ }
210
+ if (!highlighter) {
211
+ return { promise: shikijiImport };
212
+ }
213
+ if (!languages.has(lang)) {
214
+ const promise = highlighter.loadLanguage(lang).then(() => {
215
+ languages.add(lang);
216
+ }).catch(() => {
217
+ });
218
+ return { promise };
219
+ }
220
+ if (!themes.has(theme)) {
221
+ const promise = highlighter.loadTheme(theme).then(() => {
222
+ themes.add(theme);
223
+ }).catch(() => {
224
+ });
225
+ return { promise };
226
+ }
227
+ return { highlighter };
228
+ };
229
+ }
230
+ function createLazyParser(theme) {
231
+ let parser;
232
+ const highlighterLoader = createHighlighterLoader();
233
+ return function lazyParser(options) {
234
+ const language = options.language || "";
235
+ const { highlighter, promise } = highlighterLoader(language, theme);
236
+ if (!highlighter) {
237
+ return promise || [];
238
+ }
239
+ if (!parser) {
240
+ parser = createParser(highlighter);
241
+ }
242
+ return parser(options);
243
+ };
244
+ }
245
+ function defineCodeBlockShikiji(options) {
246
+ const theme = (options == null ? void 0 : options.theme) || "github-light";
247
+ const parser = createLazyParser(theme);
248
+ return defineCodeBlockHighlight({ parser });
249
+ }
45
250
 
46
251
  // src/code-block/code-block-spec.ts
47
252
  import { defineNodeSpec } from "@prosekit/core";
@@ -65,12 +270,7 @@ function defineCodeBlockSpec() {
65
270
  ],
66
271
  toDOM(node) {
67
272
  const attrs = node.attrs;
68
- return [
69
- "pre",
70
- // TODO: remove class 'hljs'
71
- { "data-language": attrs.language, class: "hljs" },
72
- ["code", 0]
73
- ];
273
+ return ["pre", { "data-language": attrs.language }, ["code", 0]];
74
274
  }
75
275
  });
76
276
  }
@@ -80,13 +280,17 @@ function defineCodeBlock() {
80
280
  return union([
81
281
  defineCodeBlockSpec(),
82
282
  defineCodeBlockInputRule(),
283
+ defineCodeBlockEnterRule(),
284
+ defineCodeBlockKeymap(),
83
285
  defineCodeBlockCommands()
84
286
  ]);
85
287
  }
86
288
  export {
87
289
  defineCodeBlock,
88
290
  defineCodeBlockCommands,
291
+ defineCodeBlockEnterRule,
89
292
  defineCodeBlockHighlight,
90
293
  defineCodeBlockInputRule,
294
+ defineCodeBlockShikiji,
91
295
  defineCodeBlockSpec
92
296
  };
@@ -1,16 +1,17 @@
1
+ import {
2
+ defineTextBlockInputRule
3
+ } from "./chunk-DYFRBXUX.js";
4
+
1
5
  // src/heading/index.ts
2
6
  import {
3
7
  defineCommands,
4
- defineInputRule,
5
8
  defineKeymap,
6
9
  defineNodeSpec,
7
- getNodeType,
8
10
  insertNode,
9
11
  setBlockType,
10
12
  toggleNode,
11
13
  union
12
14
  } from "@prosekit/core";
13
- import { textblockTypeInputRule } from "@prosekit/pm/inputrules";
14
15
  function defineHeadingSpec() {
15
16
  return defineNodeSpec({
16
17
  name: "heading",
@@ -42,13 +43,14 @@ function defineHeadingKeymap() {
42
43
  });
43
44
  }
44
45
  function defineHeadingInputRule() {
45
- return defineInputRule(({ schema }) => {
46
- const nodeSpec = getNodeType(schema, "heading");
47
- return textblockTypeInputRule(/^(#{1,6})\s/, nodeSpec, (match) => {
46
+ return defineTextBlockInputRule({
47
+ regex: /^(#{1,6})\s$/,
48
+ type: "heading",
49
+ attrs: (match) => {
48
50
  var _a, _b;
49
51
  const level = (_b = (_a = match[1]) == null ? void 0 : _a.length) != null ? _b : 1;
50
52
  return { level };
51
- });
53
+ }
52
54
  });
53
55
  }
54
56
  function defineHeadingCommands() {
@@ -0,0 +1,3 @@
1
+ export { defineInputRule } from './_tsup-dts-rollup';
2
+ export { defineTextBlockInputRule } from './_tsup-dts-rollup';
3
+ export { defineWrappingInputRule } from './_tsup-dts-rollup';
@@ -0,0 +1,10 @@
1
+ import {
2
+ defineInputRule,
3
+ defineTextBlockInputRule,
4
+ defineWrappingInputRule
5
+ } from "./chunk-DYFRBXUX.js";
6
+ export {
7
+ defineInputRule,
8
+ defineTextBlockInputRule,
9
+ defineWrappingInputRule
10
+ };
@@ -1,8 +1,11 @@
1
+ import {
2
+ defineInputRule
3
+ } from "./chunk-DYFRBXUX.js";
4
+
1
5
  // src/list/index.ts
2
6
  import {
3
7
  Priority,
4
8
  defineCommands,
5
- defineInputRule,
6
9
  defineKeymap,
7
10
  defineNodeSpec,
8
11
  definePlugin,
@@ -34,7 +37,7 @@ function defineListKeymap() {
34
37
  return defineKeymap(listKeymap);
35
38
  }
36
39
  function defineListInputRules() {
37
- return defineInputRule(() => listInputRules);
40
+ return union(listInputRules.map(defineInputRule));
38
41
  }
39
42
  function defineListCommands() {
40
43
  return defineCommands({
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@prosekit/extensions",
3
3
  "type": "module",
4
- "version": "0.2.0",
4
+ "version": "0.2.1",
5
5
  "private": false,
6
6
  "author": {
7
7
  "name": "ocavue",
@@ -65,6 +65,11 @@
65
65
  "import": "./dist/prosekit-extensions-image.js",
66
66
  "default": "./dist/prosekit-extensions-image.js"
67
67
  },
68
+ "./input-rule": {
69
+ "types": "./dist/prosekit-extensions-input-rule.d.ts",
70
+ "import": "./dist/prosekit-extensions-input-rule.js",
71
+ "default": "./dist/prosekit-extensions-input-rule.js"
72
+ },
68
73
  "./italic": {
69
74
  "types": "./dist/prosekit-extensions-italic.d.ts",
70
75
  "import": "./dist/prosekit-extensions-italic.js",
@@ -119,13 +124,22 @@
119
124
  "@prosekit/core": "^0.2.0",
120
125
  "@prosekit/pm": "^0.1.1",
121
126
  "prosemirror-flat-list": "^0.4.5",
122
- "prosemirror-highlight": "^0.3.3"
127
+ "prosemirror-highlight": "^0.4.0"
128
+ },
129
+ "peerDependencies": {
130
+ "shikiji": ">= 0.9.0"
131
+ },
132
+ "peerDependenciesMeta": {
133
+ "shikiji": {
134
+ "optional": true
135
+ }
123
136
  },
124
137
  "devDependencies": {
125
138
  "@prosekit/dev": "*",
139
+ "shikiji": "^0.9.16",
126
140
  "tsup": "^8.0.1",
127
141
  "typescript": "^5.3.3",
128
- "vitest": "^1.1.0"
142
+ "vitest": "^1.1.1"
129
143
  },
130
144
  "scripts": {
131
145
  "build:tsup": "tsup",
@@ -158,6 +172,9 @@
158
172
  "image": [
159
173
  "./dist/prosekit-extensions-image.d.ts"
160
174
  ],
175
+ "input-rule": [
176
+ "./dist/prosekit-extensions-input-rule.d.ts"
177
+ ],
161
178
  "italic": [
162
179
  "./dist/prosekit-extensions-italic.d.ts"
163
180
  ],