@magicx-eng/ai-autocomplete-react 0.18.1 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,7 +18,8 @@ A React/TypeScript SDK that provides a guided AI-powered autocomplete experience
18
18
  - **Client-side filtering** — instant substring filtering on every keystroke
19
19
  - **Datepicker** — date parameters are answered with a calendar instead of an option list. Click a day or navigate with the arrow keys; the date is committed the way it would be written — `Tuesday` for a date inside the next week, `March 23` for one later this year, `March 23 2027` for another year. Tapping a committed date re-opens the calendar on the month that text names now — for a weekday name, that is the next such day, not the one originally picked. A parameter whose options are written as spans (`September 11 - October 13`) gets the same calendar answered in two clicks, committed as one range.
20
20
  - **Option overrides** — supply the options for a parameter yourself: a fixed list, a computed one, or one fetched from your own search endpoint as the user types
21
- - **Product strip (opt-in)** — plug in any platform's product search and the dropdown renders a horizontal row of product cards below the options; the SDK owns the UI, your integration owns only `fetch` and `transform`
21
+ - **Product strip** — the dropdown renders a horizontal row of product cards below the options, filled by default with the items the `/suggest` response reports as matching the query; plug in your own product search instead with `products`, or turn the strip off with `showProducts={false}`
22
+ - **Catalog report** — every response says which of the query's constraints the catalog applied, which it had to drop, and how many items are left, on `customFields` / `custom_fields`
22
23
  - **Controlled & uncontrolled** — works out of the box or integrates with external state
23
24
  - **Ref forwarding** — imperative `focus()`, `blur()`, `reset()`, and `setMode()` via ref
24
25
  - **Accessible** — ARIA combobox 1.2 pattern with `role="listbox"`, `aria-activedescendant`
