@magicx-eng/ai-autocomplete-react 0.1.16 → 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 CHANGED
@@ -1,19 +1,22 @@
1
1
  # @magicx-eng/ai-autocomplete-react
2
2
 
3
- A React/TypeScript SDK that provides a guided AI-powered autocomplete experience with pill-based input and dropdown suggestions.
3
+ A React/TypeScript SDK that provides a guided AI-powered autocomplete experience with pill-based input and dropdown suggestions. Powered by `@magicx-eng/ai-autocomplete-vanilla` under the hood.
4
4
 
5
5
  ## Features
6
6
 
7
7
  - **Two tiers of integration** — use the full `<AIAutocomplete />` component or go headless with `useAIAutocomplete()` + `<AIAutocompleteDropdown />`
8
8
  - **Pill-based input** — inline pills for unfilled parameters, bold text for completed ones
9
+ - **Pill placement** — render pills inline in the input or inside the dropdown
10
+ - **Light/dark mode** — built-in themes with `prefers-color-scheme` support, fully overridable via CSS variables
11
+ - **Access token auth** — short-lived tokens with automatic refresh, single-flight deduplication, and 401 retry
9
12
  - **Keyboard navigation** — arrow keys, enter to submit, tab to autocomplete
10
13
  - **Client-side filtering** — instant substring filtering on every keystroke
11
14
  - **Option overrides** — inject or dynamically generate client-side options per suggestion type
12
15
  - **Controlled & uncontrolled** — works out of the box or integrates with external state
13
- - **Ref forwarding** — imperative `focus()` and `reset()` via ref
16
+ - **Ref forwarding** — imperative `focus()`, `reset()`, and `setMode()` via ref
14
17
  - **Accessible** — ARIA combobox pattern with `role="listbox"`, `aria-activedescendant`
15
- - **Animations** — option selection streak animation, text shimmer on newly added params, typewriter reveal effect
16
- - **Lightweight** — zero dependencies beyond React, styles auto-injected at runtime
18
+ - **Animations** — option selection streak animation, text shimmer on newly added params
19
+ - **Lightweight** — styles auto-injected at runtime
17
20
  - **TypeScript first** — full type definitions shipped with the package
18
21
 
19
22
  ## Installation
@@ -30,9 +33,16 @@ React 17 or later:
30
33
  pnpm add react react-dom
31
34
  ```
32
35
 
33
- ## Usage
36
+ ## Two Tiers
34
37
 
35
- ### Tier 1: Full Component
38
+ | Tier | What you get | What you own | Use when |
39
+ |---|---|---|---|
40
+ | **Tier 1: Full** | `<AIAutocomplete />` — input, dropdown, pills, state | Nothing — drop in and go | You want a complete widget with zero setup |
41
+ | **Tier 2: Headless** | `useAIAutocomplete()` — state, actions, spread props | The input and layout JSX | You need a custom input or want full control over rendering |
42
+
43
+ ---
44
+
45
+ ## Tier 1: Full Component
36
46
 
37
47
  Drop-in component that owns the input, pills, dropdown, and all state:
38
48
 
@@ -55,7 +65,38 @@ function App() {
55
65
  }
56
66
  ```
57
67
 
58
- ### Tier 2: Headless
68
+ ### Controlled Mode
69
+
70
+ ```tsx
71
+ const [text, setText] = useState("");
72
+ const [params, setParams] = useState([]);
73
+
74
+ <AIAutocomplete
75
+ value={text}
76
+ onChange={setText}
77
+ completedParams={params}
78
+ onParamsChange={setParams}
79
+ onSubmit={(result) => console.log(result)}
80
+ />
81
+ ```
82
+
83
+ ### Imperative Handle
84
+
85
+ ```tsx
86
+ import { useRef } from "react";
87
+ import { AIAutocomplete, type AIAutocompleteHandle } from "@magicx-eng/ai-autocomplete-react";
88
+
89
+ const ref = useRef<AIAutocompleteHandle>(null);
90
+ // ref.current?.focus()
91
+ // ref.current?.reset()
92
+ // ref.current?.setMode("dark")
93
+
94
+ <AIAutocomplete ref={ref} onSubmit={handleSubmit} />
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Tier 2: Headless
59
100
 
60
101
  Use the hook and dropdown separately for full control over the input and rendering:
61
102
 
@@ -86,44 +127,12 @@ function App() {
86
127
  }
87
128
  ```
88
129
 
89
- ### Controlled Mode
90
-
91
- Pass `value` and `onChange` to control the text externally:
92
-
93
- ```tsx
94
- const [text, setText] = useState("");
95
- const [params, setParams] = useState([]);
96
-
97
- <AIAutocomplete
98
- value={text}
99
- onChange={setText}
100
- completedParams={params}
101
- onParamsChange={setParams}
102
- onSubmit={(result) => console.log(result)}
103
- />
104
- ```
105
-
106
- ### Imperative Handle
107
-
108
- Use a ref to focus or reset programmatically:
109
-
110
- ```tsx
111
- import { useRef } from "react";
112
- import { AIAutocomplete, type AIAutocompleteHandle } from "@magicx-eng/ai-autocomplete-react";
113
-
114
- const ref = useRef<AIAutocompleteHandle>(null);
115
- // ref.current?.focus()
116
- // ref.current?.reset()
117
-
118
- <AIAutocomplete ref={ref} onSubmit={handleSubmit} />
119
- ```
130
+ ---
120
131
 
121
132
  ## API Reference
122
133
 
