@portabletext/plugin-character-pair-decorator 8.0.29 → 8.0.31

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/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type {EditorContext} from '@portabletext/editor'
2
+ import {JSX} from 'react'
2
3
 
3
4
  /**
4
5
  * @public
@@ -18,6 +19,6 @@ export declare function CharacterPairDecoratorPlugin(props: {
18
19
  char: string
19
20
  amount: number
20
21
  }
21
- }): null
22
+ }): JSX.Element
22
23
 
23
24
  export {}
package/dist/index.js CHANGED
@@ -1,30 +1,35 @@
1
+ import { jsx } from "react/jsx-runtime";
1
2
  import { c } from "react/compiler-runtime";
2
- import { useEditor } from "@portabletext/editor";
3
- import { defineBehavior, forward, raise, effect } from "@portabletext/editor/behaviors";
4
- import * as utils from "@portabletext/editor/utils";
5
- import { useActorRef } from "@xstate/react";
6
- import { setup, fromCallback, assign } from "xstate";
7
- import * as selectors from "@portabletext/editor/selectors";
3
+ import { defineInputRule, InputRulePlugin } from "@portabletext/plugin-input-rule";
4
+ import "react";
5
+ import { raise } from "@portabletext/editor/behaviors";
8
6
  import { getPathSubSchema } from "@portabletext/editor/traversal";
9
7
  function createCharacterPairRegex(char, amount) {
10
- const prePrefix = `(?<!\\${char})`, prefix = `\\${char}`.repeat(Math.max(amount, 1)), postPrefix = "(?!\\s)", content = `([^${char}\\n]+?)`, preSuffix = "(?<!\\s)", suffix = `\\${char}`.repeat(Math.max(amount, 1)), postSuffix = `(?!\\${char})`;
8
+ const prePrefix = `(?<!\\${char})`, prefix = `\\${char}`.repeat(Math.max(amount, 1)), postPrefix = "(?!\\s)", content = `(?<content>[^${char}\\n]+?)`, preSuffix = "(?<!\\s)", suffix = `\\${char}`.repeat(Math.max(amount, 1)), postSuffix = `(?!\\${char})`;
11
9
  return `${prePrefix}${prefix}${postPrefix}${content}${preSuffix}${suffix}${postSuffix}`;
12
10
  }
13
- function createCharacterPairDecoratorBehavior(config) {
11
+ function createCharacterPairRule(config) {
14
12
  config.pair.amount < 1 && console.warn("The amount of characters in the pair should be greater than 0");
15
- const pairRegex = createCharacterPairRegex(config.pair.char, config.pair.amount), regEx = new RegExp(`(${pairRegex})$`);
16
- return defineBehavior({
17
- on: "insert.text",
13
+ const pairRegex = createCharacterPairRegex(config.pair.char, config.pair.amount);
14
+ return defineInputRule({
15
+ // `$`-anchored: the pair must close at the insertion point, so at most
16
+ // one match fires per insert.
17
+ on: new RegExp(`${pairRegex}$`),
18
+ // A pair only deletes its markers, so a match whose CONTENT spans an
19
+ // inline object is harmless and must decorate (`**bo`, insert an
20
+ // object, `ld**`). An inline object anywhere else in the match, between
21
+ // the marker characters, means this is not a pair, and the machinery
22
+ // drops the match.
23
+ inlineObjects: {
24
+ allow: ["content"]
25
+ },
18
26
  guard: ({
19
27
  snapshot,
20
28
  event
21
29
  }) => {
22
30
  if (config.pair.amount < 1)
23
31
  return !1;
24
- const focusTextBlock = selectors.getFocusTextBlock(snapshot);
25
- if (!focusTextBlock)
26
- return !1;
27
- const subSchema = getPathSubSchema(snapshot, focusTextBlock.path), decorator = config.decorator({
32
+ const subSchema = getPathSubSchema(snapshot, event.focusBlock.path), decorator = config.decorator({
28
33
  context: {
29
34
  schema: subSchema
30
35
  },
@@ -32,291 +37,72 @@ function createCharacterPairDecoratorBehavior(config) {
32
37
  });
33
38
  if (decorator === void 0)
34
39
  return !1;
35
- const selectionStartPoint = selectors.getSelectionStartPoint(snapshot), selectionStartOffset = selectionStartPoint ? utils.spanSelectionPointToBlockOffset({
36
- snapshot,
37
- selectionPoint: selectionStartPoint
38
- }) : void 0;
39
- if (!selectionStartOffset)
40
+ const match = event.matches.at(0);
41
+ if (!match)
40
42
  return !1;
41
- const newText = `${selectors.getBlockTextBefore(snapshot)}${event.text}`, textToDecorate = newText.match(regEx)?.at(0);
42
- if (textToDecorate === void 0)
43
+ const content = match.groups.content;
44
+ if (!content)
43
45
  return !1;
44
46
  const prefixOffsets = {
45
- anchor: {
46
- path: focusTextBlock.path,
47
- // Example: "foo **bar**".length - "**bar**".length = 4
48
- offset: newText.length - textToDecorate.length
49
- },
50
- focus: {
51
- path: focusTextBlock.path,
52
- // Example: "foo **bar**".length - "**bar**".length + "*".length * 2 = 6
53
- offset: newText.length - textToDecorate.length + config.pair.char.length * config.pair.amount
54
- }
47
+ anchor: match.targetOffsets.anchor,
48
+ focus: content.targetOffsets.anchor
55
49
  }, suffixOffsets = {
56
- anchor: {
57
- path: focusTextBlock.path,
58
- // Example: "foo **bar*|" (10) + "*".length - 2 = 9
59
- offset: selectionStartOffset.offset + event.text.length - config.pair.char.length * config.pair.amount
60
- },
61
- focus: {
62
- path: focusTextBlock.path,
63
- // Example: "foo **bar*|" (10) + "*".length = 11
64
- offset: selectionStartOffset.offset + event.text.length
65
- }
50
+ anchor: content.targetOffsets.focus,
51
+ focus: match.targetOffsets.focus
66
52
  };
67
- if (prefixOffsets.focus.offset - prefixOffsets.anchor.offset > 1) {
68
- const prefixSelection = utils.blockOffsetsToSelection({
69
- snapshot,
70
- offsets: prefixOffsets
71
- }), inlineObjectBeforePrefixFocus = selectors.getPreviousInlineObject({
72
- ...snapshot,
73
- context: {
74
- ...snapshot.context,
75
- selection: prefixSelection ? {
76
- anchor: prefixSelection.focus,
77
- focus: prefixSelection.focus
78
- } : null
79
- }
80
- }), inlineObjectBeforePrefixFocusOffset = inlineObjectBeforePrefixFocus ? utils.childSelectionPointToBlockOffset({
81
- snapshot,
82
- selectionPoint: {
83
- path: inlineObjectBeforePrefixFocus.path,
84
- offset: 0
85
- }
86
- }) : void 0;
87
- if (inlineObjectBeforePrefixFocusOffset && inlineObjectBeforePrefixFocusOffset.offset > prefixOffsets.anchor.offset && inlineObjectBeforePrefixFocusOffset.offset < prefixOffsets.focus.offset)
88
- return !1;
89
- }
90
- if (suffixOffsets.focus.offset - suffixOffsets.anchor.offset > 1) {
91
- const previousInlineObject = selectors.getPreviousInlineObject(snapshot), previousInlineObjectOffset = previousInlineObject ? utils.childSelectionPointToBlockOffset({
92
- snapshot,
93
- selectionPoint: {
94
- path: previousInlineObject.path,
95
- offset: 0
96
- }
97
- }) : void 0;
98
- if (previousInlineObjectOffset && previousInlineObjectOffset.offset > suffixOffsets.anchor.offset && previousInlineObjectOffset.offset < suffixOffsets.focus.offset)
99
- return !1;
100
- }
101
53
  return {
54
+ decorator,
55
+ contentOffsets: {
56
+ anchor: content.targetOffsets.anchor,
57
+ focus: content.targetOffsets.focus
58
+ },
102
59
  prefixOffsets,
103
- suffixOffsets,
104
- decorator
60
+ suffixOffsets
105
61
  };
106
62
  },
107
- actions: [
108
- // Insert the text as usual in its own undo step
109
- ({
110
- event
111
- }) => [forward(event)],
112
- (_, {
113
- prefixOffsets,
114
- suffixOffsets,
63
+ actions: [(_, {
64
+ decorator,
65
+ contentOffsets,
66
+ prefixOffsets,
67
+ suffixOffsets
68
+ }) => [
69
+ // Decorate the text between the prefix and suffix
70
+ raise({
71
+ type: "decorator.add",
72
+ decorator,
73
+ at: contentOffsets
74
+ }),
75
+ // Delete the suffix, then the prefix (suffix first so the prefix
76
+ // offsets stay valid)
77
+ raise({
78
+ type: "delete.text",
79
+ at: suffixOffsets
80
+ }),
81
+ raise({
82
+ type: "delete.text",
83
+ at: prefixOffsets
84
+ }),
85
+ // Toggle the decorator off so the next inserted text isn't decorated
86
+ raise({
87
+ type: "decorator.remove",
115
88
  decorator
116
- }) => [
117
- // Decorate the text between the prefix and suffix
118
- raise({
119
- type: "decorator.add",
120
- decorator,
121
- at: {
122
- anchor: prefixOffsets.focus,
123
- focus: suffixOffsets.anchor
124
- }
125
- }),
126
- // Delete the suffix
127
- raise({
128
- type: "delete.text",
129
- at: suffixOffsets
130
- }),
131
- // Delete the prefix
132
- raise({
133
- type: "delete.text",
134
- at: prefixOffsets
135
- }),
136
- // Toggle the decorator off so the next inserted text isn't emphasized
137
- raise({
138
- type: "decorator.remove",
139
- decorator
140
- }),
141
- effect(() => {
142
- config.onDecorate({
143
- ...suffixOffsets.anchor,
144
- offset: suffixOffsets.anchor.offset - (prefixOffsets.focus.offset - prefixOffsets.anchor.offset)
145
- });
146
- })
147
- ]
148
- ]
89
+ })
90
+ ]]
149
91
  });
150
92
  }
151
93
  function CharacterPairDecoratorPlugin(props) {
152
- const $ = c(4), editor = useEditor();
94
+ const $ = c(7);
153
95
  let t0;
154
- return $[0] !== editor || $[1] !== props.decorator || $[2] !== props.pair ? (t0 = {
155
- input: {
156
- editor,
157
- decorator: props.decorator,
158
- pair: props.pair
159
- }
160
- }, $[0] = editor, $[1] = props.decorator, $[2] = props.pair, $[3] = t0) : t0 = $[3], useActorRef(decoratorPairMachine, t0), null;
96
+ $[0] !== props.decorator || $[1] !== props.pair ? (t0 = createCharacterPairRule({
97
+ decorator: props.decorator,
98
+ pair: props.pair
99
+ }), $[0] = props.decorator, $[1] = props.pair, $[2] = t0) : t0 = $[2];
100
+ let t1;
101
+ $[3] !== t0 ? (t1 = [t0], $[3] = t0, $[4] = t1) : t1 = $[4];
102
+ const rules = t1;
103
+ let t2;
104
+ return $[5] !== rules ? (t2 = /* @__PURE__ */ jsx(InputRulePlugin, { rules }), $[5] = rules, $[6] = t2) : t2 = $[6], t2;
161
105
  }
