@prosekit/extensions 0.2.0 → 0.2.2

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,24 @@
1
+ import { Attrs } from '@prosekit/pm/model';
2
+ import type { BundledTheme } from 'shikiji';
3
+ import type { Command } from '@prosekit/pm/state';
1
4
  import { CommandArgs } from '@prosekit/core';
5
+ import type { ContentMatch } from '@prosekit/pm/model';
2
6
  import { DedentListOptions } from 'prosemirror-flat-list';
3
7
  import { EditorState } from '@prosekit/pm/state';
4
8
  import { Extension } from '@prosekit/core';
5
9
  import { ExtensionTyping } from '@prosekit/core';
6
10
  import { IndentListOptions } from 'prosemirror-flat-list';
11
+ import { InputRule } from '@prosekit/pm/inputrules';
7
12
  import { ListAttributes } from 'prosemirror-flat-list';
8
13
  import { NodeRange } from 'prosemirror-model';
14
+ import { NodeType } from '@prosekit/pm/model';
15
+ import { NodeType as NodeType_2 } from 'prosemirror-model';
9
16
  import { Options } from 'tsup';
10
17
  import { Parser } from 'prosemirror-highlight';
11
18
  import { Plugin as Plugin_2 } from '@prosekit/pm/state';
12
19
  import { PluginKey } from '@prosekit/pm/state';
20
+ import { ProseMirrorNode } from '@prosekit/pm/model';
21
+ import type { Selection as Selection_2 } from '@prosekit/pm/state';
13
22
  import { ToggleCollapsedOptions } from 'prosemirror-flat-list';
14
23
  import { Transaction } from '@prosekit/pm/state';
15
24
  import { UnwrapListOptions } from 'prosemirror-flat-list';
@@ -56,6 +65,8 @@ export declare const default_alias_1: {
56
65
  };
57
66
  };
58
67
 
68
+ export declare function defaultBlockAt(match: ContentMatch): NodeType_2 | null;
69
+
59
70
  export declare function defaultCanMatch({ state }: {
60
71
  state: EditorState;
61
72
  }): boolean;
@@ -110,6 +121,8 @@ toggleCode: [];
110
121
  *
111
122
  * - {@link defineCodeBlockSpec}
112
123
  * - {@link defineCodeBlockInputRule}
124
+ * - {@link defineCodeBlockEnterRule}
125
+ * - {@link defineCodeBlockKeymap}
113
126
  * - {@link defineCodeBlockCommands}.
114
127
  *
115
128
  * @public
@@ -134,6 +147,15 @@ setCodeBlockLanguage: [language: string];
134
147
  export { defineCodeBlockCommands }
135
148
  export { defineCodeBlockCommands as defineCodeBlockCommands_alias_1 }
136
149
 
150
+ /**
151
+ * Adds enter rules for `codeBlock` nodes.
152
+ *
153
+ * @public
154
+ */
155
+ declare function defineCodeBlockEnterRule(): Extension<ExtensionTyping<string, string, CommandArgs>>;
156
+ export { defineCodeBlockEnterRule }
157
+ export { defineCodeBlockEnterRule as defineCodeBlockEnterRule_alias_1 }
158
+
137
159
  /**
138
160
  * Adds syntax highlighting to code blocks. This function requires a `Parser`
139
161
  * instance from the `prosemirror-highlight` package. See the
@@ -157,6 +179,27 @@ declare function defineCodeBlockInputRule(): Extension<ExtensionTyping<string, s
157
179
  export { defineCodeBlockInputRule }
158
180
  export { defineCodeBlockInputRule as defineCodeBlockInputRule_alias_1 }
159
181
 
182
+ /**
183
+ * Defines the keymap for code blocks.
184
+ */
185
+ export declare function defineCodeBlockKeymap(): Extension<ExtensionTyping<string, string, CommandArgs>>;
186
+
187
+ /**
188
+ * Adds syntax highlighting to code blocks using the [shikiji](https://github.com/antfu/shikiji) package.
189
+ *
190
+ * @public
191
+ */
192
+ declare function defineCodeBlockShikiji(options?: {
193
+ /**
194
+ * The shikiji theme to use.
195
+ *
196
+ * @default 'github-light'
197
+ */
198
+ theme?: BundledTheme;
199
+ }): Extension;
200
+ export { defineCodeBlockShikiji }
201
+ export { defineCodeBlockShikiji as defineCodeBlockShikiji_alias_1 }
202
+
160
203
  /**
161
204
  * Defines the `codeBlock` node spec.
162
205
  *
@@ -181,6 +224,15 @@ export declare function defineCodeSpec(): Extension< {
181
224
  MARKS: "code";
182
225
  }>;
183
226
 
227
+ /**
228
+ * Defines an enter rule. An enter rule applies when the text directly in front of
229
+ * the cursor matches `regex` and user presses Enter. The `regex` should end
230
+ * with `$`.
231
+ *
232
+ * @public
233
+ */
234
+ export declare function defineEnterRule({ regex, handler, }: EnterRuleOptions): Extension;
235
+
184
236
  /**
185
237
  * @public
186
238
  */