123
134
  ### `<AIAutocomplete />`
124
135
 
125
- The full component. Owns the input, pills, dropdown, and keyboard navigation.
126
-
127
136
  | Prop | Type | Default | Description |
128
137
  |---|---|---|---|
129
138
  | `onSubmit` | `(result: AutocompleteResult) => void` | **required** | Called on Enter or submit button. |
@@ -133,38 +142,66 @@ The full component. Owns the input, pills, dropdown, and keyboard navigation.
133
142
  | `placeholder?` | `string` | — | Fallback placeholder when the server doesn't return one. |
134
143
  | `className?` | `string` | — | CSS class applied to the container. |
135
144
  | `columns?` | `number` | `2` | Number of columns in the dropdown grid. |
136
- | `typewriterEffect?` | `boolean` | `true` | When `true`, reveal selected option text letter by letter with a shimmer. When `false`, show all at once with a shimmer sweep. |
145
+ | `pillPlacement?` | `"inline" \| "dropdown" \| "hidden"` | `"inline"` | Where to render unfilled pills. `"hidden"` hides pills entirely. |
146
+ | `mode?` | `"light" \| "dark" \| "auto"` | `"auto"` | Color mode. `"auto"` follows `prefers-color-scheme`. |
147
+ | `optionsPosition?` | `"above" \| "below"` | `"below"` | Where the dropdown opens relative to the input. |
148
+ | `animations?` | `boolean` | `true` | Enable/disable all SDK animations (streak + shimmer). |
149
+ | `dropdownTrigger?` | `"auto" \| "manual" \| "hidden"` | `"auto"` | When the dropdown appears. `"auto"` = when options available. `"manual"` = only on pill tap, closes after selection. `"hidden"` = never shows. |
137
150
  | `value?` | `string` | — | Controlled text value. |
138
151
  | `completedParams?` | `CompletedParamState[]` | — | Controlled completed params. |
139
152
  | `onChange?` | `(value: string) => void` | — | Called when text changes (controlled mode). |
140
153
  | `onParamsChange?` | `(params: CompletedParamState[]) => void` | — | Called when params change (controlled mode). |
141
- | `ref?` | `Ref<AIAutocompleteHandle>` | — | Imperative handle with `focus()` and `reset()`. |
154
+ | `ref?` | `Ref<AIAutocompleteHandle>` | — | Imperative handle with `focus()`, `reset()`, and `setMode()`. |
142
155
 
143
156
  ### `APIConfig`
144
157
 
158
+ A discriminated union: `APIKeyConfig | AccessTokenConfig`.
159
+
160
+ #### API Key Mode (default)
161
+
162
+ ```ts
163
+ { apiKey: "your_api_key", authScheme: "Bearer", endpoint: "/ac/suggest" }
164
+ ```
165
+
145
166
  | Field | Type | Description |
146
167
  |---|---|---|
147
- | `endpoint?` | `string` | Full URL for the suggest endpoint (e.g. `"https://api.example.com/ac/suggest"`). Default: `"/ac/suggest"`. |
148
- | `apiKey?` | `string` | API key for Authorization header. A dev warning is logged if not provided. |
168
+ | `type?` | `"apiKey"` | Optional discriminator. Default when omitted. |
169
+ | `apiKey?` | `string` | API key for Authorization header. |
149
170
  | `authScheme?` | `"Bearer" \| "Basic"` | Auth header scheme. Default: `"Bearer"`. |
150
- | `appIdentifier?` | `string` | Value for the `X-App-Identifier` header. Only sent when provided. |
171
+ | `endpoint?` | `string` | Full URL for the suggest endpoint. Default: `"/ac/suggest"`. |
172
+ | `appIdentifier?` | `string` | Value for the `X-App-Identifier` header. |
151
173
  | `headers?` | `Record<string, string>` | Additional headers merged into every request. |
152
174
 
153
- ### `AutocompleteResult`
175
+ #### Access Token Mode
154
176
 
155
- The object passed to `onSubmit`:
177
+ ```tsx
178
+ <AIAutocomplete
179
+ apiConfig={{
180
+ type: "accessToken",
181
+ getAccessToken: async () => {
182
+ const res = await fetch("/api/ac-token");
183
+ const { access_token, expires_at } = await res.json();
184
+ return { accessToken: access_token, expiresAt: expires_at };
185
+ },
186
+ }}
187
+ onSubmit={handleSubmit}
188
+ />
189
+ ```
156
190
 
157
191
  | Field | Type | Description |
158
192
  |---|---|---|
159
- | `query` | `string` | Plain text as the user sees it (e.g. `"Create a email"`). |
160
- | `raw_query` | `string` | Text with placeholder tokens (e.g. `"Create a {{TASK_1}}"`). |
161
- | `completed_params` | `CompletedParam[]` | Array of filled parameter values. |
193
+ | `type` | `"accessToken"` | **Required** discriminator. |
194
+ | `getAccessToken` | `() => Promise<AccessTokenResult>` | **Required.** Called when the SDK needs a token. |
195
+ | `accessToken?` | `string` | Initial token. Avoids one round-trip on mount. |
196
+ | `endpoint?` | `string` | Suggest endpoint URL. Default: `"/ac/suggest"`. |
197
+ | `appIdentifier?` | `string` | Value for the `X-App-Identifier` header. |
198
+ | `headers?` | `Record<string, string>` | Additional headers merged into every request. |
162
199
 
