@magicx-eng/ai-autocomplete-vanilla 0.1.17 → 0.1.18
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 +13 -5
- package/index.d.mts +68 -52
- package/index.d.ts +68 -52
- package/index.js +66 -2
- package/index.js.map +1 -1
- package/index.mjs +66 -2
- package/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -82,6 +82,7 @@ const ac = new AIAutocomplete(container, {
|
|
|
82
82
|
pillPlacement: "inline", // "inline" | "dropdown" | "hidden"
|
|
83
83
|
dropdownTrigger: "auto", // "auto" | "manual" | "hidden"
|
|
84
84
|
closeDropdownOnBlur: true, // false = keep dropdown open even when input loses focus
|
|
85
|
+
showNonTappableOptions: true, // false = hide non-tappable options from the dropdown
|
|
85
86
|
|
|
86
87
|
// Focus
|
|
87
88
|
autoFocus: true, // focus the input on mount (Tier 1 only)
|
|
@@ -201,7 +202,9 @@ const state = ac.getState();
|
|
|
201
202
|
// state.placeholderText, state.isDropdownOpen, state.activeDropdownIndex,
|
|
202
203
|
// state.newParamId, state.isLoading, state.isReady, state.error
|
|
203
204
|
|
|
204
|
-
// Subscribe to changes
|
|
205
|
+
// Subscribe to state changes. The listener receives the full state shape
|
|
206
|
+
// (raw inputs + derived fields like `segments`, `actionableSuggestions`,
|
|
207
|
+
// `filteredOptions`, `placeholderText`, `isDropdownOpen`).
|
|
205
208
|
const unsub = ac.subscribe((state) => {
|
|
206
209
|
renderMyInput(state.text, state.placeholderText);
|
|
207
210
|
renderMyPills(state.actionableSuggestions);
|
|
@@ -260,7 +263,7 @@ unsub();
|
|
|
260
263
|
| `setMode(mode)` | Switch color mode at runtime. |
|
|
261
264
|
| `update(opts)` | Update multiple options at once (e.g. `animations`, `pillPlacement`, `dropdownTrigger`, `optionsPosition`). |
|
|
262
265
|
| `getState()` | Get a snapshot of the current state. Useful for initial read before subscribing. |
|
|
263
|
-
| `subscribe(listener)` | Subscribe to state changes (
|
|
266
|
+
| `subscribe(listener)` | Subscribe to state changes. The listener receives the full state (raw inputs + derived fields). Returns unsubscribe function. |
|
|
264
267
|
| `destroy()` | Cleanup — abort fetches, clear timers, remove listeners. |
|
|
265
268
|
|
|
266
269
|
### Building a framework wrapper
|
|
@@ -324,13 +327,18 @@ The object passed to `onSubmit`:
|
|
|
324
327
|
```ts
|
|
325
328
|
const off = ac.on("submit", (result) => { ... });
|
|
326
329
|
off(); // unsubscribe
|
|
330
|
+
|
|
331
|
+
// Multiple listeners on the same event are supported — each `on()` call adds
|
|
332
|
+
// a listener; the returned function removes only the listener it registered.
|
|
333
|
+
const offA = ac.on("change", logToAnalytics);
|
|
334
|
+
const offB = ac.on("change", syncToStore);
|
|
327
335
|
```
|
|
328
336
|
|
|
329
|
-
Events: `submit`, `error`, `change`, `paramsChange`, `focus`, `blur`.
|
|
337
|
+
Events: `submit`, `error`, `change`, `paramsChange`, `stateChange`, `focus`, `blur`.
|
|
330
338
|
|
|
331
|
-
Constructor callbacks (`onSubmit`, `onChange`, etc.) are
|
|
339
|
+
Constructor callbacks (`onSubmit`, `onChange`, etc.) are registered once at construction as the initial listener for that event. Use `on()` for any additional or replacement listeners. **`update()` does not swap event listeners** — use `on()` for dynamic listener management.
|
|
332
340
|
|
|
333
|
-
**`subscribe()` vs `on()`:** Use `on()` for specific events (submit, error, text change). Use `subscribe()` (Tier 3) for full state updates —
|
|
341
|
+
**`subscribe()` vs `on()`:** Use `on()` for specific events (submit, error, text change). Use `subscribe()` (Tier 3) for full state updates — every `subscribe` callback receives the merged input + derived state, ready to read.
|
|
334
342
|
|
|
335
343
|
---
|
|
336
344
|
|
package/index.d.mts
CHANGED
|
@@ -67,8 +67,11 @@ interface AutocompleteResult {
|
|
|
67
67
|
completed_params: CompletedParam[];
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
/**
|
|
71
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Raw, user/network/internal-driven fields. The store holds *only* these —
|
|
72
|
+
* derived state is recomputed lazily on read. See {@link CoreDerivedState}.
|
|
73
|
+
*/
|
|
74
|
+
interface CoreInputState {
|
|
72
75
|
text: string;
|
|
73
76
|
completedParams: CompletedParamState[];
|
|
74
77
|
suggestions: Suggestion[];
|
|
@@ -77,11 +80,6 @@ interface CoreState {
|
|
|
77
80
|
isLoading: boolean;
|
|
78
81
|
isReady: boolean;
|
|
79
82
|
error: Error | null;
|
|
80
|
-
segments: Segment[];
|
|
81
|
-
actionableSuggestions: Suggestion[];
|
|
82
|
-
filteredOptions: SuggestionOption[];
|
|
83
|
-
placeholderText: string;
|
|
84
|
-
isDropdownOpen: boolean;
|
|
85
83
|
filterBase: number;
|
|
86
84
|
filterInProgress: boolean;
|
|
87
85
|
pillTapped: boolean;
|
|
@@ -104,10 +102,27 @@ interface CoreState {
|
|
|
104
102
|
/**
|
|
105
103
|
* True for ~500ms after a user-initiated option selection so the streak
|
|
106
104
|
* animation can finish before the dropdown switches to its loading skeleton.
|
|
107
|
-
* Set by selectOption /
|
|
105
|
+
* Set by selectOption / ReEditManager.selectOption, cleared by a timer.
|
|
108
106
|
*/
|
|
109
107
|
inSelectionAnimation: boolean;
|
|
110
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Derived fields recomputed from {@link CoreInputState} + {@link CoreOptions}.
|
|
111
|
+
* Never stored — produced lazily by the derive function and merged onto
|
|
112
|
+
* `get()` reads / subscriber notifications.
|
|
113
|
+
*/
|
|
114
|
+
interface CoreDerivedState {
|
|
115
|
+
segments: Segment[];
|
|
116
|
+
actionableSuggestions: Suggestion[];
|
|
117
|
+
filteredOptions: SuggestionOption[];
|
|
118
|
+
placeholderText: string;
|
|
119
|
+
isDropdownOpen: boolean;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Full state shape (inputs + derived) exposed to consumers via `getState()`
|
|
123
|
+
* and subscribers.
|
|
124
|
+
*/
|
|
125
|
+
type CoreState = CoreInputState & CoreDerivedState;
|
|
111
126
|
/**
|
|
112
127
|
* Render mode:
|
|
113
128
|
* - "full" (default) — renders input + dropdown + pills. Tier 1.
|
|
@@ -130,6 +145,8 @@ interface CoreOptions {
|
|
|
130
145
|
dropdownTrigger?: "auto" | "manual" | "hidden";
|
|
131
146
|
/** When true (default), the dropdown closes when the input loses focus. Set to false to keep it open whenever options are available, regardless of focus. */
|
|
132
147
|
closeDropdownOnBlur?: boolean;
|
|
148
|
+
/** When true (default), non-tappable options are rendered in the dropdown alongside tappable ones. Set to false to hide them entirely. */
|
|
149
|
+
showNonTappableOptions?: boolean;
|
|
133
150
|
/** Render mode. Default: "full". */
|
|
134
151
|
renderMode?: RenderMode;
|
|
135
152
|
/**
|
|
@@ -168,25 +185,36 @@ interface CoreOptions {
|
|
|
168
185
|
setCursor?: (offset: number) => void;
|
|
169
186
|
}
|
|
170
187
|
|
|
188
|
+
type AIAutocompleteEvents = {
|
|
189
|
+
submit: [result: AutocompleteResult];
|
|
190
|
+
error: [error: Error];
|
|
191
|
+
change: [text: string];
|
|
192
|
+
paramsChange: [params: CompletedParamState[]];
|
|
193
|
+
stateChange: [state: CoreState];
|
|
194
|
+
focus: [];
|
|
195
|
+
blur: [];
|
|
196
|
+
};
|
|
197
|
+
type CoreUpdateOptions = Partial<Omit<CoreOptions, "onSubmit" | "onError" | "onChange" | "onParamsChange" | "onStateChange" | "onFocus" | "onBlur">>;
|
|
171
198
|
declare class AIAutocomplete {
|
|
199
|
+
private inputStore;
|
|
172
200
|
private store;
|
|
173
201
|
private _listboxId;
|
|
174
202
|
private opts;
|
|
175
203
|
private fetchController;
|
|
176
204
|
private keyboardController;
|
|
177
205
|
private pillsController;
|
|
206
|
+
private reEdit;
|
|
178
207
|
private modeController;
|
|
179
208
|
private container;
|
|
180
209
|
private unsubscribers;
|
|
181
|
-
private derivedInProgress;
|
|
182
210
|
private renderMode;
|
|
183
211
|
private domRefs;
|
|
184
212
|
private dropdownRefs;
|
|
185
|
-
private
|
|
186
|
-
private
|
|
187
|
-
private selectionAnimationTimer;
|
|
188
|
-
private externalListeners;
|
|
213
|
+
private timers;
|
|
214
|
+
private emitter;
|
|
189
215
|
private sessionId;
|
|
216
|
+
private readonly emitSubmit;
|
|
217
|
+
private readonly emitError;
|
|
190
218
|
constructor(container: HTMLElement, opts?: CoreOptions);
|
|
191
219
|
focus(): void;
|
|
192
220
|
blur(): void;
|
|
@@ -211,56 +239,41 @@ declare class AIAutocomplete {
|
|
|
211
239
|
* Set the editor caret at the given plain-text offset. Uses the core's own
|
|
212
240
|
* `domRefs.input` in "full" mode; falls back to the wrapper-provided
|
|
213
241
|
* `setCursor` callback in "headless" mode where the wrapper owns the DOM.
|
|
242
|
+
*
|
|
243
|
+
* Also focuses the input — setting a selection range without focus leaves a
|
|
244
|
+
* visible caret that doesn't actually accept typing, and every caller (pill
|
|
245
|
+
* tap, post-promote refocus, re-edit entry, Backspace-into-param) expects
|
|
246
|
+
* the editor to be focused afterwards.
|
|
214
247
|
*/
|
|
215
248
|
private scheduleSetCursor;
|
|
216
249
|
clearNewParamId(): void;
|
|
217
|
-
/**
|
|
218
|
-
* Enter re-edit mode for the bold completed param with the given id. The
|
|
219
|
-
* caller is responsible for highlighting the param via the Selection API
|
|
220
|
-
* (DOM concern). This method just sets the state snapshot so the dropdown
|
|
221
|
-
* starts filtering the param's cached options.
|
|
222
|
-
*/
|
|
223
250
|
startEditingParam(paramId: string): void;
|
|
224
|
-
/**
|
|
225
|
-
* Replace the editing range (`text[editingAnchor..editingTail]`) with the
|
|
226
|
-
* given replacement. Used by the DOM layer's `beforeinput` intercept: while
|
|
227
|
-
* the edited param's strong still exists in `completedParams`, typing or
|
|
228
|
-
* backspace should swap the WHOLE param atomically (instead of inserting
|
|
229
|
-
* one char into the bold span). Returns true when the replacement was
|
|
230
|
-
* applied — callers should `preventDefault` only in that case.
|
|
231
|
-
*/
|
|
232
251
|
replaceEditingRange(replacement: string): boolean;
|
|
233
|
-
/** Clear re-edit state. Selection collapsing is a DOM concern handled by the caller. */
|
|
234
252
|
exitEditMode(): void;
|
|
235
|
-
/**
|
|
236
|
-
* Update caret offset after a text-input event. While in edit mode, extends
|
|
237
|
-
* `editingTail` to keep up with the user typing past the previous tail.
|
|
238
|
-
* Exits edit mode if the user backspaced past `editingAnchor` (deleting a
|
|
239
|
-
* character before where the edit started).
|
|
240
|
-
*/
|
|
241
253
|
handleCaretAfterInput(offset: number | null): void;
|
|
242
|
-
/**
|
|
243
|
-
* Update caret offset from a selection-move event that was NOT preceded by
|
|
244
|
-
* an input event (so the user moved the cursor by click or arrows, not by
|
|
245
|
-
* typing). Exits edit mode if the caret moved outside the edit region.
|
|
246
|
-
*/
|
|
247
254
|
handleCaretMove(offset: number | null): void;
|
|
248
255
|
setActiveDropdownIndex(index: number): void;
|
|
249
256
|
handleTextChange(value: string): void;
|
|
250
257
|
handleKeyDown(e: KeyboardEvent): void;
|
|
251
258
|
setFocused(focused: boolean): void;
|
|
252
|
-
/** Subscribe to state changes
|
|
259
|
+
/** Subscribe to state changes. Listener receives the full (input + derived) shape. */
|
|
253
260
|
subscribe(listener: (state: CoreState) => void): () => void;
|
|
254
261
|
getState(): CoreState;
|
|
255
262
|
get listboxId(): string;
|
|
256
263
|
get isReady(): boolean;
|
|
257
|
-
|
|
258
|
-
|
|
264
|
+
/**
|
|
265
|
+
* Subscribe to an event. Multiple listeners may register for the same event;
|
|
266
|
+
* `emit` fans out to all of them. The returned function removes only the
|
|
267
|
+
* listener it registered.
|
|
268
|
+
*
|
|
269
|
+
* Note: `opts.on*` listeners passed at construction are equivalent to calling
|
|
270
|
+
* `on()` once each; their unsubscribe handles are not exposed.
|
|
271
|
+
*/
|
|
272
|
+
on<E extends keyof AIAutocompleteEvents>(event: E, callback: (...args: AIAutocompleteEvents[E]) => void): () => void;
|
|
273
|
+
update(opts: CoreUpdateOptions): void;
|
|
259
274
|
selectOption(option: SuggestionOption): void;
|
|
260
275
|
private startSelectionAnimationTimer;
|
|
261
|
-
private selectOptionInEditMode;
|
|
262
276
|
private fireTelemetry;
|
|
263
|
-
private recomputeDerived;
|
|
264
277
|
private setupContainer;
|
|
265
278
|
private buildAndRenderFull;
|
|
266
279
|
private buildAndRenderDropdown;
|
|
@@ -269,6 +282,17 @@ declare class AIAutocomplete {
|
|
|
269
282
|
/** Auto-clear newParamId after shimmer animation. */
|
|
270
283
|
private subscribeNewParamTimer;
|
|
271
284
|
private handleChange;
|
|
285
|
+
/**
|
|
286
|
+
* In re-edit mode, once the user has typed enough that no *tappable* options
|
|
287
|
+
* still match (non-tappable options are kept by filterOptions regardless of
|
|
288
|
+
* the query, so they don't count as "still matching"), exit re-edit and
|
|
289
|
+
* fire an immediate fetch so the dropdown swaps over to fresh server
|
|
290
|
+
* suggestions instead of staying frozen on a dead filter.
|
|
291
|
+
*
|
|
292
|
+
* Guarded against re-entry: once we exit, editingParam is null and the
|
|
293
|
+
* subscription early-returns on subsequent fires.
|
|
294
|
+
*/
|
|
295
|
+
private maybeExitReEditOnNoMatch;
|
|
272
296
|
/**
|
|
273
297
|
* When the user has typed text that exactly matches (case-insensitive) one
|
|
274
298
|
* of the active suggestion's options, promote it to a completed param right
|
|
@@ -277,14 +301,6 @@ declare class AIAutocomplete {
|
|
|
277
301
|
* option is fully typed, without waiting 100–300ms for the round-trip.
|
|
278
302
|
*/
|
|
279
303
|
private maybePromoteExactMatch;
|
|
280
|
-
/**
|
|
281
|
-
* Edit-mode counterpart to {@link maybePromoteExactMatch}. When the user is
|
|
282
|
-
* editing a bold completed param and the typed text exactly matches one of
|
|
283
|
-
* that param's cached options, promote it back to a completed param (bold)
|
|
284
|
-
* — same instant-bold affordance as the normal typing flow, just driven by
|
|
285
|
-
* the edited param's cached options instead of the active suggestion.
|
|
286
|
-
*/
|
|
287
|
-
private maybePromoteEditExactMatch;
|
|
288
304
|
}
|
|
289
305
|
|
|
290
306
|
type Listener<S> = (next: S, prev: S) => void;
|
package/index.d.ts
CHANGED
|
@@ -67,8 +67,11 @@ interface AutocompleteResult {
|
|
|
67
67
|
completed_params: CompletedParam[];
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
/**
|
|
71
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Raw, user/network/internal-driven fields. The store holds *only* these —
|
|
72
|
+
* derived state is recomputed lazily on read. See {@link CoreDerivedState}.
|
|
73
|
+
*/
|
|
74
|
+
interface CoreInputState {
|
|
72
75
|
text: string;
|
|
73
76
|
completedParams: CompletedParamState[];
|
|
74
77
|
suggestions: Suggestion[];
|
|
@@ -77,11 +80,6 @@ interface CoreState {
|
|
|
77
80
|
isLoading: boolean;
|
|
78
81
|
isReady: boolean;
|
|
79
82
|
error: Error | null;
|
|
80
|
-
segments: Segment[];
|
|
81
|
-
actionableSuggestions: Suggestion[];
|
|
82
|
-
filteredOptions: SuggestionOption[];
|
|
83
|
-
placeholderText: string;
|
|
84
|
-
isDropdownOpen: boolean;
|
|
85
83
|
filterBase: number;
|
|
86
84
|
filterInProgress: boolean;
|
|
87
85
|
pillTapped: boolean;
|
|
@@ -104,10 +102,27 @@ interface CoreState {
|
|
|
104
102
|
/**
|
|
105
103
|
* True for ~500ms after a user-initiated option selection so the streak
|
|
106
104
|
* animation can finish before the dropdown switches to its loading skeleton.
|
|
107
|
-
* Set by selectOption /
|
|
105
|
+
* Set by selectOption / ReEditManager.selectOption, cleared by a timer.
|
|
108
106
|
*/
|
|
109
107
|
inSelectionAnimation: boolean;
|
|
110
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Derived fields recomputed from {@link CoreInputState} + {@link CoreOptions}.
|
|
111
|
+
* Never stored — produced lazily by the derive function and merged onto
|
|
112
|
+
* `get()` reads / subscriber notifications.
|
|
113
|
+
*/
|
|
114
|
+
interface CoreDerivedState {
|
|
115
|
+
segments: Segment[];
|
|
116
|
+
actionableSuggestions: Suggestion[];
|
|
117
|
+
filteredOptions: SuggestionOption[];
|
|
118
|
+
placeholderText: string;
|
|
119
|
+
isDropdownOpen: boolean;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Full state shape (inputs + derived) exposed to consumers via `getState()`
|
|
123
|
+
* and subscribers.
|
|
124
|
+
*/
|
|
125
|
+
type CoreState = CoreInputState & CoreDerivedState;
|
|
111
126
|
/**
|
|
112
127
|
* Render mode:
|
|
113
128
|
* - "full" (default) — renders input + dropdown + pills. Tier 1.
|
|
@@ -130,6 +145,8 @@ interface CoreOptions {
|
|
|
130
145
|
dropdownTrigger?: "auto" | "manual" | "hidden";
|
|
131
146
|
/** When true (default), the dropdown closes when the input loses focus. Set to false to keep it open whenever options are available, regardless of focus. */
|
|
132
147
|
closeDropdownOnBlur?: boolean;
|
|
148
|
+
/** When true (default), non-tappable options are rendered in the dropdown alongside tappable ones. Set to false to hide them entirely. */
|
|
149
|
+
showNonTappableOptions?: boolean;
|
|
133
150
|
/** Render mode. Default: "full". */
|
|
134
151
|
renderMode?: RenderMode;
|
|
135
152
|
/**
|
|
@@ -168,25 +185,36 @@ interface CoreOptions {
|
|
|
168
185
|
setCursor?: (offset: number) => void;
|
|
169
186
|
}
|
|
170
187
|
|
|
188
|
+
type AIAutocompleteEvents = {
|
|
189
|
+
submit: [result: AutocompleteResult];
|
|
190
|
+
error: [error: Error];
|
|
191
|
+
change: [text: string];
|
|
192
|
+
paramsChange: [params: CompletedParamState[]];
|
|
193
|
+
stateChange: [state: CoreState];
|
|
194
|
+
focus: [];
|
|
195
|
+
blur: [];
|
|
196
|
+
};
|
|
197
|
+
type CoreUpdateOptions = Partial<Omit<CoreOptions, "onSubmit" | "onError" | "onChange" | "onParamsChange" | "onStateChange" | "onFocus" | "onBlur">>;
|
|
171
198
|
declare class AIAutocomplete {
|
|
199
|
+
private inputStore;
|
|
172
200
|
private store;
|
|
173
201
|
private _listboxId;
|
|
174
202
|
private opts;
|
|
175
203
|
private fetchController;
|
|
176
204
|
private keyboardController;
|
|
177
205
|
private pillsController;
|
|
206
|
+
private reEdit;
|
|
178
207
|
private modeController;
|
|
179
208
|
private container;
|
|
180
209
|
private unsubscribers;
|
|
181
|
-
private derivedInProgress;
|
|
182
210
|
private renderMode;
|
|
183
211
|
private domRefs;
|
|
184
212
|
private dropdownRefs;
|
|
185
|
-
private
|
|
186
|
-
private
|
|
187
|
-
private selectionAnimationTimer;
|
|
188
|
-
private externalListeners;
|
|
213
|
+
private timers;
|
|
214
|
+
private emitter;
|
|
189
215
|
private sessionId;
|
|
216
|
+
private readonly emitSubmit;
|
|
217
|
+
private readonly emitError;
|
|
190
218
|
constructor(container: HTMLElement, opts?: CoreOptions);
|
|
191
219
|
focus(): void;
|
|
192
220
|
blur(): void;
|
|
@@ -211,56 +239,41 @@ declare class AIAutocomplete {
|
|
|
211
239
|
* Set the editor caret at the given plain-text offset. Uses the core's own
|
|
212
240
|
* `domRefs.input` in "full" mode; falls back to the wrapper-provided
|
|
213
241
|
* `setCursor` callback in "headless" mode where the wrapper owns the DOM.
|
|
242
|
+
*
|
|
243
|
+
* Also focuses the input — setting a selection range without focus leaves a
|
|
244
|
+
* visible caret that doesn't actually accept typing, and every caller (pill
|
|
245
|
+
* tap, post-promote refocus, re-edit entry, Backspace-into-param) expects
|
|
246
|
+
* the editor to be focused afterwards.
|
|
214
247
|
*/
|
|
215
248
|
private scheduleSetCursor;
|
|
216
249
|
clearNewParamId(): void;
|
|
217
|
-
/**
|
|
218
|
-
* Enter re-edit mode for the bold completed param with the given id. The
|
|
219
|
-
* caller is responsible for highlighting the param via the Selection API
|
|
220
|
-
* (DOM concern). This method just sets the state snapshot so the dropdown
|
|
221
|
-
* starts filtering the param's cached options.
|
|
222
|
-
*/
|
|
223
250
|
startEditingParam(paramId: string): void;
|
|
224
|
-
/**
|
|
225
|
-
* Replace the editing range (`text[editingAnchor..editingTail]`) with the
|
|
226
|
-
* given replacement. Used by the DOM layer's `beforeinput` intercept: while
|
|
227
|
-
* the edited param's strong still exists in `completedParams`, typing or
|
|
228
|
-
* backspace should swap the WHOLE param atomically (instead of inserting
|
|
229
|
-
* one char into the bold span). Returns true when the replacement was
|
|
230
|
-
* applied — callers should `preventDefault` only in that case.
|
|
231
|
-
*/
|
|
232
251
|
replaceEditingRange(replacement: string): boolean;
|
|
233
|
-
/** Clear re-edit state. Selection collapsing is a DOM concern handled by the caller. */
|
|
234
252
|
exitEditMode(): void;
|
|
235
|
-
/**
|
|
236
|
-
* Update caret offset after a text-input event. While in edit mode, extends
|
|
237
|
-
* `editingTail` to keep up with the user typing past the previous tail.
|
|
238
|
-
* Exits edit mode if the user backspaced past `editingAnchor` (deleting a
|
|
239
|
-
* character before where the edit started).
|
|
240
|
-
*/
|
|
241
253
|
handleCaretAfterInput(offset: number | null): void;
|
|
242
|
-
/**
|
|
243
|
-
* Update caret offset from a selection-move event that was NOT preceded by
|
|
244
|
-
* an input event (so the user moved the cursor by click or arrows, not by
|
|
245
|
-
* typing). Exits edit mode if the caret moved outside the edit region.
|
|
246
|
-
*/
|
|
247
254
|
handleCaretMove(offset: number | null): void;
|
|
248
255
|
setActiveDropdownIndex(index: number): void;
|
|
249
256
|
handleTextChange(value: string): void;
|
|
250
257
|
handleKeyDown(e: KeyboardEvent): void;
|
|
251
258
|
setFocused(focused: boolean): void;
|
|
252
|
-
/** Subscribe to state changes
|
|
259
|
+
/** Subscribe to state changes. Listener receives the full (input + derived) shape. */
|
|
253
260
|
subscribe(listener: (state: CoreState) => void): () => void;
|
|
254
261
|
getState(): CoreState;
|
|
255
262
|
get listboxId(): string;
|
|
256
263
|
get isReady(): boolean;
|
|
257
|
-
|
|
258
|
-
|
|
264
|
+
/**
|
|
265
|
+
* Subscribe to an event. Multiple listeners may register for the same event;
|
|
266
|
+
* `emit` fans out to all of them. The returned function removes only the
|
|
267
|
+
* listener it registered.
|
|
268
|
+
*
|
|
269
|
+
* Note: `opts.on*` listeners passed at construction are equivalent to calling
|
|
270
|
+
* `on()` once each; their unsubscribe handles are not exposed.
|
|
271
|
+
*/
|
|
272
|
+
on<E extends keyof AIAutocompleteEvents>(event: E, callback: (...args: AIAutocompleteEvents[E]) => void): () => void;
|
|
273
|
+
update(opts: CoreUpdateOptions): void;
|
|
259
274
|
selectOption(option: SuggestionOption): void;
|
|
260
275
|
private startSelectionAnimationTimer;
|
|
261
|
-
private selectOptionInEditMode;
|
|
262
276
|
private fireTelemetry;
|
|
263
|
-
private recomputeDerived;
|
|
264
277
|
private setupContainer;
|
|
265
278
|
private buildAndRenderFull;
|
|
266
279
|
private buildAndRenderDropdown;
|
|
@@ -269,6 +282,17 @@ declare class AIAutocomplete {
|
|
|
269
282
|
/** Auto-clear newParamId after shimmer animation. */
|
|
270
283
|
private subscribeNewParamTimer;
|
|
271
284
|
private handleChange;
|
|
285
|
+
/**
|
|
286
|
+
* In re-edit mode, once the user has typed enough that no *tappable* options
|
|
287
|
+
* still match (non-tappable options are kept by filterOptions regardless of
|
|
288
|
+
* the query, so they don't count as "still matching"), exit re-edit and
|
|
289
|
+
* fire an immediate fetch so the dropdown swaps over to fresh server
|
|
290
|
+
* suggestions instead of staying frozen on a dead filter.
|
|
291
|
+
*
|
|
292
|
+
* Guarded against re-entry: once we exit, editingParam is null and the
|
|
293
|
+
* subscription early-returns on subsequent fires.
|
|
294
|
+
*/
|
|
295
|
+
private maybeExitReEditOnNoMatch;
|
|
272
296
|
/**
|
|
273
297
|
* When the user has typed text that exactly matches (case-insensitive) one
|
|
274
298
|
* of the active suggestion's options, promote it to a completed param right
|
|
@@ -277,14 +301,6 @@ declare class AIAutocomplete {
|
|
|
277
301
|
* option is fully typed, without waiting 100–300ms for the round-trip.
|
|
278
302
|
*/
|
|
279
303
|
private maybePromoteExactMatch;
|
|
280
|
-
/**
|
|
281
|
-
* Edit-mode counterpart to {@link maybePromoteExactMatch}. When the user is
|
|
282
|
-
* editing a bold completed param and the typed text exactly matches one of
|
|
283
|
-
* that param's cached options, promote it back to a completed param (bold)
|
|
284
|
-
* — same instant-bold affordance as the normal typing flow, just driven by
|
|
285
|
-
* the edited param's cached options instead of the active suggestion.
|
|
286
|
-
*/
|
|
287
|
-
private maybePromoteEditExactMatch;
|
|
288
304
|
}
|
|
289
305
|
|
|
290
306
|
type Listener<S> = (next: S, prev: S) => void;
|