@@ -229,6 +281,15 @@ export declare function defineImageSpec(): Extension< {
229
281
  NODES: "image";
230
282
  }>;
231
283
 
284
+ /**
285
+ * Defines an input rule extension.
286
+ *
287
+ * @param rule - The ProseMirror input rule to add.
288
+ *
289
+ * @public
290
+ */
291
+ export declare function defineInputRule(rule: InputRule): Extension;
292
+
232
293
  /**
233
294
  * @public
234
295
  */
@@ -278,36 +339,36 @@ MARKS: "link";
278
339
  /**
279
340
  * @public
280
341
  */
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
- };
342
+ export declare function defineList(): Extension<{
343
+ NODES: "list";
344
+ COMMAND_ARGS: {
345
+ dedentList: [options?: DedentListOptions | undefined];
346
+ indentList: [options?: IndentListOptions | undefined];
347
+ moveList: [direction: "up" | "down"];
348
+ splitList: [];
349
+ toggleCollapsed: [(ToggleCollapsedOptions | undefined)?];
350
+ toggleList: [attrs: ListAttributes];
351
+ unwrapList: [options?: UnwrapListOptions | undefined];
352
+ wrapInList: [getAttrs: ListAttributes | ((range: NodeRange) => ListAttributes | null)];
353
+ insertList: [attrs?: ListAttributes | undefined];
354
+ };
294
355
  }>;
295
356
 
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
- };
357
+ export declare function defineListCommands(): Extension<{
358
+ COMMAND_ARGS: {
359
+ dedentList: [options?: DedentListOptions | undefined];
360
+ indentList: [options?: IndentListOptions | undefined];
361
+ moveList: [direction: "up" | "down"];
362
+ splitList: [];
363
+ toggleCollapsed: [(ToggleCollapsedOptions | undefined)?];
364
+ toggleList: [attrs: ListAttributes];
365
+ unwrapList: [options?: UnwrapListOptions | undefined];
366
+ wrapInList: [getAttrs: ListAttributes | ((range: NodeRange) => ListAttributes | null)];
367
+ insertList: [attrs?: ListAttributes | undefined];
368
+ };
308
369
  }>;
309
370
 
310
- export declare function defineListInputRules(): Extension<ExtensionTyping<string, string, CommandArgs>>;
371
+ export declare function defineListInputRules(): Extension;
311
372
 
312
373
  /**
313
374
  * Returns a extension that adds key bindings for list.
@@ -318,8 +379,8 @@ export declare function defineListKeymap(): Extension<ExtensionTyping<string, st
318
379
 
319
380
  export declare function defineListPlugins(): Extension<ExtensionTyping<string, string, CommandArgs>>;
320
381
 
321
- export declare function defineListSpec(): Extension< {
322
- NODES: "list";
382
+ export declare function defineListSpec(): Extension<{
383
+ NODES: "list";
323
384
  }>;
324
385
 
325
386
  /**
@@ -378,6 +439,37 @@ export declare function defineStrikeSpec(): Extension< {
378
439
  MARKS: "strike";
379
440
  }>;
380
441
 
442
+ /**
443
+ * Defines an enter rule that replaces the matched text with a block node.
444
+ *
445
+ * See also {@link defineEnterRule}.
446
+ *
447
+ * @public
448
+ */
449
+ export declare function defineTextBlockEnterRule({ regex, type, attrs, }: TextBlockEnterRuleOptions): Extension;
450
+
451
+ /**
452
+ * Defines an input rule that changes the type of a textblock when the matched
453
+ * text is typed into it.
454
+ *
455
+ * See also [textblockTypeInputRule](https://prosemirror.net/docs/ref/#inputrules.textblockTypeInputRule)
456
+ */
457
+ export declare function defineTextBlockInputRule({ regex, type, attrs, }: {
458
+ /**
459
+ * The regular expression to match against. You'll usually want to start it
460
+ * with `^` to that it is only matched at the start of a textblock.
461
+ */
462
+ regex: RegExp;
463
+ /**
464
+ * The node type to replace the matched text with.
465
+ */
466
+ type: string | NodeType;
467
+ /**
468
+ * Attributes to set on the node.
469
+ */
470
+ attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null);
471
+ }): Extension;
472
+
381
473
  /**
382
474
  * @public
383
475
  */