163
- ### `useAIAutocomplete(options)`
200
+ The SDK handles token refresh transparently: 401 → `getAccessToken` → retry (once). Concurrent 401s share a single refresh. Tokens refresh proactively 30s before `expiresAt`.
164
201
 
165
- The core hook. Manages state, API calls, filtering, pills, and keyboard navigation.
202
+ ### `useAIAutocomplete(options)`
166
203
 
167
- Accepts the same props as `<AIAutocomplete />` (except `className` and `ref`), with `onSubmit` receiving an `AutocompleteResult`.
204
+ The headless hook for Tier 2. Accepts the same props as `<AIAutocomplete />` (except `className` and `ref`).
168
205
 
169
206
  #### Return Value
170
207
 
@@ -172,122 +209,113 @@ Accepts the same props as `<AIAutocomplete />` (except `className` and `ref`), w
172
209
 
173
210
  | Field | Type | Description |
174
211
  |---|---|---|
175
- | `completedParams` | `CompletedParamState[]` | All parameters the user has filled so far. Each entry contains the selected option's `text`, `kind`, `type`, and `metadata`, plus client-side fields like `id` (stable UUID), `options` (cached for re-editing), `suggestionType`, and `suggestionPlaceholder`. Use these to render completed values (e.g. bold text) and to build the final query on submit. |
176
- | `suggestionPills` | `Suggestion[]` | Unfilled suggestions that should be rendered as pills (inline chips). Excludes placeholder-type suggestions. The first item is the "active" pill whose options appear in the dropdown. Tapping a pill reorders it to the front via `setActivePill`. |
177
- | `segments` | `Segment[]` | The input text split into typed segments for overlay rendering. Each segment is either `{ type: "text", value: string }` for plain text, or `{ type: "completed", value: string, param: CompletedParamState }` for a completed parameter. Use these to render a styled overlay on top of the textarea — e.g. showing completed params in bold while keeping the textarea's actual text transparent. |
178
- | `newParamId` | `string \| null` | The `id` of the most recently added completed param, or `null`. Use this to apply shimmer animations to the newly added text segment. Cleared automatically after the animation completes. |
179
- | `suggestions` | `Suggestion[]` | All suggestions from the server, including placeholder-type suggestions. Most consumers should use `suggestionPills` and `dropdownProps` instead — this is exposed for advanced use cases like custom pill or dropdown rendering. |
180
- | `activeIndex` | `number` | Index of the currently keyboard-highlighted option in the dropdown. `-1` when no option is highlighted. Automatically managed by arrow keys. |
181
- | `isLoading` | `boolean` | `true` while a fetch is in flight and no cached suggestions are visible. Only triggers on initial mount, after `reset()`, or when all cached suggestions are consumed. The dropdown stays open with cached data during background re-fetches. |
182
- | `isReady` | `boolean` | `true` when the server's `is_ready` flag indicates the query is complete and ready for execution. Use this to show a visual indicator or enable a submit button. |
183
- | `error` | `Error \| null` | The most recent fetch error, or `null` if the last fetch succeeded. Cleared on every new fetch attempt. |
212
+ | `completedParams` | `CompletedParamState[]` | Filled parameters. |
213
+ | `suggestionPills` | `Suggestion[]` | Unfilled suggestions (pills). First item is the active pill. |
214
+ | `segments` | `Segment[]` | Input text split into text vs completed segments for overlay rendering. |
215
+ | `newParamId` | `string \| null` | ID of the most recently added param (for shimmer animation). |
216
+ | `suggestions` | `Suggestion[]` | All suggestions from server (including placeholder type). |
217
+ | `activeIndex` | `number` | Highlighted option index. `-1` = none. |
218
+ | `isLoading` | `boolean` | Fetch in progress. |
219
+ | `isReady` | `boolean` | Server indicates query is complete. |
220
+ | `error` | `Error \| null` | Last fetch error. |
184
221
 
185
222
  **Actions**
186
223
 
187
224
  | Field | Type | Description |
188
225
  |---|---|---|
189
- | `setActivePill` | `(index: number) => void` | Reorder pills by moving the pill at `index` to the front, making it the active pill whose options show in the dropdown. |
190
- | `removeLastParam` | `() => void` | Remove the most recently completed parameter, restore it as a pill, and re-show its options in the dropdown. Useful for handling backspace on an empty input. |
191
- | `reEditParam` | `(param: CompletedParamState) => void` | Remove a specific completed parameter from the input text, restore it as a pill with its cached options, and open the dropdown. Use this when the user taps on a bold (completed) param to change their selection. |
192
- | `clearNewParamId` | `() => void` | Manually clear `newParamId`. Called automatically after the shimmer/typewriter animation completes. |
193
- | `reset` | `() => void` | Clear all text, completed params, and suggestions, then re-fetch initial suggestions from the server. Returns the component to its mount state. |
226
+ | `setActivePill` | `(index: number) => void` | Move pill at `index` to front (active). |
227
+ | `removeLastParam` | `() => void` | Remove last completed param, restore as pill. |
228
+ | `clearNewParamId` | `() => void` | Clear shimmer animation state. |
229
+ | `reset` | `() => void` | Clear all state, re-fetch. |
194
230
 
195
231
  **Spread Props**
196
232
 
197
233
  | Field | Type | Description |
198
234
  |---|---|---|
