@magicx-eng/ai-autocomplete-vanilla 0.1.1 → 0.1.2
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 +235 -140
- package/index.d.mts +44 -34
- package/index.d.ts +44 -34
- package/index.js +215 -174
- package/index.js.map +1 -1
- package/index.mjs +215 -174
- package/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,7 @@ A framework-agnostic vanilla JS/TypeScript library that provides a guided AI-pow
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
+
- **Three tiers of integration** — full drop-in component, dropdown-only, or fully headless
|
|
7
8
|
- **Zero dependencies** — no React, no framework, no virtual DOM
|
|
8
9
|
- **Pill-based input** — inline pills for unfilled parameters, completed text for filled ones
|
|
9
10
|
- **Pill placement** — render pills inline in the input or inside the dropdown
|
|
@@ -12,8 +13,7 @@ A framework-agnostic vanilla JS/TypeScript library that provides a guided AI-pow
|
|
|
12
13
|
- **Keyboard navigation** — arrow keys, enter to submit, tab to autocomplete
|
|
13
14
|
- **Client-side filtering** — instant substring filtering on every keystroke
|
|
14
15
|
- **Option overrides** — inject or dynamically generate client-side options per suggestion type
|
|
15
|
-
- **Controlled & uncontrolled** — works out of the box or integrates with external state
|
|
16
|
-
- **Imperative API** — `focus()`, `reset()`, `setMode()`, `destroy()`, and more
|
|
16
|
+
- **Controlled & uncontrolled** — works out of the box or integrates with external state
|
|
17
17
|
- **Accessible** — ARIA combobox pattern with `role="listbox"`, `aria-activedescendant`
|
|
18
18
|
- **Animations** — option selection streak animation, text shimmer on newly added params
|
|
19
19
|
- **Lightweight** — ~10 KB gzipped, styles auto-injected at runtime
|
|
@@ -26,121 +26,210 @@ A framework-agnostic vanilla JS/TypeScript library that provides a guided AI-pow
|
|
|
26
26
|
pnpm add @magicx-eng/ai-autocomplete-vanilla
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
##
|
|
29
|
+
## Three Tiers
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
| Tier | What you get | What you own | Use when |
|
|
32
|
+
|---|---|---|---|
|
|
33
|
+
| **Tier 1: Full** | Input + dropdown + pills + keyboard + state | Nothing — drop in and go | You want a complete widget with zero setup |
|
|
34
|
+
| **Tier 2: Dropdown** | Dropdown + pills + keyboard + state | The input element | You need a custom input (e.g. rich text, search bar) |
|
|
35
|
+
| **Tier 3: Headless** | State + controllers + derived data | All DOM | You're building a framework wrapper (React, Vue, Svelte) or need full rendering control |
|
|
33
36
|
|
|
34
|
-
|
|
35
|
-
import { AIAutocomplete } from "@magicx-eng/ai-autocomplete-vanilla";
|
|
37
|
+
---
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
## Tier 1: Full Component
|
|
40
|
+
|
|
41
|
+
Drop-in. The library creates and owns the input, dropdown, pills, and all DOM.
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { AIAutocomplete } from "@magicx-eng/ai-autocomplete-vanilla";
|
|
45
|
+
|
|
46
|
+
const ac = new AIAutocomplete(document.getElementById("root"), {
|
|
47
|
+
apiConfig: {
|
|
48
|
+
endpoint: "https://api.example.com/ac/suggest",
|
|
49
|
+
apiKey: "your_api_key",
|
|
50
|
+
},
|
|
51
|
+
onSubmit: (result) => {
|
|
52
|
+
console.log(result.query); // "Create a email"
|
|
53
|
+
console.log(result.raw_query); // "Create a {{TASK_1}}"
|
|
54
|
+
console.log(result.completed_params); // [{ placeholder: "{{TASK_1}}", type: "task", ... }]
|
|
55
|
+
},
|
|
56
|
+
});
|
|
49
57
|
```
|
|
50
58
|
|
|
51
59
|
No CSS import needed — styles are auto-injected on first instantiation.
|
|
52
60
|
|
|
53
|
-
|
|
61
|
+
### Options
|
|
54
62
|
|
|
55
|
-
|
|
63
|
+
```ts
|
|
64
|
+
const ac = new AIAutocomplete(container, {
|
|
65
|
+
// Tier: "full" (default) | "dropdown" | "headless"
|
|
66
|
+
renderMode: "full",
|
|
67
|
+
|
|
68
|
+
// API
|
|
69
|
+
apiConfig: { apiKey: "...", authScheme: "Bearer", endpoint: "/ac/suggest" },
|
|
70
|
+
optionOverrides: { account: (query) => [...] },
|
|
71
|
+
columns: 2,
|
|
72
|
+
maskCompletedText: false, // when true, omits completed params' text from API requests (PII masking)
|
|
73
|
+
|
|
74
|
+
// Appearance
|
|
75
|
+
mode: "auto", // "light" | "dark" | "auto"
|
|
76
|
+
optionsPosition: "below", // "above" | "below"
|
|
77
|
+
animations: true, // enable streak + shimmer
|
|
78
|
+
pillPlacement: "inline", // "inline" | "dropdown" | "hidden"
|
|
79
|
+
dropdownTrigger: "auto", // "auto" | "manual" | "hidden"
|
|
80
|
+
|
|
81
|
+
// Events
|
|
82
|
+
onSubmit: (result) => { ... },
|
|
83
|
+
onError: (error) => { ... },
|
|
84
|
+
onChange: (text) => { ... },
|
|
85
|
+
onParamsChange: (params) => { ... },
|
|
86
|
+
|
|
87
|
+
// Controlled mode
|
|
88
|
+
value: "initial text",
|
|
89
|
+
completedParams: [],
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Methods
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
ac.focus(); // Focus the input
|
|
97
|
+
ac.reset(); // Clear everything, re-fetch
|
|
98
|
+
ac.destroy(); // Remove DOM, listeners, timers
|
|
99
|
+
ac.setMode("dark"); // Switch color mode
|
|
100
|
+
ac.update({ animations: false, optionsPosition: "above" });
|
|
101
|
+
```
|
|
56
102
|
|
|
57
|
-
|
|
58
|
-
<template>
|
|
59
|
-
<div ref="acRef" />
|
|
60
|
-
</template>
|
|
103
|
+
---
|
|
61
104
|
|
|
62
|
-
|
|
63
|
-
|
|
105
|
+
## Tier 2: Dropdown Only
|
|
106
|
+
|
|
107
|
+
You own the input. The library renders the dropdown with pills inside a container you provide.
|
|
108
|
+
|
|
109
|
+
```ts
|
|
64
110
|
import { AIAutocomplete } from "@magicx-eng/ai-autocomplete-vanilla";
|
|
65
111
|
|
|
66
|
-
const
|
|
67
|
-
|
|
112
|
+
const input = document.getElementById("my-input");
|
|
113
|
+
const dropdownContainer = document.getElementById("my-dropdown");
|
|
68
114
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
115
|
+
const ac = new AIAutocomplete(dropdownContainer, {
|
|
116
|
+
renderMode: "dropdown",
|
|
117
|
+
apiConfig: { apiKey: "your_api_key" },
|
|
118
|
+
onChange: (text) => {
|
|
119
|
+
// Keep your input in sync when the library updates text (e.g. option selected)
|
|
120
|
+
if (input.value !== text) input.value = text;
|
|
121
|
+
},
|
|
122
|
+
onSubmit: (result) => console.log(result),
|
|
74
123
|
});
|
|
75
124
|
|
|
76
|
-
|
|
77
|
-
|
|
125
|
+
// Wire your input → library
|
|
126
|
+
input.addEventListener("input", () => ac.handleTextChange(input.value));
|
|
127
|
+
input.addEventListener("keydown", (e) => ac.handleKeyDown(e));
|
|
128
|
+
input.addEventListener("focus", () => ac.setFocused(true));
|
|
129
|
+
input.addEventListener("blur", () => ac.setFocused(false));
|
|
78
130
|
```
|
|
79
131
|
|
|
80
|
-
|
|
132
|
+
Pills always render inside the dropdown in this mode. The library creates the dropdown DOM inside your container — you just provide the element and wire up your input's events.
|
|
81
133
|
|
|
82
|
-
|
|
83
|
-
<script>
|
|
84
|
-
import { onMount, onDestroy } from "svelte";
|
|
85
|
-
import { AIAutocomplete } from "@magicx-eng/ai-autocomplete-vanilla";
|
|
134
|
+
> Focus/blur wiring is required when `dropdownTrigger` is `"auto"` (the default) — the dropdown only opens while the input is focused. Without `setFocused()`, the dropdown will never open.
|
|
86
135
|
|
|
87
|
-
|
|
88
|
-
let instance;
|
|
136
|
+
---
|
|
89
137
|
|
|
90
|
-
|
|
91
|
-
instance = new AIAutocomplete(el, {
|
|
92
|
-
apiConfig: { endpoint: "/ac/suggest", apiKey: "your_key" },
|
|
93
|
-
onSubmit: (result) => console.log(result),
|
|
94
|
-
});
|
|
95
|
-
});
|
|
138
|
+
## Tier 3: Headless
|
|
96
139
|
|
|
97
|
-
|
|
98
|
-
</script>
|
|
140
|
+
No DOM at all. You get state, controllers, and derived data. You render everything yourself.
|
|
99
141
|
|
|
100
|
-
|
|
101
|
-
```
|
|
142
|
+
This is what framework wrappers (React, Vue, Svelte) use under the hood.
|
|
102
143
|
|
|
103
|
-
|
|
144
|
+
```ts
|
|
145
|
+
import { AIAutocomplete } from "@magicx-eng/ai-autocomplete-vanilla";
|
|
104
146
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
147
|
+
const ac = new AIAutocomplete(document.createElement("div"), {
|
|
148
|
+
renderMode: "headless",
|
|
149
|
+
apiConfig: { apiKey: "your_api_key" },
|
|
150
|
+
onSubmit: (result) => console.log(result),
|
|
151
|
+
});
|
|
109
152
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
153
|
+
// Read state
|
|
154
|
+
const state = ac.getState();
|
|
155
|
+
// state.text, state.completedParams, state.suggestions,
|
|
156
|
+
// state.segments, state.filteredOptions, state.actionableSuggestions,
|
|
157
|
+
// state.placeholderText, state.isDropdownOpen, state.activeDropdownIndex,
|
|
158
|
+
// state.newParamId, state.isLoading, state.isReady, state.error
|
|
159
|
+
|
|
160
|
+
// Subscribe to changes (fires after derived state is settled)
|
|
161
|
+
const unsub = ac.subscribe((state) => {
|
|
162
|
+
renderMyInput(state.text, state.placeholderText);
|
|
163
|
+
renderMyPills(state.actionableSuggestions);
|
|
164
|
+
renderMyDropdown(state.filteredOptions, state.activeDropdownIndex, state.isDropdownOpen);
|
|
165
|
+
renderMySegments(state.segments, state.newParamId);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Forward user actions → library
|
|
169
|
+
myInput.addEventListener("input", () => ac.handleTextChange(myInput.value));
|
|
170
|
+
myInput.addEventListener("keydown", (e) => ac.handleKeyDown(e));
|
|
171
|
+
myInput.addEventListener("focus", () => ac.setFocused(true));
|
|
172
|
+
myInput.addEventListener("blur", () => ac.setFocused(false));
|
|
173
|
+
myOptionEl.addEventListener("click", () => ac.selectOption(option));
|
|
174
|
+
myPillEl.addEventListener("click", () => ac.setActivePill(index));
|
|
175
|
+
|
|
176
|
+
// Cleanup
|
|
177
|
+
ac.destroy();
|
|
178
|
+
unsub();
|
|
115
179
|
```
|
|
116
180
|
|
|
117
|
-
|
|
181
|
+
### State shape (`CoreState`)
|
|
118
182
|
|
|
119
|
-
|
|
183
|
+
| Field | Type | Description |
|
|
184
|
+
|---|---|---|
|
|
185
|
+
| `text` | `string` | Current input text |
|
|
186
|
+
| `completedParams` | `CompletedParamState[]` | Filled parameters |
|
|
187
|
+
| `suggestions` | `Suggestion[]` | All suggestions from server (including placeholder type) |
|
|
188
|
+
| `actionableSuggestions` | `Suggestion[]` | Non-placeholder suggestions (the pills) |
|
|
189
|
+
| `filteredOptions` | `SuggestionOption[]` | Options for the active suggestion, filtered by current query |
|
|
190
|
+
| `segments` | `Segment[]` | Input text split into typed text vs completed params — for overlay rendering |
|
|
191
|
+
| `placeholderText` | `string` | Placeholder text from server suggestions (joined `placeholder`-type suggestion texts) |
|
|
192
|
+
| `activeDropdownIndex` | `number` | Highlighted option index (-1 = none) |
|
|
193
|
+
| `isDropdownOpen` | `boolean` | Whether the dropdown should be visible |
|
|
194
|
+
| `newParamId` | `string \| null` | ID of the most recently added param (for shimmer animation) |
|
|
195
|
+
| `isLoading` | `boolean` | Fetch in progress |
|
|
196
|
+
| `isReady` | `boolean` | Server indicates query is complete |
|
|
197
|
+
| `error` | `Error \| null` | Last fetch error |
|
|
198
|
+
|
|
199
|
+
### Actions
|
|
120
200
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
201
|
+
| Method | Description |
|
|
202
|
+
|---|---|
|
|
203
|
+
| `handleTextChange(value)` | Call when user types. Handles capitalization, param reconciliation, filter updates. |
|
|
204
|
+
| `handleKeyDown(event)` | Forward keyboard events. Handles arrow nav, Enter, Tab, Escape. |
|
|
205
|
+
| `setFocused(focused)` | Notify the library that the input has focus. Required for `dropdownTrigger: "auto"` in Tier 2/3 — the dropdown only opens while focused. |
|
|
206
|
+
| `selectOption(option)` | Select a dropdown option. Updates text, creates completed param, triggers shimmer. |
|
|
207
|
+
| `setActiveDropdownIndex(index)` | Set the highlighted option (for mouse hover). |
|
|
208
|
+
| `setActivePill(index)` | Reorder pills — moves pill at `index` to front (active). |
|
|
209
|
+
| `removeLastParam()` | Remove last completed param, restore it as a pill. |
|
|
210
|
+
| `clearNewParamId()` | Clear shimmer animation state. |
|
|
211
|
+
| `reset()` | Clear all state, re-fetch initial suggestions. |
|
|
212
|
+
| `setValue(text)` | Set text (controlled mode). |
|
|
213
|
+
| `setCompletedParams(params)` | Set completed params (controlled mode). |
|
|
214
|
+
| `setMode(mode)` | Switch color mode at runtime. |
|
|
215
|
+
| `update(opts)` | Update multiple options at once (e.g. `animations`, `pillPlacement`, `dropdownTrigger`, `optionsPosition`). |
|
|
216
|
+
| `getState()` | Get a snapshot of the current state. Useful for initial read before subscribing. |
|
|
217
|
+
| `subscribe(listener)` | Subscribe to state changes (fires after derived state settles). Returns unsubscribe function. |
|
|
218
|
+
| `destroy()` | Cleanup — abort fetches, clear timers, remove listeners. |
|
|
124
219
|
|
|
125
|
-
###
|
|
220
|
+
### Building a framework wrapper
|
|
126
221
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
| `onSubmit?` | `(result: AutocompleteResult) => void` | — | Called on Enter or submit button. |
|
|
139
|
-
| `onError?` | `(error: Error) => void` | — | Called when a fetch fails. |
|
|
140
|
-
| `onChange?` | `(text: string) => void` | — | Called when text changes. |
|
|
141
|
-
| `onParamsChange?` | `(params: CompletedParamState[]) => void` | — | Called when completed params change. |
|
|
142
|
-
| `value?` | `string` | — | Controlled text value. |
|
|
143
|
-
| `completedParams?` | `CompletedParamState[]` | — | Controlled completed params. |
|
|
222
|
+
The pattern for any framework:
|
|
223
|
+
|
|
224
|
+
1. Create a headless instance on mount
|
|
225
|
+
2. Subscribe to state → map to framework reactivity (`useState`, `ref()`, `writable()`)
|
|
226
|
+
3. Render your own components using the state
|
|
227
|
+
4. Forward user events (input, keydown, click) → core actions
|
|
228
|
+
5. Destroy on unmount
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## API Reference
|
|
144
233
|
|
|
145
234
|
### `APIConfig`
|
|
146
235
|
|
|
@@ -172,100 +261,104 @@ A discriminated union: `APIKeyConfig | AccessTokenConfig`.
|
|
|
172
261
|
}
|
|
173
262
|
```
|
|
174
263
|
|
|
175
|
-
The SDK handles token refresh transparently: if a request returns 401, it calls `getAccessToken` once, retries, and only surfaces an error if the retry also fails. Concurrent 401s share a single refresh call.
|
|
264
|
+
The SDK handles token refresh transparently: if a request returns 401, it calls `getAccessToken` once, retries, and only surfaces an error if the retry also fails. Concurrent 401s share a single refresh call. Tokens are refreshed proactively 30 seconds before `expiresAt`.
|
|
176
265
|
|
|
177
|
-
###
|
|
266
|
+
### `AutocompleteResult`
|
|
178
267
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
|
182
|
-
|
|
183
|
-
| `
|
|
184
|
-
| `
|
|
185
|
-
| `
|
|
186
|
-
| `setValue(text)` | Set text value (controlled mode). |
|
|
187
|
-
| `setCompletedParams(params)` | Set completed params (controlled mode). |
|
|
188
|
-
| `setActivePill(index)` | Reorder pills, making the one at `index` active. |
|
|
189
|
-
| `removeLastParam()` | Remove the last completed param and restore it as a pill. |
|
|
190
|
-
| `clearNewParamId()` | Clear the shimmer animation state. |
|
|
191
|
-
| `getState()` | Get a snapshot of the full internal state. |
|
|
192
|
-
| `isReady` | `true` when the server indicates the query is ready for execution. |
|
|
268
|
+
The object passed to `onSubmit`:
|
|
269
|
+
|
|
270
|
+
| Field | Type | Description |
|
|
271
|
+
|---|---|---|
|
|
272
|
+
| `query` | `string` | Plain text as the user sees it. |
|
|
273
|
+
| `raw_query` | `string` | Text with placeholder tokens (e.g. `"Create a {{TASK_1}}"`). |
|
|
274
|
+
| `completed_params` | `CompletedParam[]` | Array of filled parameter values. |
|
|
193
275
|
|
|
194
276
|
### Event Subscription
|
|
195
277
|
|
|
196
278
|
```ts
|
|
197
|
-
const off =
|
|
279
|
+
const off = ac.on("submit", (result) => { ... });
|
|
198
280
|
off(); // unsubscribe
|
|
199
281
|
```
|
|
200
282
|
|
|
201
|
-
Events: `submit`, `error`, `change`, `paramsChange
|
|
283
|
+
Events: `submit`, `error`, `change`, `paramsChange`.
|
|
202
284
|
|
|
203
285
|
Constructor callbacks (`onSubmit`, `onChange`, etc.) are equivalent to calling `on()`.
|
|
204
286
|
|
|
205
|
-
|
|
287
|
+
**`subscribe()` vs `on()`:** Use `on()` for specific events (submit, error, text change). Use `subscribe()` (Tier 3) for full state updates — it fires after derived state is settled, making it safe for rendering.
|
|
206
288
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
| Field | Type | Description |
|
|
210
|
-
|---|---|---|
|
|
211
|
-
| `query` | `string` | Plain text as the user sees it. |
|
|
212
|
-
| `raw_query` | `string` | Text with placeholder tokens (e.g. `"Create a {{TASK_1}}"`). |
|
|
213
|
-
| `completed_params` | `CompletedParam[]` | Array of filled parameter values. |
|
|
289
|
+
---
|
|
214
290
|
|
|
215
291
|
## CSS Customization
|
|
216
292
|
|
|
217
|
-
Styles are auto-injected at runtime
|
|
293
|
+
Styles are auto-injected at runtime (Tier 1 and Tier 2). No CSS import needed. The component ships built-in light and dark defaults.
|
|
218
294
|
|
|
219
295
|
### CSS Variables
|
|
220
296
|
|
|
221
|
-
Override these on the container element
|
|
297
|
+
Override these on the container element. All built-in defaults use `:where()` (zero specificity) — your overrides always win without `!important`.
|
|
222
298
|
|
|
223
299
|
| Variable | Default (light) | Default (dark) | Description |
|
|
224
300
|
|---|---|---|---|
|
|
225
|
-
| `--
|
|
226
|
-
| `--
|
|
227
|
-
| `--
|
|
228
|
-
| `--
|
|
229
|
-
| `--
|
|
230
|
-
| `--
|
|
231
|
-
| `--
|
|
232
|
-
| `--
|
|
233
|
-
| `--
|
|
301
|
+
| `--aia-pill-bg` | `#bdbdbd` | `#bdbdbd` | Pill background |
|
|
302
|
+
| `--aia-pill-color` | `#000000` | `#ffffff` | Pill text |
|
|
303
|
+
| `--aia-pill-font-size` | `19px` | `19px` | Pill font size |
|
|
304
|
+
| `--aia-option-bg` | `transparent` | `transparent` | Option background (highlighted) |
|
|
305
|
+
| `--aia-option-color` | `#000000` | `#ffffff` | Option text |
|
|
306
|
+
| `--aia-option-color-selected` | `#000000` | `#ffffff` | Highlighted option text |
|
|
307
|
+
| `--aia-option-font-size` | `19px` | `19px` | Option font size |
|
|
308
|
+
| `--aia-written-text-color` | `#000000` | `#ffffff` | Input text |
|
|
309
|
+
| `--aia-written-text-font-size` | `19px` | `19px` | Input text font size |
|
|
310
|
+
| `--aia-caret-color` | `--aia-written-text-color` | `--aia-written-text-color` | Textarea caret color. Override independently of input text color. |
|
|
311
|
+
| `--aia-submit-bg` | `#000000` | `#ffffff` | Submit button background |
|
|
312
|
+
| `--aia-submit-color` | `#ffffff` | `#000000` | Submit button icon color |
|
|
313
|
+
| `--aia-dropdown-bg` | — | — | Optional bg color the dropdown's "glass" rim shadow tints toward. Set this to the page background behind the dropdown so the bottom-corner glow blends seamlessly. |
|
|
314
|
+
| `--aia-scrollbar-thumb` | `rgba(0, 0, 0, 0.3)` | `rgba(0, 0, 0, 0.3)` | Color of the option list's scrollbar thumb (Firefox + WebKit). |
|
|
315
|
+
| `--aia-streak-rgb` | `99, 102, 241` | `255, 255, 255` | Comma-separated RGB triplet used to tint the option-selection streak animation (consumed via `rgba(var(--aia-streak-rgb), …)`). |
|
|
316
|
+
| `--aia-streak-glass-bg` | `rgba(99, 102, 241, 0.1)` | `rgba(255, 255, 255, 0.1)` | Background fill for the streak's glass-pill effect. |
|
|
234
317
|
|
|
235
318
|
### Per-mode Overrides
|
|
236
319
|
|
|
237
320
|
```css
|
|
238
321
|
#my-autocomplete[data-mode="light"] {
|
|
239
|
-
--
|
|
240
|
-
--ac-written-text-color: #1e293b;
|
|
322
|
+
--aia-pill-bg: #e2e8f0;
|
|
241
323
|
}
|
|
242
|
-
|
|
243
324
|
#my-autocomplete[data-mode="dark"] {
|
|
244
|
-
--
|
|
245
|
-
--ac-written-text-color: #f8fafc;
|
|
325
|
+
--aia-pill-bg: #334155;
|
|
246
326
|
}
|
|
247
327
|
```
|
|
248
328
|
|
|
249
329
|
### Live Preview
|
|
250
330
|
|
|
251
|
-
CSS variables update instantly — great for theme editors:
|
|
252
|
-
|
|
253
331
|
```ts
|
|
254
332
|
document.getElementById("my-autocomplete")
|
|
255
|
-
.style.setProperty("--
|
|
333
|
+
.style.setProperty("--aia-pill-bg", newColor);
|
|
256
334
|
```
|
|
257
335
|
|
|
258
|
-
|
|
336
|
+
### Selector Hooks
|
|
259
337
|
|
|
260
|
-
|
|
338
|
+
For styling beyond the CSS variables, target these stable `data-aia-*` attributes:
|
|
339
|
+
|
|
340
|
+
| Attribute | Element |
|
|
261
341
|
|---|---|
|
|
262
|
-
|
|
|
263
|
-
|
|
|
264
|
-
|
|
|
265
|
-
|
|
|
266
|
-
|
|
|
267
|
-
|
|
|
268
|
-
|
|
|
342
|
+
| `[data-aia-editor]` | Editor area wrapping the textarea + overlay |
|
|
343
|
+
| `[data-aia-textarea]` | The `<textarea>` |
|
|
344
|
+
| `[data-aia-submit]` | Submit button |
|
|
345
|
+
| `[data-aia-pill]` | Each unfilled-suggestion pill |
|
|
346
|
+
| `[data-aia-pillbar]` | Pill bar container inside the dropdown |
|
|
347
|
+
| `[data-aia-option]` | Each suggestion option |
|
|
348
|
+
| `[data-aia-dropdown]` | The dropdown root (listbox) |
|
|
349
|
+
|
|
350
|
+
The vanilla core also exposes stable BEM class names (`magicx-aia-*`); both can be used.
|
|
351
|
+
|
|
352
|
+
```css
|
|
353
|
+
/* Solid (non-glass) dropdown */
|
|
354
|
+
#my-autocomplete [data-aia-dropdown] {
|
|
355
|
+
background: #fff;
|
|
356
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
|
357
|
+
backdrop-filter: none;
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
---
|
|
269
362
|
|
|
270
363
|
## Option Overrides
|
|
271
364
|
|
|
@@ -286,6 +379,8 @@ new AIAutocomplete(el, {
|
|
|
286
379
|
});
|
|
287
380
|
```
|
|
288
381
|
|
|
382
|
+
---
|
|
383
|
+
|
|
289
384
|
## License
|
|
290
385
|
|
|
291
386
|
Private package. All rights reserved.
|
package/index.d.mts
CHANGED
|
@@ -43,7 +43,7 @@ interface APIConfigBase {
|
|
|
43
43
|
}
|
|
44
44
|
interface APIKeyConfig extends APIConfigBase {
|
|
45
45
|
type?: "apiKey";
|
|
46
|
-
apiKey
|
|
46
|
+
apiKey?: string;
|
|
47
47
|
authScheme?: "Bearer" | "Basic";
|
|
48
48
|
}
|
|
49
49
|
interface AccessTokenConfig extends APIConfigBase {
|
|
@@ -64,14 +64,6 @@ interface AutocompleteResult {
|
|
|
64
64
|
completed_params: CompletedParam[];
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
type Listener<S> = (next: S, prev: S) => void;
|
|
68
|
-
interface Store<S> {
|
|
69
|
-
get: () => S;
|
|
70
|
-
set: (patch: Partial<S> | ((s: S) => Partial<S>)) => void;
|
|
71
|
-
subscribe: (listener: Listener<S>) => () => void;
|
|
72
|
-
}
|
|
73
|
-
declare function createStore<S>(initial: S): Store<S>;
|
|
74
|
-
|
|
75
67
|
/** Internal state managed by the store. */
|
|
76
68
|
interface CoreState {
|
|
77
69
|
text: string;
|
|
@@ -92,19 +84,30 @@ interface CoreState {
|
|
|
92
84
|
pillTapped: boolean;
|
|
93
85
|
skipNextFetch: boolean;
|
|
94
86
|
lastRawQuery: string;
|
|
87
|
+
isFocused: boolean;
|
|
95
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Render mode:
|
|
91
|
+
* - "full" (default) — renders input + dropdown + pills. Tier 1.
|
|
92
|
+
* - "dropdown" — renders only the dropdown with pills. Consumer owns the input. Tier 2.
|
|
93
|
+
* - "headless" — no DOM. State + controllers only. For framework wrappers.
|
|
94
|
+
*/
|
|
95
|
+
type RenderMode = "full" | "dropdown" | "headless";
|
|
96
96
|
/** Options passed to the AIAutocomplete constructor. */
|
|
97
97
|
interface CoreOptions {
|
|
98
98
|
apiConfig?: APIConfig;
|
|
99
99
|
optionOverrides?: OptionOverrides;
|
|
100
100
|
maskCompletedText?: boolean;
|
|
101
|
-
placeholder?: string;
|
|
102
101
|
columns?: number;
|
|
103
|
-
|
|
104
|
-
pillPlacement?: "inline" | "dropdown";
|
|
102
|
+
/** Where pills render in "full" mode. Ignored in "dropdown" mode (always in dropdown). */
|
|
103
|
+
pillPlacement?: "inline" | "dropdown" | "hidden";
|
|
105
104
|
mode?: AppearanceMode;
|
|
106
105
|
optionsPosition?: "above" | "below";
|
|
107
106
|
animations?: boolean;
|
|
107
|
+
/** When the dropdown appears. "auto" = shows when options available. "manual" = only on pill tap. "hidden" = never. Default: "auto". */
|
|
108
|
+
dropdownTrigger?: "auto" | "manual" | "hidden";
|
|
109
|
+
/** Render mode. Default: "full". */
|
|
110
|
+
renderMode?: RenderMode;
|
|
108
111
|
onSubmit?: (result: AutocompleteResult) => void;
|
|
109
112
|
onError?: (error: Error) => void;
|
|
110
113
|
onChange?: (text: string) => void;
|
|
@@ -114,36 +117,23 @@ interface CoreOptions {
|
|
|
114
117
|
completedParams?: CompletedParamState[];
|
|
115
118
|
}
|
|
116
119
|
|
|
117
|
-
interface KeyboardContext {
|
|
118
|
-
columns: number;
|
|
119
|
-
listboxId: string;
|
|
120
|
-
onSubmit?: (result: AutocompleteResult) => void;
|
|
121
|
-
selectOption: (option: SuggestionOption) => void;
|
|
122
|
-
}
|
|
123
|
-
declare class KeyboardController {
|
|
124
|
-
private store;
|
|
125
|
-
private ctx;
|
|
126
|
-
constructor(store: Store<CoreState>, ctx: KeyboardContext);
|
|
127
|
-
handleKeyDown(e: KeyboardEvent): void;
|
|
128
|
-
private getTappableIndices;
|
|
129
|
-
private clickOrSelect;
|
|
130
|
-
/** Delegate to pills controller logic inline (avoids circular dep) */
|
|
131
|
-
private pillsSetActivePill;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
120
|
declare class AIAutocomplete {
|
|
135
121
|
private store;
|
|
136
|
-
private
|
|
122
|
+
private _listboxId;
|
|
137
123
|
private opts;
|
|
138
124
|
private fetchController;
|
|
139
|
-
keyboardController
|
|
125
|
+
private keyboardController;
|
|
140
126
|
private pillsController;
|
|
141
127
|
private modeController;
|
|
142
128
|
private container;
|
|
143
129
|
private unsubscribers;
|
|
144
130
|
private derivedInProgress;
|
|
131
|
+
private renderMode;
|
|
145
132
|
private domRefs;
|
|
133
|
+
private dropdownRefs;
|
|
146
134
|
private newParamTimer;
|
|
135
|
+
private suggestionRemovalTimer;
|
|
136
|
+
private externalListeners;
|
|
147
137
|
constructor(container: HTMLElement, opts?: CoreOptions);
|
|
148
138
|
focus(): void;
|
|
149
139
|
reset(): void;
|
|
@@ -154,15 +144,35 @@ declare class AIAutocomplete {
|
|
|
154
144
|
setActivePill(index: number): void;
|
|
155
145
|
removeLastParam(): void;
|
|
156
146
|
clearNewParamId(): void;
|
|
147
|
+
setActiveDropdownIndex(index: number): void;
|
|
148
|
+
handleTextChange(value: string): void;
|
|
149
|
+
handleKeyDown(e: KeyboardEvent): void;
|
|
150
|
+
setFocused(focused: boolean): void;
|
|
151
|
+
/** Subscribe to state changes (fires after derived state is settled). Returns unsubscribe function. */
|
|
152
|
+
subscribe(listener: (state: CoreState) => void): () => void;
|
|
157
153
|
getState(): CoreState;
|
|
154
|
+
get listboxId(): string;
|
|
158
155
|
get isReady(): boolean;
|
|
159
156
|
on(event: string, callback: (...args: unknown[]) => void): () => void;
|
|
160
157
|
update(opts: Partial<CoreOptions>): void;
|
|
161
|
-
|
|
158
|
+
selectOption(option: SuggestionOption): void;
|
|
162
159
|
private recomputeDerived;
|
|
163
160
|
private setupContainer;
|
|
164
|
-
private
|
|
161
|
+
private buildAndRenderFull;
|
|
162
|
+
private buildAndRenderDropdown;
|
|
163
|
+
/** Batched render subscriber — coalesces multiple store.set calls into one DOM update. */
|
|
164
|
+
private subscribeBatchedRender;
|
|
165
|
+
/** Auto-clear newParamId after shimmer animation. */
|
|
166
|
+
private subscribeNewParamTimer;
|
|
165
167
|
private handleChange;
|
|
166
168
|
}
|
|
167
169
|
|
|
168
|
-
|
|
170
|
+
type Listener<S> = (next: S, prev: S) => void;
|
|
171
|
+
interface Store<S> {
|
|
172
|
+
get: () => S;
|
|
173
|
+
set: (patch: Partial<S> | ((s: S) => Partial<S>)) => void;
|
|
174
|
+
subscribe: (listener: Listener<S>) => () => void;
|
|
175
|
+
}
|
|
176
|
+
declare function createStore<S>(initial: S): Store<S>;
|
|
177
|
+
|
|
178
|
+
export { AIAutocomplete, type APIConfig, type APIKeyConfig, type AccessTokenConfig, type AccessTokenResult, type AppearanceMode, type AutocompleteResult, type CompletedParam, type CompletedParamState, type CoreOptions, type CoreState, type RenderMode, type Segment, type Store, type Suggestion, type SuggestionOption, createStore };
|