@kerebron/extension-autocomplete 0.4.25 → 0.4.27

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kerebron/extension-autocomplete",
3
- "version": "0.4.25",
3
+ "version": "0.4.27",
4
4
  "license": "MIT",
5
5
  "module": "./esm/mod.js",
6
6
  "exports": {
@@ -18,8 +18,9 @@
18
18
  }
19
19
  },
20
20
  "scripts": {},
21
+ "files": [],
21
22
  "dependencies": {
22
- "@kerebron/editor": "0.4.25",
23
+ "@kerebron/editor": "0.4.27",
23
24
  "prosemirror-model": "1.25.3",
24
25
  "prosemirror-state": "1.4.3",
25
26
  "prosemirror-view": "1.40.0"
@@ -1,12 +0,0 @@
1
- .kb-autocomplete__wrapper {
2
- position: fixed;
3
- z-index: 99999;
4
- padding: 6px var(--kb-space-sm);
5
- background: var(--kb-color-surface-elevated);
6
- list-style: none;
7
- }
8
-
9
- .kb-autocomplete__wrapper li.active {
10
- background: rgba(232, 234, 237, 0.08);
11
- color: #fff;
12
- }
@@ -1,8 +0,0 @@
1
- import { Plugin, PluginKey } from 'prosemirror-state';
2
- import { type CoreEditor } from '@kerebron/editor';
3
- import type { AutocompleteConfig } from './ExtensionAutocomplete.js';
4
- export declare const AutocompletePluginKey: PluginKey<any>;
5
- export declare class AutocompletePlugin<Item, TSelected> extends Plugin {
6
- constructor(config: AutocompleteConfig, editor: CoreEditor);
7
- }
8
- //# sourceMappingURL=AutocompletePlugin.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AutocompletePlugin.d.ts","sourceRoot":"","sources":["../src/AutocompletePlugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAGtD,OAAO,EAAE,KAAK,UAAU,EAAkB,MAAM,kBAAkB,CAAC;AAEnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AASrE,eAAO,MAAM,qBAAqB,gBAAgC,CAAC;AAEnE,qBAAa,kBAAkB,CAAC,IAAI,EAAE,SAAS,CAAE,SAAQ,MAAM;gBACjD,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,UAAU;CA2R3D"}
@@ -1,232 +0,0 @@
1
- import { Plugin, PluginKey } from 'prosemirror-state';
2
- import { Decoration, DecorationSet } from 'prosemirror-view';
3
- import { createDefaultMatcher } from './createDefaultMatcher.js';
4
- import { DefaultRenderer } from './DefaultRenderer.js';
5
- export const AutocompletePluginKey = new PluginKey('autocomplete');
6
- export class AutocompletePlugin extends Plugin {
7
- constructor(config, editor) {
8
- let props;
9
- const renderer = config.renderer ||
10
- new DefaultRenderer(editor);
11
- super({
12
- key: AutocompletePluginKey,
13
- view() {
14
- return {
15
- update: async (view, prevState) => {
16
- const prev = this.key?.getState(prevState);
17
- const next = this.key?.getState(view.state);
18
- const moved = prev.active && next.active &&
19
- prev.range.from !== next.range.from;
20
- const started = !prev.active && next.active;
21
- const stopped = prev.active && !next.active;
22
- const changed = !started && !stopped && prev.query !== next.query;
23
- const handleStart = started || (moved && changed);
24
- const handleChange = changed || moved;
25
- let handleExit = stopped || (moved && changed);
26
- if (!handleStart && !handleChange && !handleExit) {
27
- return;
28
- }
29
- const state = handleExit && !handleStart ? prev : next;
30
- // await new Promise(r => setTimeout(r, 100));
31
- const decorationNode = view.dom.querySelector(`[data-decoration-id="${state.decorationId}"]`);
32
- props = {
33
- range: state.range,
34
- query: state.query,
35
- text: state.text,
36
- items: [],
37
- command: (selected) => {
38
- if (!config.onSelect) {
39
- return () => { };
40
- }
41
- return config.onSelect(selected, state.range);
42
- },
43
- decorationNode,
44
- // virtual node for popper.js or tippy.js
45
- // this can be used for building popups without a DOM node
46
- clientRect: decorationNode
47
- ? () => {
48
- // because of `items` can be asynchrounous we’ll search for the current decoration node
49
- const { decorationId } = this.key?.getState(editor.state); // eslint-disable-line
50
- const currentDecorationNode = view.dom.querySelector(`[data-decoration-id="${decorationId}"]`);
51
- return currentDecorationNode?.getBoundingClientRect() || null;
52
- }
53
- : null,
54
- };
55
- if (handleStart) {
56
- renderer?.onBeforeStart?.(props);
57
- }
58
- if (handleChange) {
59
- renderer?.onBeforeUpdate?.(props);
60
- }
61
- if (handleChange || handleStart) {
62
- if (config.getItems) {
63
- try {
64
- const ctx = { state, range: state.range, isActive: true };
65
- props.items = await config.getItems(state.query, ctx);
66
- }
67
- catch (err) {
68
- if (err.isLSP) {
69
- props.items = [];
70
- console.error('LSP error config.getItems()', err.message, config.getItems);
71
- }
72
- else {
73
- throw err;
74
- }
75
- }
76
- if (props.items.length === 0) {
77
- handleExit = true;
78
- }
79
- }
80
- }
81
- if (handleExit) {
82
- renderer?.onExit?.(props);
83
- }
84
- if (handleChange) {
85
- renderer?.onUpdate?.(props);
86
- }
87
- if (handleStart) {
88
- renderer?.onStart?.(props);
89
- }
90
- },
91
- destroy: () => {
92
- if (!props) {
93
- return;
94
- }
95
- renderer?.onExit?.(props);
96
- },
97
- };
98
- },
99
- state: {
100
- // Initialize the plugin's internal state.
101
- init() {
102
- const state = {
103
- manual: false,
104
- active: false,
105
- range: {
106
- from: 0,
107
- to: 0,
108
- },
109
- query: null,
110
- text: null,
111
- composing: false,
112
- };
113
- return state;
114
- },
115
- // Apply changes to the plugin state from a view transaction.
116
- apply(transaction, prev, _oldState, state) {
117
- // const { isEditable } = editor; // TODO
118
- const isEditable = true;
119
- const { composing } = editor.view;
120
- const { selection } = transaction;
121
- const { empty, from } = selection;
122
- const next = { ...prev };
123
- const meta = transaction.getMeta(AutocompletePluginKey);
124
- if (!meta && !transaction.isGeneric) {
125
- return next;
126
- }
127
- if (meta?.type === 'deactivate') {
128
- console.info('Deactivate autocomplete');
129
- next.active = false;
130
- return next;
131
- }
132
- if (meta?.type === 'activate') {
133
- console.info('Trigger manual autocomplete');
134
- next.range = { from: selection.from, to: selection.to };
135
- next.active = true;
136
- next.manual = true;
137
- next.query = null;
138
- return next;
139
- }
140
- next.composing = composing;
141
- const parentNode = selection.$anchor.parent;
142
- if (!['code_block'].includes(parentNode?.type.name) && isEditable &&
143
- (empty || editor.view.composing)) {
144
- // Reset active state if we just left the previous suggestion range
145
- if ((from < prev.range.from || from > prev.range.to) && !composing &&
146
- !prev.composing) {
147
- next.active = false;
148
- }
149
- const matchers = config.matchers ||
150
- [createDefaultMatcher()];
151
- let match = undefined;
152
- for (const matcher of matchers) {
153
- match = matcher(selection.$from);
154
- if (match) {
155
- break;
156
- }
157
- }
158
- const decorationId = `id_${Math.floor(Math.random() * 0xffffffff)}`;
159
- // If we found a match, update the current state to show it
160
- if (match && (!config.allow || config.allow({
161
- state,
162
- range: match.range,
163
- isActive: prev.active,
164
- }))) {
165
- console.info('Trigger matcher autocomplete', match);
166
- next.active = true;
167
- next.decorationId = prev.decorationId
168
- ? prev.decorationId
169
- : decorationId;
170
- next.range = match.range;
171
- next.query = match.query;
172
- next.text = match.text;
173
- }
174
- else {
175
- next.active = false;
176
- }
177
- }
178
- else {
179
- next.active = false;
180
- }
181
- next.manual = false;
182
- // Make sure to empty the range if suggestion is inactive
183
- if (!next.active) {
184
- next.decorationId = null;
185
- next.range = { from: 0, to: 0 };
186
- next.query = null;
187
- next.text = null;
188
- }
189
- return next;
190
- },
191
- },
192
- props: {
193
- // Call the keydown hook if suggestion is active.
194
- handleKeyDown(view, event) {
195
- const { active, range } = this.getState(view.state);
196
- if (event.key === ' ' && event.ctrlKey) {
197
- const tr = view.state.tr.setMeta(AutocompletePluginKey, {
198
- type: 'activate',
199
- });
200
- console.info('Manual autocomplete key');
201
- view.dispatch(tr);
202
- return true;
203
- }
204
- if (active) {
205
- return renderer?.onKeyDown?.({ view, event, range }) || false;
206
- }
207
- return false;
208
- },
209
- // Setup decorator on the currently active suggestion.
210
- decorations(state) {
211
- const { active, range, decorationId } = this.getState(state);
212
- if (!active) {
213
- return null;
214
- }
215
- const node = document.createElement('span');
216
- node.className = config.decorationClass || 'kb-autocomplete--decor';
217
- node.setAttribute('data-decoration-id', decorationId);
218
- return DecorationSet.create(state.doc, [
219
- Decoration.widget(range.from, node),
220
- ]);
221
- },
222
- },
223
- });
224
- renderer.addEventListener('close', () => {
225
- const tr = editor.state.tr.setMeta(AutocompletePluginKey, {
226
- type: 'deactivate',
227
- });
228
- console.info('Manual autocomplete deactivate');
229
- editor.view.dispatch(tr);
230
- });
231
- }
232
- }
@@ -1,17 +0,0 @@
1
- import { CoreEditor } from '@kerebron/editor';
2
- import { AutocompleteRenderer, SuggestionKeyDownProps, SuggestionProps } from './types.js';
3
- export declare class DefaultRenderer<Item> extends EventTarget implements AutocompleteRenderer {
4
- private editor;
5
- command: (props: any) => void;
6
- wrapper: HTMLElement | undefined;
7
- items: Array<Item>;
8
- pos: number;
9
- constructor(editor: CoreEditor);
10
- onStart(props: SuggestionProps<Item>): void;
11
- onUpdate(props: SuggestionProps<Item>): void;
12
- onExit(): void;
13
- onKeyDown(props: SuggestionKeyDownProps): boolean;
14
- createListItem(item: Item, cnt: number): any;
15
- recreateList(props?: SuggestionProps<Item>): void;
16
- }
17
- //# sourceMappingURL=DefaultRenderer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"DefaultRenderer.d.ts","sourceRoot":"","sources":["../src/DefaultRenderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EAChB,MAAM,YAAY,CAAC;AAIpB,qBAAa,eAAe,CAAC,IAAI,CAAE,SAAQ,WACzC,YAAW,oBAAoB;IAMnB,OAAO,CAAC,MAAM;IAL1B,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC9B,OAAO,EAAE,WAAW,GAAG,SAAS,CAAC;IACjC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAM;IACxB,GAAG,EAAE,MAAM,CAAM;gBAEG,MAAM,EAAE,UAAU;IAKtC,OAAO,CAAC,KAAK,EAAE,eAAe,CAAC,IAAI,CAAC;IAiBpC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC,IAAI,CAAC;IAMrC,MAAM;IASN,SAAS,CAAC,KAAK,EAAE,sBAAsB;IA0CvC,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM;IAatC,YAAY,CAAC,KAAK,CAAC,EAAE,eAAe,CAAC,IAAI,CAAC;CAyB3C"}
@@ -1,113 +0,0 @@
1
- const CSS_PREFIX = 'kb-autocomplete';
2
- export class DefaultRenderer extends EventTarget {
3
- editor;
4
- command;
5
- wrapper;
6
- items = [];
7
- pos = -1;
8
- constructor(editor) {
9
- super();
10
- this.editor = editor;
11
- this.command = () => { };
12
- }
13
- onStart(props) {
14
- this.command = props.command;
15
- if (this.wrapper) {
16
- this.wrapper.parentElement?.removeChild(this.wrapper);
17
- }
18
- const root = ('root' in this.editor.view)
19
- ? this.editor.view.root
20
- : document || document;
21
- this.wrapper = document.createElement('ul');
22
- this.wrapper.classList.add(CSS_PREFIX + '__wrapper');
23
- root.appendChild(this.wrapper);
24
- this.items.splice(0, this.items.length, ...props.items);
25
- this.recreateList(props);
26
- }
27
- onUpdate(props) {
28
- this.command = props.command;
29
- this.items.splice(0, this.items.length, ...props.items);
30
- this.recreateList(props);
31
- }
32
- onExit() {
33
- if (this.wrapper) {
34
- this.wrapper.parentNode?.removeChild(this.wrapper);
35
- this.wrapper = undefined;
36
- }
37
- this.dispatchEvent(new Event('close'));
38
- this.pos = -1;
39
- }
40
- onKeyDown(props) {
41
- if (!this.wrapper) {
42
- return false;
43
- }
44
- if (this.items.length === 0) {
45
- return false;
46
- }
47
- if (props.event.key === 'Escape') {
48
- if (this.wrapper) {
49
- this.wrapper.parentNode?.removeChild(this.wrapper);
50
- this.wrapper = undefined;
51
- return true;
52
- }
53
- }
54
- if (props.event.key === 'ArrowUp') {
55
- if (this.pos > -1) {
56
- this.pos = this.pos - 1;
57
- this.recreateList();
58
- }
59
- return true;
60
- }
61
- if (props.event.key === 'ArrowDown') {
62
- if (this.pos < this.items.length - 1) {
63
- this.pos++;
64
- this.recreateList();
65
- }
66
- return true;
67
- }
68
- if (props.event.key === 'Enter') {
69
- if (this.pos > -1 && this.pos < this.items.length) {
70
- this.command(this.items[this.pos]);
71
- this.items.splice(0, this.items.length);
72
- this.onExit();
73
- return true;
74
- }
75
- }
76
- return false;
77
- }
78
- createListItem(item, cnt) {
79
- const li = document.createElement('li');
80
- if (cnt === this.pos) {
81
- li.classList.add('active');
82
- }
83
- li.innerText = '' + item; // TODO item to string and item formatting
84
- li.style.cursor = 'pointer';
85
- li.addEventListener('click', () => {
86
- this.command(item);
87
- });
88
- return li;
89
- }
90
- recreateList(props) {
91
- if (!this.wrapper) {
92
- return;
93
- }
94
- this.wrapper.innerHTML = '';
95
- for (let cnt = 0; cnt < this.items.length; cnt++) {
96
- const item = this.items[cnt];
97
- this.wrapper.appendChild(this.createListItem(item, cnt));
98
- }
99
- if (this.items.length === 0) {
100
- this.wrapper.style.display = 'none';
101
- }
102
- else {
103
- this.wrapper.style.display = '';
104
- }
105
- const rect = props?.clientRect?.();
106
- if (rect?.height) {
107
- const left = rect.left;
108
- const bottom = rect.bottom;
109
- this.wrapper.style.left = left + 'px';
110
- this.wrapper.style.top = bottom + 'px';
111
- }
112
- }
113
- }
@@ -1,26 +0,0 @@
1
- import type { EditorState, Plugin } from 'prosemirror-state';
2
- import { Extension, type TextRange } from '@kerebron/editor';
3
- import { AutocompletePlugin } from './AutocompletePlugin.js';
4
- import { AutocompleteMatcher, AutocompleteRenderer } from './types.js';
5
- export interface AutocompleteProps {
6
- state: EditorState;
7
- range: TextRange;
8
- isActive?: boolean;
9
- }
10
- export interface AutocompleteConfig<I = any, TSelected = any> {
11
- getItems: (query: string, props: AutocompleteProps) => I[] | Promise<I[]>;
12
- onSelect?: (selected: TSelected, range: TextRange) => void;
13
- allow?: (props: AutocompleteProps) => boolean;
14
- matchers?: AutocompleteMatcher[];
15
- renderer?: AutocompleteRenderer<I, TSelected>;
16
- decorationTag?: string;
17
- decorationClass?: string;
18
- }
19
- export declare class ExtensionAutocomplete extends Extension {
20
- protected config: AutocompleteConfig;
21
- name: string;
22
- plugin: AutocompletePlugin<unknown, unknown>;
23
- constructor(config: AutocompleteConfig);
24
- getProseMirrorPlugins(): Plugin[];
25
- }
26
- //# sourceMappingURL=ExtensionAutocomplete.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ExtensionAutocomplete.d.ts","sourceRoot":"","sources":["../src/ExtensionAutocomplete.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG7D,OAAO,EAAmB,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvE,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB,CAAC,CAAC,GAAG,GAAG,EAAE,SAAS,GAAG,GAAG;IAC1D,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,KAAK,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IAE1E,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IAC3D,KAAK,CAAC,EAAE,CACN,KAAK,EAAE,iBAAiB,KACrB,OAAO,CAAC;IAEb,QAAQ,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACjC,QAAQ,CAAC,EAAE,oBAAoB,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IAE9C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,qBAAsB,SAAQ,SAAS;cAK7B,MAAM,EAAE,kBAAkB;IAJ/C,IAAI,SAAkB;IACtB,MAAM,EAAG,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBAGzB,MAAM,EAAE,kBAAkB;IAKtC,qBAAqB,IAAI,MAAM,EAAE;CAK3C"}
@@ -1,16 +0,0 @@
1
- import { Extension } from '@kerebron/editor';
2
- import { AutocompletePlugin } from './AutocompletePlugin.js';
3
- export class ExtensionAutocomplete extends Extension {
4
- config;
5
- name = 'autocomplete';
6
- plugin;
7
- constructor(config) {
8
- super(config);
9
- this.config = config;
10
- }
11
- getProseMirrorPlugins() {
12
- return [
13
- new AutocompletePlugin(this.config, this.editor),
14
- ];
15
- }
16
- }
@@ -1,11 +0,0 @@
1
- import { AutocompleteMatcher } from './types.js';
2
- export declare function escapeForRegEx(string: string): string;
3
- export interface MatcherConfig {
4
- char?: string;
5
- allowSpaces?: boolean;
6
- allowToIncludeChar?: boolean;
7
- allowedPrefixes?: string[] | null;
8
- startOfLine?: boolean;
9
- }
10
- export declare function createDefaultMatcher(config?: MatcherConfig): AutocompleteMatcher;
11
- //# sourceMappingURL=createDefaultMatcher.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"createDefaultMatcher.d.ts","sourceRoot":"","sources":["../src/createDefaultMatcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAmB,MAAM,YAAY,CAAC;AAGlE,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAClC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAgB,oBAAoB,CAClC,MAAM,GAAE,aAAkB,GACzB,mBAAmB,CA4ErB"}
@@ -1,58 +0,0 @@
1
- // source: https://stackoverflow.com/a/6969486
2
- export function escapeForRegEx(string) {
3
- return string.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
4
- }
5
- export function createDefaultMatcher(config = {}) {
6
- const char = config.char || '@';
7
- const allowToIncludeChar = config.allowToIncludeChar || false;
8
- const allowedPrefixes = config.allowedPrefixes || [' '];
9
- const startOfLine = config.startOfLine || false;
10
- return ($position) => {
11
- const allowSpaces = config.allowSpaces && !allowToIncludeChar;
12
- const escapedChar = escapeForRegEx(char);
13
- const suffix = new RegExp(`\\s${escapedChar}$`);
14
- const prefix = startOfLine ? '^' : '';
15
- const finalEscapedChar = allowToIncludeChar ? '' : escapedChar;
16
- const regexp = allowSpaces
17
- ? new RegExp(`${prefix}${escapedChar}.*?(?=\\s${finalEscapedChar}|$)`, 'gm')
18
- : new RegExp(`${prefix}(?:^)?${escapedChar}[^\\s${finalEscapedChar}]*`, 'gm');
19
- const text = $position.nodeBefore?.isText && $position.nodeBefore.text;
20
- if (!text) {
21
- return null;
22
- }
23
- const textFrom = $position.pos - text.length;
24
- const match = Array.from(text.matchAll(regexp)).pop();
25
- if (!match || match.input === undefined || match.index === undefined) {
26
- return null;
27
- }
28
- // JavaScript doesn't have lookbehinds. This hacks a check that first character
29
- // is a space or the start of the line
30
- const matchPrefix = match.input.slice(Math.max(0, match.index - 1), match.index);
31
- const matchPrefixIsAllowed = new RegExp(`^[${allowedPrefixes?.join('')}\0]?$`)
32
- .test(matchPrefix);
33
- if (allowedPrefixes !== null && !matchPrefixIsAllowed) {
34
- return null;
35
- }
36
- // The absolute position of the match in the document
37
- const from = textFrom + match.index;
38
- let to = from + match[0].length;
39
- // Edge case handling; if spaces are allowed and we're directly in between
40
- // two triggers
41
- if (allowSpaces && suffix.test(text.slice(to - 1, to + 1))) {
42
- match[0] += ' ';
43
- to += 1;
44
- }
45
- // If the $position is located within the matched substring, return that range
46
- if (from < $position.pos && to >= $position.pos) {
47
- return {
48
- range: {
49
- from,
50
- to,
51
- },
52
- query: match[0],
53
- text: match[0],
54
- };
55
- }
56
- return null;
57
- };
58
- }
@@ -1,4 +0,0 @@
1
- import { AutocompleteMatcher } from './types.js';
2
- export declare function ensureAnchor(expr: RegExp, start: boolean): RegExp;
3
- export declare function createRegexMatcher(regexes: RegExp[]): AutocompleteMatcher;
4
- //# sourceMappingURL=createRegexMatcher.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"createRegexMatcher.d.ts","sourceRoot":"","sources":["../src/createRegexMatcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAmB,MAAM,YAAY,CAAC;AAElE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,UASxD;AAqBD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EAAE,GAChB,mBAAmB,CAmCrB"}
@@ -1,50 +0,0 @@
1
- export function ensureAnchor(expr, start) {
2
- let { source } = expr;
3
- let addStart = start && source[0] != '^', addEnd = source[source.length - 1] != '$';
4
- if (!addStart && !addEnd)
5
- return expr;
6
- return new RegExp(`${addStart ? '^' : ''}(?:${source})${addEnd ? '$' : ''}`, expr.flags ?? (expr.ignoreCase ? 'i' : ''));
7
- }
8
- function matchBefore($position, expr) {
9
- const text = $position.nodeBefore?.isText && $position.nodeBefore.text;
10
- if (!text) {
11
- return null;
12
- }
13
- const textFrom = $position.pos - text.length;
14
- const start = Math.max(textFrom, $position.pos - 250);
15
- const str = text.slice();
16
- const found = str.search(ensureAnchor(expr, false));
17
- return found < 0
18
- ? null
19
- : { from: start + found, to: $position.pos, text: str.slice(found) };
20
- }
21
- export function createRegexMatcher(regexes) {
22
- return ($position) => {
23
- const text = $position.nodeBefore?.isText && $position.nodeBefore.text;
24
- if (!text) {
25
- return null;
26
- }
27
- const textFrom = $position.pos - text.length;
28
- const matches = regexes.map((regex) => matchBefore($position, regex))
29
- .filter((m) => !!m);
30
- if (matches.length === 0) {
31
- return null;
32
- }
33
- matches.sort((a, b) => b.text.length - a.text.length);
34
- let from = matches[0].from;
35
- let matchedText = matches[0].text;
36
- let to = matches[0].to;
37
- while (matchedText.match(/^\s/)) {
38
- matchedText = matchedText.substring(1);
39
- from++;
40
- }
41
- return {
42
- range: {
43
- from,
44
- to,
45
- },
46
- query: matchedText,
47
- text: matchedText,
48
- };
49
- };
50
- }
package/esm/mod.d.ts DELETED
@@ -1,3 +0,0 @@
1
- export * from './ExtensionAutocomplete.js';
2
- export { createRegexMatcher } from './createRegexMatcher.js';
3
- //# sourceMappingURL=mod.d.ts.map
package/esm/mod.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC"}
package/esm/mod.js DELETED
@@ -1,2 +0,0 @@
1
- export * from './ExtensionAutocomplete.js';
2
- export { createRegexMatcher } from './createRegexMatcher.js';
package/esm/package.json DELETED
@@ -1,3 +0,0 @@
1
- {
2
- "type": "module"
3
- }
package/esm/types.d.ts DELETED
@@ -1,60 +0,0 @@
1
- import type { ResolvedPos } from 'prosemirror-model';
2
- import type { TextRange } from '@kerebron/editor';
3
- import { EditorView } from 'prosemirror-view';
4
- export type SuggestionMatch = {
5
- range: TextRange;
6
- query: string;
7
- text: string;
8
- } | null;
9
- export type AutocompleteMatcher = (pos: ResolvedPos) => SuggestionMatch;
10
- export interface SuggestionKeyDownProps {
11
- view: EditorView;
12
- event: KeyboardEvent;
13
- range: TextRange;
14
- }
15
- export interface AutocompleteRenderer<I = any, TSelected = any> {
16
- onBeforeStart?: (props: SuggestionProps<I, TSelected>) => void;
17
- onStart?: (props: SuggestionProps<I, TSelected>) => void;
18
- onBeforeUpdate?: (props: SuggestionProps<I, TSelected>) => void;
19
- onUpdate?: (props: SuggestionProps<I, TSelected>) => void;
20
- onExit?: (props: SuggestionProps<I, TSelected>) => void;
21
- onKeyDown?: (props: SuggestionKeyDownProps) => boolean;
22
- addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
23
- removeEventListener(type: string, callback: EventListenerOrEventListenerObject | null, options?: EventListenerOptions | boolean): void;
24
- }
25
- export interface SuggestionProps<I = any, TSelected = any> {
26
- /**
27
- * The range of the suggestion.
28
- */
29
- range: TextRange;
30
- /**
31
- * The current suggestion query.
32
- */
33
- query: string;
34
- /**
35
- * The current suggestion text.
36
- */
37
- text: string;
38
- /**
39
- * The suggestion items array.
40
- */
41
- items: I[];
42
- /**
43
- * A function that is called when a suggestion is selected.
44
- * @param props The props object.
45
- * @returns void
46
- */
47
- command: (props: TSelected) => void;
48
- /**
49
- * The decoration node HTML element
50
- * @default null
51
- */
52
- decorationNode: Element | null;
53
- /**
54
- * The function that returns the client rect
55
- * @default null
56
- * @example () => new DOMRect(0, 0, 0, 0)
57
- */
58
- clientRect?: (() => DOMRect | null) | null;
59
- }
60
- //# sourceMappingURL=types.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,IAAI,CAAC;AAET,MAAM,MAAM,mBAAmB,GAAG,CAAC,GAAG,EAAE,WAAW,KAAK,eAAe,CAAC;AAExE,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,aAAa,CAAC;IACrB,KAAK,EAAE,SAAS,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB,CAAC,CAAC,GAAG,GAAG,EAAE,SAAS,GAAG,GAAG;IAC5D,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC,EAAE,SAAS,CAAC,KAAK,IAAI,CAAC;IAC/D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC,EAAE,SAAS,CAAC,KAAK,IAAI,CAAC;IACzD,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC,EAAE,SAAS,CAAC,KAAK,IAAI,CAAC;IAChE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC,EAAE,SAAS,CAAC,KAAK,IAAI,CAAC;IAC1D,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC,EAAE,SAAS,CAAC,KAAK,IAAI,CAAC;IACxD,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,OAAO,CAAC;IAEvD,gBAAgB,CACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,kCAAkC,EAC5C,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAC1C,IAAI,CAAC;IACR,mBAAmB,CACjB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,kCAAkC,GAAG,IAAI,EACnD,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,GACvC,IAAI,CAAC;CACT;AAED,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,GAAG,EAAE,SAAS,GAAG,GAAG;IACvD;;OAEG;IACH,KAAK,EAAE,SAAS,CAAC;IAEjB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,KAAK,EAAE,CAAC,EAAE,CAAC;IAEX;;;;OAIG;IACH,OAAO,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IAEpC;;;OAGG;IACH,cAAc,EAAE,OAAO,GAAG,IAAI,CAAC;IAE/B;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;CAC5C"}
package/esm/types.js DELETED
@@ -1 +0,0 @@
1
- export {};