@portabletext/plugin-input-rule 5.0.30 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -5
- package/dist/index.d.ts +70 -19
- package/dist/index.js +70 -26
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -71,6 +71,34 @@ export function MyMarkdownPlugin() {
|
|
|
71
71
|
|
|
72
72
|
> **Tip:** The [`@portabletext/plugin-markdown-shortcuts`](../plugin-markdown-shortcuts/) package is already built using Input Rules and provides common markdown shortcuts out of the box.
|
|
73
73
|
|
|
74
|
+
## Working with capture groups
|
|
75
|
+
|
|
76
|
+
When a rule needs the location of a _part_ of its match, capture that part in a **named group** and read it back by name from `match.groups`:
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
const mentionRule = defineInputRule({
|
|
80
|
+
on: /@(?<handle>\w+)!$/,
|
|
81
|
+
guard: ({event}) => {
|
|
82
|
+
const match = event.matches.at(0)
|
|
83
|
+
const handle = match?.groups['handle']
|
|
84
|
+
|
|
85
|
+
if (!handle) {
|
|
86
|
+
return false
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return {handle}
|
|
90
|
+
},
|
|
91
|
+
actions: [
|
|
92
|
+
(_, {handle}) => [
|
|
93
|
+
// `handle.text` is the captured text, `handle.targetOffsets` its
|
|
94
|
+
// location — same shape as the match itself
|
|
95
|
+
],
|
|
96
|
+
],
|
|
97
|
+
})
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`match.groups` mirrors the platform's `RegExpMatchArray.groups`: entries exist only for named groups that participated in the match, so always handle `undefined` (an optional group may not have matched). A capture group must be named (`(?<name>...)`) for its location to be handed back — unnamed groups remain useful for regex mechanics like alternation (`/^(-|\*) /`) but get no location. In codebases with `noPropertyAccessFromIndexSignature` enabled, access entries with brackets: `match.groups['handle']`.
|
|
101
|
+
|
|
74
102
|
## Text transformation rules
|
|
75
103
|
|
|
76
104
|
Text transformations are so common that the plugin provides a high-level `defineTextTransformRule` helper to configure them without any boilerplate:
|
|
@@ -93,6 +121,18 @@ export function MyTypographyPlugin() {
|
|
|
93
121
|
|
|
94
122
|
In fact, the production-ready [`@portabletext/plugin-typography`](../plugin-typography/) is built on top of Input Rules and comes packed with common text transformations like this.
|
|
95
123
|
|
|
124
|
+
A transform that should replace only _part_ of its match uses the record form of `transform`: keys name the capture groups to replace, each with its own transform:
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
const multiplicationRule = defineTextTransformRule({
|
|
128
|
+
on: /\d+\s?(?<operator>[*x])\s?\d+/,
|
|
129
|
+
// Only the operator's span is replaced; the digits around it stay
|
|
130
|
+
transform: {operator: () => '×'},
|
|
131
|
+
})
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Unlike ProseMirror's and TipTap's input rules, which implicitly replace the first capture group when one exists, replacement targets are always declared: a function transform replaces the whole match, regardless of any capture groups, and a record transform replaces exactly the groups its keys name. `defineTextTransformRule` throws at definition time when a key names a group the pattern doesn't have, and a match in which none of the keys participated is skipped. The surrounding context (like the digits above) must stay _inside_ the match rather than in lookarounds, a rule only fires when its match involves the just-inserted text, so a trailing lookahead would leave the match entirely in already-typed text and the rule would never trigger.
|
|
135
|
+
|
|
96
136
|
## Advanced examples
|
|
97
137
|
|
|
98
138
|
Input Rules can handle more complex transformations. Here are two advanced examples:
|
|
@@ -106,7 +146,7 @@ This example shows how to convert markdown-style link syntax `[text](url)` into
|
|
|
106
146
|
|
|
107
147
|
```tsx
|
|
108
148
|
const markdownLinkRule = defineInputRule({
|
|
109
|
-
on: /\[(
|
|
149
|
+
on: /\[(?<text>.+)]\((?<href>.+)\)/,
|
|
110
150
|
actions: [
|
|
111
151
|
({snapshot, event}) => {
|
|
112
152
|
const newText = event.textBefore + event.textInserted
|
|
@@ -114,8 +154,8 @@ const markdownLinkRule = defineInputRule({
|
|
|
114
154
|
const actions: Array<BehaviorAction> = []
|
|
115
155
|
|
|
116
156
|
for (const match of event.matches.reverse()) {
|
|
117
|
-
const textMatch = match.
|
|
118
|
-
const hrefMatch = match.
|
|
157
|
+
const textMatch = match.groups['text']
|
|
158
|
+
const hrefMatch = match.groups['href']
|
|
119
159
|
|
|
120
160
|
if (textMatch === undefined || hrefMatch === undefined) {
|
|
121
161
|
continue
|
|
@@ -193,7 +233,7 @@ This example demonstrates how to convert text patterns like `{AAPL}` into custom
|
|
|
193
233
|
|
|
194
234
|
```tsx
|
|
195
235
|
const stockTickerRule = defineInputRule({
|
|
196
|
-
on: /\{(
|
|
236
|
+
on: /\{(?<symbol>.+)\}/,
|
|
197
237
|
guard: ({snapshot, event}) => {
|
|
198
238
|
const match = event.matches.at(0)
|
|
199
239
|
|
|
@@ -201,7 +241,7 @@ const stockTickerRule = defineInputRule({
|
|
|
201
241
|
return false
|
|
202
242
|
}
|
|
203
243
|
|
|
204
|
-
const symbolMatch = match.
|
|
244
|
+
const symbolMatch = match.groups['symbol']
|
|
205
245
|
|
|
206
246
|
if (symbolMatch === undefined) {
|
|
207
247
|
return false
|
|
@@ -252,3 +292,27 @@ const stockTickerRule = defineInputRule({
|
|
|
252
292
|
],
|
|
253
293
|
})
|
|
254
294
|
```
|
|
295
|
+
|
|
296
|
+
## Matches spanning inline objects
|
|
297
|
+
|
|
298
|
+
Inline objects don't contribute to the text your RegExp matches against, so a pattern can match "across" one without knowing it. By default, such a match is dropped before your `guard` runs: for rules like the stock ticker above, whose actions `delete` the matched range and replace it, firing would destroy the inline object sitting inside the range.
|
|
299
|
+
|
|
300
|
+
Rules whose actions leave part of the matched range in place can grant leniency per named capture group:
|
|
301
|
+
|
|
302
|
+
```tsx
|
|
303
|
+
const strongPairRule = defineInputRule({
|
|
304
|
+
on: /\*\*(?<content>[^*\n]+?)\*\*$/,
|
|
305
|
+
// The actions decorate the content and delete only the `**` markers, so
|
|
306
|
+
// an inline object inside the content is harmless and the match should
|
|
307
|
+
// fire. An inline object anywhere else in the match, between the marker
|
|
308
|
+
// characters, still drops the match.
|
|
309
|
+
inlineObjects: {allow: ['content']},
|
|
310
|
+
// ...
|
|
311
|
+
})
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
The match survives when every inline object inside it sits within a listed group's matched span (inclusive of its edges). Unlisted groups and the text between groups, the rule's syntax markers, stay protected. This also expresses "this group's text becomes data": a markdown link rule can allow objects in its `text` group while leaving its `href` group protected, an inline object inside the href would make the captured text a lie.
|
|
315
|
+
|
|
316
|
+
The [`@portabletext/plugin-character-pair-decorator`](../plugin-character-pair-decorator/) package is built exactly this way: `inlineObjects: {allow: ['content']}` lets `**bo`⟨inline object⟩`ld**` decorate across the object, while a match with an inline object between the marker characters stays literal.
|
|
317
|
+
|
|
318
|
+
To allow inline objects anywhere in the match, capture the whole pattern in a named group and list it.
|
package/dist/index.d.ts
CHANGED
|
@@ -8,14 +8,14 @@ import {EditorSelection} from '@portabletext/editor'
|
|
|
8
8
|
import type {PortableTextBlock} from '@portabletext/editor'
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* @
|
|
11
|
+
* @public
|
|
12
12
|
*/
|
|
13
13
|
export declare function defineInputRule<TGuardResponse = true>(
|
|
14
14
|
config: InputRule<TGuardResponse>,
|
|
15
15
|
): InputRule<TGuardResponse>
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* @
|
|
18
|
+
* @public
|
|
19
19
|
*/
|
|
20
20
|
export declare function defineInputRuleBehavior(config: {
|
|
21
21
|
rules: Array<InputRule<any>>
|
|
@@ -142,23 +142,40 @@ export declare function defineInputRuleBehavior(config: {
|
|
|
142
142
|
* })
|
|
143
143
|
* ```
|
|
144
144
|
*
|
|
145
|
-
* @
|
|
145
|
+
* @public
|
|
146
146
|
*/
|
|
147
147
|
export declare function defineTextTransformRule<TGuardResponse = true>(
|
|
148
148
|
config: TextTransformRule<TGuardResponse>,
|
|
149
149
|
): InputRule<TGuardResponse>
|
|
150
150
|
|
|
151
151
|
/**
|
|
152
|
-
* @
|
|
152
|
+
* @public
|
|
153
153
|
*/
|
|
154
154
|
export declare type InputRule<TGuardResponse = true> = {
|
|
155
155
|
on: RegExp
|
|
156
|
+
/**
|
|
157
|
+
* Named capture groups inside which an inline object may sit without
|
|
158
|
+
* dropping the match.
|
|
159
|
+
*
|
|
160
|
+
* Inline objects contribute nothing to the text the RegExp matches
|
|
161
|
+
* against, so a match can span one invisibly. By default any such match
|
|
162
|
+
* is dropped: rules commonly delete the matched range, and deleting
|
|
163
|
+
* across an inline object destroys it. Listing a group allows inline
|
|
164
|
+
* objects within that group's matched span (inclusive of its edges,
|
|
165
|
+
* so an object adjacent to the span does not drop the match); everything
|
|
166
|
+
* else in the match, unlisted groups and the text between groups, stays
|
|
167
|
+
* protected. To allow inline objects anywhere in the match, capture the
|
|
168
|
+
* whole pattern in a named group and list it.
|
|
169
|
+
*/
|
|
170
|
+
inlineObjects?: {
|
|
171
|
+
allow: Array<string>
|
|
172
|
+
}
|
|
156
173
|
guard?: InputRuleGuard<TGuardResponse>
|
|
157
174
|
actions: Array<BehaviorActionSet<InputRuleEvent, TGuardResponse>>
|
|
158
175
|
}
|
|
159
176
|
|
|
160
177
|
/**
|
|
161
|
-
* @
|
|
178
|
+
* @public
|
|
162
179
|
*/
|
|
163
180
|
export declare type InputRuleEvent = {
|
|
164
181
|
type: 'custom.input rule'
|
|
@@ -184,7 +201,7 @@ export declare type InputRuleEvent = {
|
|
|
184
201
|
}
|
|
185
202
|
|
|
186
203
|
/**
|
|
187
|
-
* @
|
|
204
|
+
* @public
|
|
188
205
|
*/
|
|
189
206
|
export declare type InputRuleGuard<TGuardResponse = true> = BehaviorGuard<
|
|
190
207
|
InputRuleEvent,
|
|
@@ -193,13 +210,23 @@ export declare type InputRuleGuard<TGuardResponse = true> = BehaviorGuard<
|
|
|
193
210
|
|
|
194
211
|
/**
|
|
195
212
|
* Match found in the text after the insertion
|
|
196
|
-
* @
|
|
213
|
+
* @public
|
|
197
214
|
*/
|
|
198
215
|
export declare type InputRuleMatch = InputRuleMatchLocation & {
|
|
199
|
-
|
|
216
|
+
/**
|
|
217
|
+
* Locations of the match's named capture groups, keyed by group name.
|
|
218
|
+
* Only groups that participated in the match are present, and a capture
|
|
219
|
+
* group must be named (`(?<name>...)`) for its location to be exposed;
|
|
220
|
+
* unnamed groups remain usable for regex mechanics (alternation) but get
|
|
221
|
+
* no location.
|
|
222
|
+
*/
|
|
223
|
+
groups: Record<string, InputRuleMatchLocation | undefined>
|
|
200
224
|
}
|
|
201
225
|
|
|
202
|
-
|
|
226
|
+
/**
|
|
227
|
+
* @public
|
|
228
|
+
*/
|
|
229
|
+
export declare type InputRuleMatchLocation = {
|
|
203
230
|
/**
|
|
204
231
|
* The matched text
|
|
205
232
|
*/
|
|
@@ -232,7 +259,7 @@ declare type InputRuleMatchLocation = {
|
|
|
232
259
|
* <InputRulePlugin rules={smartQuotesRules} />
|
|
233
260
|
* ```
|
|
234
261
|
*
|
|
235
|
-
* @
|
|
262
|
+
* @public
|
|
236
263
|
*/
|
|
237
264
|
export declare function InputRulePlugin(props: InputRulePluginProps): null
|
|
238
265
|
|
|
@@ -241,19 +268,43 @@ declare type InputRulePluginProps = {
|
|
|
241
268
|
}
|
|
242
269
|
|
|
243
270
|
/**
|
|
244
|
-
* @
|
|
271
|
+
* @public
|
|
272
|
+
*/
|
|
273
|
+
export declare type TextTransform<TGuardResponse = true> = (
|
|
274
|
+
{
|
|
275
|
+
location,
|
|
276
|
+
}: {
|
|
277
|
+
location: InputRuleMatchLocation
|
|
278
|
+
},
|
|
279
|
+
guardResponse: TGuardResponse,
|
|
280
|
+
) => string
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* @public
|
|
245
284
|
*/
|
|
246
285
|
export declare type TextTransformRule<TGuardResponse = true> = {
|
|
247
286
|
on: RegExp
|
|
248
287
|
guard?: InputRuleGuard<TGuardResponse>
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
288
|
+
/**
|
|
289
|
+
* What to replace, and with what.
|
|
290
|
+
*
|
|
291
|
+
* A function replaces the whole match, always, regardless of any capture
|
|
292
|
+
* groups in the pattern. A record replaces only the spans of the named
|
|
293
|
+
* capture groups given as keys, each with its own transform,
|
|
294
|
+
* `/\d+\s?(?<operator>[*x])\s?\d+/` with
|
|
295
|
+
* `transform: {operator: () => '×'}` turns `2x3` into `2×3` rather than
|
|
296
|
+
* `×`. Use the record form when the pattern needs surrounding context to
|
|
297
|
+
* decide *when* to fire but only part of the match should change; the
|
|
298
|
+
* context must sit inside the match rather than in lookarounds, a rule
|
|
299
|
+
* only fires when its match involves the just-inserted text.
|
|
300
|
+
*
|
|
301
|
+
* Every key must exist as a named capture group in `on`;
|
|
302
|
+
* `defineTextTransformRule` throws otherwise. A match in which none of
|
|
303
|
+
* the keys participated has nothing to replace and is skipped.
|
|
304
|
+
*/
|
|
305
|
+
transform:
|
|
306
|
+
| TextTransform<TGuardResponse>
|
|
307
|
+
| Record<string, TextTransform<TGuardResponse>>
|
|
257
308
|
}
|
|
258
309
|
|
|
259
310
|
export {}
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { c } from "react/compiler-runtime";
|
|
|
2
2
|
import { useEditor } from "@portabletext/editor";
|
|
3
3
|
import { defineBehavior, raise, effect, forward } from "@portabletext/editor/behaviors";
|
|
4
4
|
import { getNextInlineObjects, getPreviousInlineObjects, getBlockOffsets, getFocusBlock, getBlockTextBefore, getMarkState } from "@portabletext/editor/selectors";
|
|
5
|
-
import { blockOffsetToSpanSelectionPoint, isKeyedSegment, isEqualSelections, isSelectionCollapsed } from "@portabletext/editor/utils";
|
|
5
|
+
import { blockOffsetToSpanSelectionPoint, childSelectionPointToBlockOffset, isKeyedSegment, isEqualSelections, isSelectionCollapsed } from "@portabletext/editor/utils";
|
|
6
6
|
import { useActorRef } from "@xstate/react";
|
|
7
7
|
import { setup, fromCallback } from "xstate";
|
|
8
8
|
function defineInputRule(config) {
|
|
@@ -13,7 +13,8 @@ function getInputRuleMatchLocation({
|
|
|
13
13
|
adjustIndexBy,
|
|
14
14
|
snapshot,
|
|
15
15
|
focusBlock,
|
|
16
|
-
originalTextBefore
|
|
16
|
+
originalTextBefore,
|
|
17
|
+
allowedInlineObjectRanges
|
|
17
18
|
}) {
|
|
18
19
|
const [text, start, end] = match, adjustedIndex = start + adjustIndexBy, targetOffsets = {
|
|
19
20
|
anchor: {
|
|
@@ -57,13 +58,23 @@ function getInputRuleMatchLocation({
|
|
|
57
58
|
focus: selection.anchor
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
|
-
}), inlineObjectsBefore = getPreviousInlineObjects(snapshot);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
61
|
+
}), inlineObjectsBefore = getPreviousInlineObjects(snapshot), inlineObjectsInMatch = inlineObjectsAfterMatch.filter((inlineObjectAfter) => inlineObjectsBefore.some((inlineObjectBefore) => inlineObjectAfter.node._key === inlineObjectBefore.node._key));
|
|
62
|
+
for (const inlineObject of inlineObjectsInMatch) {
|
|
63
|
+
const inlineObjectOffset = childSelectionPointToBlockOffset({
|
|
64
|
+
snapshot,
|
|
65
|
+
selectionPoint: {
|
|
66
|
+
path: inlineObject.path,
|
|
67
|
+
offset: 0
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
if (!inlineObjectOffset || !allowedInlineObjectRanges.some((range) => inlineObjectOffset.offset >= range.start + adjustIndexBy && inlineObjectOffset.offset <= range.end + adjustIndexBy))
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
text,
|
|
75
|
+
selection,
|
|
76
|
+
targetOffsets
|
|
77
|
+
};
|
|
67
78
|
}
|
|
68
79
|
function defineInputRuleBehavior(config) {
|
|
69
80
|
return defineBehavior({
|
|
@@ -92,12 +103,19 @@ function defineInputRuleBehavior(config) {
|
|
|
92
103
|
const match = regExpMatch.indices.at(0);
|
|
93
104
|
if (!match)
|
|
94
105
|
return [];
|
|
95
|
-
const
|
|
106
|
+
const allowedInlineObjectRanges = (rule.inlineObjects?.allow ?? []).flatMap((groupName) => {
|
|
107
|
+
const span = regExpMatch.indices?.groups?.[groupName];
|
|
108
|
+
return span ? [{
|
|
109
|
+
start: span[0],
|
|
110
|
+
end: span[1]
|
|
111
|
+
}] : [];
|
|
112
|
+
}), matchLocation = getInputRuleMatchLocation({
|
|
96
113
|
match: [regExpMatch.at(0) ?? "", ...match],
|
|
97
114
|
adjustIndexBy: originalNewText.length - newText.length,
|
|
98
115
|
snapshot,
|
|
99
116
|
focusBlock,
|
|
100
|
-
originalTextBefore
|
|
117
|
+
originalTextBefore,
|
|
118
|
+
allowedInlineObjectRanges
|
|
101
119
|
});
|
|
102
120
|
if (!matchLocation)
|
|
103
121
|
return [];
|
|
@@ -105,21 +123,25 @@ function defineInputRuleBehavior(config) {
|
|
|
105
123
|
return [];
|
|
106
124
|
if (foundMatches.some((foundMatch) => foundMatch.targetOffsets.anchor.offset === matchLocation.targetOffsets.anchor.offset))
|
|
107
125
|
return [];
|
|
108
|
-
const
|
|
126
|
+
const groups = {};
|
|
127
|
+
for (const [groupName, span] of Object.entries(regExpMatch.indices.groups ?? {})) {
|
|
128
|
+
if (!span)
|
|
129
|
+
continue;
|
|
130
|
+
const groupLocation = getInputRuleMatchLocation({
|
|
131
|
+
match: [regExpMatch.groups?.[groupName] ?? "", span[0], span[1]],
|
|
132
|
+
adjustIndexBy: originalNewText.length - newText.length,
|
|
133
|
+
snapshot,
|
|
134
|
+
focusBlock,
|
|
135
|
+
originalTextBefore,
|
|
136
|
+
allowedInlineObjectRanges
|
|
137
|
+
});
|
|
138
|
+
groupLocation && (groups[groupName] = groupLocation);
|
|
139
|
+
}
|
|
109
140
|
return [{
|
|
110
141
|
text: matchLocation.text,
|
|
111
142
|
selection: matchLocation.selection,
|
|
112
143
|
targetOffsets: matchLocation.targetOffsets,
|
|
113
|
-
|
|
114
|
-
const text = regExpMatch.at(index + 1) ?? "";
|
|
115
|
-
return getInputRuleMatchLocation({
|
|
116
|
-
match: [text, ...match2],
|
|
117
|
-
adjustIndexBy: originalNewText.length - newText.length,
|
|
118
|
-
snapshot,
|
|
119
|
-
focusBlock,
|
|
120
|
-
originalTextBefore
|
|
121
|
-
}) || [];
|
|
122
|
-
})
|
|
144
|
+
groups
|
|
123
145
|
}];
|
|
124
146
|
});
|
|
125
147
|
if (ruleMatches.length > 0) {
|
|
@@ -150,7 +172,10 @@ function defineInputRuleBehavior(config) {
|
|
|
150
172
|
for (const actionSet of actionSets)
|
|
151
173
|
for (const action of actionSet)
|
|
152
174
|
foundActions.push(action);
|
|
153
|
-
const matches = ruleMatches.flatMap((match) =>
|
|
175
|
+
const matches = ruleMatches.flatMap((match) => {
|
|
176
|
+
const groupLocations = Object.values(match.groups).filter((location) => location !== void 0);
|
|
177
|
+
return groupLocations.length === 0 ? [match] : groupLocations;
|
|
178
|
+
});
|
|
154
179
|
for (const match of matches)
|
|
155
180
|
foundMatches.push(match), textBefore = newText.slice(0, match.targetOffsets.focus.offset ?? 0), newText = originalNewText.slice(match.targetOffsets.focus.offset ?? 0);
|
|
156
181
|
} else
|
|
@@ -335,6 +360,13 @@ const inputRuleListenerCallback = ({
|
|
|
335
360
|
}
|
|
336
361
|
});
|
|
337
362
|
function defineTextTransformRule(config) {
|
|
363
|
+
const transformRecord = typeof config.transform == "function" ? void 0 : config.transform;
|
|
364
|
+
if (transformRecord) {
|
|
365
|
+
const probeFlags = config.on.flags.replace(/[gyd]/g, ""), namedGroups = Object.keys(new RegExp(`${config.on.source}|`, probeFlags).exec("")?.groups ?? {});
|
|
366
|
+
for (const groupName of Object.keys(transformRecord))
|
|
367
|
+
if (!namedGroups.includes(groupName))
|
|
368
|
+
throw new Error(`defineTextTransformRule: \`transform\` targets the group "${groupName}", but \`on\` (${config.on}) has no such named capture group` + (namedGroups.length > 0 ? `. Named groups: ${namedGroups.map((name) => `"${name}"`).join(", ")}` : ". The pattern has no named capture groups"));
|
|
369
|
+
}
|
|
338
370
|
return {
|
|
339
371
|
on: config.on,
|
|
340
372
|
guard: config.guard ?? (() => !0),
|
|
@@ -342,11 +374,23 @@ function defineTextTransformRule(config) {
|
|
|
342
374
|
snapshot,
|
|
343
375
|
event
|
|
344
376
|
}, guardResponse) => {
|
|
345
|
-
const
|
|
377
|
+
const targets = event.matches.flatMap((match) => transformRecord ? Object.entries(transformRecord).flatMap(([groupName, groupTransform]) => {
|
|
378
|
+
const location = match.groups[groupName];
|
|
379
|
+
return location ? [{
|
|
380
|
+
location,
|
|
381
|
+
transform: groupTransform
|
|
382
|
+
}] : [];
|
|
383
|
+
}) : [{
|
|
384
|
+
location: match,
|
|
385
|
+
transform: config.transform
|
|
386
|
+
}]).sort((a, b) => a.location.targetOffsets.anchor.offset - b.location.targetOffsets.anchor.offset), newText = event.textBefore + event.textInserted;
|
|
346
387
|
let textLengthDelta = 0;
|
|
347
388
|
const actions = [];
|
|
348
|
-
for (const
|
|
349
|
-
|
|
389
|
+
for (const {
|
|
390
|
+
location,
|
|
391
|
+
transform
|
|
392
|
+
} of targets.reverse()) {
|
|
393
|
+
const text = transform({
|
|
350
394
|
location
|
|
351
395
|
}, guardResponse);
|
|
352
396
|
textLengthDelta = textLengthDelta - (text.length - (location.targetOffsets.focus.offset - location.targetOffsets.anchor.offset)), actions.push(raise({
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/input-rule.ts","../src/input-rule-match-location.ts","../src/plugin.input-rule.tsx","../src/text-transform-rule.ts"],"sourcesContent":["import type {BlockPath, PortableTextBlock} from '@portabletext/editor'\nimport type {\n BehaviorActionSet,\n BehaviorGuard,\n} from '@portabletext/editor/behaviors'\nimport type {InputRuleMatchLocation} from './input-rule-match-location'\n\n/**\n * Match found in the text after the insertion\n * @alpha\n */\nexport type InputRuleMatch = InputRuleMatchLocation & {\n groupMatches: Array<InputRuleMatchLocation>\n}\n\n/**\n * @alpha\n */\nexport type InputRuleEvent = {\n type: 'custom.input rule'\n /**\n * Matches found by the input rule\n */\n matches: Array<InputRuleMatch>\n /**\n * The text before the insertion\n */\n textBefore: string\n /**\n * The text is destined to be inserted\n */\n textInserted: string\n /**\n * The block where the insertion takes place\n */\n focusBlock: {\n path: BlockPath\n node: PortableTextBlock\n }\n}\n\n/**\n * @alpha\n */\nexport type InputRuleGuard<TGuardResponse = true> = BehaviorGuard<\n InputRuleEvent,\n TGuardResponse\n>\n\n/**\n * @alpha\n */\nexport type InputRule<TGuardResponse = true> = {\n on: RegExp\n guard?: InputRuleGuard<TGuardResponse>\n actions: Array<BehaviorActionSet<InputRuleEvent, TGuardResponse>>\n}\n\n/**\n * @alpha\n */\nexport function defineInputRule<TGuardResponse = true>(\n config: InputRule<TGuardResponse>,\n): InputRule<TGuardResponse> {\n return config\n}\n","import type {\n BlockOffset,\n BlockPath,\n EditorSelection,\n EditorSnapshot,\n} from '@portabletext/editor'\nimport {\n getNextInlineObjects,\n getPreviousInlineObjects,\n} from '@portabletext/editor/selectors'\nimport {blockOffsetToSpanSelectionPoint} from '@portabletext/editor/utils'\n\nexport type InputRuleMatchLocation = {\n /**\n * The matched text\n */\n text: string\n /**\n * Estimated selection of where in the original text the match is located.\n * The selection is estimated since the match is found in the text after\n * insertion.\n */\n selection: NonNullable<EditorSelection>\n /**\n * Block offsets of the match in the text after the insertion\n */\n targetOffsets: {\n anchor: BlockOffset\n focus: BlockOffset\n backward: boolean\n }\n}\n\nexport function getInputRuleMatchLocation({\n match,\n adjustIndexBy,\n snapshot,\n focusBlock,\n originalTextBefore,\n}: {\n match: [string, number, number]\n adjustIndexBy: number\n snapshot: EditorSnapshot\n focusBlock: {\n path: BlockPath\n }\n originalTextBefore: string\n}): InputRuleMatchLocation | undefined {\n const [text, start, end] = match\n const adjustedIndex = start + adjustIndexBy\n\n const targetOffsets = {\n anchor: {\n path: focusBlock.path,\n offset: adjustedIndex,\n },\n focus: {\n path: focusBlock.path,\n offset: adjustedIndex + end - start,\n },\n backward: false,\n }\n const normalizedOffsets = {\n anchor: {\n path: focusBlock.path,\n offset: Math.min(targetOffsets.anchor.offset, originalTextBefore.length),\n },\n focus: {\n path: focusBlock.path,\n offset: Math.min(targetOffsets.focus.offset, originalTextBefore.length),\n },\n backward: false,\n }\n\n const anchorBackwards = blockOffsetToSpanSelectionPoint({\n snapshot,\n blockOffset: normalizedOffsets.anchor,\n direction: 'backward',\n })\n const focusForwards = blockOffsetToSpanSelectionPoint({\n snapshot,\n blockOffset: normalizedOffsets.focus,\n direction: 'forward',\n })\n\n if (!anchorBackwards || !focusForwards) {\n return undefined\n }\n\n const selection = {\n anchor: anchorBackwards,\n focus: focusForwards,\n }\n\n const inlineObjectsAfterMatch = getNextInlineObjects({\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: {\n anchor: selection.anchor,\n focus: selection.anchor,\n },\n },\n })\n const inlineObjectsBefore = getPreviousInlineObjects(snapshot)\n\n if (\n inlineObjectsAfterMatch.some((inlineObjectAfter) =>\n inlineObjectsBefore.some(\n (inlineObjectBefore) =>\n inlineObjectAfter.node._key === inlineObjectBefore.node._key,\n ),\n )\n ) {\n return undefined\n }\n\n return {\n text,\n selection,\n targetOffsets,\n }\n}\n","import {\n useEditor,\n type BlockOffset,\n type Editor,\n type EditorSelection,\n} from '@portabletext/editor'\nimport {\n defineBehavior,\n effect,\n forward,\n raise,\n type BehaviorAction,\n} from '@portabletext/editor/behaviors'\nimport {\n getBlockOffsets,\n getBlockTextBefore,\n getFocusBlock,\n} from '@portabletext/editor/selectors'\nimport {\n isEqualSelections,\n isKeyedSegment,\n isSelectionCollapsed,\n} from '@portabletext/editor/utils'\nimport {useActorRef} from '@xstate/react'\nimport {\n fromCallback,\n setup,\n type AnyEventObject,\n type CallbackLogicFunction,\n} from 'xstate'\nimport type {InputRule, InputRuleMatch} from './input-rule'\nimport {getInputRuleMatchLocation} from './input-rule-match-location'\n\n/**\n * @alpha\n */\nexport function defineInputRuleBehavior(config: {\n rules: Array<InputRule<any>>\n onApply?: ({\n endOffsets,\n endSelection,\n }: {\n endOffsets: {start: BlockOffset; end: BlockOffset} | undefined\n endSelection: EditorSelection\n }) => void\n}) {\n return defineBehavior({\n on: 'insert.text',\n guard: ({snapshot, event, dom}) => {\n if (\n !snapshot.context.selection ||\n !isSelectionCollapsed(snapshot.context.selection)\n ) {\n return false\n }\n\n const focusBlock = getFocusBlock(snapshot)\n\n if (!focusBlock) {\n return false\n }\n\n const originalTextBefore = getBlockTextBefore(snapshot)\n let textBefore = originalTextBefore\n const originalNewText = textBefore + event.text\n let newText = originalNewText\n\n const foundMatches: Array<InputRuleMatch['groupMatches'][number]> = []\n const foundActions: Array<BehaviorAction> = []\n\n for (const rule of config.rules) {\n // Preserve a known-safe subset of flags from `rule.on`: `i` for\n // case-insensitive matches, `m` for multi-line anchors, `s` for\n // dot-matches-newline, `u` for Unicode property escapes. The plugin\n // sets `g` (to drive `matchAll`) and `d` (to read match indices)\n // itself, so user-set `g`/`d` are dropped to avoid `Invalid flags`\n // and keep the loop's contract. `y` (sticky) is also dropped: the\n // plugin's `matchAll` loop slices and re-feeds `newText` after each\n // match, which is incompatible with sticky's `lastIndex`-anchored\n // semantics.\n const safeUserFlags = rule.on.flags.replace(/[^imsu]/g, '')\n const matcher = new RegExp(rule.on.source, `gd${safeUserFlags}`)\n\n while (true) {\n // Find matches in the text after the insertion\n const ruleMatches = [...newText.matchAll(matcher)].flatMap(\n (regExpMatch) => {\n if (regExpMatch.indices === undefined) {\n return []\n }\n\n const match = regExpMatch.indices.at(0)\n\n if (!match) {\n return []\n }\n\n const matchLocation = getInputRuleMatchLocation({\n match: [regExpMatch.at(0) ?? '', ...match],\n adjustIndexBy: originalNewText.length - newText.length,\n snapshot,\n focusBlock,\n originalTextBefore,\n })\n\n if (!matchLocation) {\n return []\n }\n\n const existsInTextBefore =\n matchLocation.targetOffsets.focus.offset <=\n originalTextBefore.length\n\n // Ignore if this match occurs in the text before the insertion\n if (existsInTextBefore) {\n return []\n }\n\n const alreadyFound = foundMatches.some(\n (foundMatch) =>\n foundMatch.targetOffsets.anchor.offset ===\n matchLocation.targetOffsets.anchor.offset,\n )\n\n // Ignore if this match has already been found\n if (alreadyFound) {\n return []\n }\n\n const groupMatches =\n regExpMatch.indices.length > 1\n ? regExpMatch.indices\n .slice(1)\n .filter((indices) => indices !== undefined)\n : []\n\n const ruleMatch = {\n text: matchLocation.text,\n selection: matchLocation.selection,\n targetOffsets: matchLocation.targetOffsets,\n groupMatches: groupMatches.flatMap((match, index) => {\n const text = regExpMatch.at(index + 1) ?? ''\n const groupMatchLocation = getInputRuleMatchLocation({\n match: [text, ...match],\n adjustIndexBy: originalNewText.length - newText.length,\n snapshot,\n focusBlock,\n originalTextBefore,\n })\n\n if (!groupMatchLocation) {\n return []\n }\n\n return groupMatchLocation\n }),\n }\n\n return [ruleMatch]\n },\n )\n\n if (ruleMatches.length > 0) {\n const guardResult =\n rule.guard?.({\n snapshot,\n event: {\n type: 'custom.input rule',\n matches: ruleMatches,\n focusBlock,\n textBefore: originalTextBefore,\n textInserted: event.text,\n },\n dom,\n }) ?? true\n\n if (!guardResult) {\n break\n }\n\n const actionSets = rule.actions.map((action) =>\n action(\n {\n snapshot,\n event: {\n type: 'custom.input rule',\n matches: ruleMatches,\n focusBlock,\n textBefore: originalTextBefore,\n textInserted: event.text,\n },\n dom,\n },\n guardResult,\n ),\n )\n\n for (const actionSet of actionSets) {\n for (const action of actionSet) {\n foundActions.push(action)\n }\n }\n\n const matches = ruleMatches.flatMap((match) =>\n match.groupMatches.length === 0 ? [match] : match.groupMatches,\n )\n\n for (const match of matches) {\n // Remember each match and adjust `textBefore` and `newText` so\n // no subsequent matches can overlap with this one\n foundMatches.push(match)\n textBefore = newText.slice(\n 0,\n match.targetOffsets.focus.offset ?? 0,\n )\n newText = originalNewText.slice(\n match.targetOffsets.focus.offset ?? 0,\n )\n }\n } else {\n // If no match was found, break out of the loop to try the next\n // rule\n break\n }\n }\n }\n\n if (foundActions.length === 0) {\n return false\n }\n\n return {actions: foundActions}\n },\n actions: [\n ({event}) => [forward(event)],\n (_, {actions}) => actions,\n ({snapshot}) => [\n effect(() => {\n const blockOffsets = getBlockOffsets(snapshot)\n\n config.onApply?.({\n endOffsets: blockOffsets,\n endSelection: snapshot.context.selection,\n })\n }),\n ],\n ],\n })\n}\n\ntype InputRulePluginProps = {\n rules: Array<InputRule<any>>\n}\n\n/**\n * Turn an array of `InputRule`s into a Behavior that can be used to apply the\n * rules to the editor.\n *\n * The plugin handles undo/redo out of the box including smart undo with\n * Backspace.\n *\n * @example\n * ```tsx\n * <InputRulePlugin rules={smartQuotesRules} />\n * ```\n *\n * @alpha\n */\nexport function InputRulePlugin(props: InputRulePluginProps) {\n const editor = useEditor()\n\n useActorRef(inputRuleMachine, {\n input: {editor, rules: props.rules},\n })\n\n return null\n}\n\ntype InputRuleMachineEvent =\n | {\n type: 'input rule raised'\n endOffsets: {start: BlockOffset; end: BlockOffset} | undefined\n endSelection: EditorSelection\n }\n | {type: 'history.undo raised'}\n | {\n type: 'selection changed'\n blockOffsets: {start: BlockOffset; end: BlockOffset} | undefined\n selection: EditorSelection\n }\n\nconst inputRuleListenerCallback: CallbackLogicFunction<\n AnyEventObject,\n InputRuleMachineEvent,\n {\n editor: Editor\n rules: Array<InputRule>\n }\n> = ({input, sendBack}) => {\n const unregister = input.editor.registerBehavior({\n behavior: defineInputRuleBehavior({\n rules: input.rules,\n onApply: ({endOffsets, endSelection}) => {\n sendBack({type: 'input rule raised', endOffsets, endSelection})\n },\n }),\n })\n\n return () => {\n unregister()\n }\n}\n\nconst deleteBackwardListenerCallback: CallbackLogicFunction<\n AnyEventObject,\n InputRuleMachineEvent,\n {editor: Editor}\n> = ({input, sendBack}) => {\n return input.editor.registerBehavior({\n behavior: defineBehavior({\n on: 'delete.backward',\n actions: [\n () => [\n raise({type: 'history.undo'}),\n effect(() => {\n sendBack({type: 'history.undo raised'})\n }),\n ],\n ],\n }),\n })\n}\n\nconst selectionListenerCallback: CallbackLogicFunction<\n AnyEventObject,\n InputRuleMachineEvent,\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 const blockOffsets = getBlockOffsets({\n ...input.editor.getSnapshot(),\n context: {\n ...input.editor.getSnapshot().context,\n selection: event.selection,\n },\n })\n\n sendBack({\n type: 'selection changed',\n blockOffsets,\n selection: event.selection,\n })\n })\n\n return () => subscription.unsubscribe()\n}\n\nconst inputRuleSetup = setup({\n types: {\n context: {} as {\n editor: Editor\n rules: Array<InputRule>\n endOffsets: {start: BlockOffset; end: BlockOffset} | undefined\n endSelection: EditorSelection\n },\n input: {} as {\n editor: Editor\n rules: Array<InputRule>\n },\n events: {} as InputRuleMachineEvent,\n },\n actors: {\n 'delete.backward listener': fromCallback(deleteBackwardListenerCallback),\n 'input rule listener': fromCallback(inputRuleListenerCallback),\n 'selection listener': fromCallback(selectionListenerCallback),\n },\n guards: {\n 'selection changed': ({context, event}) => {\n if (event.type !== 'selection changed') {\n return false\n }\n\n // When block offsets are available for both the end state and the\n // current selection, compare them. Block offsets normalize away\n // span-level differences (e.g. cursor at the same position but in a\n // different span after normalization).\n if (event.blockOffsets && context.endOffsets) {\n const contextStartBlock = context.endOffsets.start.path.at(-1)\n const eventStartBlock = event.blockOffsets.start.path.at(-1)\n const contextEndBlock = context.endOffsets.end.path.at(-1)\n const eventEndBlock = event.blockOffsets.end.path.at(-1)\n\n if (\n !isKeyedSegment(contextStartBlock) ||\n !isKeyedSegment(eventStartBlock) ||\n !isKeyedSegment(contextEndBlock) ||\n !isKeyedSegment(eventEndBlock)\n ) {\n return false\n }\n\n const startChanged =\n contextStartBlock._key !== eventStartBlock._key ||\n context.endOffsets.start.offset !== event.blockOffsets.start.offset\n const endChanged =\n contextEndBlock._key !== eventEndBlock._key ||\n context.endOffsets.end.offset !== event.blockOffsets.end.offset\n\n return startChanged || endChanged\n }\n\n // Block offsets can't be computed when the cursor is on an inline\n // object (e.g. after a stock ticker rule inserts one). Fall back to\n // comparing the raw selections.\n return !isEqualSelections(context.endSelection, event.selection)\n },\n },\n})\n\nconst assignEndState = inputRuleSetup.assign({\n endOffsets: ({context, event}) =>\n event.type === 'input rule raised' ? event.endOffsets : context.endOffsets,\n endSelection: ({context, event}) =>\n event.type === 'input rule raised'\n ? event.endSelection\n : context.endSelection,\n})\n\nconst inputRuleMachine = inputRuleSetup.createMachine({\n id: 'input rule',\n context: ({input}) => ({\n editor: input.editor,\n rules: input.rules,\n endOffsets: undefined,\n endSelection: null,\n }),\n initial: 'idle',\n invoke: {\n src: 'input rule listener',\n input: ({context}) => ({\n editor: context.editor,\n rules: context.rules,\n }),\n },\n on: {\n 'input rule raised': {\n target: '.input rule applied',\n actions: assignEndState,\n },\n },\n states: {\n 'idle': {},\n 'input rule applied': {\n invoke: [\n {\n src: 'delete.backward listener',\n input: ({context}) => ({editor: context.editor}),\n },\n {\n src: 'selection listener',\n input: ({context}) => ({editor: context.editor}),\n },\n ],\n on: {\n 'selection changed': {\n target: 'idle',\n guard: 'selection changed',\n },\n 'history.undo raised': {\n target: 'idle',\n },\n },\n },\n },\n})\n","import {raise, type BehaviorAction} from '@portabletext/editor/behaviors'\nimport {getMarkState} from '@portabletext/editor/selectors'\nimport type {InputRule, InputRuleGuard} from './input-rule'\nimport type {InputRuleMatchLocation} from './input-rule-match-location'\n\n/**\n * @alpha\n */\nexport type TextTransformRule<TGuardResponse = true> = {\n on: RegExp\n guard?: InputRuleGuard<TGuardResponse>\n transform: (\n {location}: {location: InputRuleMatchLocation},\n guardResponse: TGuardResponse,\n ) => string\n}\n\n/**\n * Define an `InputRule` specifically designed to transform matched text into\n * some other text.\n *\n * @example\n * ```tsx\n * const transformRule = defineTextTransformRule({\n * on: /--/,\n * transform: () => '—',\n * })\n * ```\n *\n * @alpha\n */\nexport function defineTextTransformRule<TGuardResponse = true>(\n config: TextTransformRule<TGuardResponse>,\n): InputRule<TGuardResponse> {\n return {\n on: config.on,\n guard: config.guard ?? (() => true as TGuardResponse),\n actions: [\n ({snapshot, event}, guardResponse) => {\n const locations = event.matches.flatMap((match) =>\n match.groupMatches.length === 0 ? [match] : match.groupMatches,\n )\n const newText = event.textBefore + event.textInserted\n\n let textLengthDelta = 0\n const actions: Array<BehaviorAction> = []\n\n for (const location of locations.reverse()) {\n const text = config.transform({location}, guardResponse)\n\n textLengthDelta =\n textLengthDelta -\n (text.length -\n (location.targetOffsets.focus.offset -\n location.targetOffsets.anchor.offset))\n\n actions.push(raise({type: 'select', at: location.targetOffsets}))\n actions.push(raise({type: 'delete', at: location.targetOffsets}))\n actions.push(\n raise({\n type: 'insert.child',\n child: {\n _type: snapshot.context.schema.span.name,\n text,\n marks:\n getMarkState({\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: {\n anchor: location.selection.anchor,\n focus: {\n path: location.selection.focus.path,\n offset: Math.min(\n location.selection.focus.offset,\n event.textBefore.length,\n ),\n },\n },\n },\n })?.marks ?? [],\n },\n }),\n )\n }\n\n const endCaretPosition = {\n path: event.focusBlock.path,\n offset: newText.length - textLengthDelta,\n }\n\n return [\n ...actions,\n raise({\n type: 'select',\n at: {\n anchor: endCaretPosition,\n focus: endCaretPosition,\n },\n }),\n ]\n },\n ],\n }\n}\n"],"names":["defineInputRule","config","getInputRuleMatchLocation","match","adjustIndexBy","snapshot","focusBlock","originalTextBefore","text","start","end","adjustedIndex","targetOffsets","anchor","path","offset","focus","backward","normalizedOffsets","Math","min","length","anchorBackwards","blockOffsetToSpanSelectionPoint","blockOffset","direction","focusForwards","selection","inlineObjectsAfterMatch","getNextInlineObjects","context","inlineObjectsBefore","getPreviousInlineObjects","some","inlineObjectAfter","inlineObjectBefore","node","_key","defineInputRuleBehavior","defineBehavior","on","guard","event","dom","isSelectionCollapsed","getFocusBlock","getBlockTextBefore","textBefore","originalNewText","newText","foundMatches","foundActions","rule","rules","safeUserFlags","flags","replace","matcher","RegExp","source","ruleMatches","matchAll","flatMap","regExpMatch","indices","undefined","at","matchLocation","foundMatch","groupMatches","slice","filter","index","guardResult","type","matches","textInserted","actionSets","actions","map","action","actionSet","push","forward","_","effect","blockOffsets","getBlockOffsets","onApply","endOffsets","endSelection","InputRulePlugin","props","$","_c","editor","useEditor","t0","input","useActorRef","inputRuleMachine","inputRuleListenerCallback","sendBack","unregister","registerBehavior","behavior","deleteBackwardListenerCallback","raise","selectionListenerCallback","subscription","getSnapshot","unsubscribe","inputRuleSetup","setup","types","events","actors","fromCallback","guards","selection changed","contextStartBlock","eventStartBlock","contextEndBlock","eventEndBlock","isKeyedSegment","startChanged","endChanged","isEqualSelections","assignEndState","assign","createMachine","id","initial","invoke","src","target","states","defineTextTransformRule","guardResponse","locations","textLengthDelta","location","reverse","transform","child","_type","schema","span","name","marks","getMarkState","endCaretPosition"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/input-rule.ts","../src/input-rule-match-location.ts","../src/plugin.input-rule.tsx","../src/text-transform-rule.ts"],"sourcesContent":["import type {BlockPath, PortableTextBlock} from '@portabletext/editor'\nimport type {\n BehaviorActionSet,\n BehaviorGuard,\n} from '@portabletext/editor/behaviors'\nimport type {InputRuleMatchLocation} from './input-rule-match-location'\n\n/**\n * Match found in the text after the insertion\n * @public\n */\nexport type InputRuleMatch = InputRuleMatchLocation & {\n /**\n * Locations of the match's named capture groups, keyed by group name.\n * Only groups that participated in the match are present, and a capture\n * group must be named (`(?<name>...)`) for its location to be exposed;\n * unnamed groups remain usable for regex mechanics (alternation) but get\n * no location.\n */\n groups: Record<string, InputRuleMatchLocation | undefined>\n}\n\n/**\n * @public\n */\nexport type InputRuleEvent = {\n type: 'custom.input rule'\n /**\n * Matches found by the input rule\n */\n matches: Array<InputRuleMatch>\n /**\n * The text before the insertion\n */\n textBefore: string\n /**\n * The text is destined to be inserted\n */\n textInserted: string\n /**\n * The block where the insertion takes place\n */\n focusBlock: {\n path: BlockPath\n node: PortableTextBlock\n }\n}\n\n/**\n * @public\n */\nexport type InputRuleGuard<TGuardResponse = true> = BehaviorGuard<\n InputRuleEvent,\n TGuardResponse\n>\n\n/**\n * @public\n */\nexport type InputRule<TGuardResponse = true> = {\n on: RegExp\n /**\n * Named capture groups inside which an inline object may sit without\n * dropping the match.\n *\n * Inline objects contribute nothing to the text the RegExp matches\n * against, so a match can span one invisibly. By default any such match\n * is dropped: rules commonly delete the matched range, and deleting\n * across an inline object destroys it. Listing a group allows inline\n * objects within that group's matched span (inclusive of its edges,\n * so an object adjacent to the span does not drop the match); everything\n * else in the match, unlisted groups and the text between groups, stays\n * protected. To allow inline objects anywhere in the match, capture the\n * whole pattern in a named group and list it.\n */\n inlineObjects?: {allow: Array<string>}\n guard?: InputRuleGuard<TGuardResponse>\n actions: Array<BehaviorActionSet<InputRuleEvent, TGuardResponse>>\n}\n\n/**\n * @public\n */\nexport function defineInputRule<TGuardResponse = true>(\n config: InputRule<TGuardResponse>,\n): InputRule<TGuardResponse> {\n return config\n}\n","import type {\n BlockOffset,\n BlockPath,\n EditorSelection,\n EditorSnapshot,\n} from '@portabletext/editor'\nimport {\n getNextInlineObjects,\n getPreviousInlineObjects,\n} from '@portabletext/editor/selectors'\nimport {\n blockOffsetToSpanSelectionPoint,\n childSelectionPointToBlockOffset,\n} from '@portabletext/editor/utils'\n\n/**\n * @public\n */\nexport type InputRuleMatchLocation = {\n /**\n * The matched text\n */\n text: string\n /**\n * Estimated selection of where in the original text the match is located.\n * The selection is estimated since the match is found in the text after\n * insertion.\n */\n selection: NonNullable<EditorSelection>\n /**\n * Block offsets of the match in the text after the insertion\n */\n targetOffsets: {\n anchor: BlockOffset\n focus: BlockOffset\n backward: boolean\n }\n}\n\nexport function getInputRuleMatchLocation({\n match,\n adjustIndexBy,\n snapshot,\n focusBlock,\n originalTextBefore,\n allowedInlineObjectRanges,\n}: {\n match: [string, number, number]\n adjustIndexBy: number\n snapshot: EditorSnapshot\n focusBlock: {\n path: BlockPath\n }\n originalTextBefore: string\n /**\n * Index ranges (in the same text space as `match`) inside which an inline\n * object may sit without invalidating the match. Empty means any inline\n * object inside the match invalidates it.\n */\n allowedInlineObjectRanges: Array<{start: number; end: number}>\n}): InputRuleMatchLocation | undefined {\n const [text, start, end] = match\n const adjustedIndex = start + adjustIndexBy\n\n const targetOffsets = {\n anchor: {\n path: focusBlock.path,\n offset: adjustedIndex,\n },\n focus: {\n path: focusBlock.path,\n offset: adjustedIndex + end - start,\n },\n backward: false,\n }\n const normalizedOffsets = {\n anchor: {\n path: focusBlock.path,\n offset: Math.min(targetOffsets.anchor.offset, originalTextBefore.length),\n },\n focus: {\n path: focusBlock.path,\n offset: Math.min(targetOffsets.focus.offset, originalTextBefore.length),\n },\n backward: false,\n }\n\n const anchorBackwards = blockOffsetToSpanSelectionPoint({\n snapshot,\n blockOffset: normalizedOffsets.anchor,\n direction: 'backward',\n })\n const focusForwards = blockOffsetToSpanSelectionPoint({\n snapshot,\n blockOffset: normalizedOffsets.focus,\n direction: 'forward',\n })\n\n if (!anchorBackwards || !focusForwards) {\n return undefined\n }\n\n const selection = {\n anchor: anchorBackwards,\n focus: focusForwards,\n }\n\n const inlineObjectsAfterMatch = getNextInlineObjects({\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: {\n anchor: selection.anchor,\n focus: selection.anchor,\n },\n },\n })\n const inlineObjectsBefore = getPreviousInlineObjects(snapshot)\n\n const inlineObjectsInMatch = inlineObjectsAfterMatch.filter(\n (inlineObjectAfter) =>\n inlineObjectsBefore.some(\n (inlineObjectBefore) =>\n inlineObjectAfter.node._key === inlineObjectBefore.node._key,\n ),\n )\n\n for (const inlineObject of inlineObjectsInMatch) {\n const inlineObjectOffset = childSelectionPointToBlockOffset({\n snapshot,\n selectionPoint: {path: inlineObject.path, offset: 0},\n })\n\n if (!inlineObjectOffset) {\n return undefined\n }\n\n const allowed = allowedInlineObjectRanges.some(\n (range) =>\n inlineObjectOffset.offset >= range.start + adjustIndexBy &&\n inlineObjectOffset.offset <= range.end + adjustIndexBy,\n )\n\n if (!allowed) {\n return undefined\n }\n }\n\n return {\n text,\n selection,\n targetOffsets,\n }\n}\n","import {\n useEditor,\n type BlockOffset,\n type Editor,\n type EditorSelection,\n} from '@portabletext/editor'\nimport {\n defineBehavior,\n effect,\n forward,\n raise,\n type BehaviorAction,\n} from '@portabletext/editor/behaviors'\nimport {\n getBlockOffsets,\n getBlockTextBefore,\n getFocusBlock,\n} from '@portabletext/editor/selectors'\nimport {\n isEqualSelections,\n isKeyedSegment,\n isSelectionCollapsed,\n} from '@portabletext/editor/utils'\nimport {useActorRef} from '@xstate/react'\nimport {\n fromCallback,\n setup,\n type AnyEventObject,\n type CallbackLogicFunction,\n} from 'xstate'\nimport type {InputRule} from './input-rule'\nimport {\n getInputRuleMatchLocation,\n type InputRuleMatchLocation,\n} from './input-rule-match-location'\n\n/**\n * @public\n */\nexport function defineInputRuleBehavior(config: {\n rules: Array<InputRule<any>>\n onApply?: ({\n endOffsets,\n endSelection,\n }: {\n endOffsets: {start: BlockOffset; end: BlockOffset} | undefined\n endSelection: EditorSelection\n }) => void\n}) {\n return defineBehavior({\n on: 'insert.text',\n guard: ({snapshot, event, dom}) => {\n if (\n !snapshot.context.selection ||\n !isSelectionCollapsed(snapshot.context.selection)\n ) {\n return false\n }\n\n const focusBlock = getFocusBlock(snapshot)\n\n if (!focusBlock) {\n return false\n }\n\n const originalTextBefore = getBlockTextBefore(snapshot)\n let textBefore = originalTextBefore\n const originalNewText = textBefore + event.text\n let newText = originalNewText\n\n const foundMatches: Array<InputRuleMatchLocation> = []\n const foundActions: Array<BehaviorAction> = []\n\n for (const rule of config.rules) {\n // Preserve a known-safe subset of flags from `rule.on`: `i` for\n // case-insensitive matches, `m` for multi-line anchors, `s` for\n // dot-matches-newline, `u` for Unicode property escapes. The plugin\n // sets `g` (to drive `matchAll`) and `d` (to read match indices)\n // itself, so user-set `g`/`d` are dropped to avoid `Invalid flags`\n // and keep the loop's contract. `y` (sticky) is also dropped: the\n // plugin's `matchAll` loop slices and re-feeds `newText` after each\n // match, which is incompatible with sticky's `lastIndex`-anchored\n // semantics.\n const safeUserFlags = rule.on.flags.replace(/[^imsu]/g, '')\n const matcher = new RegExp(rule.on.source, `gd${safeUserFlags}`)\n\n while (true) {\n // Find matches in the text after the insertion\n const ruleMatches = [...newText.matchAll(matcher)].flatMap(\n (regExpMatch) => {\n if (regExpMatch.indices === undefined) {\n return []\n }\n\n const match = regExpMatch.indices.at(0)\n\n if (!match) {\n return []\n }\n\n // Resolve the rule's allowed groups to their matched spans;\n // a listed group that didn't participate in this match grants\n // no leniency.\n const allowedInlineObjectRanges = (\n rule.inlineObjects?.allow ?? []\n ).flatMap((groupName) => {\n const span = regExpMatch.indices?.groups?.[groupName]\n\n return span ? [{start: span[0], end: span[1]}] : []\n })\n\n const matchLocation = getInputRuleMatchLocation({\n match: [regExpMatch.at(0) ?? '', ...match],\n adjustIndexBy: originalNewText.length - newText.length,\n snapshot,\n focusBlock,\n originalTextBefore,\n allowedInlineObjectRanges,\n })\n\n if (!matchLocation) {\n return []\n }\n\n const existsInTextBefore =\n matchLocation.targetOffsets.focus.offset <=\n originalTextBefore.length\n\n // Ignore if this match occurs in the text before the insertion\n if (existsInTextBefore) {\n return []\n }\n\n const alreadyFound = foundMatches.some(\n (foundMatch) =>\n foundMatch.targetOffsets.anchor.offset ===\n matchLocation.targetOffsets.anchor.offset,\n )\n\n // Ignore if this match has already been found\n if (alreadyFound) {\n return []\n }\n\n const groups: Record<string, InputRuleMatchLocation | undefined> =\n {}\n\n for (const [groupName, span] of Object.entries(\n regExpMatch.indices.groups ?? {},\n )) {\n if (!span) {\n continue\n }\n\n const groupLocation = getInputRuleMatchLocation({\n match: [\n regExpMatch.groups?.[groupName] ?? '',\n span[0],\n span[1],\n ],\n adjustIndexBy: originalNewText.length - newText.length,\n snapshot,\n focusBlock,\n originalTextBefore,\n allowedInlineObjectRanges,\n })\n\n if (groupLocation) {\n groups[groupName] = groupLocation\n }\n }\n\n const ruleMatch = {\n text: matchLocation.text,\n selection: matchLocation.selection,\n targetOffsets: matchLocation.targetOffsets,\n groups,\n }\n\n return [ruleMatch]\n },\n )\n\n if (ruleMatches.length > 0) {\n const guardResult =\n rule.guard?.({\n snapshot,\n event: {\n type: 'custom.input rule',\n matches: ruleMatches,\n focusBlock,\n textBefore: originalTextBefore,\n textInserted: event.text,\n },\n dom,\n }) ?? true\n\n if (!guardResult) {\n break\n }\n\n const actionSets = rule.actions.map((action) =>\n action(\n {\n snapshot,\n event: {\n type: 'custom.input rule',\n matches: ruleMatches,\n focusBlock,\n textBefore: originalTextBefore,\n textInserted: event.text,\n },\n dom,\n },\n guardResult,\n ),\n )\n\n for (const actionSet of actionSets) {\n for (const action of actionSet) {\n foundActions.push(action)\n }\n }\n\n const matches = ruleMatches.flatMap((match) => {\n const groupLocations = Object.values(match.groups).filter(\n (location) => location !== undefined,\n )\n\n return groupLocations.length === 0 ? [match] : groupLocations\n })\n\n for (const match of matches) {\n // Remember each match and adjust `textBefore` and `newText` so\n // no subsequent matches can overlap with this one\n foundMatches.push(match)\n textBefore = newText.slice(\n 0,\n match.targetOffsets.focus.offset ?? 0,\n )\n newText = originalNewText.slice(\n match.targetOffsets.focus.offset ?? 0,\n )\n }\n } else {\n // If no match was found, break out of the loop to try the next\n // rule\n break\n }\n }\n }\n\n if (foundActions.length === 0) {\n return false\n }\n\n return {actions: foundActions}\n },\n actions: [\n ({event}) => [forward(event)],\n (_, {actions}) => actions,\n ({snapshot}) => [\n effect(() => {\n const blockOffsets = getBlockOffsets(snapshot)\n\n config.onApply?.({\n endOffsets: blockOffsets,\n endSelection: snapshot.context.selection,\n })\n }),\n ],\n ],\n })\n}\n\ntype InputRulePluginProps = {\n rules: Array<InputRule<any>>\n}\n\n/**\n * Turn an array of `InputRule`s into a Behavior that can be used to apply the\n * rules to the editor.\n *\n * The plugin handles undo/redo out of the box including smart undo with\n * Backspace.\n *\n * @example\n * ```tsx\n * <InputRulePlugin rules={smartQuotesRules} />\n * ```\n *\n * @public\n */\nexport function InputRulePlugin(props: InputRulePluginProps) {\n const editor = useEditor()\n\n useActorRef(inputRuleMachine, {\n input: {editor, rules: props.rules},\n })\n\n return null\n}\n\ntype InputRuleMachineEvent =\n | {\n type: 'input rule raised'\n endOffsets: {start: BlockOffset; end: BlockOffset} | undefined\n endSelection: EditorSelection\n }\n | {type: 'history.undo raised'}\n | {\n type: 'selection changed'\n blockOffsets: {start: BlockOffset; end: BlockOffset} | undefined\n selection: EditorSelection\n }\n\nconst inputRuleListenerCallback: CallbackLogicFunction<\n AnyEventObject,\n InputRuleMachineEvent,\n {\n editor: Editor\n rules: Array<InputRule>\n }\n> = ({input, sendBack}) => {\n const unregister = input.editor.registerBehavior({\n behavior: defineInputRuleBehavior({\n rules: input.rules,\n onApply: ({endOffsets, endSelection}) => {\n sendBack({type: 'input rule raised', endOffsets, endSelection})\n },\n }),\n })\n\n return () => {\n unregister()\n }\n}\n\nconst deleteBackwardListenerCallback: CallbackLogicFunction<\n AnyEventObject,\n InputRuleMachineEvent,\n {editor: Editor}\n> = ({input, sendBack}) => {\n return input.editor.registerBehavior({\n behavior: defineBehavior({\n on: 'delete.backward',\n actions: [\n () => [\n raise({type: 'history.undo'}),\n effect(() => {\n sendBack({type: 'history.undo raised'})\n }),\n ],\n ],\n }),\n })\n}\n\nconst selectionListenerCallback: CallbackLogicFunction<\n AnyEventObject,\n InputRuleMachineEvent,\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 const blockOffsets = getBlockOffsets({\n ...input.editor.getSnapshot(),\n context: {\n ...input.editor.getSnapshot().context,\n selection: event.selection,\n },\n })\n\n sendBack({\n type: 'selection changed',\n blockOffsets,\n selection: event.selection,\n })\n })\n\n return () => subscription.unsubscribe()\n}\n\nconst inputRuleSetup = setup({\n types: {\n context: {} as {\n editor: Editor\n rules: Array<InputRule>\n endOffsets: {start: BlockOffset; end: BlockOffset} | undefined\n endSelection: EditorSelection\n },\n input: {} as {\n editor: Editor\n rules: Array<InputRule>\n },\n events: {} as InputRuleMachineEvent,\n },\n actors: {\n 'delete.backward listener': fromCallback(deleteBackwardListenerCallback),\n 'input rule listener': fromCallback(inputRuleListenerCallback),\n 'selection listener': fromCallback(selectionListenerCallback),\n },\n guards: {\n 'selection changed': ({context, event}) => {\n if (event.type !== 'selection changed') {\n return false\n }\n\n // When block offsets are available for both the end state and the\n // current selection, compare them. Block offsets normalize away\n // span-level differences (e.g. cursor at the same position but in a\n // different span after normalization).\n if (event.blockOffsets && context.endOffsets) {\n const contextStartBlock = context.endOffsets.start.path.at(-1)\n const eventStartBlock = event.blockOffsets.start.path.at(-1)\n const contextEndBlock = context.endOffsets.end.path.at(-1)\n const eventEndBlock = event.blockOffsets.end.path.at(-1)\n\n if (\n !isKeyedSegment(contextStartBlock) ||\n !isKeyedSegment(eventStartBlock) ||\n !isKeyedSegment(contextEndBlock) ||\n !isKeyedSegment(eventEndBlock)\n ) {\n return false\n }\n\n const startChanged =\n contextStartBlock._key !== eventStartBlock._key ||\n context.endOffsets.start.offset !== event.blockOffsets.start.offset\n const endChanged =\n contextEndBlock._key !== eventEndBlock._key ||\n context.endOffsets.end.offset !== event.blockOffsets.end.offset\n\n return startChanged || endChanged\n }\n\n // Block offsets can't be computed when the cursor is on an inline\n // object (e.g. after a stock ticker rule inserts one). Fall back to\n // comparing the raw selections.\n return !isEqualSelections(context.endSelection, event.selection)\n },\n },\n})\n\nconst assignEndState = inputRuleSetup.assign({\n endOffsets: ({context, event}) =>\n event.type === 'input rule raised' ? event.endOffsets : context.endOffsets,\n endSelection: ({context, event}) =>\n event.type === 'input rule raised'\n ? event.endSelection\n : context.endSelection,\n})\n\nconst inputRuleMachine = inputRuleSetup.createMachine({\n id: 'input rule',\n context: ({input}) => ({\n editor: input.editor,\n rules: input.rules,\n endOffsets: undefined,\n endSelection: null,\n }),\n initial: 'idle',\n invoke: {\n src: 'input rule listener',\n input: ({context}) => ({\n editor: context.editor,\n rules: context.rules,\n }),\n },\n on: {\n 'input rule raised': {\n target: '.input rule applied',\n actions: assignEndState,\n },\n },\n states: {\n 'idle': {},\n 'input rule applied': {\n invoke: [\n {\n src: 'delete.backward listener',\n input: ({context}) => ({editor: context.editor}),\n },\n {\n src: 'selection listener',\n input: ({context}) => ({editor: context.editor}),\n },\n ],\n on: {\n 'selection changed': {\n target: 'idle',\n guard: 'selection changed',\n },\n 'history.undo raised': {\n target: 'idle',\n },\n },\n },\n },\n})\n","import {raise, type BehaviorAction} from '@portabletext/editor/behaviors'\nimport {getMarkState} from '@portabletext/editor/selectors'\nimport type {InputRule, InputRuleGuard} from './input-rule'\nimport type {InputRuleMatchLocation} from './input-rule-match-location'\n\n/**\n * @public\n */\nexport type TextTransform<TGuardResponse = true> = (\n {location}: {location: InputRuleMatchLocation},\n guardResponse: TGuardResponse,\n) => string\n\n/**\n * @public\n */\nexport type TextTransformRule<TGuardResponse = true> = {\n on: RegExp\n guard?: InputRuleGuard<TGuardResponse>\n /**\n * What to replace, and with what.\n *\n * A function replaces the whole match, always, regardless of any capture\n * groups in the pattern. A record replaces only the spans of the named\n * capture groups given as keys, each with its own transform,\n * `/\\d+\\s?(?<operator>[*x])\\s?\\d+/` with\n * `transform: {operator: () => '×'}` turns `2x3` into `2×3` rather than\n * `×`. Use the record form when the pattern needs surrounding context to\n * decide *when* to fire but only part of the match should change; the\n * context must sit inside the match rather than in lookarounds, a rule\n * only fires when its match involves the just-inserted text.\n *\n * Every key must exist as a named capture group in `on`;\n * `defineTextTransformRule` throws otherwise. A match in which none of\n * the keys participated has nothing to replace and is skipped.\n */\n transform:\n | TextTransform<TGuardResponse>\n | Record<string, TextTransform<TGuardResponse>>\n}\n\n/**\n * Define an `InputRule` specifically designed to transform matched text into\n * some other text.\n *\n * @example\n * ```tsx\n * const transformRule = defineTextTransformRule({\n * on: /--/,\n * transform: () => '—',\n * })\n * ```\n *\n * @public\n */\nexport function defineTextTransformRule<TGuardResponse = true>(\n config: TextTransformRule<TGuardResponse>,\n): InputRule<TGuardResponse> {\n const transformRecord =\n typeof config.transform === 'function' ? undefined : config.transform\n\n if (transformRecord) {\n // The appended `|` adds an empty alternative that matches the empty\n // string, so `exec('')` always produces a match whose `groups` object\n // carries a key for every named capture group in the pattern. `g`/`y`/\n // `d` are dropped (irrelevant for the probe, and sticky would anchor\n // it); the remaining flags are kept because recompiling without them\n // can be a syntax error (`\\u{...}` requires `u`).\n const probeFlags = config.on.flags.replace(/[gyd]/g, '')\n const namedGroups = Object.keys(\n new RegExp(`${config.on.source}|`, probeFlags).exec('')?.groups ?? {},\n )\n\n for (const groupName of Object.keys(transformRecord)) {\n if (!namedGroups.includes(groupName)) {\n throw new Error(\n `defineTextTransformRule: \\`transform\\` targets the group \"${groupName}\", but \\`on\\` (${config.on}) has no such named capture group` +\n (namedGroups.length > 0\n ? `. Named groups: ${namedGroups\n .map((name) => `\"${name}\"`)\n .join(', ')}`\n : `. The pattern has no named capture groups`),\n )\n }\n }\n }\n\n return {\n on: config.on,\n guard: config.guard ?? (() => true as TGuardResponse),\n actions: [\n ({snapshot, event}, guardResponse) => {\n const targets = event.matches\n .flatMap(\n (\n match,\n ): Array<{\n location: InputRuleMatchLocation\n transform: TextTransform<TGuardResponse>\n }> => {\n if (!transformRecord) {\n return [\n {\n location: match,\n transform:\n config.transform as TextTransform<TGuardResponse>,\n },\n ]\n }\n\n // Only participating keyed groups are replaced; a match in\n // which none of them participated is skipped.\n return Object.entries(transformRecord).flatMap(\n ([groupName, groupTransform]) => {\n const location = match.groups[groupName]\n\n return location ? [{location, transform: groupTransform}] : []\n },\n )\n },\n )\n // Right-to-left processing below relies on document order, which\n // the `replace` array's order doesn't guarantee.\n .sort(\n (a, b) =>\n a.location.targetOffsets.anchor.offset -\n b.location.targetOffsets.anchor.offset,\n )\n const newText = event.textBefore + event.textInserted\n\n let textLengthDelta = 0\n const actions: Array<BehaviorAction> = []\n\n for (const {location, transform} of targets.reverse()) {\n const text = transform({location}, guardResponse)\n\n textLengthDelta =\n textLengthDelta -\n (text.length -\n (location.targetOffsets.focus.offset -\n location.targetOffsets.anchor.offset))\n\n actions.push(raise({type: 'select', at: location.targetOffsets}))\n actions.push(raise({type: 'delete', at: location.targetOffsets}))\n actions.push(\n raise({\n type: 'insert.child',\n child: {\n _type: snapshot.context.schema.span.name,\n text,\n marks:\n getMarkState({\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: {\n anchor: location.selection.anchor,\n focus: {\n path: location.selection.focus.path,\n offset: Math.min(\n location.selection.focus.offset,\n event.textBefore.length,\n ),\n },\n },\n },\n })?.marks ?? [],\n },\n }),\n )\n }\n\n const endCaretPosition = {\n path: event.focusBlock.path,\n offset: newText.length - textLengthDelta,\n }\n\n return [\n ...actions,\n raise({\n type: 'select',\n at: {\n anchor: endCaretPosition,\n focus: endCaretPosition,\n },\n }),\n ]\n },\n ],\n }\n}\n"],"names":["defineInputRule","config","getInputRuleMatchLocation","match","adjustIndexBy","snapshot","focusBlock","originalTextBefore","allowedInlineObjectRanges","text","start","end","adjustedIndex","targetOffsets","anchor","path","offset","focus","backward","normalizedOffsets","Math","min","length","anchorBackwards","blockOffsetToSpanSelectionPoint","blockOffset","direction","focusForwards","selection","inlineObjectsAfterMatch","getNextInlineObjects","context","inlineObjectsBefore","getPreviousInlineObjects","inlineObjectsInMatch","filter","inlineObjectAfter","some","inlineObjectBefore","node","_key","inlineObject","inlineObjectOffset","childSelectionPointToBlockOffset","selectionPoint","range","defineInputRuleBehavior","defineBehavior","on","guard","event","dom","isSelectionCollapsed","getFocusBlock","getBlockTextBefore","textBefore","originalNewText","newText","foundMatches","foundActions","rule","rules","safeUserFlags","flags","replace","matcher","RegExp","source","ruleMatches","matchAll","flatMap","regExpMatch","indices","undefined","at","inlineObjects","allow","groupName","span","groups","matchLocation","foundMatch","Object","entries","groupLocation","guardResult","type","matches","textInserted","actionSets","actions","map","action","actionSet","push","groupLocations","values","location","slice","forward","_","effect","blockOffsets","getBlockOffsets","onApply","endOffsets","endSelection","InputRulePlugin","props","$","_c","editor","useEditor","t0","input","useActorRef","inputRuleMachine","inputRuleListenerCallback","sendBack","unregister","registerBehavior","behavior","deleteBackwardListenerCallback","raise","selectionListenerCallback","subscription","getSnapshot","unsubscribe","inputRuleSetup","setup","types","events","actors","fromCallback","guards","selection changed","contextStartBlock","eventStartBlock","contextEndBlock","eventEndBlock","isKeyedSegment","startChanged","endChanged","isEqualSelections","assignEndState","assign","createMachine","id","initial","invoke","src","target","states","defineTextTransformRule","transformRecord","transform","probeFlags","namedGroups","keys","exec","includes","Error","name","join","guardResponse","targets","groupTransform","sort","a","b","textLengthDelta","reverse","child","_type","schema","marks","getMarkState","endCaretPosition"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portabletext/plugin-input-rule",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.1",
|
|
4
4
|
"description": "Easily configure Input Rules in the Portable Text Editor",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"input-rule",
|
|
@@ -48,12 +48,12 @@
|
|
|
48
48
|
"typescript": "6.0.3",
|
|
49
49
|
"typescript-eslint": "^8.62.0",
|
|
50
50
|
"vitest": "^4.1.9",
|
|
51
|
-
"@portabletext/editor": "^7.10.
|
|
51
|
+
"@portabletext/editor": "^7.10.4",
|
|
52
52
|
"@portabletext/schema": "2.2.2",
|
|
53
53
|
"racejar": "2.0.9"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@portabletext/editor": "^7.10.
|
|
56
|
+
"@portabletext/editor": "^7.10.4",
|
|
57
57
|
"react": "^19.2"
|
|
58
58
|
},
|
|
59
59
|
"engines": {
|
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
"test:browser:firefox": "vitest run --project \"browser (firefox)\"",
|
|
76
76
|
"test:browser:firefox:watch": "vitest watch --project \"browser (firefox)\"",
|
|
77
77
|
"test:browser:webkit": "vitest run --project \"browser (webkit)\"",
|
|
78
|
-
"test:browser:webkit:watch": "vitest watch --project \"browser (webkit)\""
|
|
78
|
+
"test:browser:webkit:watch": "vitest watch --project \"browser (webkit)\"",
|
|
79
|
+
"test:unit": "vitest run --project unit"
|
|
79
80
|
}
|
|
80
81
|
}
|