162
- const decorateListener = ({
163
- sendBack,
164
- input
165
- }) => input.editor.registerBehavior({
166
- behavior: createCharacterPairDecoratorBehavior({
167
- decorator: input.decorator,
168
- pair: input.pair,
169
- onDecorate: (offset) => {
170
- sendBack({
171
- type: "decorator.add",
172
- blockOffset: offset
173
- });
174
- }
175
- })
176
- }), selectionListenerCallback = ({
177
- sendBack,
178
- input
179
- }) => {
180
- const subscription = input.editor.on("selection", (event) => {
181
- if (!event.selection) {
182
- sendBack({
183
- type: "selection",
184
- blockOffsets: void 0,
185
- selection: null
186
- });
187
- return;
188
- }
189
- const snapshot = input.editor.getSnapshot(), anchor = utils.spanSelectionPointToBlockOffset({
190
- snapshot,
191
- selectionPoint: event.selection.anchor
192
- }), focus = utils.spanSelectionPointToBlockOffset({
193
- snapshot,
194
- selectionPoint: event.selection.focus
195
- });
196
- if (!anchor || !focus) {
197
- sendBack({
198
- type: "selection",
199
- blockOffsets: void 0,
200
- selection: event.selection
201
- });
202
- return;
203
- }
204
- sendBack({
205
- type: "selection",
206
- blockOffsets: {
207
- anchor,
208
- focus
209
- },
210
- selection: event.selection
211
- });
212
- });
213
- return () => subscription.unsubscribe();
214
- }, deleteBackwardListenerCallback = ({
215
- sendBack,
216
- input
217
- }) => input.editor.registerBehavior({
218
- behavior: defineBehavior({
219
- on: "delete.backward",
220
- actions: [() => [raise({
221
- type: "history.undo"
222
- }), effect(() => {
223
- sendBack({
224
- type: "delete.backward"
225
- });
226
- })]]
227
- })
228
- }), decoratorPairMachine = setup({
229
- types: {
230
- context: {},
231
- input: {},
232
- events: {}
233
- },
234
- actors: {
235
- "decorate listener": fromCallback(decorateListener),
236
- "delete.backward listener": fromCallback(deleteBackwardListenerCallback),
237
- "selection listener": fromCallback(selectionListenerCallback)
238
- }
239
- }).createMachine({
240
- id: "decorator pair",
241
- context: ({
242
- input
243
- }) => ({
244
- decorator: input.decorator,
245
- editor: input.editor,
246
- endSelection: null,
247
- pair: input.pair
248
- }),
249
- initial: "idle",
250
- states: {
251
- idle: {
252
- invoke: [{
253
- src: "decorate listener",
254
- input: ({
255
- context
256
- }) => ({
257
- decorator: context.decorator,
258
- editor: context.editor,
259
- pair: context.pair
260
- })
261
- }],
262
- on: {
263
- "decorator.add": {
264
- target: "decorator added",
265
- actions: assign({
266
- offsetAfterDecorator: ({
267
- event
268
- }) => event.blockOffset,
269
- endSelection: ({
270
- context
271
- }) => context.editor.getSnapshot().context.selection
272
- })
273
- }
274
- }
275
- },
276
- "decorator added": {
277
- exit: [assign({
278
- offsetAfterDecorator: void 0
279
- })],
280
- invoke: [{
281
- src: "selection listener",
282
- input: ({
283
- context
284
- }) => ({
285
- editor: context.editor
286
- })
287
- }, {
288
- src: "delete.backward listener",
289
- input: ({
290
- context
291
- }) => ({
292
- editor: context.editor
293
- })
294
- }],
295
- on: {
296
- selection: {
297
- target: "idle",
298
- guard: ({
299
- context,
300
- event
301
- }) => {
302
- const offsetAfterDecorator = context.offsetAfterDecorator;
303
- if (event.blockOffsets && offsetAfterDecorator) {
304
- const decoratorBlock = offsetAfterDecorator.path.at(-1), anchorBlock = event.blockOffsets.anchor.path.at(-1), focusBlock = event.blockOffsets.focus.path.at(-1);
305
- if (!utils.isKeyedSegment(decoratorBlock) || !utils.isKeyedSegment(anchorBlock) || !utils.isKeyedSegment(focusBlock))
306
- return !1;
307
- const anchorChanged = decoratorBlock._key !== anchorBlock._key || offsetAfterDecorator.offset !== event.blockOffsets.anchor.offset, focusChanged = decoratorBlock._key !== focusBlock._key || offsetAfterDecorator.offset !== event.blockOffsets.focus.offset;
308
- return anchorChanged || focusChanged;
309
- }
310
- return !utils.isEqualSelections(context.endSelection, event.selection);
311
- }
312
- },
313
- "delete.backward": {
314
- target: "idle"
315
- }
316
- }
317
- }
318
- }
319
- });
320
106
  export {
321
107
  CharacterPairDecoratorPlugin
322
108
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/regex.character-pair.ts","../src/behavior.character-pair-decorator.ts","../src/plugin.character-pair-decorator.ts"],"sourcesContent":["export function createCharacterPairRegex(char: string, amount: number) {\n // Negative lookbehind: Ensures that the matched sequence is not preceded by the same character\n const prePrefix = `(?<!\\\\${char})`\n\n // Repeats the character `amount` times\n const prefix = `\\\\${char}`.repeat(Math.max(amount, 1))\n\n // Negative lookahead: Ensures that the opening pair (**, *, etc.) is not followed by a space\n const postPrefix = `(?!\\\\s)`\n\n // Captures the content inside the pair\n const content = `([^${char}\\\\n]+?)`\n\n // Negative lookbehind: Ensures that the content is not followed by a space\n const preSuffix = `(?<!\\\\s)`\n\n // Repeats the character `amount` times\n const suffix = `\\\\${char}`.repeat(Math.max(amount, 1))\n\n // Negative lookahead: Ensures that the matched sequence is not followed by the same character\n const postSuffix = `(?!\\\\${char})`\n\n return `${prePrefix}${prefix}${postPrefix}${content}${preSuffix}${suffix}${postSuffix}`\n}\n","import type {BlockOffset, EditorContext} from '@portabletext/editor'\nimport {\n defineBehavior,\n effect,\n forward,\n raise,\n} from '@portabletext/editor/behaviors'\nimport * as selectors from '@portabletext/editor/selectors'\nimport {getPathSubSchema} from '@portabletext/editor/traversal'\nimport * as utils from '@portabletext/editor/utils'\nimport {createCharacterPairRegex} from './regex.character-pair'\n\nexport function createCharacterPairDecoratorBehavior(config: {\n decorator: ({\n context,\n schema,\n }: {\n context: Pick<EditorContext, 'schema'>\n /**\n * @deprecated Use `context.schema` instead\n */\n schema: EditorContext['schema']\n }) => string | undefined\n pair: {char: string; amount: number}\n onDecorate: (offset: BlockOffset) => void\n}) {\n if (config.pair.amount < 1) {\n console.warn(\n `The amount of characters in the pair should be greater than 0`,\n )\n }\n\n const pairRegex = createCharacterPairRegex(\n config.pair.char,\n config.pair.amount,\n )\n const regEx = new RegExp(`(${pairRegex})$`)\n\n return defineBehavior({\n on: 'insert.text',\n guard: ({snapshot, event}) => {\n if (config.pair.amount < 1) {\n return false\n }\n\n const focusTextBlock = selectors.getFocusTextBlock(snapshot)\n\n if (!focusTextBlock) {\n return false\n }\n\n const subSchema = getPathSubSchema(snapshot, focusTextBlock.path)\n const decorator = config.decorator({\n context: {schema: subSchema},\n schema: subSchema,\n })\n\n if (decorator === undefined) {\n return false\n }\n\n const selectionStartPoint = selectors.getSelectionStartPoint(snapshot)\n const selectionStartOffset = selectionStartPoint\n ? utils.spanSelectionPointToBlockOffset({\n snapshot,\n selectionPoint: selectionStartPoint,\n })\n : undefined\n\n if (!selectionStartOffset) {\n return false\n }\n\n const textBefore = selectors.getBlockTextBefore(snapshot)\n const newText = `${textBefore}${event.text}`\n const textToDecorate = newText.match(regEx)?.at(0)\n\n if (textToDecorate === undefined) {\n return false\n }\n\n const prefixOffsets = {\n anchor: {\n path: focusTextBlock.path,\n // Example: \"foo **bar**\".length - \"**bar**\".length = 4\n offset: newText.length - textToDecorate.length,\n },\n focus: {\n path: focusTextBlock.path,\n // Example: \"foo **bar**\".length - \"**bar**\".length + \"*\".length * 2 = 6\n offset:\n newText.length -\n textToDecorate.length +\n config.pair.char.length * config.pair.amount,\n },\n }\n\n const suffixOffsets = {\n anchor: {\n path: focusTextBlock.path,\n // Example: \"foo **bar*|\" (10) + \"*\".length - 2 = 9\n offset:\n selectionStartOffset.offset +\n event.text.length -\n config.pair.char.length * config.pair.amount,\n },\n focus: {\n path: focusTextBlock.path,\n // Example: \"foo **bar*|\" (10) + \"*\".length = 11\n offset: selectionStartOffset.offset + event.text.length,\n },\n }\n\n // If the prefix is more than one character, then we need to check if\n // there is an inline object inside it\n if (prefixOffsets.focus.offset - prefixOffsets.anchor.offset > 1) {\n const prefixSelection = utils.blockOffsetsToSelection({\n snapshot,\n offsets: prefixOffsets,\n })\n const inlineObjectBeforePrefixFocus = selectors.getPreviousInlineObject(\n {\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: prefixSelection\n ? {\n anchor: prefixSelection.focus,\n focus: prefixSelection.focus,\n }\n : null,\n },\n },\n )\n const inlineObjectBeforePrefixFocusOffset =\n inlineObjectBeforePrefixFocus\n ? utils.childSelectionPointToBlockOffset({\n snapshot,\n selectionPoint: {\n path: inlineObjectBeforePrefixFocus.path,\n offset: 0,\n },\n })\n : undefined\n\n if (\n inlineObjectBeforePrefixFocusOffset &&\n inlineObjectBeforePrefixFocusOffset.offset >\n prefixOffsets.anchor.offset &&\n inlineObjectBeforePrefixFocusOffset.offset <\n prefixOffsets.focus.offset\n ) {\n return false\n }\n }\n\n // If the suffix is more than one character, then we need to check if\n // there is an inline object inside it\n if (suffixOffsets.focus.offset - suffixOffsets.anchor.offset > 1) {\n const previousInlineObject = selectors.getPreviousInlineObject(snapshot)\n const previousInlineObjectOffset = previousInlineObject\n ? utils.childSelectionPointToBlockOffset({\n snapshot,\n selectionPoint: {\n path: previousInlineObject.path,\n offset: 0,\n },\n })\n : undefined\n\n if (\n previousInlineObjectOffset &&\n previousInlineObjectOffset.offset > suffixOffsets.anchor.offset &&\n previousInlineObjectOffset.offset < suffixOffsets.focus.offset\n ) {\n return false\n }\n }\n\n return {\n prefixOffsets,\n suffixOffsets,\n decorator,\n }\n },\n actions: [\n // Insert the text as usual in its own undo step\n ({event}) => [forward(event)],\n (_, {prefixOffsets, suffixOffsets, decorator}) => [\n // Decorate the text between the prefix and suffix\n raise({\n type: 'decorator.add',\n decorator,\n at: {\n anchor: prefixOffsets.focus,\n focus: suffixOffsets.anchor,\n },\n }),\n // Delete the suffix\n raise({\n type: 'delete.text',\n at: suffixOffsets,\n }),\n // Delete the prefix\n raise({\n type: 'delete.text',\n at: prefixOffsets,\n }),\n // Toggle the decorator off so the next inserted text isn't emphasized\n raise({\n type: 'decorator.remove',\n decorator,\n }),\n effect(() => {\n config.onDecorate({\n ...suffixOffsets.anchor,\n offset:\n suffixOffsets.anchor.offset -\n (prefixOffsets.focus.offset - prefixOffsets.anchor.offset),\n })\n }),\n ],\n ],\n })\n}\n","import type {\n BlockOffset,\n Editor,\n EditorContext,\n EditorSelection,\n} from '@portabletext/editor'\nimport {useEditor} from '@portabletext/editor'\nimport {defineBehavior, effect, raise} from '@portabletext/editor/behaviors'\nimport * as utils from '@portabletext/editor/utils'\nimport {useActorRef} from '@xstate/react'\nimport {\n assign,\n fromCallback,\n setup,\n type AnyEventObject,\n type CallbackLogicFunction,\n} from 'xstate'\nimport {createCharacterPairDecoratorBehavior} from './behavior.character-pair-decorator'\n\n/**\n * @public\n */\nexport function CharacterPairDecoratorPlugin(props: {\n decorator: ({\n context,\n schema,\n }: {\n context: Pick<EditorContext, 'schema'>\n /**\n * @deprecated Use `context.schema` instead\n */\n schema: EditorContext['schema']\n }) => string | undefined\n pair: {char: string; amount: number}\n}) {\n const editor = useEditor()\n\n useActorRef(decoratorPairMachine, {\n input: {\n editor,\n decorator: props.decorator,\n pair: props.pair,\n },\n })\n\n return null\n}\n\ntype DecoratorPairEvent =\n | {\n type: 'decorator.add'\n blockOffset: BlockOffset\n }\n | {\n type: 'selection'\n blockOffsets?: {\n anchor: BlockOffset\n focus: BlockOffset\n }\n selection: EditorSelection\n }\n | {\n type: 'delete.backward'\n }\n\nconst decorateListener: CallbackLogicFunction<\n AnyEventObject,\n DecoratorPairEvent,\n {\n decorator: ({\n context,\n schema,\n }: {\n context: Pick<EditorContext, 'schema'>\n /**\n * @deprecated Use `context.schema` instead\n */\n schema: EditorContext['schema']\n }) => string | undefined\n editor: Editor\n pair: {char: string; amount: number}\n }\n> = ({sendBack, input}) => {\n const unregister = input.editor.registerBehavior({\n behavior: createCharacterPairDecoratorBehavior({\n decorator: input.decorator,\n pair: input.pair,\n onDecorate: (offset) => {\n sendBack({type: 'decorator.add', blockOffset: offset})\n },\n }),\n })\n\n return unregister\n}\n\nconst selectionListenerCallback: CallbackLogicFunction<\n AnyEventObject,\n DecoratorPairEvent,\n {editor: Editor}\n> = ({sendBack, input}) => {\n // Listen for the emitted 'selection' event which fires after ANY cursor\n // movement (typing, clicking, pasting, etc.) - not just explicit 'select'\n // behavior events.\n const subscription = input.editor.on('selection', (event) => {\n if (!event.selection) {\n sendBack({type: 'selection', blockOffsets: undefined, selection: null})\n return\n }\n\n const snapshot = input.editor.getSnapshot()\n const anchor = utils.spanSelectionPointToBlockOffset({\n snapshot,\n selectionPoint: event.selection.anchor,\n })\n const focus = utils.spanSelectionPointToBlockOffset({\n snapshot,\n selectionPoint: event.selection.focus,\n })\n\n if (!anchor || !focus) {\n sendBack({\n type: 'selection',\n blockOffsets: undefined,\n selection: event.selection,\n })\n return\n }\n\n sendBack({\n type: 'selection',\n blockOffsets: {anchor, focus},\n selection: event.selection,\n })\n })\n\n return () => subscription.unsubscribe()\n}\n\nconst deleteBackwardListenerCallback: CallbackLogicFunction<\n AnyEventObject,\n DecoratorPairEvent,\n {editor: Editor}\n> = ({sendBack, input}) => {\n const unregister = input.editor.registerBehavior({\n behavior: defineBehavior({\n on: 'delete.backward',\n actions: [\n () => [\n raise({\n type: 'history.undo',\n }),\n effect(() => {\n sendBack({type: 'delete.backward'})\n }),\n ],\n ],\n }),\n })\n\n return unregister\n}\n\nconst decoratorPairMachine = setup({\n types: {\n context: {} as {\n decorator: ({\n context,\n schema,\n }: {\n context: Pick<EditorContext, 'schema'>\n /**\n * @deprecated Use `context.schema` instead\n */\n schema: EditorContext['schema']\n }) => string | undefined\n editor: Editor\n offsetAfterDecorator?: BlockOffset\n endSelection: EditorSelection\n pair: {char: string; amount: number}\n },\n input: {} as {\n decorator: ({\n context,\n schema,\n }: {\n context: Pick<EditorContext, 'schema'>\n /**\n * @deprecated Use `context.schema` instead\n */\n schema: EditorContext['schema']\n }) => string | undefined\n editor: Editor\n pair: {char: string; amount: number}\n },\n events: {} as DecoratorPairEvent,\n },\n actors: {\n 'decorate listener': fromCallback(decorateListener),\n 'delete.backward listener': fromCallback(deleteBackwardListenerCallback),\n 'selection listener': fromCallback(selectionListenerCallback),\n },\n}).createMachine({\n id: 'decorator pair',\n context: ({input}) => ({\n decorator: input.decorator,\n editor: input.editor,\n endSelection: null,\n pair: input.pair,\n }),\n initial: 'idle',\n states: {\n 'idle': {\n invoke: [\n {\n src: 'decorate listener',\n input: ({context}) => ({\n decorator: context.decorator,\n editor: context.editor,\n pair: context.pair,\n }),\n },\n ],\n on: {\n 'decorator.add': {\n target: 'decorator added',\n actions: assign({\n offsetAfterDecorator: ({event}) => event.blockOffset,\n endSelection: ({context}) =>\n context.editor.getSnapshot().context.selection,\n }),\n },\n },\n },\n 'decorator added': {\n exit: [\n assign({\n offsetAfterDecorator: undefined,\n }),\n ],\n invoke: [\n {\n src: 'selection listener',\n input: ({context}) => ({editor: context.editor}),\n },\n {\n src: 'delete.backward listener',\n input: ({context}) => ({editor: context.editor}),\n },\n ],\n on: {\n 'selection': {\n target: 'idle',\n guard: ({context, event}) => {\n const offsetAfterDecorator = context.offsetAfterDecorator\n\n if (event.blockOffsets && offsetAfterDecorator) {\n // Block offsets are compared rather than raw selection points\n // because they normalize away span-level differences (e.g. the\n // cursor at the same position but in a different span after\n // normalization).\n const decoratorBlock = offsetAfterDecorator.path.at(-1)\n const anchorBlock = event.blockOffsets.anchor.path.at(-1)\n const focusBlock = event.blockOffsets.focus.path.at(-1)\n\n if (\n !utils.isKeyedSegment(decoratorBlock) ||\n !utils.isKeyedSegment(anchorBlock) ||\n !utils.isKeyedSegment(focusBlock)\n ) {\n return false\n }\n\n const anchorChanged =\n decoratorBlock._key !== anchorBlock._key ||\n offsetAfterDecorator.offset !== event.blockOffsets.anchor.offset\n const focusChanged =\n decoratorBlock._key !== focusBlock._key ||\n offsetAfterDecorator.offset !== event.blockOffsets.focus.offset\n\n return anchorChanged || focusChanged\n }\n\n // Block offsets can't be computed when the cursor is on an inline\n // object, so fall back to comparing the raw selections.\n return !utils.isEqualSelections(\n context.endSelection,\n event.selection,\n )\n },\n },\n 'delete.backward': {\n target: 'idle',\n },\n },\n },\n },\n})\n"],"names":["createCharacterPairRegex","char","amount","prePrefix","prefix","repeat","Math","max","postPrefix","content","preSuffix","suffix","postSuffix","createCharacterPairDecoratorBehavior","config","pair","console","warn","pairRegex","regEx","RegExp","defineBehavior","on","guard","snapshot","event","focusTextBlock","selectors","getFocusTextBlock","subSchema","getPathSubSchema","path","decorator","context","schema","undefined","selectionStartPoint","getSelectionStartPoint","selectionStartOffset","utils","spanSelectionPointToBlockOffset","selectionPoint","newText","getBlockTextBefore","text","textToDecorate","match","at","prefixOffsets","anchor","offset","length","focus","suffixOffsets","prefixSelection","blockOffsetsToSelection","offsets","inlineObjectBeforePrefixFocus","getPreviousInlineObject","selection","inlineObjectBeforePrefixFocusOffset","childSelectionPointToBlockOffset","previousInlineObject","previousInlineObjectOffset","actions","forward","_","raise","type","effect","onDecorate","CharacterPairDecoratorPlugin","props","$","_c","editor","useEditor","t0","input","useActorRef","decoratorPairMachine","decorateListener","sendBack","registerBehavior","behavior","blockOffset","selectionListenerCallback","subscription","blockOffsets","getSnapshot","unsubscribe","deleteBackwardListenerCallback","setup","types","events","actors","fromCallback","createMachine","id","endSelection","initial","states","invoke","src","target","assign","offsetAfterDecorator","exit","decoratorBlock","anchorBlock","focusBlock","isKeyedSegment","anchorChanged","_key","focusChanged","isEqualSelections"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/regex.character-pair.ts","../src/rule.character-pair.ts","../src/plugin.character-pair-decorator.tsx"],"sourcesContent":["export function createCharacterPairRegex(char: string, amount: number) {\n // Negative lookbehind: Ensures that the matched sequence is not preceded by the same character\n const prePrefix = `(?<!\\\\${char})`\n\n // Repeats the character `amount` times\n const prefix = `\\\\${char}`.repeat(Math.max(amount, 1))\n\n // Negative lookahead: Ensures that the opening pair (**, *, etc.) is not followed by a space\n const postPrefix = `(?!\\\\s)`\n\n // Captures the content inside the pair as the named group `content`\n const content = `(?<content>[^${char}\\\\n]+?)`\n\n // Negative lookbehind: Ensures that the content is not followed by a space\n const preSuffix = `(?<!\\\\s)`\n\n // Repeats the character `amount` times\n const suffix = `\\\\${char}`.repeat(Math.max(amount, 1))\n\n // Negative lookahead: Ensures that the matched sequence is not followed by the same character\n const postSuffix = `(?!\\\\${char})`\n\n return `${prePrefix}${prefix}${postPrefix}${content}${preSuffix}${suffix}${postSuffix}`\n}\n","import type {EditorContext} from '@portabletext/editor'\nimport {raise} from '@portabletext/editor/behaviors'\nimport {getPathSubSchema} from '@portabletext/editor/traversal'\nimport {defineInputRule} from '@portabletext/plugin-input-rule'\nimport {createCharacterPairRegex} from './regex.character-pair'\n\n/**\n * A character pair is an input rule: typing the closing half of a pair\n * (`**bold**`, `_em_`) decorates the content between the markers and deletes\n * the markers. `defineInputRule` supplies the match locations; the\n * `InputRulePlugin` machinery supplies smart-undo on Backspace and the\n * caret-departure tracking the plugin previously hand-rolled.\n */\nexport function createCharacterPairRule(config: {\n decorator: ({\n context,\n schema,\n }: {\n context: Pick<EditorContext, 'schema'>\n /**\n * @deprecated Use `context.schema` instead\n */\n schema: EditorContext['schema']\n }) => string | undefined\n pair: {char: string; amount: number}\n}) {\n if (config.pair.amount < 1) {\n console.warn(\n `The amount of characters in the pair should be greater than 0`,\n )\n }\n\n const pairRegex = createCharacterPairRegex(\n config.pair.char,\n config.pair.amount,\n )\n\n return defineInputRule({\n // `$`-anchored: the pair must close at the insertion point, so at most\n // one match fires per insert.\n on: new RegExp(`${pairRegex}$`),\n // A pair only deletes its markers, so a match whose CONTENT spans an\n // inline object is harmless and must decorate (`**bo`, insert an\n // object, `ld**`). An inline object anywhere else in the match, between\n // the marker characters, means this is not a pair, and the machinery\n // drops the match.\n inlineObjects: {allow: ['content']},\n guard: ({snapshot, event}) => {\n if (config.pair.amount < 1) {\n return false\n }\n\n const subSchema = getPathSubSchema(snapshot, event.focusBlock.path)\n const decorator = config.decorator({\n context: {schema: subSchema},\n schema: subSchema,\n })\n\n if (decorator === undefined) {\n return false\n }\n\n const match = event.matches.at(0)\n\n if (!match) {\n return false\n }\n\n // `createCharacterPairRegex` captures the content between the markers\n // as the named group `content`. Prefix and suffix offsets derive from\n // the whole match and the content group.\n const content = match.groups['content']\n\n if (!content) {\n return false\n }\n\n const prefixOffsets = {\n anchor: match.targetOffsets.anchor,\n focus: content.targetOffsets.anchor,\n }\n const suffixOffsets = {\n anchor: content.targetOffsets.focus,\n focus: match.targetOffsets.focus,\n }\n\n return {\n decorator,\n contentOffsets: {\n anchor: content.targetOffsets.anchor,\n focus: content.targetOffsets.focus,\n },\n prefixOffsets,\n suffixOffsets,\n }\n },\n actions: [\n (_, {decorator, contentOffsets, prefixOffsets, suffixOffsets}) => [\n // Decorate the text between the prefix and suffix\n raise({\n type: 'decorator.add',\n decorator,\n at: contentOffsets,\n }),\n // Delete the suffix, then the prefix (suffix first so the prefix\n // offsets stay valid)\n raise({\n type: 'delete.text',\n at: suffixOffsets,\n }),\n raise({\n type: 'delete.text',\n at: prefixOffsets,\n }),\n // Toggle the decorator off so the next inserted text isn't decorated\n raise({\n type: 'decorator.remove',\n decorator,\n }),\n ],\n ],\n })\n}\n","import type {EditorContext} from '@portabletext/editor'\nimport {InputRulePlugin} from '@portabletext/plugin-input-rule'\nimport {useMemo} from 'react'\nimport {createCharacterPairRule} from './rule.character-pair'\n\n/**\n * @public\n */\nexport function CharacterPairDecoratorPlugin(props: {\n decorator: ({\n context,\n schema,\n }: {\n context: Pick<EditorContext, 'schema'>\n /**\n * @deprecated Use `context.schema` instead\n */\n schema: EditorContext['schema']\n }) => string | undefined\n pair: {char: string; amount: number}\n}) {\n const rules = useMemo(\n () => [\n createCharacterPairRule({\n decorator: props.decorator,\n pair: props.pair,\n }),\n ],\n [props.decorator, props.pair],\n )\n\n return <InputRulePlugin rules={rules} />\n}\n"],"names":["createCharacterPairRegex","char","amount","prePrefix","prefix","repeat","Math","max","postPrefix","content","preSuffix","suffix","postSuffix","createCharacterPairRule","config","pair","console","warn","pairRegex","defineInputRule","on","RegExp","inlineObjects","allow","guard","snapshot","event","subSchema","getPathSubSchema","focusBlock","path","decorator","context","schema","undefined","match","matches","at","groups","prefixOffsets","anchor","targetOffsets","focus","suffixOffsets","contentOffsets","actions","_","raise","type","CharacterPairDecoratorPlugin","props","$","_c","t0","t1","rules","t2"],"mappings":";;;;;;AAAO,SAASA,yBAAyBC,MAAcC,QAAgB;AAErE,QAAMC,YAAY,SAASF,IAAI,KAGzBG,SAAS,KAAKH,IAAI,GAAGI,OAAOC,KAAKC,IAAIL,QAAQ,CAAC,CAAC,GAG/CM,aAAa,WAGbC,UAAU,gBAAgBR,IAAI,WAG9BS,YAAY,YAGZC,SAAS,KAAKV,IAAI,GAAGI,OAAOC,KAAKC,IAAIL,QAAQ,CAAC,CAAC,GAG/CU,aAAa,QAAQX,IAAI;AAE/B,SAAO,GAAGE,SAAS,GAAGC,MAAM,GAAGI,UAAU,GAAGC,OAAO,GAAGC,SAAS,GAAGC,MAAM,GAAGC,UAAU;AACvF;ACVO,SAASC,wBAAwBC,QAYrC;AACGA,SAAOC,KAAKb,SAAS,KACvBc,QAAQC,KACN,+DACF;AAGF,QAAMC,YAAYlB,yBAChBc,OAAOC,KAAKd,MACZa,OAAOC,KAAKb,MACd;AAEA,SAAOiB,gBAAgB;AAAA;AAAA;AAAA,IAGrBC,IAAI,IAAIC,OAAO,GAAGH,SAAS,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAM9BI,eAAe;AAAA,MAACC,OAAO,CAAC,SAAS;AAAA,IAAA;AAAA,IACjCC,OAAOA,CAAC;AAAA,MAACC;AAAAA,MAAUC;AAAAA,IAAAA,MAAW;AAC5B,UAAIZ,OAAOC,KAAKb,SAAS;AACvB,eAAO;AAGT,YAAMyB,YAAYC,iBAAiBH,UAAUC,MAAMG,WAAWC,IAAI,GAC5DC,YAAYjB,OAAOiB,UAAU;AAAA,QACjCC,SAAS;AAAA,UAACC,QAAQN;AAAAA,QAAAA;AAAAA,QAClBM,QAAQN;AAAAA,MAAAA,CACT;AAED,UAAII,cAAcG;AAChB,eAAO;AAGT,YAAMC,QAAQT,MAAMU,QAAQC,GAAG,CAAC;AAEhC,UAAI,CAACF;AACH,eAAO;AAMT,YAAM1B,UAAU0B,MAAMG,OAAO;AAE7B,UAAI,CAAC7B;AACH,eAAO;AAGT,YAAM8B,gBAAgB;AAAA,QACpBC,QAAQL,MAAMM,cAAcD;AAAAA,QAC5BE,OAAOjC,QAAQgC,cAAcD;AAAAA,MAAAA,GAEzBG,gBAAgB;AAAA,QACpBH,QAAQ/B,QAAQgC,cAAcC;AAAAA,QAC9BA,OAAOP,MAAMM,cAAcC;AAAAA,MAAAA;AAG7B,aAAO;AAAA,QACLX;AAAAA,QACAa,gBAAgB;AAAA,UACdJ,QAAQ/B,QAAQgC,cAAcD;AAAAA,UAC9BE,OAAOjC,QAAQgC,cAAcC;AAAAA,QAAAA;AAAAA,QAE/BH;AAAAA,QACAI;AAAAA,MAAAA;AAAAA,IAEJ;AAAA,IACAE,SAAS,CACP,CAACC,GAAG;AAAA,MAACf;AAAAA,MAAWa;AAAAA,MAAgBL;AAAAA,MAAeI;AAAAA,IAAAA,MAAmB;AAAA;AAAA,MAEhEI,MAAM;AAAA,QACJC,MAAM;AAAA,QACNjB;AAAAA,QACAM,IAAIO;AAAAA,MAAAA,CACL;AAAA;AAAA;AAAA,MAGDG,MAAM;AAAA,QACJC,MAAM;AAAA,QACNX,IAAIM;AAAAA,MAAAA,CACL;AAAA,MACDI,MAAM;AAAA,QACJC,MAAM;AAAA,QACNX,IAAIE;AAAAA,MAAAA,CACL;AAAA;AAAA,MAEDQ,MAAM;AAAA,QACJC,MAAM;AAAA,QACNjB;AAAAA,MAAAA,CACD;AAAA,IAAA,CACF;AAAA,EAAA,CAEJ;AACH;AClHO,SAAAkB,6BAAAC,OAAA;AAAA,QAAAC,IAAAC,EAAA,CAAA;AAAA,MAAAC;AAAAF,IAAA,CAAA,MAAAD,MAAAnB,aAAAoB,EAAA,CAAA,MAAAD,MAAAnC,QAeDsC,KAAAxC,wBAAwB;AAAA,IAAAkB,WACXmB,MAAKnB;AAAAA,IAAUhB,MACpBmC,MAAKnC;AAAAA,EAAAA,CACZ,GAACoC,EAAA,CAAA,IAAAD,MAAAnB,WAAAoB,EAAA,CAAA,IAAAD,MAAAnC,MAAAoC,OAAAE,MAAAA,KAAAF,EAAA,CAAA;AAAA,MAAAG;AAAAH,WAAAE,MAJEC,KAAA,CACJD,EAGE,GACHF,OAAAE,IAAAF,OAAAG,MAAAA,KAAAH,EAAA,CAAA;AANH,QAAAI,QACQD;AAOP,MAAAE;AAAA,SAAAL,SAAAI,SAEMC,yBAAC,iBAAA,EAAuBD,OAAK,GAAIJ,OAAAI,OAAAJ,OAAAK,MAAAA,KAAAL,EAAA,CAAA,GAAjCK;AAAiC;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/plugin-character-pair-decorator",
3
- "version": "8.0.29",
3
+ "version": "8.0.31",
4
4
  "description": "Automatically match a pair of characters and decorate the text in between",
5
5
  "keywords": [
6
6
  "behaviors",
@@ -32,8 +32,7 @@
32
32
  "./package.json": "./package.json"
33
33
  },
34
34
  "dependencies": {
35
- "@xstate/react": "^6.1.0",
36
- "xstate": "^5.32.2"
35
+ "@portabletext/plugin-input-rule": "^6.0.0"
37
36
  },
38
37
  "devDependencies": {
39
38
  "@sanity/pkg-utils": "^10.8.2",
@@ -49,13 +48,14 @@
49
48
  "typescript": "6.0.3",
50
49
  "typescript-eslint": "^8.62.0",
51
50
  "vitest": "^4.1.9",
52
- "@portabletext/editor": "^7.10.2",
51
+ "@portabletext/editor": "^7.10.3",
53
52
  "@portabletext/schema": "2.2.2",
53
+ "@portabletext/test": "1.0.4",
54
54
  "racejar": "2.0.9"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "react": "^19.2",
58
- "@portabletext/editor": "^7.10.2"
58
+ "@portabletext/editor": "^7.10.3"
59
59
  },
60
60
  "engines": {
61
61
  "node": ">=20.19 <22 || >=22.12"