@@ -400,6 +492,63 @@ export declare function defineUnderlineSpec(): Extension< {
400
492
  MARKS: "underline";
401
493
  }>;
402
494
 
495
+ /**
496
+ * Defines an input rule for automatically wrapping a textblock when a given
497
+ * string is typed.
498
+ *
499
+ * See also [wrappingInputRule](https://prosemirror.net/docs/ref/#inputrules.wrappingInputRule)
500
+ */
501
+ export declare function defineWrappingInputRule({ regex, type, attrs, join, }: {
502
+ /**
503
+ * The regular expression to match against. You'll usually want to start it
504
+ * with `^` to that it is only matched at the start of a textblock.
505
+ */
506
+ regex: RegExp;
507
+ /**
508
+ * The type of node to wrap in.
509
+ */
510
+ type: string | NodeType;
511
+ /**
512
+ * Attributes to set on the node.
513
+ */
514
+ attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null);
515
+ /**
516
+ * By default, if there's a node with the same type above the newly wrapped
517
+ * node, the rule will try to
518
+ * [join](https://prosemirror.net/docs/ref/#transform.Transform.join) those
519
+ * two nodes. You can pass a join predicate, which takes a regular expression
520
+ * match and the node before the wrapped node, and can return a boolean to
521
+ * indicate whether a join should happen.
522
+ */
523
+ join?: (match: RegExpMatchArray, node: ProseMirrorNode) => boolean;
524
+ }): Extension;
525
+
526
+ /**
527
+ * @public
528
+ */
529
+ export declare type EnterRuleHandler = (options: {
530
+ state: EditorState;
531
+ from: number;
532
+ to: number;
533
+ match: RegExpExecArray;
534
+ }) => Transaction | null;
535
+
536
+ /**
537
+ * Options for {@link createEnterRule}.
538
+ *
539
+ * @public
540
+ */
541
+ export declare type EnterRuleOptions = {
542
+ /**
543
+ * The regular expression to match against. It should end with `$`.
544
+ */
545
+ regex: RegExp;
546
+ /**
547
+ * A handler function to be called when an enter rule is triggered.
548
+ */
549
+ handler: EnterRuleHandler;
550
+ };
551
+
403
552
  export declare function getPluginState(state: EditorState): PredictionPluginState | undefined;
404
553
 
405
554
  export declare function getTrMeta(tr: Transaction): PredictionPluginState;
@@ -421,6 +570,8 @@ export declare interface ImageAttrs {
421
570
  src?: string | null;
422
571
  }
423
572
 
573
+ export declare function isInCodeBlock(selection: Selection_2): boolean | undefined;
574
+
424
575
  /**
425
576
  * @public
426
577
  */
@@ -445,7 +596,9 @@ export declare interface MentionAttrs {
445
596
  value: string;
446
597
  }
447
598
 
448
- export declare const OBJECT_REPLACEMENT = "\uFFFC";
599
+ export declare const NO_BREAK_SPACE = "\u00A0";
600
+
601
+ export declare const OBJECT_REPLACEMENT_CHARACTER = "\uFFFC";
449
602
 
450
603
  export declare interface PlaceholderOptions {
451
604
  /**
@@ -477,4 +630,26 @@ export declare interface PredictionPluginState {
477
630
 
478
631
  export declare function setTrMeta(tr: Transaction, meta: PredictionPluginState): Transaction;
479
632
 
633
+ /**
634
+ * Options for {@link createTextBlockEnterRule}.
635
+ *
636
+ * @public
637
+ */
638
+ export declare type TextBlockEnterRuleOptions = {
639
+ /**
640
+ * The regular expression to match against. It should end with `$`.
641
+ */
642
+ regex: RegExp;
643
+ /**
644
+ * The node type to replace the matched text with.
645
+ */
646
+ type: string | NodeType;
647
+ /**
648
+ * Attributes to set on the node.
649
+ */
650
+ attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null);
651
+ };
652
+
653
+ export declare function withSkipCodeBlock(command: Command): Command;
654
+
480
655
  export { }