199
- | `inputProps` | `object` | Spread onto a `<textarea>` element. Includes `value`, `placeholder`, `onChange`, `onKeyDown`, and ARIA attributes (`role="combobox"`, `aria-expanded`, `aria-activedescendant`, `aria-autocomplete`, `aria-controls`). Handles keyboard navigation, text capitalization, param reconciliation, and exact-match auto-completion internally. |
200
- | `dropdownProps` | `AIAutocompleteDropdownProps` | Spread onto `<AIAutocompleteDropdown />`. Includes the filtered options for the active suggestion, highlight index, selection handler, highlight handler, open state, and listbox ID. The dropdown handles mouse selection and hover highlighting. |
235
+ | `inputProps` | `object` | Spread onto a `<textarea>`. Includes `value`, `placeholder`, `onChange`, `onKeyDown`, and ARIA attributes. |
236
+ | `dropdownProps` | `AIAutocompleteDropdownProps` | Spread onto `<AIAutocompleteDropdown />`. Includes options, highlight, selection, pills, and open state. |
201
237
 
202
238
  ### `<AIAutocompleteDropdown />`
203
239
 
204
- The dropdown component for Tier 2 headless integration. Spread `dropdownProps` from the hook.
240
+ The dropdown component for Tier 2. Spread `dropdownProps` from the hook.
205
241
 
206
242
  | Prop | Type | Description |
207
243
  |---|---|---|
208
- | `suggestions` | `Suggestion[]` | Suggestions to display (from `dropdownProps`). |
209
- | `activeIndex` | `number` | Currently highlighted option index. |
244
+ | `suggestions` | `Suggestion[]` | Suggestions to display. |
245
+ | `activeIndex` | `number` | Highlighted option index. |
210
246
  | `onSelect` | `(option: SuggestionOption) => void` | Called when an option is selected. |
211
- | `onHighlight` | `(index: number) => void` | Called when an option is highlighted (mouse hover). |
247
+ | `onHighlight` | `(index: number) => void` | Called on mouse hover. |
212
248
  | `isOpen` | `boolean` | Whether the dropdown is visible. |
213
249
  | `id` | `string` | Listbox ID for ARIA. |
214
250
  | `className?` | `string` | CSS class applied to the dropdown. |
251
+ | `pills?` | `Suggestion[]` | Pills to render inside the dropdown. |
252
+ | `onPillClick?` | `(index: number) => void` | Called when a pill is clicked. |
253
+ | `showPills?` | `boolean` | Whether to render pills. Default: `true`. |
215
254
 
216
- ### `Suggestion`
217
-
218
- Represents an unfilled parameter slot. Rendered as a pill in Tier 1, or available via `suggestionPills` in Tier 2.
255
+ ### `AutocompleteResult`
219
256
 
220
257
  | Field | Type | Description |
221
258
  |---|---|---|
222
- | `type` | `string` | Suggestion type identifier (e.g. `"task"`, `"goal"`, `"contact"`). Used as the key for `optionOverrides`. |
223
- | `text` | `string` | Display text for the pill (e.g. `"type"`, `"goal"`). |
224
- | `required` | `boolean` | Whether this suggestion must be filled before submission. |
225
- | `options?` | `SuggestionOption[]` | Available options the user can select from. May be `undefined` if the server omits it. |
259
+ | `query` | `string` | Plain text as the user sees it. |
260
+ | `raw_query` | `string` | Text with placeholder tokens (e.g. `"Create a {{TASK_1}}"`). |
261
+ | `completed_params` | `CompletedParam[]` | Filled parameter values. |
226
262
 
227
- ### `SuggestionOption`
263
+ ---
228
264
 
229
- An individual option within a suggestion's dropdown.
265
+ ## CSS Customization
230
266
 
231
- | Field | Type | Description |
232
- |---|---|---|
233
- | `text` | `string` | Display text for the option. |
234
- | `icon?` | `string` | Icon shown before the text (e.g. emoji). |
235
- | `tag?` | `string` | Label shown after the text in smaller, muted style. |
236
- | `is_tappable` | `boolean` | Whether the option can be selected. Non-tappable options are always-visible hints that cannot be chosen. |
237
- | `kind` | `string \| null` | Category of the option (e.g. `"automation"`, `"email"`), or `null`. Passed through to `CompletedParam` on selection. |
238
- | `metadata?` | `Record<string, unknown>` | Arbitrary metadata passed through with the selection. Available on the completed param after the user selects this option. |
267
+ Styles are auto-injected at runtime no CSS import needed. Built-in light and dark defaults apply automatically based on `mode`.
239
268
 
240
- ## CSS Customization
269
+ ### CSS Variables
270
+
271
+ Override on the container (via `className`). All defaults use `:where()` (zero specificity) — your overrides always win.
272
+
273
+ | Variable | Light | Dark | Description |
274
+ |---|---|---|---|
275
+ | `--aia-pill-bg` | `#bdbdbd` | `#bdbdbd` | Pill background |
276
+ | `--aia-pill-color` | `#000000` | `#ffffff` | Pill text |
277
+ | `--aia-pill-font-size` | `19px` | `19px` | Pill font size |
278
+ | `--aia-option-bg` | `transparent` | `transparent` | Highlighted option background |
279
+ | `--aia-option-color` | `#000000` | `#ffffff` | Option text |
280
+ | `--aia-option-color-selected` | `#000000` | `#ffffff` | Highlighted option text |
281
+ | `--aia-option-font-size` | `19px` | `19px` | Option font size |
282
+ | `--aia-written-text-color` | `#000000` | `#ffffff` | Input text |
283
+ | `--aia-written-text-font-size` | `19px` | `19px` | Input text font size |
284
+
285
+ Legacy `--aia-color-*` variables are still supported as fallbacks.
241
286
 
