0
# Input and Keymaps
1
2
Input rules provide automatic text transformations based on typing patterns, while keymaps bind keyboard commands to editor actions. Together they create responsive editing experiences.
3
4
## Capabilities
5
6
### Input Rules
7
8
Input rules automatically transform text as users type, providing shortcuts and typography improvements.
9
10
```typescript { .api }
11
/**
12
* An input rule transforms text based on pattern matching
13
*/
14
class InputRule {
15
/**
16
* Create a new input rule
17
*/
18
constructor(
19
match: RegExp,
20
handler: string | ((state: EditorState, match: string[], start: number, end: number) => Transaction | null),
21
options?: { undoable?: boolean }
22
);
23
}
24
```
25
26
### Input Rule Plugin
27
28
Create and manage input rule plugins.
29
30
```typescript { .api }
31
/**
32
* Create an input rules plugin
33
*/
34
function inputRules(config: { rules: InputRule[] }): Plugin;
35
36
/**
37
* Undo the last input rule transformation
38
*/
39
function undoInputRule(state: EditorState, dispatch?: (tr: Transaction) => void): boolean;
40
```
41
42
### Input Rule Helpers
43
44
Convenient functions for common input rule patterns.
45
46
```typescript { .api }
47
/**
48
* Create an input rule that wraps matched text in a node
49
*/
50
function wrappingInputRule(
51
regexp: RegExp,
52
nodeType: NodeType,
53
getAttrs?: Attrs | ((match: string[]) => Attrs | null),
54
joinPredicate?: (match: string[], node: Node) => boolean
55
): InputRule;
56
57
/**
58
* Create an input rule that changes text block type
59
*/
60
function textblockTypeInputRule(
61
regexp: RegExp,
62
nodeType: NodeType,
63
getAttrs?: Attrs | ((match: string[]) => Attrs | null)
64
): InputRule;
65
```
66
67
### Typography Rules
68
69
Pre-defined input rules for common typography transformations.
70
71
```typescript { .api }
72
/**
73
* Convert -- to em dash
74
*/
75
const emDash: InputRule;
76
77
/**
78
* Convert ... to ellipsis character
79
*/
80
const ellipsis: InputRule;
81
82
/**
83
* Convert opening quotes
84
*/
85
const openDoubleQuote: InputRule;
86
87
/**
88
* Convert closing quotes
89
*/
90
const closeDoubleQuote: InputRule;
91
92
/**
93
* Convert opening single quotes
94
*/
95
const openSingleQuote: InputRule;
96
97
/**
98
* Convert closing single quotes
99
*/
100
const closeSingleQuote: InputRule;
101
102
/**
103
* Combined smart quote rules
104
*/
105
const smartQuotes: InputRule[];
106
```
107
108
### Keymap System
109
110
Keymaps bind keyboard shortcuts to editor commands.
111
112
```typescript { .api }
113
/**
114
* Create a keymap plugin from key bindings
115
*/
116
function keymap(bindings: { [key: string]: Command | false }): Plugin;
117
118
/**
119
* Create a keydown event handler from bindings
120
*/
121
function keydownHandler(bindings: { [key: string]: Command | false }): (view: EditorView, event: KeyboardEvent) => boolean;
122
```
123
124
### Key Notation
125
126
Key combinations use standard notation for cross-platform compatibility.
127
128
```typescript { .api }
129
/**
130
* Key binding specification
131
*/
132
interface KeyBinding {
133
[key: string]: Command | false;
134
}
135
136
/**
137
* Key notation examples:
138
* - "Mod-b" (Ctrl on PC/Linux, Cmd on Mac)
139
* - "Alt-Enter"
140
* - "Shift-Ctrl-z"
141
* - "Backspace"
142
* - "Delete"
143
* - "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"
144
* - "Home", "End"
145
* - "PageUp", "PageDown"
146
*/
147
```
148
149
**Usage Examples:**
150
151
```typescript
152
import {
153
InputRule,
154
inputRules,
155
wrappingInputRule,
156
textblockTypeInputRule,
157
smartQuotes,
158
emDash,
159
ellipsis
160
} from "@tiptap/pm/inputrules";
161
import { keymap } from "@tiptap/pm/keymap";
162
import { toggleMark, setBlockType } from "@tiptap/pm/commands";
163
164
// Custom input rules
165
const rules = [
166
// Convert ** to bold
167
new InputRule(
168
/\*\*([^*]+)\*\*$/,
169
(state, match, start, end) => {
170
const tr = state.tr.delete(start, end);
171
const mark = state.schema.marks.strong.create();
172
return tr.insert(start, state.schema.text(match[1], [mark]));
173
}
174
),
175
176
// Wrap selection in blockquote with >
177
wrappingInputRule(
178
/^\s*>\s$/,
179
state.schema.nodes.blockquote
180
),
181
182
// Convert # to heading
183
textblockTypeInputRule(
184
/^#\s/,
185
state.schema.nodes.heading,
186
{ level: 1 }
187
),
188
189
// Typography rules
190
...smartQuotes,
191
emDash,
192
ellipsis
193
];
194
195
// Create input rules plugin
196
const inputRulesPlugin = inputRules({ rules });
197
198
// Custom keymap
199
const myKeymap = keymap({
200
// Formatting
201
"Mod-b": toggleMark(schema.marks.strong),
202
"Mod-i": toggleMark(schema.marks.em),
203
"Mod-`": toggleMark(schema.marks.code),
204
205
// Block types
206
"Shift-Ctrl-1": setBlockType(schema.nodes.heading, { level: 1 }),
207
"Shift-Ctrl-2": setBlockType(schema.nodes.heading, { level: 2 }),
208
"Shift-Ctrl-0": setBlockType(schema.nodes.paragraph),
209
210
// Custom commands
211
"Mod-Enter": (state, dispatch) => {
212
if (dispatch) {
213
dispatch(state.tr.insertText("\n"));
214
}
215
return true;
216
},
217
218
// Disable default behavior
219
"Ctrl-d": false
220
});
221
222
// Combine with editor
223
const state = EditorState.create({
224
schema: mySchema,
225
plugins: [
226
inputRulesPlugin,
227
myKeymap
228
]
229
});
230
```
231
232
## Advanced Input Rules
233
234
### Conditional Rules
235
236
Create rules that apply conditionally based on context.
237
238
```typescript
239
// Only apply in specific node types
240
const codeBlockRule = new InputRule(
241
/```(\w+)?\s$/,
242
(state, match, start, end) => {
243
// Only apply at document level
244
if (state.selection.$from.depth > 1) return null;
245
246
const attrs = match[1] ? { language: match[1] } : {};
247
return state.tr
248
.delete(start, end)
249
.setBlockType(start, start, schema.nodes.code_block, attrs);
250
}
251
);
252
253
// Rules with custom undo behavior
254
const undoableRule = new InputRule(
255
/-->/g,
256
"→",
257
{ undoable: true }
258
);
259
```
260
261
### Complex Transformations
262
263
Input rules can perform sophisticated document transformations.
264
265
```typescript
266
// Auto-link URLs
267
const autoLinkRule = new InputRule(
268
/(https?:\/\/[^\s]+)$/,
269
(state, match, start, end) => {
270
const url = match[1];
271
const mark = schema.marks.link.create({ href: url });
272
return state.tr
273
.addMark(start, end, mark)
274
.insertText(" ", end);
275
}
276
);
277
278
// Smart list continuation
279
const listContinueRule = new InputRule(
280
/^(\d+)\.\s$/,
281
(state, match, start, end) => {
282
const number = parseInt(match[1]);
283
return state.tr
284
.delete(start, end)
285
.wrapIn(schema.nodes.ordered_list, { order: number })
286
.wrapIn(schema.nodes.list_item);
287
}
288
);
289
```
290
291
## Keymap Composition
292
293
### Layered Keymaps
294
295
Combine multiple keymaps with priority ordering.
296
297
```typescript
298
import { baseKeymap } from "@tiptap/pm/commands";
299
300
const editorKeymaps = [
301
// Highest priority - custom overrides
302
keymap({
303
"Enter": customEnterCommand,
304
"Tab": customTabCommand
305
}),
306
307
// Medium priority - feature-specific
308
keymap({
309
"Mod-k": insertLinkCommand,
310
"Mod-Shift-k": removeLinkCommand
311
}),
312
313
// Lowest priority - base commands
314
keymap(baseKeymap)
315
];
316
317
const state = EditorState.create({
318
schema: mySchema,
319
plugins: editorKeymaps
320
});
321
```
322
323
### Platform-Specific Bindings
324
325
Handle platform differences in key bindings.
326
327
```typescript
328
const platformKeymap = keymap({
329
// Cross-platform modifier
330
"Mod-z": undo,
331
"Mod-Shift-z": redo,
332
333
// Platform-specific alternatives
334
...(navigator.platform.includes("Mac") ? {
335
"Cmd-y": redo // Mac alternative
336
} : {
337
"Ctrl-y": redo // Windows/Linux alternative
338
})
339
});
340
```
341
342
## Types
343
344
```typescript { .api }
345
/**
346
* Input rule configuration
347
*/
348
interface InputRuleConfig {
349
rules: InputRule[];
350
}
351
352
/**
353
* Key binding map
354
*/
355
interface Keymap {
356
[key: string]: Command | false;
357
}
358
359
/**
360
* Input rule handler function
361
*/
362
type InputRuleHandler = (
363
state: EditorState,
364
match: string[],
365
start: number,
366
end: number
367
) => Transaction | null;
368
369
/**
370
* Input rule options
371
*/
372
interface InputRuleOptions {
373
undoable?: boolean;
374
}
375
```