@@ -0,0 +1,9 @@
1
+ // src/utils/is-in-code-block.ts
2
+ function isInCodeBlock(selection) {
3
+ const type = selection.$from.parent.type;
4
+ return type.spec.code && type.isBlock;
5
+ }
6
+
7
+ export {
8
+ isInCodeBlock
9
+ };
@@ -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,32 @@
1
+ import {
2
+ isInCodeBlock
3
+ } from "./chunk-DI46QWLX.js";
4
+ import {
5
+ defineTextBlockInputRule
6
+ } from "./chunk-DYFRBXUX.js";
7
+
1
8
  // src/heading/index.ts
2
9
  import {
3
10
  defineCommands,
4
- defineInputRule,
5
11
  defineKeymap,
6
12
  defineNodeSpec,
7
- getNodeType,
8
13
  insertNode,
9
14
  setBlockType,
10
15
  toggleNode,
11
16
  union
12
17
  } from "@prosekit/core";
13
- import { textblockTypeInputRule } from "@prosekit/pm/inputrules";
18
+
19
+ // src/utils/with-skip-code-block.ts
20
+ function withSkipCodeBlock(command) {
21
+ return (state, dispatch, view) => {
22
+ if (isInCodeBlock(state.selection)) {
23
+ return false;
24
+ }
25
+ return command(state, dispatch, view);
26
+ };
27
+ }
28
+
29
+ // src/heading/index.ts
14
30
  function defineHeadingSpec() {
15
31
  return defineNodeSpec({
16
32
  name: "heading",
@@ -33,22 +49,26 @@ function defineHeadingSpec() {
33
49
  }
34
50
  function defineHeadingKeymap() {
35
51
  return defineKeymap({
36
- "mod-1": toggleNode({ type: "heading", attrs: { level: 1 } }),
37
- "mod-2": toggleNode({ type: "heading", attrs: { level: 2 } }),
38
- "mod-3": toggleNode({ type: "heading", attrs: { level: 3 } }),
39
- "mod-4": toggleNode({ type: "heading", attrs: { level: 4 } }),
40
- "mod-5": toggleNode({ type: "heading", attrs: { level: 5 } }),
41
- "mod-6": toggleNode({ type: "heading", attrs: { level: 6 } })
52
+ "mod-1": toggleHeadingKeybinding(1),
53
+ "mod-2": toggleHeadingKeybinding(2),
54
+ "mod-3": toggleHeadingKeybinding(3),
55
+ "mod-4": toggleHeadingKeybinding(4),
56
+ "mod-5": toggleHeadingKeybinding(5),
57
+ "mod-6": toggleHeadingKeybinding(6)
42
58
  });
43
59
  }
60
+ function toggleHeadingKeybinding(level) {
61
+ return withSkipCodeBlock(toggleNode({ type: "heading", attrs: { level } }));
62
+ }
44
63
  function defineHeadingInputRule() {
45
- return defineInputRule(({ schema }) => {
46
- const nodeSpec = getNodeType(schema, "heading");
47
- return textblockTypeInputRule(/^(#{1,6})\s/, nodeSpec, (match) => {
64
+ return defineTextBlockInputRule({
65
+ regex: /^(#{1,6})\s$/,
66
+ type: "heading",
67
+ attrs: (match) => {
48
68
  var _a, _b;
49
69
  const level = (_b = (_a = match[1]) == null ? void 0 : _a.length) != null ? _b : 1;
50
70
  return { level };
51
- });
71
+ }
52
72
  });
53
73
  }
54
74
  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({
@@ -1,3 +1,7 @@
1
+ import {
2
+ isInCodeBlock
3
+ } from "./chunk-DI46QWLX.js";
4
+
1
5
  // src/placeholder/index.ts
2
6
  import { definePlugin } from "@prosekit/core";
3
7
  import "@prosekit/pm/model";
@@ -14,6 +18,9 @@ function createPlaceholderPlugin(options) {
14
18
  if (options.strategy === "doc" && !isDocEmpty(state.doc)) {
15
19
  return null;
16
20
  }
21
+ if (isInCodeBlock(state.selection)) {
22
+ return null;
23
+ }
17
24
  const placeholderText = options.placeholder;
18
25
  const deco = createPlaceholderDecoration(state, placeholderText);
19
26
  if (!deco) {
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.2",
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",
@@ -116,16 +121,25 @@
116
121
  "dist"
117
122
  ],
118
123
  "dependencies": {
119
- "@prosekit/core": "^0.2.0",
124
+ "@prosekit/core": "^0.2.2",
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
  ],