242
- Styles are auto-injected at runtime — no CSS import needed. The component uses CSS variables for theming. Override them on a parent element:
287
+ ### Per-mode Overrides
243
288
 
244
289
  ```css
245
- .my-autocomplete {
246
- --ac-color-border-default: #ccc; /* Input border */
247
- --ac-color-text-default: #333; /* Input text, caret, submit button bg, highlighted option */
248
- --ac-color-text-muted: #999; /* Placeholder, pill text, option text */
249
- --ac-color-bg-default: #000; /* Submit button icon color (foreground on button) */
250
- --ac-color-background-default: #fff; /* Dropdown background */
251
- --ac-color-background-supportive: #eee; /* Pill background */
290
+ .my-autocomplete[data-mode="light"] {
291
+ --aia-pill-bg: #e2e8f0;
292
+ }
293
+ .my-autocomplete[data-mode="dark"] {
294
+ --aia-pill-bg: #334155;
252
295
  }
253
296
  ```
254
297
 
255
- ## Keyboard Behavior
256
-
257
- | Key | Behavior |
258
- |---|---|
259
- | **Arrow Down** | Open dropdown (if closed and cursor at end) and select first option, or navigate to next option. |
260
- | **Arrow Up** | Navigate to previous option. Deselects if on the first row. Does nothing if no option is selected. |
261
- | **Arrow Right** | Jump to next column when an option is highlighted. Cycles to the next suggestion pill when cursor is at the end and no option is selected. |
262
- | **Arrow Left** | Jump to previous column when an option is highlighted. |
263
- | **Tab** | Select the first tappable option (or highlighted one). Accepts placeholder when input is empty. |
264
- | **Enter** | Select highlighted option if one is highlighted. Otherwise submits the query. |
265
- | **Escape** | Deselect the highlighted option. |
298
+ ---
266
299
 
267
300
  ## Option Overrides
268
301
 
269
- Use `optionOverrides` to inject client-side options per suggestion type:
270
-
271
302
  ```tsx
272
- const overrides = {
273
- // Static options
274
- account: () => [
275
- { text: "Savings", is_tappable: true, kind: null },
276
- { text: "Checking", is_tappable: true, kind: null },
277
- ],
278
- // Dynamic options based on filter query
279
- value: (query) => {
280
- const digits = query.replace(/\D/g, "");
281
- if (!digits) return [{ text: "$100", is_tappable: true, kind: null }];
282
- return [{ text: `$${digits}`, is_tappable: true, kind: null }];
283
- },
284
- };
285
-
286
- <AIAutocomplete optionOverrides={overrides} onSubmit={handleSubmit} />
303
+ <AIAutocomplete
304
+ optionOverrides={{
305
+ account: () => [
306
+ { text: "Savings", is_tappable: true, kind: null },
307
+ { text: "Checking", is_tappable: true, kind: null },
308
+ ],
309
+ value: (query) => {
310
+ const digits = query.replace(/\D/g, "");
311
+ if (!digits) return [{ text: "$100", is_tappable: true, kind: null }];
312
+ return [{ text: `$${digits}`, is_tappable: true, kind: null }];
313
+ },
314
+ }}
315
+ onSubmit={handleSubmit}
316
+ />
287
317
  ```
288
318
 
289
- Override options are prepended to server options (deduplicated by `text`). When a filter query is active and an override function exists, it replaces the default client-side filtering entirely.
290
-
291
319
  ## License
292
320
 
293
321
  Private package. All rights reserved.
