@domternal/extension-mention 0.2.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Domternal contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # @domternal/extension-mention
2
+
3
+ `@mention` autocomplete extension for Domternal with multi-trigger and async support.
4
+
5
+ Part of the [Domternal](https://github.com/domternal/domternal) toolkit. Full docs at [domternal.dev](https://domternal.dev).
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @domternal/core @domternal/extension-mention
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```ts
16
+ import { Editor, StarterKit } from '@domternal/core';
17
+ import { Mention } from '@domternal/extension-mention';
18
+
19
+ const editor = new Editor({
20
+ element: document.getElementById('editor')!,
21
+ extensions: [
22
+ StarterKit,
23
+ Mention.configure({
24
+ suggestion: {
25
+ char: '@',
26
+ name: 'user',
27
+ items: ({ query }) =>
28
+ users.filter((u) => u.label.toLowerCase().includes(query.toLowerCase())),
29
+ render: createMentionRenderer,
30
+ },
31
+ }),
32
+ ],
33
+ });
34
+
35
+ // Insert a mention programmatically
36
+ editor.commands.insertMention({ id: '1', label: 'Alice' });
37
+ ```
38
+
39
+ ### Multi-trigger
40
+
41
+ Use the `triggers` option to support multiple trigger characters (for example, `@` for users and `#` for tags):
42
+
43
+ ```ts
44
+ Mention.configure({
45
+ triggers: [
46
+ {
47
+ char: '@',
48
+ name: 'user',
49
+ items: ({ query }) => fetchUsers(query),
50
+ render: createUserRenderer,
51
+ },
52
+ {
53
+ char: '#',
54
+ name: 'tag',
55
+ items: ({ query }) => fetchTags(query),
56
+ render: createTagRenderer,
57
+ },
58
+ ],
59
+ })
60
+ ```
61
+
62
+ ### Features
63
+
64
+ - **Autocomplete dropdown** - type a trigger character followed by a query to see matching items
65
+ - **Async item fetching** - the `items` function can return a `Promise` for server-side lookups
66
+ - **Multi-trigger** - define multiple trigger characters, each with its own item source and renderer
67
+ - **Custom rendering** - provide a `render` function for full control over the suggestion dropdown UI
68
+
69
+ ## License
70
+
71
+ [MIT](https://github.com/domternal/domternal/blob/main/LICENSE)
package/dist/index.cjs ADDED
@@ -0,0 +1,445 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var core = require('@domternal/core');
6
+ var state = require('@domternal/pm/state');
7
+ var view = require('@domternal/pm/view');
8
+
9
+ // src/Mention.ts
10
+ var INITIAL_STATE = {
11
+ active: false,
12
+ query: "",
13
+ range: null
14
+ };
15
+ var pluginKeyCache = /* @__PURE__ */ new Map();
16
+ function getPluginKey(triggerName) {
17
+ let key = pluginKeyCache.get(triggerName);
18
+ if (!key) {
19
+ key = new state.PluginKey(`mentionSuggestion_${triggerName}`);
20
+ pluginKeyCache.set(triggerName, key);
21
+ }
22
+ return key;
23
+ }
24
+ function findMentionQuery(state, triggerChar, allowSpaces, invalidNodes) {
25
+ const { selection } = state;
26
+ if (!selection.empty) return null;
27
+ const { $from } = selection;
28
+ if (invalidNodes.length > 0) {
29
+ const parentNodeType = $from.parent.type.name;
30
+ if (invalidNodes.includes(parentNodeType)) return null;
31
+ }
32
+ const textBefore = $from.parent.textBetween(
33
+ 0,
34
+ $from.parentOffset,
35
+ void 0,
36
+ "\uFFFC"
37
+ );
38
+ const triggerIndex = textBefore.lastIndexOf(triggerChar);
39
+ if (triggerIndex === -1) return null;
40
+ if (triggerIndex > 0 && textBefore[triggerIndex - 1] !== " ") return null;
41
+ const queryText = textBefore.slice(triggerIndex + triggerChar.length);
42
+ const validPattern = allowSpaces ? /^[a-zA-Z0-9_.\-+ ]*$/ : /^[a-zA-Z0-9_.\-+]*$/;
43
+ if (!validPattern.test(queryText)) return null;
44
+ const from = $from.start() + triggerIndex;
45
+ const to = $from.pos;
46
+ return { query: queryText, range: { from, to } };
47
+ }
48
+ function createMentionSuggestionPlugin(options) {
49
+ const { trigger, nodeType } = options;
50
+ const triggerChar = trigger.char;
51
+ const minQueryLength = trigger.minQueryLength ?? 0;
52
+ const allowSpaces = trigger.allowSpaces ?? false;
53
+ const appendText = trigger.appendText ?? " ";
54
+ const invalidNodes = trigger.invalidNodes ?? [];
55
+ const getItems = trigger.items;
56
+ const getRender = trigger.render;
57
+ const key = getPluginKey(trigger.name);
58
+ let renderer = null;
59
+ let debounceTimer = null;
60
+ const debounceMs = trigger.debounce ?? 0;
61
+ const decorationClass = trigger.decorationClass ?? "mention-suggestion";
62
+ const decorationTag = trigger.decorationTag ?? "span";
63
+ const shouldShow = trigger.shouldShow;
64
+ function cleanup() {
65
+ if (debounceTimer !== null) {
66
+ clearTimeout(debounceTimer);
67
+ debounceTimer = null;
68
+ }
69
+ }
70
+ function notifyRenderer(props) {
71
+ if (!renderer && getRender) {
72
+ renderer = getRender();
73
+ renderer.onStart(props);
74
+ } else if (renderer) {
75
+ renderer.onUpdate(props);
76
+ }
77
+ }
78
+ return new state.Plugin({
79
+ key,
80
+ state: {
81
+ init() {
82
+ return { ...INITIAL_STATE };
83
+ },
84
+ apply(tr, prev, _oldState, newState) {
85
+ if (tr.getMeta(key) === "dismiss") {
86
+ return { ...INITIAL_STATE };
87
+ }
88
+ const isUserInput = tr.docChanged || tr.selectionSet;
89
+ if (!isUserInput) return prev;
90
+ const result = findMentionQuery(newState, triggerChar, allowSpaces, invalidNodes);
91
+ if (result && result.query.length >= minQueryLength) {
92
+ return {
93
+ active: true,
94
+ query: result.query,
95
+ range: result.range
96
+ };
97
+ }
98
+ if (prev.active) {
99
+ return { ...INITIAL_STATE };
100
+ }
101
+ return prev;
102
+ }
103
+ },
104
+ view() {
105
+ return {
106
+ update(view) {
107
+ const pluginState = key.getState(view.state);
108
+ if (!pluginState) return;
109
+ if (pluginState.active && pluginState.range) {
110
+ if (shouldShow && !shouldShow({ state: view.state, view })) {
111
+ if (renderer) {
112
+ cleanup();
113
+ renderer.onExit();
114
+ renderer = null;
115
+ }
116
+ return;
117
+ }
118
+ const command = (item) => {
119
+ const currentState = key.getState(view.state);
120
+ if (!currentState?.range || !nodeType) return;
121
+ const { tr } = view.state;
122
+ const node = nodeType.create({
123
+ id: item.id,
124
+ label: item.label,
125
+ type: trigger.name
126
+ });
127
+ tr.replaceWith(currentState.range.from, currentState.range.to, node);
128
+ if (appendText) {
129
+ tr.insertText(appendText);
130
+ }
131
+ view.dispatch(tr);
132
+ };
133
+ const clientRect = () => {
134
+ const currentState = key.getState(view.state);
135
+ if (!currentState?.range) return null;
136
+ try {
137
+ const coords = view.coordsAtPos(currentState.range.from);
138
+ return new DOMRect(coords.left, coords.top, 0, coords.bottom - coords.top);
139
+ } catch {
140
+ return null;
141
+ }
142
+ };
143
+ const fetchAndRender = () => {
144
+ const cur = key.getState(view.state);
145
+ if (!cur?.active || !cur.range) return;
146
+ const result = getItems({ query: cur.query, trigger });
147
+ if (result instanceof Promise) {
148
+ void result.then((items) => {
149
+ const latest = key.getState(view.state);
150
+ if (!latest?.active || !latest.range) return;
151
+ notifyRenderer({ query: latest.query, range: latest.range, items, command, clientRect });
152
+ }).catch(() => {
153
+ });
154
+ } else {
155
+ notifyRenderer({ query: cur.query, range: cur.range, items: result, command, clientRect });
156
+ }
157
+ };
158
+ cleanup();
159
+ if (debounceMs > 0) {
160
+ debounceTimer = setTimeout(fetchAndRender, debounceMs);
161
+ } else {
162
+ fetchAndRender();
163
+ }
164
+ } else if (renderer) {
165
+ cleanup();
166
+ renderer.onExit();
167
+ renderer = null;
168
+ }
169
+ },
170
+ destroy() {
171
+ cleanup();
172
+ if (renderer) {
173
+ renderer.onExit();
174
+ renderer = null;
175
+ }
176
+ }
177
+ };
178
+ },
179
+ props: {
180
+ handleKeyDown(view, event) {
181
+ const state = key.getState(view.state);
182
+ if (!state?.active) return false;
183
+ if (event.key === "Escape") {
184
+ const { tr } = view.state;
185
+ tr.setMeta(key, "dismiss");
186
+ view.dispatch(tr);
187
+ return true;
188
+ }
189
+ if (renderer) {
190
+ return renderer.onKeyDown(event);
191
+ }
192
+ return false;
193
+ },
194
+ decorations(state) {
195
+ const pluginState = key.getState(state);
196
+ if (!pluginState?.active || !pluginState.range) {
197
+ return view.DecorationSet.empty;
198
+ }
199
+ return view.DecorationSet.create(state.doc, [
200
+ view.Decoration.inline(pluginState.range.from, pluginState.range.to, {
201
+ class: decorationClass,
202
+ nodeName: decorationTag
203
+ })
204
+ ]);
205
+ }
206
+ }
207
+ });
208
+ }
209
+ function dismissMentionSuggestion(view, triggerName) {
210
+ const key = getPluginKey(triggerName);
211
+ const { tr } = view.state;
212
+ tr.setMeta(key, "dismiss");
213
+ view.dispatch(tr);
214
+ }
215
+
216
+ // src/Mention.ts
217
+ function getMentionText(node, charMap) {
218
+ const label = node.attrs["label"] ?? "";
219
+ const typeName = node.attrs["type"] ?? "mention";
220
+ const triggerChar = charMap.get(typeName) ?? "@";
221
+ return `${triggerChar}${label}`;
222
+ }
223
+ function resolveTriggers(options) {
224
+ if (options.triggers.length > 0) return options.triggers;
225
+ if (options.suggestion) return [options.suggestion];
226
+ return [];
227
+ }
228
+ var Mention = core.Node.create({
229
+ name: "mention",
230
+ group: "inline",
231
+ inline: true,
232
+ atom: true,
233
+ selectable: false,
234
+ draggable: false,
235
+ addOptions() {
236
+ return {
237
+ suggestion: null,
238
+ triggers: [],
239
+ deleteTriggerWithBackspace: false,
240
+ renderHTML: null,
241
+ renderText: null,
242
+ HTMLAttributes: {}
243
+ };
244
+ },
245
+ addStorage() {
246
+ return {
247
+ findMentions: () => [],
248
+ _triggerCharMap: /* @__PURE__ */ new Map()
249
+ };
250
+ },
251
+ onCreate() {
252
+ const triggers = resolveTriggers(this.options);
253
+ const charMap = /* @__PURE__ */ new Map();
254
+ for (const t of triggers) {
255
+ charMap.set(t.name, t.char);
256
+ }
257
+ this.storage._triggerCharMap = charMap;
258
+ this.storage.findMentions = () => {
259
+ const editor = this.editor;
260
+ if (!editor) return [];
261
+ const mentions = [];
262
+ const { doc } = editor.state;
263
+ const mentionType = this.nodeType;
264
+ if (!mentionType) return [];
265
+ doc.descendants((node, pos) => {
266
+ if (node.type === mentionType) {
267
+ mentions.push({
268
+ id: node.attrs["id"],
269
+ label: node.attrs["label"],
270
+ type: node.attrs["type"],
271
+ pos
272
+ });
273
+ }
274
+ });
275
+ return mentions;
276
+ };
277
+ },
278
+ addAttributes() {
279
+ return {
280
+ id: {
281
+ default: null,
282
+ parseHTML: (element) => element.getAttribute("data-id"),
283
+ renderHTML: (attributes) => {
284
+ if (!attributes["id"]) return {};
285
+ return { "data-id": attributes["id"] };
286
+ }
287
+ },
288
+ label: {
289
+ default: null,
290
+ parseHTML: (element) => element.getAttribute("data-label"),
291
+ renderHTML: (attributes) => {
292
+ if (!attributes["label"]) return {};
293
+ return { "data-label": attributes["label"] };
294
+ }
295
+ },
296
+ type: {
297
+ default: "mention",
298
+ parseHTML: (element) => element.getAttribute("data-mention-type") ?? "mention",
299
+ renderHTML: (attributes) => {
300
+ return { "data-mention-type": attributes["type"] };
301
+ }
302
+ }
303
+ };
304
+ },
305
+ parseHTML() {
306
+ return [
307
+ { tag: 'span[data-type="mention"]' },
308
+ { tag: "span[data-mention]" }
309
+ ];
310
+ },
311
+ renderHTML({ node, HTMLAttributes }) {
312
+ if (this.options.renderHTML) {
313
+ return this.options.renderHTML({
314
+ node,
315
+ options: this.options,
316
+ HTMLAttributes
317
+ });
318
+ }
319
+ return [
320
+ "span",
321
+ {
322
+ ...this.options.HTMLAttributes,
323
+ ...HTMLAttributes,
324
+ "data-type": "mention",
325
+ class: [
326
+ this.options.HTMLAttributes["class"],
327
+ HTMLAttributes["class"],
328
+ "mention"
329
+ ].filter(Boolean).join(" ")
330
+ },
331
+ getMentionText(node, this.storage._triggerCharMap)
332
+ ];
333
+ },
334
+ leafText(node) {
335
+ if (this.options.renderText) {
336
+ return this.options.renderText({ node, options: this.options });
337
+ }
338
+ return getMentionText(node, this.storage._triggerCharMap);
339
+ },
340
+ addCommands() {
341
+ return {
342
+ insertMention: (attrs) => ({ tr, dispatch }) => {
343
+ if (!attrs.id || !attrs.label) return false;
344
+ if (!this.nodeType) return false;
345
+ if (dispatch) {
346
+ const mentionNode = this.nodeType.create({
347
+ id: attrs.id,
348
+ label: attrs.label,
349
+ type: attrs.type ?? "mention"
350
+ });
351
+ tr.replaceSelectionWith(mentionNode);
352
+ dispatch(tr);
353
+ }
354
+ return true;
355
+ },
356
+ deleteMention: (id) => ({ tr, dispatch, state }) => {
357
+ if (id) {
358
+ const mentionType = this.nodeType;
359
+ if (!mentionType) return false;
360
+ let foundPos = -1;
361
+ let foundSize = 0;
362
+ state.doc.descendants((node, pos) => {
363
+ if (foundPos >= 0) return false;
364
+ if (node.type === mentionType && node.attrs["id"] === id) {
365
+ foundPos = pos;
366
+ foundSize = node.nodeSize;
367
+ return false;
368
+ }
369
+ return void 0;
370
+ });
371
+ if (foundPos < 0) return false;
372
+ if (dispatch) {
373
+ tr.delete(foundPos, foundPos + foundSize);
374
+ dispatch(tr);
375
+ }
376
+ return true;
377
+ }
378
+ const { $from } = state.selection;
379
+ if (!state.selection.empty) return false;
380
+ const nodeBefore = $from.nodeBefore;
381
+ if (nodeBefore?.type.name !== "mention") return false;
382
+ if (dispatch) {
383
+ const from = $from.pos - nodeBefore.nodeSize;
384
+ tr.delete(from, $from.pos);
385
+ dispatch(tr);
386
+ }
387
+ return true;
388
+ }
389
+ };
390
+ },
391
+ addKeyboardShortcuts() {
392
+ return {
393
+ Backspace: () => {
394
+ const editor = this.editor;
395
+ if (!editor) return false;
396
+ const { state } = editor;
397
+ const { selection } = state;
398
+ const { empty, $from } = selection;
399
+ if (!empty) return false;
400
+ const nodeBefore = $from.nodeBefore;
401
+ if (nodeBefore?.type.name !== "mention") return false;
402
+ const from = $from.pos - nodeBefore.nodeSize;
403
+ const { tr } = state;
404
+ if (this.options.deleteTriggerWithBackspace) {
405
+ const triggerType = nodeBefore.attrs["type"] ?? "mention";
406
+ const triggerChar = this.storage._triggerCharMap.get(triggerType);
407
+ if (triggerChar && from > 0) {
408
+ const $before = state.doc.resolve(from);
409
+ const charBefore = $before.parent.textBetween(
410
+ Math.max(0, $before.parentOffset - triggerChar.length),
411
+ $before.parentOffset,
412
+ void 0,
413
+ "\uFFFC"
414
+ );
415
+ if (charBefore === triggerChar) {
416
+ tr.delete(from - triggerChar.length, $from.pos);
417
+ editor.view.dispatch(tr);
418
+ return true;
419
+ }
420
+ }
421
+ }
422
+ tr.delete(from, $from.pos);
423
+ editor.view.dispatch(tr);
424
+ return true;
425
+ }
426
+ };
427
+ },
428
+ addProseMirrorPlugins() {
429
+ const triggers = resolveTriggers(this.options);
430
+ if (triggers.length === 0) return [];
431
+ return triggers.map(
432
+ (trigger) => createMentionSuggestionPlugin({
433
+ trigger,
434
+ nodeType: this.nodeType
435
+ })
436
+ );
437
+ }
438
+ });
439
+
440
+ exports.Mention = Mention;
441
+ exports.createMentionSuggestionPlugin = createMentionSuggestionPlugin;
442
+ exports.default = Mention;
443
+ exports.dismissMentionSuggestion = dismissMentionSuggestion;
444
+ //# sourceMappingURL=index.cjs.map
445
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mentionSuggestionPlugin.ts","../src/Mention.ts"],"names":["PluginKey","Plugin","DecorationSet","Decoration","Node"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Mention Suggestion Plugin\n *\n * Headless ProseMirror plugin that watches for trigger characters (e.g. '@', '#'),\n * tracks the query, supports async item fetching with debounce, and calls render\n * callbacks so framework wrappers can display a dropdown picker.\n *\n * Adapted from the emoji suggestion plugin with additions:\n * - Async items support with 150ms debounce\n * - Multiple plugin instances (one per trigger, unique PluginKey)\n * - Inline decoration on active trigger+query range\n * - invalidNodes context check\n * - appendText after insertion\n */\nimport { Plugin, PluginKey } from '@domternal/pm/state';\nimport type { EditorState, Transaction } from '@domternal/pm/state';\nimport type { EditorView } from '@domternal/pm/view';\nimport type { NodeType } from '@domternal/pm/model';\nimport { Decoration, DecorationSet } from '@domternal/pm/view';\n\n// ─── Public Types ────────────────────────────────────────────────────────────\n\n/** A single mention item returned by the items() callback. */\nexport interface MentionItem {\n /** Unique identifier (e.g., user ID, tag slug). */\n id: string;\n /** Display text (e.g., \"John Doe\", \"feature-request\"). */\n label: string;\n /** Allow extra data (avatar URL, email, role, etc.). */\n [key: string]: unknown;\n}\n\n/** Configuration for a single mention trigger. */\nexport interface MentionTrigger {\n /** Trigger character. Default: '@' */\n char: string;\n /** Unique name for this trigger type (e.g., 'user', 'tag'). */\n name: string;\n /** Fetch items matching a query. Supports sync and async (Promise). */\n items: (props: { query: string; trigger: MentionTrigger }) => MentionItem[] | Promise<MentionItem[]>;\n /** Render callbacks for the suggestion popup. */\n render?: () => MentionSuggestionRenderer;\n /** Minimum query length before showing suggestions. Default: 0 */\n minQueryLength?: number;\n /** Allow spaces in query. Default: false */\n allowSpaces?: boolean;\n /** Text to append after mention insertion. Default: ' ' (space) */\n appendText?: string;\n /** Node types where suggestion should NOT activate (e.g. ['codeBlock']). Default: [] */\n invalidNodes?: string[];\n /** Debounce delay in ms for items() calls. Use >0 for async/API-backed items. Default: 0 (immediate) */\n debounce?: number;\n /** CSS class for the inline decoration on the active trigger+query range. Default: 'mention-suggestion' */\n decorationClass?: string;\n /** HTML tag for the inline decoration element. Default: 'span' */\n decorationTag?: string;\n /**\n * Controls whether the suggestion should be shown. Return `false` to suppress.\n * Useful for collaboration (suppress suggestions triggered by remote cursors)\n * or any custom logic based on editor state.\n */\n shouldShow?: (props: { state: EditorState; view: EditorView }) => boolean;\n}\n\n/** Props passed to suggestion renderer callbacks. */\nexport interface MentionSuggestionProps {\n /** Current query string (text after trigger char). */\n query: string;\n /** Document range of the trigger + query (for replacement). */\n range: { from: number; to: number };\n /** Filtered mention items matching the query. */\n items: MentionItem[];\n /** Call to insert a mention and close the suggestion. */\n command: (item: MentionItem) => void;\n /** Returns the client rect of the cursor for positioning the popup. */\n clientRect: (() => DOMRect | null) | null;\n}\n\n/** Render callbacks for the suggestion popup. */\nexport interface MentionSuggestionRenderer {\n /** Called when suggestion is first activated. */\n onStart: (props: MentionSuggestionProps) => void;\n /** Called when query or items change. */\n onUpdate: (props: MentionSuggestionProps) => void;\n /** Called when suggestion is deactivated. */\n onExit: () => void;\n /** Called on keydown — return true to prevent default editor handling. */\n onKeyDown: (event: KeyboardEvent) => boolean;\n}\n\n// ─── Internal Types ──────────────────────────────────────────────────────────\n\ninterface SuggestionPluginOptions {\n trigger: MentionTrigger;\n nodeType: NodeType | null;\n}\n\ninterface SuggestionState {\n active: boolean;\n query: string;\n range: { from: number; to: number } | null;\n}\n\nconst INITIAL_STATE: SuggestionState = {\n active: false,\n query: '',\n range: null,\n};\n\n// ─── Plugin Key Registry ─────────────────────────────────────────────────────\n\nconst pluginKeyCache = new Map<string, PluginKey<SuggestionState>>();\n\n/** Get or create a PluginKey for a given trigger name. */\nfunction getPluginKey(triggerName: string): PluginKey<SuggestionState> {\n let key = pluginKeyCache.get(triggerName);\n if (!key) {\n key = new PluginKey<SuggestionState>(`mentionSuggestion_${triggerName}`);\n pluginKeyCache.set(triggerName, key);\n }\n return key;\n}\n\n// ─── Query Detection ─────────────────────────────────────────────────────────\n\n/**\n * Extracts the suggestion query from the current text before the cursor.\n * Returns null if no active suggestion is found.\n */\nfunction findMentionQuery(\n state: EditorState,\n triggerChar: string,\n allowSpaces: boolean,\n invalidNodes: string[],\n): { query: string; range: { from: number; to: number } } | null {\n const { selection } = state;\n\n // Only work with collapsed cursor selections\n if (!selection.empty) return null;\n\n const { $from } = selection;\n\n // Check if cursor is in an invalid node\n if (invalidNodes.length > 0) {\n const parentNodeType = $from.parent.type.name;\n if (invalidNodes.includes(parentNodeType)) return null;\n }\n\n const textBefore = $from.parent.textBetween(\n 0,\n $from.parentOffset,\n undefined,\n '\\ufffc',\n );\n\n // Find the last trigger character\n const triggerIndex = textBefore.lastIndexOf(triggerChar);\n if (triggerIndex === -1) return null;\n\n // The trigger must be at the start of the textblock or preceded by a space\n if (triggerIndex > 0 && textBefore[triggerIndex - 1] !== ' ') return null;\n\n const queryText = textBefore.slice(triggerIndex + triggerChar.length);\n\n // Query must contain only valid chars (alphanumeric, underscore, dash, dot, plus; spaces if allowed)\n const validPattern = allowSpaces\n ? /^[a-zA-Z0-9_.\\-+ ]*$/\n : /^[a-zA-Z0-9_.\\-+]*$/;\n if (!validPattern.test(queryText)) return null;\n\n const from = $from.start() + triggerIndex;\n const to = $from.pos;\n\n return { query: queryText, range: { from, to } };\n}\n\n// ─── Plugin Factory ──────────────────────────────────────────────────────────\n\n/**\n * Creates a ProseMirror plugin for mention suggestion/autocomplete.\n * One plugin instance per trigger.\n */\nexport function createMentionSuggestionPlugin(\n options: SuggestionPluginOptions,\n): Plugin {\n const { trigger, nodeType } = options;\n const triggerChar = trigger.char;\n const minQueryLength = trigger.minQueryLength ?? 0;\n const allowSpaces = trigger.allowSpaces ?? false;\n const appendText = trigger.appendText ?? ' ';\n const invalidNodes = trigger.invalidNodes ?? [];\n const getItems = trigger.items;\n const getRender = trigger.render;\n\n const key = getPluginKey(trigger.name);\n let renderer: MentionSuggestionRenderer | null = null;\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n const debounceMs = trigger.debounce ?? 0;\n const decorationClass = trigger.decorationClass ?? 'mention-suggestion';\n const decorationTag = trigger.decorationTag ?? 'span';\n const shouldShow = trigger.shouldShow;\n\n function cleanup(): void {\n if (debounceTimer !== null) {\n clearTimeout(debounceTimer);\n debounceTimer = null;\n }\n }\n\n function notifyRenderer(props: MentionSuggestionProps): void {\n if (!renderer && getRender) {\n renderer = getRender();\n renderer.onStart(props);\n } else if (renderer) {\n renderer.onUpdate(props);\n }\n }\n\n return new Plugin<SuggestionState>({\n key,\n\n state: {\n init(): SuggestionState {\n return { ...INITIAL_STATE };\n },\n\n apply(\n tr: Transaction,\n prev: SuggestionState,\n _oldState: EditorState,\n newState: EditorState,\n ): SuggestionState {\n // Dismiss via Escape key (or programmatic dismiss)\n if (tr.getMeta(key) === 'dismiss') {\n return { ...INITIAL_STATE };\n }\n\n // Check if this transaction is from user input\n const isUserInput = tr.docChanged || tr.selectionSet;\n if (!isUserInput) return prev;\n\n const result = findMentionQuery(newState, triggerChar, allowSpaces, invalidNodes);\n\n if (result && result.query.length >= minQueryLength) {\n return {\n active: true,\n query: result.query,\n range: result.range,\n };\n }\n\n // If was active and now no match, deactivate\n if (prev.active) {\n return { ...INITIAL_STATE };\n }\n\n return prev;\n },\n },\n\n view() {\n return {\n update(view: EditorView) {\n const pluginState = key.getState(view.state);\n if (!pluginState) return;\n\n if (pluginState.active && pluginState.range) {\n // Check shouldShow — if it returns false, treat as inactive\n if (shouldShow && !shouldShow({ state: view.state, view })) {\n if (renderer) {\n cleanup();\n renderer.onExit();\n renderer = null;\n }\n return;\n }\n // Command reads current plugin state when invoked, not stale closure\n const command = (item: MentionItem): void => {\n const currentState = key.getState(view.state);\n if (!currentState?.range || !nodeType) return;\n\n const { tr } = view.state;\n const node = nodeType.create({\n id: item.id,\n label: item.label,\n type: trigger.name,\n });\n\n tr.replaceWith(currentState.range.from, currentState.range.to, node);\n\n if (appendText) {\n tr.insertText(appendText);\n }\n\n view.dispatch(tr);\n };\n\n const clientRect = (): DOMRect | null => {\n const currentState = key.getState(view.state);\n if (!currentState?.range) return null;\n try {\n const coords = view.coordsAtPos(currentState.range.from);\n return new DOMRect(coords.left, coords.top, 0, coords.bottom - coords.top);\n } catch {\n return null;\n }\n };\n\n // Fetch items and notify renderer\n const fetchAndRender = (): void => {\n const cur = key.getState(view.state);\n if (!cur?.active || !cur.range) return;\n\n const result = getItems({ query: cur.query, trigger });\n\n if (result instanceof Promise) {\n void result.then((items) => {\n const latest = key.getState(view.state);\n if (!latest?.active || !latest.range) return;\n notifyRenderer({ query: latest.query, range: latest.range, items, command, clientRect });\n }).catch(() => {\n // Swallow — suggestion stays active with no items\n });\n } else {\n notifyRenderer({ query: cur.query, range: cur.range, items: result, command, clientRect });\n }\n };\n\n cleanup();\n\n if (debounceMs > 0) {\n // Debounced: delay the entire items() call to avoid hammering APIs\n debounceTimer = setTimeout(fetchAndRender, debounceMs);\n } else {\n // Immediate: call items() now\n fetchAndRender();\n }\n } else if (renderer) {\n cleanup();\n renderer.onExit();\n renderer = null;\n }\n },\n\n destroy() {\n cleanup();\n if (renderer) {\n renderer.onExit();\n renderer = null;\n }\n },\n };\n },\n\n props: {\n handleKeyDown(view: EditorView, event: KeyboardEvent): boolean {\n const state = key.getState(view.state);\n if (!state?.active) return false;\n\n // Escape closes suggestion\n if (event.key === 'Escape') {\n const { tr } = view.state;\n tr.setMeta(key, 'dismiss');\n view.dispatch(tr);\n return true;\n }\n\n // Delegate to renderer for ArrowUp/Down/Enter\n if (renderer) {\n return renderer.onKeyDown(event);\n }\n\n return false;\n },\n\n decorations(state: EditorState): DecorationSet {\n const pluginState = key.getState(state);\n if (!pluginState?.active || !pluginState.range) {\n return DecorationSet.empty;\n }\n\n return DecorationSet.create(state.doc, [\n Decoration.inline(pluginState.range.from, pluginState.range.to, {\n class: decorationClass,\n nodeName: decorationTag,\n }),\n ]);\n },\n },\n });\n}\n\n// ─── Utilities ───────────────────────────────────────────────────────────────\n\n/**\n * Programmatically dismiss the mention suggestion for a given trigger.\n * Dispatches a meta transaction to reset the plugin state.\n */\nexport function dismissMentionSuggestion(\n view: EditorView,\n triggerName: string,\n): void {\n const key = getPluginKey(triggerName);\n const { tr } = view.state;\n tr.setMeta(key, 'dismiss');\n view.dispatch(tr);\n}\n","/**\n * Mention Node Extension\n *\n * Inline atom node for @mentions with multi-trigger support,\n * headless suggestion plugin, async item fetching, and configurable rendering.\n *\n * @example\n * ```ts\n * import { Mention } from '@domternal/extension-mention';\n *\n * const editor = new Editor({\n * extensions: [\n * Mention.configure({\n * suggestion: {\n * char: '@',\n * name: 'user',\n * items: ({ query }) => users.filter(u => u.label.includes(query)),\n * render: createMentionRenderer,\n * },\n * }),\n * ],\n * });\n *\n * // Insert mention programmatically\n * editor.commands.insertMention({ id: '1', label: 'Alice' });\n * ```\n */\nimport { Node } from '@domternal/core';\nimport type { CommandSpec } from '@domternal/core';\nimport type { Node as PMNode, DOMOutputSpec } from '@domternal/pm/model';\nimport { createMentionSuggestionPlugin } from './mentionSuggestionPlugin.js';\nimport type { MentionTrigger } from './mentionSuggestionPlugin.js';\n\ndeclare module '@domternal/core' {\n interface RawCommands {\n insertMention: CommandSpec<[attrs: { id: string; label: string; type?: string }]>;\n deleteMention: CommandSpec<[id?: string]>;\n }\n}\n\nexport interface MentionOptions {\n /** Single trigger config (shorthand). Ignored if `triggers` is non-empty. */\n suggestion: MentionTrigger | null;\n /** Multiple trigger configs. Takes precedence over `suggestion`. */\n triggers: MentionTrigger[];\n /** Delete trigger char alongside mention on Backspace. Default: false */\n deleteTriggerWithBackspace: boolean;\n /** Custom render for HTML output (e.g. render as <a>). */\n renderHTML:\n | ((props: {\n node: PMNode;\n options: MentionOptions;\n HTMLAttributes: Record<string, unknown>;\n }) => DOMOutputSpec)\n | null;\n /** Custom render for plain text output. */\n renderText:\n | ((props: { node: PMNode; options: MentionOptions }) => string)\n | null;\n /** HTML attributes for the mention element. */\n HTMLAttributes: Record<string, unknown>;\n}\n\nexport interface MentionStorage {\n /** Find all mention nodes in the document. */\n findMentions: () => {\n id: string;\n label: string;\n type: string;\n pos: number;\n }[];\n /** @internal Trigger name → char lookup map. */\n _triggerCharMap: Map<string, string>;\n}\n\n/** Resolve trigger char + label into display text (e.g. \"@Alice\", \"#feature\"). */\nfunction getMentionText(node: PMNode, charMap: Map<string, string>): string {\n const label = (node.attrs['label'] ?? '') as string;\n const typeName = (node.attrs['type'] ?? 'mention') as string;\n const triggerChar = charMap.get(typeName) ?? '@';\n return `${triggerChar}${label}`;\n}\n\n/** Resolve the effective triggers array from options. */\nfunction resolveTriggers(options: MentionOptions): MentionTrigger[] {\n if (options.triggers.length > 0) return options.triggers;\n if (options.suggestion) return [options.suggestion];\n return [];\n}\n\nexport const Mention = Node.create<MentionOptions, MentionStorage>({\n name: 'mention',\n group: 'inline',\n inline: true,\n atom: true,\n selectable: false,\n draggable: false,\n\n addOptions() {\n return {\n suggestion: null,\n triggers: [],\n deleteTriggerWithBackspace: false,\n renderHTML: null,\n renderText: null,\n HTMLAttributes: {},\n };\n },\n\n addStorage() {\n return {\n findMentions: () => [],\n _triggerCharMap: new Map(),\n };\n },\n\n onCreate() {\n const triggers = resolveTriggers(this.options);\n const charMap = new Map<string, string>();\n for (const t of triggers) {\n charMap.set(t.name, t.char);\n }\n this.storage._triggerCharMap = charMap;\n\n // Set up findMentions to scan the document\n this.storage.findMentions = () => {\n const editor = this.editor;\n if (!editor) return [];\n\n const mentions: {\n id: string;\n label: string;\n type: string;\n pos: number;\n }[] = [];\n const { doc } = editor.state;\n const mentionType = this.nodeType;\n if (!mentionType) return [];\n\n doc.descendants((node, pos) => {\n if (node.type === mentionType) {\n mentions.push({\n id: node.attrs['id'] as string,\n label: node.attrs['label'] as string,\n type: node.attrs['type'] as string,\n pos,\n });\n }\n });\n\n return mentions;\n };\n },\n\n addAttributes() {\n return {\n id: {\n default: null,\n parseHTML: (element: HTMLElement) =>\n element.getAttribute('data-id'),\n renderHTML: (attributes: Record<string, unknown>) => {\n if (!attributes['id']) return {};\n return { 'data-id': attributes['id'] as string };\n },\n },\n label: {\n default: null,\n parseHTML: (element: HTMLElement) =>\n element.getAttribute('data-label'),\n renderHTML: (attributes: Record<string, unknown>) => {\n if (!attributes['label']) return {};\n return { 'data-label': attributes['label'] as string };\n },\n },\n type: {\n default: 'mention',\n parseHTML: (element: HTMLElement) =>\n element.getAttribute('data-mention-type') ?? 'mention',\n renderHTML: (attributes: Record<string, unknown>) => {\n return { 'data-mention-type': attributes['type'] as string };\n },\n },\n };\n },\n\n parseHTML() {\n return [\n { tag: 'span[data-type=\"mention\"]' },\n { tag: 'span[data-mention]' },\n ];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n // Custom renderHTML takes precedence\n if (this.options.renderHTML) {\n return this.options.renderHTML({\n node,\n options: this.options,\n HTMLAttributes,\n });\n }\n\n return [\n 'span',\n {\n ...this.options.HTMLAttributes,\n ...HTMLAttributes,\n 'data-type': 'mention',\n class: [\n this.options.HTMLAttributes['class'] as string | undefined,\n HTMLAttributes['class'] as string | undefined,\n 'mention',\n ]\n .filter(Boolean)\n .join(' '),\n },\n getMentionText(node, this.storage._triggerCharMap),\n ];\n },\n\n leafText(node) {\n if (this.options.renderText) {\n return this.options.renderText({ node, options: this.options });\n }\n\n return getMentionText(node, this.storage._triggerCharMap);\n },\n\n addCommands() {\n return {\n insertMention:\n (attrs: { id: string; label: string; type?: string }) =>\n ({ tr, dispatch }) => {\n if (!attrs.id || !attrs.label) return false;\n if (!this.nodeType) return false;\n\n if (dispatch) {\n const mentionNode = this.nodeType.create({\n id: attrs.id,\n label: attrs.label,\n type: attrs.type ?? 'mention',\n });\n tr.replaceSelectionWith(mentionNode);\n dispatch(tr);\n }\n\n return true;\n },\n\n deleteMention:\n (id?: string) =>\n ({ tr, dispatch, state }) => {\n // If id is provided, find and delete first mention with that id\n if (id) {\n const mentionType = this.nodeType;\n if (!mentionType) return false;\n\n let foundPos = -1;\n let foundSize = 0;\n state.doc.descendants((node, pos) => {\n if (foundPos >= 0) return false;\n if (node.type === mentionType && node.attrs['id'] === id) {\n foundPos = pos;\n foundSize = node.nodeSize;\n return false;\n }\n return undefined;\n });\n\n if (foundPos < 0) return false;\n\n if (dispatch) {\n tr.delete(foundPos, foundPos + foundSize);\n dispatch(tr);\n }\n\n return true;\n }\n\n // No id: delete mention at cursor (nodeBefore)\n const { $from } = state.selection;\n if (!state.selection.empty) return false;\n\n const nodeBefore = $from.nodeBefore;\n if (nodeBefore?.type.name !== 'mention') return false;\n\n if (dispatch) {\n const from = $from.pos - nodeBefore.nodeSize;\n tr.delete(from, $from.pos);\n dispatch(tr);\n }\n\n return true;\n },\n };\n },\n\n addKeyboardShortcuts() {\n return {\n Backspace: () => {\n const editor = this.editor;\n if (!editor) return false;\n\n const { state } = editor;\n const { selection } = state;\n const { empty, $from } = selection;\n\n if (!empty) return false;\n\n const nodeBefore = $from.nodeBefore;\n if (nodeBefore?.type.name !== 'mention') return false;\n\n // Delete the mention node\n const from = $from.pos - nodeBefore.nodeSize;\n const { tr } = state;\n\n if (this.options.deleteTriggerWithBackspace) {\n // Also delete the trigger char if it precedes the mention\n const triggerType = (nodeBefore.attrs['type'] ?? 'mention') as string;\n const triggerChar = this.storage._triggerCharMap.get(triggerType);\n\n if (triggerChar && from > 0) {\n const $before = state.doc.resolve(from);\n const charBefore = $before.parent.textBetween(\n Math.max(0, $before.parentOffset - triggerChar.length),\n $before.parentOffset,\n undefined,\n '\\ufffc',\n );\n\n if (charBefore === triggerChar) {\n tr.delete(from - triggerChar.length, $from.pos);\n editor.view.dispatch(tr);\n return true;\n }\n }\n }\n\n tr.delete(from, $from.pos);\n editor.view.dispatch(tr);\n return true;\n },\n };\n },\n\n addProseMirrorPlugins() {\n const triggers = resolveTriggers(this.options);\n if (triggers.length === 0) return [];\n\n return triggers.map((trigger) =>\n createMentionSuggestionPlugin({\n trigger,\n nodeType: this.nodeType,\n }),\n );\n },\n});\n"]}