@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.
- package/dist/_tsup-dts-rollup.d.ts +204 -29
- package/dist/chunk-DI46QWLX.js +9 -0
- package/dist/chunk-DYFRBXUX.js +56 -0
- package/dist/chunk-HQZORKGY.js +6 -0
- package/dist/prosekit-extensions-autocomplete.js +11 -4
- package/dist/prosekit-extensions-code-block.d.ts +2 -0
- package/dist/prosekit-extensions-code-block.js +218 -14
- package/dist/prosekit-extensions-heading.js +33 -13
- package/dist/prosekit-extensions-input-rule.d.ts +3 -0
- package/dist/prosekit-extensions-input-rule.js +10 -0
- package/dist/prosekit-extensions-list.js +5 -2
- package/dist/prosekit-extensions-placeholder.js +7 -0
- package/package.json +21 -4
@@ -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
|
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
|
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,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
|
+
};
|
@@ -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
|
-
|
81
|
+
OBJECT_REPLACEMENT_CHARACTER
|
79
82
|
);
|
80
83
|
const deleteMatch = () => {
|
81
|
-
if (view.state.doc.textBetween(
|
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
|
-
|
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
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
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":
|
37
|
-
"mod-2":
|
38
|
-
"mod-3":
|
39
|
-
"mod-4":
|
40
|
-
"mod-5":
|
41
|
-
"mod-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
|
46
|
-
|
47
|
-
|
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() {
|
@@ -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
|
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.
|
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.
|
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.
|
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.
|
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
|
],
|