package/dist/index.css ADDED
@@ -0,0 +1,2 @@
1
+ :where(.magicx-aia),:where(.magicx-aia[data-mode=light]){--aia-pill-bg: #bdbdbd;--aia-pill-color: #000000;--aia-pill-font-size: 19px;--aia-option-bg: transparent;--aia-option-color: #000000;--aia-option-color-selected: #000000;--aia-option-font-size: 19px;--aia-written-text-color: #000000;--aia-written-text-font-size: 19px}:where(.magicx-aia[data-mode=dark]){--aia-pill-bg: #bdbdbd;--aia-pill-color: #ffffff;--aia-pill-font-size: 19px;--aia-option-bg: transparent;--aia-option-color: #ffffff;--aia-option-color-selected: #ffffff;--aia-option-font-size: 19px;--aia-written-text-color: #ffffff;--aia-written-text-font-size: 19px}:where(.magicx-aia[data-options-position=above]) [data-aia-dropdown]{top:auto;bottom:100%;margin-top:0;margin-bottom:6px}:where(.magicx-aia[data-animations=off]) *,:where(.magicx-aia[data-animations=off]) *:before,:where(.magicx-aia[data-animations=off]) *:after{animation-duration:0s!important;transition-duration:0s!important}
2
+ /*# sourceMappingURL=index.css.map */
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/appearance.css"],"sourcesContent":["/*\n * Built-in appearance defaults — zero specificity via :where().\n * Consumer CSS always wins without !important.\n *\n * Resolution priority (highest wins):\n * 1. Consumer CSS targeting new vars (--aia-pill-bg, etc.)\n * 2. Consumer CSS targeting legacy vars (--aia-color-*, via fallback chain)\n * 3. These built-in defaults\n */\n\n/* Light mode defaults (base) */\n:where(.magicx-aia),\n:where(.magicx-aia[data-mode=\"light\"]) {\n --aia-pill-bg: #bdbdbd;\n --aia-pill-color: #000000;\n --aia-pill-font-size: 19px;\n\n --aia-option-bg: transparent;\n --aia-option-color: #000000;\n --aia-option-color-selected: #000000;\n --aia-option-font-size: 19px;\n\n --aia-written-text-color: #000000;\n --aia-written-text-font-size: 19px;\n}\n\n/* Dark mode defaults */\n:where(.magicx-aia[data-mode=\"dark\"]) {\n --aia-pill-bg: #bdbdbd;\n --aia-pill-color: #ffffff;\n --aia-pill-font-size: 19px;\n\n --aia-option-bg: transparent;\n --aia-option-color: #ffffff;\n --aia-option-color-selected: #ffffff;\n --aia-option-font-size: 19px;\n\n --aia-written-text-color: #ffffff;\n --aia-written-text-font-size: 19px;\n}\n\n/* optionsPosition: dropdown above the input */\n:where(.magicx-aia[data-options-position=\"above\"]) [data-aia-dropdown] {\n top: auto;\n bottom: 100%;\n margin-top: 0;\n margin-bottom: 6px;\n}\n\n/* Disable all animations when data-animations=\"off\" */\n:where(.magicx-aia[data-animations=\"off\"]) *,\n:where(.magicx-aia[data-animations=\"off\"]) *::before,\n:where(.magicx-aia[data-animations=\"off\"]) *::after {\n animation-duration: 0s !important;\n transition-duration: 0s !important;\n}\n"],"mappings":"AAWA,OAAO,CAAC,YACR,OAAO,CADC,UACU,CAAC,kBACjB,eAAe,QACf,kBAAkB,QAClB,sBAAsB,KAEtB,iBAAiB,YACjB,oBAAoB,QACpB,6BAA6B,QAC7B,wBAAwB,KAExB,0BAA0B,QAC1B,8BAA8B,IAChC,CAGA,OAAO,CAhBC,UAgBU,CAAC,iBACjB,eAAe,QACf,kBAAkB,QAClB,sBAAsB,KAEtB,iBAAiB,YACjB,oBAAoB,QACpB,6BAA6B,QAC7B,wBAAwB,KAExB,0BAA0B,QAC1B,8BAA8B,IAChC,CAGA,OAAO,CA/BC,UA+BU,CAAC,8BAAgC,CAAC,mBAClD,IAAK,KACL,OAAQ,KACR,WAAY,EACZ,cAAe,GACjB,CAGA,OAAO,CAvCC,UAuCU,CAAC,sBAAwB,EAC3C,OAAO,CAxCC,UAwCU,CAAC,sBAAwB,CAAC,QAC5C,OAAO,CAzCC,UAyCU,CAAC,sBAAwB,CAAC,OAC1C,mBAAoB,aACpB,oBAAqB,YACvB","names":[]}
package/dist/index.d.mts CHANGED
@@ -39,17 +39,33 @@ type Segment = {
39
39
  value: string;
40
40
  param: CompletedParamState;
41
41
  };
42
+ type AppearanceMode = "light" | "dark" | "auto";
42
43
  interface AIAutocompleteHandle {
43
44
  focus: () => void;
44
45
  reset: () => void;
46
+ setMode: (mode: AppearanceMode) => void;
45
47
  }
46
- interface APIConfig {
48
+ interface APIConfigBase {
47
49
  endpoint?: string;
48
- apiKey?: string;
49
- authScheme?: "Bearer" | "Basic";
50
50
  appIdentifier?: string;
51
51
  headers?: Record<string, string>;
52
52
  }
53
+ interface APIKeyConfig extends APIConfigBase {
54
+ type?: "apiKey";
55
+ apiKey?: string;
56
+ authScheme?: "Bearer" | "Basic";
57
+ }
58
+ interface AccessTokenConfig extends APIConfigBase {
59
+ type: "accessToken";
60
+ accessToken?: string;
61
+ getAccessToken: () => Promise<AccessTokenResult>;
62
+ }
63
+ interface AccessTokenResult {
64
+ accessToken: string;
65
+ /** UNIX ms. If provided, SDK refreshes proactively 30s before expiry. */
66
+ expiresAt?: number;
67
+ }
68
+ type APIConfig = APIKeyConfig | AccessTokenConfig;
53
69
  type OptionOverrides = Record<string, (query: string) => SuggestionOption[]>;
54
70
  interface AIAutocompleteProps {
55
71
  onSubmit: (result: AutocompleteResult) => void;
@@ -60,8 +76,16 @@ interface AIAutocompleteProps {
60
76
  className?: string;
61
77
  apiConfig?: APIConfig;
62
78
  columns?: number;
63
- /** When true (default), reveal selected option text letter by letter. */
64
- typewriterEffect?: boolean;
79
+ /** Where to render unfilled pills. "inline" (default) renders them in the input; "dropdown" renders them above the options grid. */
80
+ pillPlacement?: "inline" | "dropdown" | "hidden";
81
+ /** Color mode. "auto" listens to prefers-color-scheme. Default: "auto" */
82
+ mode?: AppearanceMode;
83
+ /** Where the dropdown opens relative to the input. Default: "below" */
84
+ optionsPosition?: "above" | "below";
85
+ /** Enable all SDK animations (streak + shimmer). Default: true */
86
+ animations?: boolean;
87
+ /** When the dropdown appears. "auto" (default) = shows when options available. "manual" = only on pill tap, closes after selection. "hidden" = never shows. */
88
+ dropdownTrigger?: "auto" | "manual" | "hidden";
65
89
  value?: string;
66
90
  completedParams?: CompletedParamState[];
67
91
  onChange?: (value: string) => void;
@@ -80,8 +104,8 @@ interface UseAIAutocompleteOptions {
80
104
  placeholder?: string;
81
105
  apiConfig?: APIConfig;
82
106
  columns?: number;
83
- /** When true (default), reveal selected option text letter by letter. */
84
- typewriterEffect?: boolean;
107
+ /** When the dropdown appears. Default: "auto". */
108
+ dropdownTrigger?: "auto" | "manual" | "hidden";
85
109
  value?: string;
86
110
  completedParams?: CompletedParamState[];
87
111
  onChange?: (value: string) => void;
@@ -92,7 +116,6 @@ interface UseAIAutocompleteReturn {
92
116
  suggestionPills: Suggestion[];
93
117
  setActivePill: (index: number) => void;
94
118
  removeLastParam: () => void;
95
- reEditParam: (param: CompletedParamState) => void;
96
119
  reset: () => void;
97
120
  segments: Segment[];
98
121
  newParamId: string | null;
@@ -123,12 +146,18 @@ interface AIAutocompleteDropdownProps {
123
146
  isOpen: boolean;
124
147
  id: string;
125
148
  className?: string;
149
+ /** Pills to render inside the dropdown. Provided by `dropdownProps` from the hook. */
150
+ pills?: Suggestion[];
151
+ /** Called when a pill inside the dropdown is clicked. Provided by `dropdownProps` from the hook. */
152
+ onPillClick?: (index: number) => void;
153
+ /** Whether to render pills inside the dropdown. Default: true. Tier 2 consumers who render their own pills should set this to false. */
154
+ showPills?: boolean;
126
155
  }
127
156
 
128
157
  declare const AIAutocomplete: react.ForwardRefExoticComponent<AIAutocompleteProps & react.RefAttributes<AIAutocompleteHandle>>;
129
158
 
130
- declare function AIAutocompleteDropdown({ suggestions, activeIndex, onSelect, onHighlight, isOpen, id, className, }: AIAutocompleteDropdownProps): react_jsx_runtime.JSX.Element;
159
+ declare function AIAutocompleteDropdown({ suggestions, activeIndex, onSelect, onHighlight, isOpen, id, className, pills, onPillClick, showPills, }: AIAutocompleteDropdownProps): react_jsx_runtime.JSX.Element;
131
160
 
132
- declare function useAIAutocomplete({ onSubmit, onError, optionOverrides, maskCompletedText, placeholder: customPlaceholder, apiConfig, columns, value: controlledValue, completedParams: controlledParams, onChange: onChangeProp, onParamsChange, }: UseAIAutocompleteOptions): UseAIAutocompleteReturn;
161
+ declare function useAIAutocomplete({ onSubmit, onError, optionOverrides, maskCompletedText, placeholder: customPlaceholder, apiConfig, columns, dropdownTrigger, value: controlledValue, completedParams: controlledParams, onChange: onChangeProp, onParamsChange, }: UseAIAutocompleteOptions): UseAIAutocompleteReturn;
133
162
 
134
- export { AIAutocomplete, AIAutocompleteDropdown, type AIAutocompleteDropdownProps, type AIAutocompleteHandle, type AIAutocompleteProps, type APIConfig, type AutocompleteResult, type CompletedParam, type CompletedParamState, type OptionOverrides, type Segment, type Suggestion, type SuggestionOption, type TaskKind, type UseAIAutocompleteOptions, type UseAIAutocompleteReturn, useAIAutocomplete };
163
+ export { AIAutocomplete, AIAutocompleteDropdown, type AIAutocompleteDropdownProps, type AIAutocompleteHandle, type AIAutocompleteProps, type APIConfig, type APIKeyConfig, type AccessTokenConfig, type AccessTokenResult, type AppearanceMode, type AutocompleteResult, type CompletedParam, type CompletedParamState, type OptionOverrides, type Segment, type Suggestion, type SuggestionOption, type TaskKind, type UseAIAutocompleteOptions, type UseAIAutocompleteReturn, useAIAutocomplete };
package/dist/index.d.ts CHANGED
@@ -39,17 +39,33 @@ type Segment = {
39
39
  value: string;
40
40
  param: CompletedParamState;
41
41
  };
42
+ type AppearanceMode = "light" | "dark" | "auto";
42
43
  interface AIAutocompleteHandle {
43
44
  focus: () => void;
44
45
  reset: () => void;
46
+ setMode: (mode: AppearanceMode) => void;
45
47
  }
46
- interface APIConfig {
48
+ interface APIConfigBase {
47
49
  endpoint?: string;
48
- apiKey?: string;
49
- authScheme?: "Bearer" | "Basic";
50
50
  appIdentifier?: string;
51
51
  headers?: Record<string, string>;
52
52
  }
53
+ interface APIKeyConfig extends APIConfigBase {
54
+ type?: "apiKey";
55
+ apiKey?: string;
56
+ authScheme?: "Bearer" | "Basic";
57
+ }
58
+ interface AccessTokenConfig extends APIConfigBase {
59
+ type: "accessToken";
60
+ accessToken?: string;
61
+ getAccessToken: () => Promise<AccessTokenResult>;
62
+ }
63
+ interface AccessTokenResult {
64
+ accessToken: string;
65
+ /** UNIX ms. If provided, SDK refreshes proactively 30s before expiry. */
66
+ expiresAt?: number;
67
+ }
68
+ type APIConfig = APIKeyConfig | AccessTokenConfig;
53
69
  type OptionOverrides = Record<string, (query: string) => SuggestionOption[]>;
54
70
  interface AIAutocompleteProps {
55
71
  onSubmit: (result: AutocompleteResult) => void;
@@ -60,8 +76,16 @@ interface AIAutocompleteProps {
60
76
  className?: string;
61
77
  apiConfig?: APIConfig;
62
78
  columns?: number;
63
- /** When true (default), reveal selected option text letter by letter. */
64
- typewriterEffect?: boolean;
79
+ /** Where to render unfilled pills. "inline" (default) renders them in the input; "dropdown" renders them above the options grid. */
80
+ pillPlacement?: "inline" | "dropdown" | "hidden";
81
+ /** Color mode. "auto" listens to prefers-color-scheme. Default: "auto" */
82
+ mode?: AppearanceMode;
83
+ /** Where the dropdown opens relative to the input. Default: "below" */
84
+ optionsPosition?: "above" | "below";
85
+ /** Enable all SDK animations (streak + shimmer). Default: true */
86
+ animations?: boolean;
87
+ /** When the dropdown appears. "auto" (default) = shows when options available. "manual" = only on pill tap, closes after selection. "hidden" = never shows. */
88
+ dropdownTrigger?: "auto" | "manual" | "hidden";
65
89
  value?: string;
66
90
  completedParams?: CompletedParamState[];
67
91
  onChange?: (value: string) => void;
@@ -80,8 +104,8 @@ interface UseAIAutocompleteOptions {
80
104
  placeholder?: string;
81
105
  apiConfig?: APIConfig;
82
106
  columns?: number;
83
- /** When true (default), reveal selected option text letter by letter. */
84
- typewriterEffect?: boolean;
107
+ /** When the dropdown appears. Default: "auto". */
108
+ dropdownTrigger?: "auto" | "manual" | "hidden";
85
109
  value?: string;
86
110
  completedParams?: CompletedParamState[];
87
111
  onChange?: (value: string) => void;
@@ -92,7 +116,6 @@ interface UseAIAutocompleteReturn {
92
116
  suggestionPills: Suggestion[];
93
117
  setActivePill: (index: number) => void;
94
118
  removeLastParam: () => void;
95
- reEditParam: (param: CompletedParamState) => void;
96
119
  reset: () => void;
97
120
  segments: Segment[];
98
121
  newParamId: string | null;
@@ -123,12 +146,18 @@ interface AIAutocompleteDropdownProps {
123
146
  isOpen: boolean;
124
147
  id: string;
125
148
  className?: string;
149
+ /** Pills to render inside the dropdown. Provided by `dropdownProps` from the hook. */
150
+ pills?: Suggestion[];
151
+ /** Called when a pill inside the dropdown is clicked. Provided by `dropdownProps` from the hook. */
152
+ onPillClick?: (index: number) => void;
153
+ /** Whether to render pills inside the dropdown. Default: true. Tier 2 consumers who render their own pills should set this to false. */
154
+ showPills?: boolean;
126
155
  }
127
156
 
128
157
  declare const AIAutocomplete: react.ForwardRefExoticComponent<AIAutocompleteProps & react.RefAttributes<AIAutocompleteHandle>>;
129
158
 
130
- declare function AIAutocompleteDropdown({ suggestions, activeIndex, onSelect, onHighlight, isOpen, id, className, }: AIAutocompleteDropdownProps): react_jsx_runtime.JSX.Element;
159
+ declare function AIAutocompleteDropdown({ suggestions, activeIndex, onSelect, onHighlight, isOpen, id, className, pills, onPillClick, showPills, }: AIAutocompleteDropdownProps): react_jsx_runtime.JSX.Element;
131
160
 
132
- declare function useAIAutocomplete({ onSubmit, onError, optionOverrides, maskCompletedText, placeholder: customPlaceholder, apiConfig, columns, value: controlledValue, completedParams: controlledParams, onChange: onChangeProp, onParamsChange, }: UseAIAutocompleteOptions): UseAIAutocompleteReturn;
161
+ declare function useAIAutocomplete({ onSubmit, onError, optionOverrides, maskCompletedText, placeholder: customPlaceholder, apiConfig, columns, dropdownTrigger, value: controlledValue, completedParams: controlledParams, onChange: onChangeProp, onParamsChange, }: UseAIAutocompleteOptions): UseAIAutocompleteReturn;
133
162
 
134
- export { AIAutocomplete, AIAutocompleteDropdown, type AIAutocompleteDropdownProps, type AIAutocompleteHandle, type AIAutocompleteProps, type APIConfig, type AutocompleteResult, type CompletedParam, type CompletedParamState, type OptionOverrides, type Segment, type Suggestion, type SuggestionOption, type TaskKind, type UseAIAutocompleteOptions, type UseAIAutocompleteReturn, useAIAutocomplete };
163
+ export { AIAutocomplete, AIAutocompleteDropdown, type AIAutocompleteDropdownProps, type AIAutocompleteHandle, type AIAutocompleteProps, type APIConfig, type APIKeyConfig, type AccessTokenConfig, type AccessTokenResult, type AppearanceMode, type AutocompleteResult, type CompletedParam, type CompletedParamState, type OptionOverrides, type Segment, type Suggestion, type SuggestionOption, type TaskKind, type UseAIAutocompleteOptions, type UseAIAutocompleteReturn, useAIAutocomplete };