@@ -357,17 +358,85 @@ function App() {
357
358
  | `completedParams?` | `CompletedParamState[]` | — | Controlled completed params. |
358
359
  | `onChange?` | `(value: string) => void` | — | Called when text changes (controlled mode). |
359
360
  | `onParamsChange?` | `(params: CompletedParamState[]) => void` | — | Called when params change (controlled mode). |
360
- | `products?` | `ProductsConfig` | | Opt-in product strip — see [Product strip](#product-strip). Omit it and nothing about the dropdown changes. |
361
+ | `showProducts?` | `boolean` | `true` | Render the product strip below the options whenever there are products to show — see [Product strip](#product-strip). Set to `false` to render no strip. |
362
+ | `products?` | `ProductsConfig` | — | Custom source for the product strip — see [Your own product search](#your-own-product-search-products). Omit it and the strip shows the items the `/suggest` response reports. |
361
363
  | `onProductSelect?` | `(product: Product) => void` | — | Called when a product card is activated. The SDK never navigates. |
362
364
  | `submitButton?` | `ReactNode` | — | Custom submit button. Pass any `ReactNode` to replace the default arrow button. Pass `null` to render no button. Clicks bubble up and trigger submit, so consumer-supplied buttons work without re-wiring `onClick`. |
363
365
  | `ref?` | `Ref<AIAutocompleteHandle>` | — | Imperative handle with `focus()`, `blur()`, `reset()`, and `setMode()`. |
364
366
 
365
367
  #### Product strip
366
368
 
367
- Opt in with `products` and the dropdown renders a horizontal row of product
368
- cards below the options grid. Every platform (Shopify today, others later) has
369
- its own search endpoint and its own response shape, so the SDK owns the UI and
370
- the integration owns only the fetching and the mapping.
369
+ The dropdown renders a horizontal row of product cards below the options grid
370
+ whenever it has products to show. By default those are the items the
371
+ `/suggest` response itself reports as matching the query no configuration,
372
+ no extra request: the cards land in the same state update as the suggestions,
373
+ so what the strip shows and what the pills offer always describe the same
374
+ narrowing.
375
+
376
+ ```tsx
377
+ <AIAutocomplete
378
+ onSubmit={handleSubmit}
379
+ // Selection emits — the SDK never navigates.
380
+ onProductSelect={(product) => product.url && router.push(product.url)}
381
+ />
382
+ ```
383
+
384
+ Each server item becomes a `Product` with `id` (the item's stable id), `title`,
385
+ `price` (the item's lowest price, verbatim as the catalog spells it — the
386
+ currency is yours to add), `vendor`, `imageUrl`, and a **relative** `url` of
387
+ `/products/{handle}` — the right link for a widget running on the storefront
388
+ itself. An item the catalog has no handle for gets no `url`: its card is still
389
+ activatable and still emits `onProductSelect`, it just has nothing for
390
+ cmd-click to open. `productFromMatchedItem` / `productsFromCustomFields` are
391
+ exported for a hand-rolled strip that wants the same mapping.
392
+
393
+ Pass `showProducts={false}` to render no strip at all. The catalog report
394
+ (below) stays readable either way.
395
+
396
+ #### The catalog report (`customFields`)
397
+
398
+ Every response that read the catalog also says what it did with the query. The
399
+ hook returns it as `customFields`, and it rides on `AutocompleteResult` as
400
+ `custom_fields`, so `onResult` on either tier carries it too:
401
+
402
+ ```tsx
403
+ const { customFields } = useAIAutocomplete({ ... });
404
+
405
+ // Or, on Tier 1:
406
+ <AIAutocomplete onResult={(result) => setReport(result.custom_fields)} />
407
+
408
+ {report && <p>{report.items.total} results</p>}
409
+ {report?.dropped_filters.length > 0 && (
410
+ // e.g. { param: "Color", value: "Chartreuse", op: "eq" } — the shopper asked
411
+ // for a colour no item carries, so the results ignore it. Say so.
412
+ <p>No match for {report.dropped_filters.map((f) => f.value).join(", ")}</p>
413
+ )}
414
+ ```
415
+
416
+ - `applied_filters` / `dropped_filters` — the constraints the server read out
417
+ of the query as `{ param, value, op }` (`op` is one of `eq`, `ne`, `lt`,
418
+ `lte`, `gt`, `gte`), split into the ones the catalog answered and the ones it
419
+ could not (a parameter it does not have, a bound that is not a number, a
420
+ value no item carries). The items answer the applied set, so the dropped
421
+ list is what tells "87 black t-shirts" from "no black item, so here are
422
+ t-shirts". Both are always arrays.
423
+ - `items.total` — how many items the applied filters left standing. It also
424
+ moves with the query's own words, so it drops as a typed word starts
425
+ matching a title. `items.matched` is the list the strip renders, capped by
426
+ the server; it is empty (with a non-zero `total`) when nothing narrowed the
427
+ catalog, where listing arbitrary items as matches would mislead.
428
+ - `null` (no `custom_fields` key on the result) means the response carried no
429
+ report — no catalog was read for it (a product with no catalog, the starting
430
+ state, a response with no option-bearing parameter to offer). That is "no
431
+ news", not an empty catalog. The strip clears on such a response rather than
432
+ keeping an older query's items.
433
+
434
+ #### Your own product search (`products`)
435
+
436
+ To source the cards from your own search instead — a platform with its own
437
+ endpoint and response shape — pass `products`. The SDK owns the UI; you own
438
+ only the fetching and the mapping. The server's items are then not rendered
439
+ (they stay readable on `customFields`).
371
440
 
372
441
  ```tsx
373
442
  import { AIAutocomplete, type Product } from "@magicx-eng/ai-autocomplete-react";
@@ -397,18 +466,17 @@ const products = {
397
466
  <AIAutocomplete
398
467
  onSubmit={handleSubmit}
399
468
  products={products}
400
- // Selection emits the SDK never navigates.
401
- onProductSelect={(product) => router.push(product.url)}
469
+ onProductSelect={(product) => product.url && router.push(product.url)}
402
470
  />;
403
471
  ```
404
472
 
405
- `Product` is exactly six fields; only `id`, `title` and `url` are required:
473
+ `Product` is exactly six fields; only `id` and `title` are required:
406
474
 
407
475
  ```ts
408
476
  type Product = {
409
477
  id: string;
410
478
  title: string;
411
- url: string;
479
+ url?: string; // absent = a card with no link (still activatable)
412
480
  imageUrl?: string | null;
413
481
  price?: string;
414
482
  vendor?: string;
@@ -432,13 +500,14 @@ type Product = {
432
500
  `dropdownTrigger` (`manual` / `hidden`) still gates the panel as before.
433
501
  - **Inline configs are safe.** `products={{ fetch, transform }}` written inline
434
502
  re-creates the object on every render; the hook forwards through a stable
435
- proxy, so only turning the strip on or off reaches the core. Two deliberate
436
- consequences: swapping one live config for another leaves the previous
437
- integration's cards up until the next request replaces them (toggle
438
- `products` off and back on to clear immediately), and turning the strip on
439
- mid-session shows nothing until the next `/suggest` request fires — the
440
- product search rides that one scheduler, and enabling the strip doesn't
441
- itself change the query.
503
+ proxy, so only setting or removing the config reaches the core. Two
504
+ deliberate consequences: swapping one live config for another leaves the
505
+ previous integration's cards up until the next request replaces them
506
+ (remove `products` and set it again to clear immediately), and setting a
507
+ config mid-session shows nothing until the next `/suggest` request fires —
508
+ the product search rides that one scheduler, and configuring it doesn't
509
+ itself change the query. Removing the config restores the server's items at
510
+ once.
442
511
 
443
512
  **Selection and links.** Cards are real `<a href>` elements, so cmd/ctrl-click,
444
513
  middle-click and "copy link address" behave natively. A plain left click is
@@ -446,7 +515,7 @@ intercepted (`preventDefault`) and emits `onProductSelect` instead.
446
515
 
447
516
  `product.url` is used verbatim — the SDK trusts your `transform` output and
448
517
  does not sanitise it, so validate the URL there if the platform response isn't
449
- fully under your control. (Angular additionally runs its own `[href]`
518
+ fully under your control. (Angular additionally runs its own `href`
450
519
  sanitiser, so unrecognised schemes like `myapp://…` are rewritten to
451
520
  `unsafe:…` in that package only.)
452
521
 
@@ -456,9 +525,10 @@ visible focus ring. Tabbing into the strip does not close the panel. The row
456
525
  scrolls horizontally by trackpad, wheel, touch and keyboard; the page never
457
526
  scrolls sideways and the scrollbar chrome is hidden in all engines.
458
527
 
459
- Tier 2 gets the same thing: pass `products` / `onProductSelect` to
460
- `useAIAutocomplete()` and the strip renders inside `<AIAutocompleteDropdown />`
461
- via `dropdownProps`. Tier 3 consumers read `products` from the hook and call
528
+ Tier 2 gets the same thing: `useAIAutocomplete()` takes `showProducts`,
529
+ `products` and `onProductSelect`, and the strip renders inside
530
+ `<AIAutocompleteDropdown />` via `dropdownProps`. Tier 3 consumers read
531
+ `products` (and `customFields`) from the hook and call
462
532
  `selectProduct(product)` from their own cards.
463
533
 
464
534
  #### Custom submit button
@@ -542,7 +612,8 @@ The headless hook for Tier 2 and Tier 3. Accepts the same options as `<AIAutocom
542
612
  | `isDropdownOpen` | `boolean` | Whether the dropdown should be visible. Drive your own dropdown's visibility with this in Tier 3. |
543
613
  | `placeholderText` | `string` | Suggested placeholder text for the current step. |
544
614
  | `error` | `Error \| null` | Last fetch error. |
545
- | `products` | `Product[]` | Results of the latest product search. Empty unless `products` is configured. Already spread into `dropdownProps` — read it directly only if you render your own strip. |
615
+ | `products` | `Product[]` | What the product strip shows: the items the latest response reported as matching the query, or the results of a custom `products` search. Already spread into `dropdownProps` — read it directly only if you render your own strip. |
616
+ | `customFields` | `CustomFieldsReport \| null` | The catalog report the latest response carried — applied and dropped filters, and the matching items' count and list — or `null` when it carried none. See [The catalog report](#the-catalog-report-customfields). |
546
617
 
547
618
  **Actions**
548
619
 
@@ -589,7 +660,7 @@ The dropdown component for Tier 2. Spread `dropdownProps` from the hook (and add
589
660
  | `pills?` | `Suggestion[]` | Pills to render inside the dropdown. |
590
661
  | `onPillClick?` | `(index: number) => void` | Called when a pill is clicked. |
591
662
  | `showPills?` | `boolean` | Whether to render pills. Default: `true`. |
592
- | `products?` | `Product[]` | Product cards to render below the options grid. Empty (the default) renders no strip. |
663
+ | `products?` | `Product[]` | Product cards to render below the options grid. Empty renders no strip. |
593
664
  | `onProductSelect?` | `(product: Product) => void` | Called when a card is activated. |
594
665
  | `onProductFocusChange?` | `(focused: boolean) => void` | Called when focus enters/leaves the strip. Wired to `setFocused` by `dropdownProps` so a tabbed-to card doesn't close the panel. |
595
666
  | `isLoading?` | `boolean` | When `true`, the dropdown renders its pills + options as a skeleton: text masked, layout (count and widths) preserved, shimmer pulse animating. Falls back to a generic 3-bar placeholder when no pills/options are cached. |
@@ -605,6 +676,7 @@ The structured query as the SDK currently understands it. The same object is pas
605
676
  | `completed_params` | `CompletedParam[]` | Filled parameter values in query order, followed by any the user skipped (see below). |
606
677
  | `identified_params` | `IdentifiedParam[]` | Parameters the server recognised in the user's own words, as `{ type, value }` — e.g. `{ type: "due_date", value: "Friday" }` when the user typed "by Friday" instead of picking a date. They are not tokenized in `raw_query`, and they are tentative: each response replaces the set, and an entry drops out as soon as its text is edited away. |
607
678
  | `is_ready` | `boolean` | Whether the server considers the query complete enough to act on. |
679
+ | `custom_fields?` | `CustomFieldsReport` | What catalog grounding did for the latest applied response, when it read a catalog at all — see [Product strip](#product-strip). `applied_filters` and `dropped_filters` list the query's constraints as `{ param, value, op }`; `items.total` is how many items the applied ones left standing and `items.matched` the ones the strip renders. Absent (no key) when the response carried no report. |
608
680
 
609
681
  #### Reading the query as it's built
610
682
 
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { AutocompleteResult, OptionOverrides, APIConfig, AppearanceMode, CompletedParamState, ProductsConfig, Product, Suggestion, SuggestionOption, FormatType, DateMonthView, DateRange, SkippedParamState, IdentifiedParamState, Segment } from '@magicx-eng/ai-autocomplete-vanilla';
2
- export { APIConfig, APIKeyConfig, AccessTokenConfig, AccessTokenResult, AppearanceMode, AutocompleteResult, CompletedParam, CompletedParamState, DATE_RANGE_META_END, DATE_RANGE_META_START, DateMonthView, DateRange, FormatType, IdentifiedParam, IdentifiedParamState, OptionOverrides, Product, ProductsConfig, Segment, SkippedParamState, SubmitResultExtras, Suggestion, SuggestionOption, TaskKind, WEEKDAY_LABELS, buildSubmitResult, cellDay, cellIso, dateCellMarks, formatDate, formatDateRange, isoDate, monthLabel, optionLabel, parseDate, parseLooseDate, parseLooseDateRange, sanitizeOptionIconSvg, selectedRangeFor, visibleDateRange, withSkippedParams } from '@magicx-eng/ai-autocomplete-vanilla';
1
+ import { AutocompleteResult, OptionOverrides, APIConfig, AppearanceMode, CompletedParamState, ProductsConfig, Product, Suggestion, SuggestionOption, FormatType, DateMonthView, DateRange, SkippedParamState, IdentifiedParamState, Segment, CustomFieldsReport } from '@magicx-eng/ai-autocomplete-vanilla';
2
+ export { APIConfig, APIKeyConfig, AccessTokenConfig, AccessTokenResult, AppearanceMode, AutocompleteResult, CompletedParam, CompletedParamState, CustomFieldsReport, DATE_RANGE_META_END, DATE_RANGE_META_START, DateMonthView, DateRange, ExpressedFilter, FilterOp, FormatType, IdentifiedParam, IdentifiedParamState, MatchedItem, MatchedItems, OptionOverrides, PRODUCT_PATH_PREFIX, Product, ProductsConfig, Segment, SkippedParamState, SubmitResultExtras, Suggestion, SuggestionOption, TaskKind, WEEKDAY_LABELS, buildSubmitResult, cellDay, cellIso, dateCellMarks, formatDate, formatDateRange, isoDate, monthLabel, optionLabel, parseDate, parseLooseDate, parseLooseDateRange, productFromMatchedItem, productsFromCustomFields, sanitizeOptionIconSvg, selectedRangeFor, visibleDateRange, withSkippedParams } from '@magicx-eng/ai-autocomplete-vanilla';
3
3
  import * as react from 'react';
4
4
  import { ReactNode, KeyboardEvent, ChangeEvent } from 'react';
5
5
  import * as react_jsx_runtime from 'react/jsx-runtime';
@@ -87,23 +87,33 @@ interface AIAutocompleteProps {
87
87
  onChange?: (value: string) => void;
88
88
  onParamsChange?: (params: CompletedParamState[]) => void;
89
89
  /**
90
- * Opt-in product strip. When set, the dropdown renders a horizontal row of
91
- * product cards below the options grid, fed by your own search endpoint on
92
- * the SDK's existing fetch cadence. Omit it and nothing about the dropdown
93
- * changes no request, no markup, no layout shift.
90
+ * When true (default), the dropdown renders a horizontal row of product
91
+ * cards below the options grid whenever it has products to show: by default
92
+ * the items the `/suggest` response reports as matching the query, or the
93
+ * results of a custom `products` search. Set to false to render no strip
94
+ * and run no custom search.
95
+ */
96
+ showProducts?: boolean;
97
+ /**
98
+ * Custom source for the product strip. Omit it and the strip shows the
99
+ * items the `/suggest` response itself reports as matching the query, with
100
+ * no extra request. Set it and the dropdown is fed by your own search
101
+ * endpoint on the SDK's existing fetch cadence instead; the server's items
102
+ * are then not rendered.
94
103
  *
95
104
  * The object may be re-created on every render: the hook forwards calls
96
- * through a stable proxy, so only toggling the strip on or off reaches the
97
- * core.
105
+ * through a stable proxy, so only setting or removing the config reaches
106
+ * the core.
98
107
  *
99
108
  * Two consequences of that proxy, both deliberate:
100
109
  * - Swapping one live config for another (A → B, both defined) can't be told
101
110
  * apart from a re-render, so A's cards stay on screen until the next
102
- * request replaces them. Toggle `products` off and back on to clear
111
+ * request replaces them. Remove `products` and set it again to clear
103
112
  * immediately.
104
- * - Turning the strip on mid-session shows nothing until the next `/suggest`
105
- * request fires, because the product search rides that one scheduler and
106
- * enabling the strip doesn't itself change the query.
113
+ * - Setting a custom config mid-session shows nothing until the next
114
+ * `/suggest` request fires, because the product search rides that one
115
+ * scheduler and configuring it doesn't itself change the query. Removing
116
+ * the config restores the server's items at once.
107
117
  */
108
118
  products?: ProductsConfig;
109
119
  /**
@@ -173,23 +183,33 @@ interface UseAIAutocompleteOptions {
173
183
  /** Called when the input loses focus. */
174
184
  onBlur?: () => void;
175
185
  /**
176
- * Opt-in product strip. When set, the dropdown renders a horizontal row of
177
- * product cards below the options grid, fed by your own search endpoint on
178
- * the SDK's existing fetch cadence. Omit it and nothing about the dropdown
179
- * changes no request, no markup, no layout shift.
186
+ * When true (default), the dropdown renders a horizontal row of product
187
+ * cards below the options grid whenever it has products to show: by default
188
+ * the items the `/suggest` response reports as matching the query, or the
189
+ * results of a custom `products` search. Set to false to render no strip
190
+ * and run no custom search.
191
+ */
192
+ showProducts?: boolean;
193
+ /**
194
+ * Custom source for the product strip. Omit it and the strip shows the
195
+ * items the `/suggest` response itself reports as matching the query, with
196
+ * no extra request. Set it and the dropdown is fed by your own search
197
+ * endpoint on the SDK's existing fetch cadence instead; the server's items
198
+ * are then not rendered.
180
199
  *
181
200
  * The object may be re-created on every render: the hook forwards calls
182
- * through a stable proxy, so only toggling the strip on or off reaches the
183
- * core.
201
+ * through a stable proxy, so only setting or removing the config reaches
202
+ * the core.
184
203
  *
185
204
  * Two consequences of that proxy, both deliberate:
186
205
  * - Swapping one live config for another (A → B, both defined) can't be told
187
206
  * apart from a re-render, so A's cards stay on screen until the next
188
- * request replaces them. Toggle `products` off and back on to clear
207
+ * request replaces them. Remove `products` and set it again to clear
189
208
  * immediately.
190
- * - Turning the strip on mid-session shows nothing until the next `/suggest`
191
- * request fires, because the product search rides that one scheduler and
192
- * enabling the strip doesn't itself change the query.
209
+ * - Setting a custom config mid-session shows nothing until the next
210
+ * `/suggest` request fires, because the product search rides that one
211
+ * scheduler and configuring it doesn't itself change the query. Removing
212
+ * the config restores the server's items at once.
193
213
  */
194
214
  products?: ProductsConfig;
195
215
  /**
@@ -258,11 +278,19 @@ interface UseAIAutocompleteReturn {
258
278
  listboxId: string;
259
279
  error: Error | null;
260
280
  /**
261
- * Results of the latest product search. Always empty unless `products` is
262
- * configured. Already spread into `dropdownProps` read it directly only if
281
+ * What the product strip shows: the items the latest `/suggest` response
282
+ * reported as matching the query, or the results of a custom `products`
283
+ * search. Already spread into `dropdownProps` — read it directly only if
263
284
  * you render your own strip.
264
285
  */
265
286
  products: Product[];
287
+ /**
288
+ * The catalog grounding report the latest response carried, or null when it
289
+ * carried none: which of the query's constraints the catalog applied, which
290
+ * it dropped, and how many items (and which) the applied ones left
291
+ * standing. Null is "no news", not an empty catalog.
292
+ */
293
+ customFields: CustomFieldsReport | null;
266
294
  /**
267
295
  * Announce a product selection (fires `onProductSelect`). The built-in
268
296
  * dropdown calls this for you; hand-rolled strips call it themselves. The SDK
@@ -422,6 +450,6 @@ declare const AIAutocomplete: react.ForwardRefExoticComponent<AIAutocompleteProp
422
450
 
423
451
  declare function AIAutocompleteDropdown({ suggestions, activeIndex, onSelect, onHighlight, isOpen, id, className, pills, onPillClick, showPills, onSkip, showSkipButton, showOptionIcons, skipDisabled, activeSelected, isLoading, isInputEmpty, products, onProductSelect, onProductFocusChange, formatType, dateView, selectedDateIso, selectedDateRange, dateRangeStart, onPreviousMonth, onNextMonth, optionsPosition, mode, }: AIAutocompleteDropdownProps): react_jsx_runtime.JSX.Element;
424
452
 
425
- declare function useAIAutocomplete({ onSubmit, onResult, onError, optionOverrides, maskCompletedText, apiConfig, additionalContext, columns, dropdownTrigger, optionsPosition, closeDropdownOnBlur, showNonTappableOptions, showOptionIcons, showSkipButton, onFocus, onBlur, value: controlledValue, completedParams: controlledParams, onChange: onChangeProp, onParamsChange, products, onProductSelect, source, setCursor, }: UseAIAutocompleteOptions): UseAIAutocompleteReturn;
453
+ declare function useAIAutocomplete({ onSubmit, onResult, onError, optionOverrides, maskCompletedText, apiConfig, additionalContext, columns, dropdownTrigger, optionsPosition, closeDropdownOnBlur, showNonTappableOptions, showOptionIcons, showSkipButton, showProducts, onFocus, onBlur, value: controlledValue, completedParams: controlledParams, onChange: onChangeProp, onParamsChange, products, onProductSelect, source, setCursor, }: UseAIAutocompleteOptions): UseAIAutocompleteReturn;
426
454
 
427
455
  export { AIAutocomplete, AIAutocompleteDropdown, type AIAutocompleteDropdownProps, type AIAutocompleteHandle, type AIAutocompleteProps, type UseAIAutocompleteOptions, type UseAIAutocompleteReturn, useAIAutocomplete };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { AutocompleteResult, OptionOverrides, APIConfig, AppearanceMode, CompletedParamState, ProductsConfig, Product, Suggestion, SuggestionOption, FormatType, DateMonthView, DateRange, SkippedParamState, IdentifiedParamState, Segment } from '@magicx-eng/ai-autocomplete-vanilla';
2
- export { APIConfig, APIKeyConfig, AccessTokenConfig, AccessTokenResult, AppearanceMode, AutocompleteResult, CompletedParam, CompletedParamState, DATE_RANGE_META_END, DATE_RANGE_META_START, DateMonthView, DateRange, FormatType, IdentifiedParam, IdentifiedParamState, OptionOverrides, Product, ProductsConfig, Segment, SkippedParamState, SubmitResultExtras, Suggestion, SuggestionOption, TaskKind, WEEKDAY_LABELS, buildSubmitResult, cellDay, cellIso, dateCellMarks, formatDate, formatDateRange, isoDate, monthLabel, optionLabel, parseDate, parseLooseDate, parseLooseDateRange, sanitizeOptionIconSvg, selectedRangeFor, visibleDateRange, withSkippedParams } from '@magicx-eng/ai-autocomplete-vanilla';
1
+ import { AutocompleteResult, OptionOverrides, APIConfig, AppearanceMode, CompletedParamState, ProductsConfig, Product, Suggestion, SuggestionOption, FormatType, DateMonthView, DateRange, SkippedParamState, IdentifiedParamState, Segment, CustomFieldsReport } from '@magicx-eng/ai-autocomplete-vanilla';
2
+ export { APIConfig, APIKeyConfig, AccessTokenConfig, AccessTokenResult, AppearanceMode, AutocompleteResult, CompletedParam, CompletedParamState, CustomFieldsReport, DATE_RANGE_META_END, DATE_RANGE_META_START, DateMonthView, DateRange, ExpressedFilter, FilterOp, FormatType, IdentifiedParam, IdentifiedParamState, MatchedItem, MatchedItems, OptionOverrides, PRODUCT_PATH_PREFIX, Product, ProductsConfig, Segment, SkippedParamState, SubmitResultExtras, Suggestion, SuggestionOption, TaskKind, WEEKDAY_LABELS, buildSubmitResult, cellDay, cellIso, dateCellMarks, formatDate, formatDateRange, isoDate, monthLabel, optionLabel, parseDate, parseLooseDate, parseLooseDateRange, productFromMatchedItem, productsFromCustomFields, sanitizeOptionIconSvg, selectedRangeFor, visibleDateRange, withSkippedParams } from '@magicx-eng/ai-autocomplete-vanilla';
3
3
  import * as react from 'react';
4
4
  import { ReactNode, KeyboardEvent, ChangeEvent } from 'react';
5
5
  import * as react_jsx_runtime from 'react/jsx-runtime';
@@ -87,23 +87,33 @@ interface AIAutocompleteProps {
87
87
  onChange?: (value: string) => void;
88
88
  onParamsChange?: (params: CompletedParamState[]) => void;
89
89
  /**
90
- * Opt-in product strip. When set, the dropdown renders a horizontal row of
91
- * product cards below the options grid, fed by your own search endpoint on
92
- * the SDK's existing fetch cadence. Omit it and nothing about the dropdown
93
- * changes no request, no markup, no layout shift.
90
+ * When true (default), the dropdown renders a horizontal row of product
91
+ * cards below the options grid whenever it has products to show: by default
92
+ * the items the `/suggest` response reports as matching the query, or the
93
+ * results of a custom `products` search. Set to false to render no strip
94
+ * and run no custom search.
95
+ */
96
+ showProducts?: boolean;
97
+ /**
98
+ * Custom source for the product strip. Omit it and the strip shows the
99
+ * items the `/suggest` response itself reports as matching the query, with
100
+ * no extra request. Set it and the dropdown is fed by your own search
101
+ * endpoint on the SDK's existing fetch cadence instead; the server's items
102
+ * are then not rendered.
94
103
  *
95
104
  * The object may be re-created on every render: the hook forwards calls
96
- * through a stable proxy, so only toggling the strip on or off reaches the
97
- * core.
105
+ * through a stable proxy, so only setting or removing the config reaches
106
+ * the core.
98
107
  *
99
108
  * Two consequences of that proxy, both deliberate:
100
109
  * - Swapping one live config for another (A → B, both defined) can't be told
101
110
  * apart from a re-render, so A's cards stay on screen until the next
102
- * request replaces them. Toggle `products` off and back on to clear
111
+ * request replaces them. Remove `products` and set it again to clear
103
112
  * immediately.
104
- * - Turning the strip on mid-session shows nothing until the next `/suggest`
105
- * request fires, because the product search rides that one scheduler and
106
- * enabling the strip doesn't itself change the query.
113
+ * - Setting a custom config mid-session shows nothing until the next
114
+ * `/suggest` request fires, because the product search rides that one
115
+ * scheduler and configuring it doesn't itself change the query. Removing
116
+ * the config restores the server's items at once.
107
117
  */
108
118
  products?: ProductsConfig;
109
119
  /**
@@ -173,23 +183,33 @@ interface UseAIAutocompleteOptions {
173
183
  /** Called when the input loses focus. */
174
184
  onBlur?: () => void;
175
185
  /**
176
- * Opt-in product strip. When set, the dropdown renders a horizontal row of
177
- * product cards below the options grid, fed by your own search endpoint on
178
- * the SDK's existing fetch cadence. Omit it and nothing about the dropdown
179
- * changes no request, no markup, no layout shift.
186
+ * When true (default), the dropdown renders a horizontal row of product
187
+ * cards below the options grid whenever it has products to show: by default
188
+ * the items the `/suggest` response reports as matching the query, or the
189
+ * results of a custom `products` search. Set to false to render no strip
190
+ * and run no custom search.
191
+ */
192
+ showProducts?: boolean;
193
+ /**
194
+ * Custom source for the product strip. Omit it and the strip shows the
195
+ * items the `/suggest` response itself reports as matching the query, with
196
+ * no extra request. Set it and the dropdown is fed by your own search
197
+ * endpoint on the SDK's existing fetch cadence instead; the server's items
198
+ * are then not rendered.
180
199
  *
181
200
  * The object may be re-created on every render: the hook forwards calls
182
- * through a stable proxy, so only toggling the strip on or off reaches the
183
- * core.
201
+ * through a stable proxy, so only setting or removing the config reaches
202
+ * the core.
184
203
  *
185
204
  * Two consequences of that proxy, both deliberate:
186
205
  * - Swapping one live config for another (A → B, both defined) can't be told
187
206
  * apart from a re-render, so A's cards stay on screen until the next
188
- * request replaces them. Toggle `products` off and back on to clear
207
+ * request replaces them. Remove `products` and set it again to clear
189
208
  * immediately.
190
- * - Turning the strip on mid-session shows nothing until the next `/suggest`
191
- * request fires, because the product search rides that one scheduler and
192
- * enabling the strip doesn't itself change the query.
209
+ * - Setting a custom config mid-session shows nothing until the next
210
+ * `/suggest` request fires, because the product search rides that one
211
+ * scheduler and configuring it doesn't itself change the query. Removing
212
+ * the config restores the server's items at once.
193
213
  */
194
214
  products?: ProductsConfig;
195
215
  /**
@@ -258,11 +278,19 @@ interface UseAIAutocompleteReturn {
258
278
  listboxId: string;
259
279
  error: Error | null;
260
280
  /**
261
- * Results of the latest product search. Always empty unless `products` is
262
- * configured. Already spread into `dropdownProps` read it directly only if
281
+ * What the product strip shows: the items the latest `/suggest` response
282
+ * reported as matching the query, or the results of a custom `products`
283
+ * search. Already spread into `dropdownProps` — read it directly only if
263
284
  * you render your own strip.
264
285
  */
265
286
  products: Product[];
287
+ /**
288
+ * The catalog grounding report the latest response carried, or null when it
289
+ * carried none: which of the query's constraints the catalog applied, which
290
+ * it dropped, and how many items (and which) the applied ones left
291
+ * standing. Null is "no news", not an empty catalog.
292
+ */
293
+ customFields: CustomFieldsReport | null;
266
294
  /**
267
295
  * Announce a product selection (fires `onProductSelect`). The built-in
268
296
  * dropdown calls this for you; hand-rolled strips call it themselves. The SDK
@@ -422,6 +450,6 @@ declare const AIAutocomplete: react.ForwardRefExoticComponent<AIAutocompleteProp
422
450
 
423
451
  declare function AIAutocompleteDropdown({ suggestions, activeIndex, onSelect, onHighlight, isOpen, id, className, pills, onPillClick, showPills, onSkip, showSkipButton, showOptionIcons, skipDisabled, activeSelected, isLoading, isInputEmpty, products, onProductSelect, onProductFocusChange, formatType, dateView, selectedDateIso, selectedDateRange, dateRangeStart, onPreviousMonth, onNextMonth, optionsPosition, mode, }: AIAutocompleteDropdownProps): react_jsx_runtime.JSX.Element;
424
452
 
425
- declare function useAIAutocomplete({ onSubmit, onResult, onError, optionOverrides, maskCompletedText, apiConfig, additionalContext, columns, dropdownTrigger, optionsPosition, closeDropdownOnBlur, showNonTappableOptions, showOptionIcons, showSkipButton, onFocus, onBlur, value: controlledValue, completedParams: controlledParams, onChange: onChangeProp, onParamsChange, products, onProductSelect, source, setCursor, }: UseAIAutocompleteOptions): UseAIAutocompleteReturn;
453
+ declare function useAIAutocomplete({ onSubmit, onResult, onError, optionOverrides, maskCompletedText, apiConfig, additionalContext, columns, dropdownTrigger, optionsPosition, closeDropdownOnBlur, showNonTappableOptions, showOptionIcons, showSkipButton, showProducts, onFocus, onBlur, value: controlledValue, completedParams: controlledParams, onChange: onChangeProp, onParamsChange, products, onProductSelect, source, setCursor, }: UseAIAutocompleteOptions): UseAIAutocompleteReturn;
426
454
 
427
455
  export { AIAutocomplete, AIAutocompleteDropdown, type AIAutocompleteDropdownProps, type AIAutocompleteHandle, type AIAutocompleteProps, type UseAIAutocompleteOptions, type UseAIAutocompleteReturn, useAIAutocomplete };