@magicx-eng/ai-autocomplete-angular
v0.5.0
Published
AI Autocomplete Angular SDK — guided autocomplete with pill-based input and dropdown suggestions
Downloads
1,820
Maintainers
Readme
@magicx-eng/ai-autocomplete-angular
An Angular 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.
Status: v0.1 — first release. Built for Angular 15+. The published artifact uses ng-packagr's partial-Ivy (APF) output, which is forward-compatible with newer Angular majors. Smoke-tested on Angular 15 consumer apps today; matrix expansion to 16 / 17 / 18 / 19 is planned for v0.2.
Features
- Three tiers of integration — the full
<ai-autocomplete>component,AIAutocompleteController+[aiaInput]+<ai-autocomplete-dropdown>(our dropdown, your input), or the controller alone (render the dropdown yourself) - Rich inline input (Tier 1) — a single
contentEditablesurface: typed text, bold completed params, and inline pills share one editing context - Pill-based input — non-editable inline pills for unfilled parameters, bold inline text for completed ones
- Instant exact-match bolding — typing the full text of an option immediately promotes it to a completed param (no debounced fetch wait). Works in the normal typing flow and while re-editing an existing completed param.
- Re-edit completed params — tap a bold completed param to replace it; the dropdown re-opens with the cached options the server originally returned for that param
- Pill placement — render pills inline in the input or inside the dropdown
- Light/dark mode — built-in themes with
prefers-color-schemesupport, fully overridable via CSS variables - Inherits your font — defaults to the host page's font, with
--aia-font-familyto pin a specific font on the library - Access token auth — short-lived tokens with automatic refresh, single-flight deduplication, and 401 retry
- Keyboard navigation — arrow keys, enter to submit, tab to autocomplete, backspace to un-bold the last completed param
- IME-safe — composition events are buffered so input text is committed once, after composition ends
- Client-side filtering — instant substring filtering on every keystroke
- Option overrides — inject or dynamically generate client-side options per suggestion type
- Controlled & uncontrolled — works out of the box or integrates with external state, including Reactive Forms via
ControlValueAccessor - Imperative handle —
focus(),blur(),reset(), andsetMode()via@ViewChild - Accessible — ARIA combobox 1.2 pattern with
role="listbox",aria-activedescendant - Animations — option selection streak animation, text shimmer on newly added params
- Loading skeleton — while a fetch is in flight, the dropdown and inline pills keep the previous layout (same count and widths) with their text masked and a shimmer pulse
- Lightweight — styles bundled via ng-packagr; no global style import required
- TypeScript first — full type definitions shipped with the package
Installation
pnpm add @magicx-eng/ai-autocomplete-angularPeer Dependencies
pnpm add @angular/common @angular/core @angular/forms rxjsThe package works on Angular 15 and up. RxJS 7 or later is required.
Three Tiers
| Tier | What you get | What you own | Use when |
|---|---|---|---|
| Tier 1: Full | <ai-autocomplete> — input, dropdown, pills, state | Nothing — drop in and go | You want a complete widget with zero setup |
| Tier 2: Controller + dropdown | AIAutocompleteController + [aiaInput] + <ai-autocomplete-dropdown> | The input element and its layout | You need a custom input but want our dropdown UI |
| Tier 3: Headless | AIAutocompleteController alone — observables + actions | Everything, including the dropdown | You need full control over every piece of the UI |
Tier 1: Full Component
Drop-in component that owns the editor, pills, dropdown, and all state:
import { Component } from "@angular/core";
import {
AIAutocompleteComponent,
type AutocompleteResult,
} from "@magicx-eng/ai-autocomplete-angular";
@Component({
standalone: true,
selector: "app-search",
imports: [AIAutocompleteComponent],
template: `
<ai-autocomplete
[apiConfig]="apiConfig"
class="my-autocomplete"
(submitted)="handleSubmit($event)"
></ai-autocomplete>
`,
})
export class SearchComponent {
apiConfig = { endpoint: "https://api.example.com/ac/suggest", apiKey: "your_api_key" };
handleSubmit(result: AutocompleteResult): void {
console.log(result.query); // "Create a email"
console.log(result.raw_query); // "Create a {{TASK_1}}"
console.log(result.completed_params); // [{ placeholder: "{{TASK_1}}", type: "task", ... }]
}
}Controlled Mode
Use [value] / [completedParams] with their Change outputs, or hook into Reactive Forms.
Two-way binding:
<ai-autocomplete
[(value)]="text"
[(completedParams)]="params"
(submitted)="handleSubmit($event)"
></ai-autocomplete>Reactive Forms (ControlValueAccessor):
import { FormControl, ReactiveFormsModule } from "@angular/forms";
import { AIAutocompleteComponent } from "@magicx-eng/ai-autocomplete-angular";
@Component({
standalone: true,
imports: [AIAutocompleteComponent, ReactiveFormsModule],
template: `
<ai-autocomplete [apiConfig]="apiConfig" [formControl]="query"></ai-autocomplete>
`,
})
export class SearchComponent {
query = new FormControl("");
apiConfig = { endpoint: "...", apiKey: "..." };
}Imperative Handle
The component exposes focus(), blur(), reset(), and setMode() as public instance methods — reach them through @ViewChild:
import { Component, ViewChild } from "@angular/core";
import { AIAutocompleteComponent } from "@magicx-eng/ai-autocomplete-angular";
@Component({
standalone: true,
imports: [AIAutocompleteComponent],
template: `
<ai-autocomplete #ac [apiConfig]="apiConfig" (submitted)="onSubmit($event)"></ai-autocomplete>
<button (click)="ac.focus()">Focus</button>
<button (click)="ac.setMode('dark')">Dark mode</button>
`,
})
export class SearchComponent {
@ViewChild("ac") ac!: AIAutocompleteComponent;
// ac.focus() / ac.blur() / ac.reset() / ac.setMode("dark" | "light" | "auto")
}Focus Control
The component auto-focuses the contentEditable editor on mount. To opt out, pass [autoFocus]="false". Listen to focus changes with (focused) / (blurred):
<ai-autocomplete
[autoFocus]="false"
(focused)="onFocus()"
(blurred)="onBlur()"
(submitted)="handleSubmit($event)"
></ai-autocomplete>Backspace into a completed param
Pressing Backspace while the caret is inside or immediately after a bold completed param drops the param's "completed" status and removes one grapheme before the caret. The remaining text stays in the editor as plain (un-bold) text so the user can keep editing instead of losing the whole phrase.
Re-edit a completed param
Tapping a bold completed param enters re-edit mode — the dropdown re-opens with the cached options the server originally returned for that param. From there:
- Typing atomically replaces the bold with what you type. If what you type exactly matches one of the cached options, it's re-promoted to a bold completed param immediately.
- Clicking an option replaces the bold with the new selection.
- Arrow keys, Escape, or clicking outside the param exit re-edit mode without changing anything.
After a completed param is added (by any means — option click, exact-match typing, or re-edit), the caret always lands right after the trailing space following the bold so typing can continue immediately.
Tier 2: Controller + Dropdown
Drive state with the controller and render our dropdown; you own the input element and layout:
import { Component, OnDestroy, OnInit } from "@angular/core";
import {
AIAutocompleteController,
AIAutocompleteDropdownComponent,
AIAutocompleteInputDirective,
type AutocompleteResult,
} from "@magicx-eng/ai-autocomplete-angular";
@Component({
standalone: true,
selector: "app-search",
imports: [AIAutocompleteDropdownComponent, AIAutocompleteInputDirective],
template: `
<textarea [aiaInput]="ac" placeholder="Ask anything..."></textarea>
<!-- mode styles + themes the dropdown on its own — no .magicx-aia wrapper -->
<ai-autocomplete-dropdown [controller]="ac" mode="auto"></ai-autocomplete-dropdown>
`,
})
export class SearchComponent implements OnInit, OnDestroy {
ac!: AIAutocompleteController;
ngOnInit(): void {
this.ac = new AIAutocompleteController({
apiConfig: { apiKey: "..." },
onSubmit: (result) => {
this.handleSubmit(result);
this.ac.reset(); // start a new session
},
});
}
ngOnDestroy(): void {
this.ac.destroy(); // release resources
}
private handleSubmit(_result: AutocompleteResult) { /* ... */ }
}Always call
reset()after handling submit. It clears the input and rotates the per-sessionsession_id. This applies whether the submit was triggered by Enter, a custom button, or any other mechanism.
Always call
destroy()inngOnDestroy. The controller owns RxJS subscriptions and a vanilla-core instance; failing to dispose leaks them.
The [aiaInput] directive implements ControlValueAccessor, so it works with Reactive Forms out of the box:
<textarea [aiaInput]="ac" [formControl]="myControl"></textarea>
<ai-autocomplete-dropdown [controller]="ac" mode="auto"></ai-autocomplete-dropdown>Color mode & position
Pass [mode] ("light" | "dark" | "auto") to a standalone <ai-autocomplete-dropdown> and it self-scopes the SDK's design tokens to its own root — no .magicx-aia wrapper needed ("auto" follows prefers-color-scheme). To open the dropdown above the input, set optionsPosition: "above" in the controller options — the dropdown reads it from the controller automatically (via dropdown$), and arrow-key direction matches. Leave mode unset inside Tier 1, which themes for you.
Custom / rich-text inputs
[aiaInput] is for a <textarea>/<input>. For a contentEditable or rich-text editor, skip the directive and call the controller directly: handleTextChange(text) on every edit, setFocused(bool) on focus/blur, handleKeyDown(event) for Arrow/Enter/Tab/Escape while the dropdown is open, and handleCaretMove(offset) so arrow keys can move into the dropdown.
Tier 3: Headless
Skip <ai-autocomplete-dropdown> and render the suggestions UI yourself. Subscribe to the controller's dropdown$ (or the individual observables) for the data and call selectOption() / setActiveDropdownIndex() for the actions:
@Component({
standalone: true,
selector: "app-search",
imports: [CommonModule, AIAutocompleteInputDirective],
template: `
<textarea [aiaInput]="ac" placeholder="Ask anything..."></textarea>
<ng-container *ngIf="ac.dropdown$ | async as dd">
<ul class="my-dropdown" role="listbox" *ngIf="dd.isOpen">
<li
*ngFor="let option of dd.suggestions[0]?.options ?? []; let i = index"
role="option"
[attr.aria-selected]="i === dd.activeIndex"
(mouseenter)="ac.setActiveDropdownIndex(i)"
(mousedown)="$event.preventDefault(); ac.selectOption(option)"
>
{{ option.text }}
</li>
</ul>
</ng-container>
`,
})
export class SearchComponent implements OnInit, OnDestroy {
ac!: AIAutocompleteController;
ngOnInit() { this.ac = new AIAutocompleteController({ apiConfig: { apiKey: "..." } }); }
ngOnDestroy() { this.ac.destroy(); }
}Convenience NgModule
For codebases that prefer the older NgModule pattern, an AIAutocompleteModule re-exports every public component and directive:
import { NgModule } from "@angular/core";
import { AIAutocompleteModule } from "@magicx-eng/ai-autocomplete-angular";
@NgModule({ imports: [AIAutocompleteModule], /* ... */ })
export class AppModule {}Standalone-first apps don't need this — import the components/directive directly.
API Reference
<ai-autocomplete>
| Input | Type | Default | Description |
|---|---|---|---|
| apiConfig | APIConfig | — | Runtime API configuration (see below). |
| optionOverrides | Record<string, (query: string) => SuggestionOption[]> | — | Override options per suggestion type. |
| maskCompletedText | boolean | false | When true, omits completed params' literal text from API requests (for masking PII/sensitive values from the server). |
| columns | number | 2 | Number of columns in the dropdown grid. |
| pillPlacement | "inline" \| "dropdown" \| "hidden" | "dropdown" | Where to render unfilled pills. "hidden" hides pills entirely. |
| mode | "light" \| "dark" \| "auto" | "auto" | Color mode. "auto" follows prefers-color-scheme. |
| optionsPosition | "above" \| "below" | "below" | Where the dropdown opens relative to the input. |
| animations | boolean | true | Enable/disable all SDK animations (streak + shimmer). |
| dropdownTrigger | "auto" \| "manual" \| "hidden" | "auto" | When the dropdown appears. "auto" = when options available. "manual" = only on pill tap, closes after selection. "hidden" = never shows. |
| closeDropdownOnBlur | boolean | true | When true, the dropdown closes if the input loses focus. Set to false to keep it open whenever options are available, regardless of focus. |
| showNonTappableOptions | boolean | true | When true, non-tappable options are rendered alongside tappable ones in the dropdown. Set to false to hide non-tappable options entirely. |
| autoFocus | boolean | true | Focus the input on mount. Set to false to leave focus to the consumer. |
| value | string | — | Controlled text value. Pair with (valueChange) or use [(value)]. |
| completedParams | CompletedParamState[] | — | Controlled completed params. Pair with (completedParamsChange) or use [(completedParams)]. |
| submitButton | TemplateRef<unknown> \| null | — | Custom submit button. Pass a <ng-template> 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. |
| Output | Payload | Description |
|---|---|---|
| submitted | AutocompleteResult | Fires on Enter or submit-button click. |
| errored | Error | Fires when a fetch fails. |
| valueChange | string | Two-way binding partner for [value]. |
| completedParamsChange | CompletedParamState[] | Two-way binding partner for [completedParams]. |
| focused | void | The input gained focus. |
| blurred | void | The input lost focus. |
Imperative methods (via @ViewChild):
| Method | Description |
|---|---|
| focus() | Focus the editor. |
| blur() | Blur the editor. |
| reset() | Clear all state, re-fetch, and start a new session (rotates session_id). |
| setMode(mode) | Switch color mode at runtime. |
Custom submit button
<ng-template #goBtn>
<button class="my-button">Go</button>
</ng-template>
<ai-autocomplete
[submitButton]="goBtn"
(submitted)="handleSubmit($event)"
></ai-autocomplete>
<!-- Or hide the button entirely: -->
<ai-autocomplete [submitButton]="null" (submitted)="handleSubmit($event)"></ai-autocomplete>APIConfig
A discriminated union: APIKeyConfig | AccessTokenConfig.
API Key Mode (default)
{ apiKey: "your_api_key", authScheme: "Bearer", endpoint: "/ac/suggest" }| Field | Type | Description |
|---|---|---|
| type? | "apiKey" | Optional discriminator. Default when omitted. |
| apiKey? | string | API key for Authorization header. |
| authScheme? | "Bearer" \| "Basic" | Auth header scheme. Default: "Bearer". |
| endpoint? | string | Full URL for the suggest endpoint. Default: "https://api.ai-autocomplete.com/api/suggest". |
| appIdentifier? | string | Value for the X-App-Identifier header. |
| headers? | Record<string, string> | Additional headers merged into every request. |
Access Token Mode
<ai-autocomplete
[apiConfig]="apiConfig"
(submitted)="handleSubmit($event)"
></ai-autocomplete>apiConfig: AccessTokenConfig = {
type: "accessToken",
getAccessToken: async () => {
const res = await fetch("/api/ac-token");
const { access_token, expires_at } = await res.json();
return { accessToken: access_token, expiresAt: expires_at };
},
};| Field | Type | Description |
|---|---|---|
| type | "accessToken" | Required discriminator. |
| getAccessToken | () => Promise<AccessTokenResult> | Required. Called when the SDK needs a token. |
| accessToken? | string | Initial token. Avoids one round-trip on mount. |
| endpoint? | string | Suggest endpoint URL. Default: "https://api.ai-autocomplete.com/api/suggest". |
| appIdentifier? | string | Value for the X-App-Identifier header. |
| headers? | Record<string, string> | Additional headers merged into every request. |
The SDK handles token refresh transparently: 401 → getAccessToken → retry (once). Concurrent 401s share a single refresh. Tokens refresh proactively 30s before expiresAt.
AIAutocompleteController (Tier 2)
The headless logic surface for Tier 2 — the Angular equivalent of React's useAIAutocomplete() hook.
const ac = new AIAutocompleteController({
apiConfig: { ... },
onSubmit: (result) => { ... },
});
// ... wire up [aiaInput] and <ai-autocomplete-dropdown> in your template ...
// On teardown:
ac.destroy();Constructor options
Accepts the same shape as <ai-autocomplete>'s inputs (apiConfig, optionOverrides, columns, dropdownTrigger, optionsPosition, closeDropdownOnBlur, showNonTappableOptions), plus event callbacks (onSubmit, onError, onChange, onParamsChange, onFocus, onBlur) and controlled-mode initial values (value, completedParams).
State observables
| Observable | Type | Description |
|---|---|---|
| text$ | Observable<string> | Editor's plain-text content. |
| completedParams$ | Observable<CompletedParamState[]> | Bold completed params currently in the editor. |
| suggestionPills$ | Observable<Suggestion[]> | Actionable suggestion pills (first item is the active pill). |
| segments$ | Observable<Segment[]> | Text + completed-param segments for rendering the editor body. |
| newParamId$ | Observable<string \| null> | ID of the most-recently-promoted param (drives shimmer animation). |
| suggestions$ | Observable<Suggestion[]> | All suggestions from the server (including non-actionable ones). |
| activeIndex$ | Observable<number> | Currently highlighted dropdown option index. -1 = none. |
| isLoading$ | Observable<boolean> | UI-visible loading flag (suppressed during selection animation and re-edit). |
| isReady$ | Observable<boolean> | Server-reported "query is complete" flag. |
| isFocused$ | Observable<boolean> | Whether the editor currently has focus. |
| isDropdownOpen$ | Observable<boolean> | Whether the dropdown should currently be visible. |
| placeholderText$ | Observable<string> | Server-suggested placeholder text for the editor. |
| error$ | Observable<Error \| null> | Last fetch error, or null. |
| editingParam$ | Observable<CompletedParamState \| null> | Param currently being re-edited, or null. |
| editingAnchor$ | Observable<number \| null> | Plain-text offset where the edit region starts. |
| caretOffset$ | Observable<number \| null> | Live caret offset within the editor. |
| state$ | Observable<AIAutocompleteControllerState> | Composite snapshot of every public field. |
| dropdown$ | Observable<AIAutocompleteDropdownState> | Composite snapshot for the dropdown (suggestions, activeIndex, isOpen, pills, optionsPosition, …). Bound to <ai-autocomplete-dropdown>, or subscribe directly to render your own dropdown in Tier 3. |
Every observable is gated by distinctUntilChanged + shareReplay({ bufferSize: 1, refCount: true }), so multiple async-pipe subscriptions share one upstream subscription and dedupe identical emissions.
Action methods
| Method | Description |
|---|---|
| reset() | Clear all state, rotate session_id, and re-fetch. Call after submit. |
| selectOption(option) | Pick a dropdown option (or replace the re-edited param if in re-edit mode). |
| setActivePill(index) | Promote the pill at index to active. |
| removeLastParam() | Drop the last completed param's "completed" status. |
| clearNewParamId() | Clear the shimmer-animation state. |
| setValue(text) | Controlled-mode setter for editor text. |
| setCompletedParams(params) | Controlled-mode setter for completed params. |
| handleTextChange(value) | Forward plain-text input from your <textarea> (the [aiaInput] directive does this for you). |
| handleKeyDown(event) | Forward keyboard events (also handled by [aiaInput]). |
| setFocused(focused) | Notify the controller of focus state (custom inputs only — [aiaInput] does this). |
| handleCaretMove(offset) | Report the caret position (plain-text offset) so arrow keys can move into the dropdown (custom inputs only). |
| setActiveDropdownIndex(index) | Set the highlighted option index (typically driven by hover). |
| update(opts) | Apply runtime changes to a subset of options (everything except event callbacks). |
| destroy() | Release all resources. Must be called in ngOnDestroy. |
Getters
| Field | Type | Description |
|---|---|---|
| listboxId | string | Stable ARIA listbox ID. |
| destroyed | boolean | Whether destroy() has been called. |
| getState() | AIAutocompleteControllerState | Synchronous snapshot of the current state. |
[aiaInput] directive
Wires a <textarea> (or <input>) to an AIAutocompleteController:
<textarea [aiaInput]="controller" placeholder="..."></textarea>The directive automatically:
- Sets
role="combobox",aria-autocomplete="list",aria-controls,aria-expanded,aria-activedescendantfor the WAI-ARIA combobox 1.2 pattern. - Forwards
input/keydown/focus/blurto the controller. - Auto-capitalizes the first character of the user's first keystroke (matching the SDK's UX). Skipped during IME composition.
- Mirrors the controller's
text$back into the textarea (so programmaticcontroller.setValue(...)updates the DOM), preserving the cursor position when possible. - Implements
ControlValueAccessor— use[formControl]or[(ngModel)]directly.
<ai-autocomplete-dropdown>
The dropdown component for Tier 2:
<ai-autocomplete-dropdown [controller]="ac" mode="auto"></ai-autocomplete-dropdown>| Input | Type | Default | Description |
|---|---|---|---|
| controller | AIAutocompleteController | — | The shared controller. |
| showPills | boolean | true | Whether to render pills above the options grid. |
| mode | "light" \| "dark" \| "auto" | — | Color mode for a standalone dropdown — self-scopes the SDK tokens so no .magicx-aia wrapper is needed. "auto" follows prefers-color-scheme. Leave unset when nested inside a .magicx-aia ancestor (e.g. Tier 1). |
| className | string | — | Extra CSS class applied to the dropdown root. |
Placement (optionsPosition) is read from the controller's options via dropdown$ — set it once on the controller, not on the dropdown. All actions (option selection, hover, pill clicks) are forwarded to the controller — no outputs to wire.
AutocompleteResult
| Field | Type | Description |
|---|---|---|
| query | string | Plain text as the user sees it. |
| raw_query | string | Text with placeholder tokens (e.g. "Create a {{TASK_1}}"). |
| completed_params | CompletedParam[] | Filled parameter values. |
CSS Customization
Styles are bundled with the package; the published library wires its CSS up automatically. Built-in light and dark defaults apply based on [mode].
CSS Variables
Override on the host element (via class). All defaults use :where() (zero specificity) — your overrides always win.
| Variable | Light | Dark | Description |
|---|---|---|---|
| --aia-font-family | inherit | inherit | Font used by the library. Defaults to inherit so the library picks up your page's font automatically. Set this to pin a specific font on the library without changing the surrounding page. |
| --aia-pill-bg | #bdbdbd | #bdbdbd | Pill background |
| --aia-pill-color | #000000 | #ffffff | Pill text |
| --aia-pill-font-size | 19px | 19px | Pill font size |
| --aia-option-bg | transparent | transparent | Highlighted option background |
| --aia-option-color | #000000 | #ffffff | Option text |
| --aia-option-color-selected | #000000 | #ffffff | Highlighted option text |
| --aia-option-font-size | 19px | 19px | Option font size |
| --aia-written-text-color | #000000 | #ffffff | Input text |
| --aia-written-text-font-size | 19px | 19px | Input text font size |
| --aia-caret-color | --aia-written-text-color | --aia-written-text-color | Editor caret color. Override independently of input text color. |
| --aia-submit-bg | #000000 | #ffffff | Submit button background |
| --aia-submit-color | #ffffff | #000000 | Submit button icon color |
| --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. |
| --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). |
| --aia-streak-rgb | 99, 102, 241 | 255, 255, 255 | Comma-separated RGB triplet used to tint the option-selection streak animation. |
| --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. |
| --aia-skeleton-bg | rgba(189, 189, 189, 0.51) | #333539 | Fill color for the loading skeleton bars and masked text in cached pills/options. |
Per-mode Overrides
.my-autocomplete[data-mode="light"] {
--aia-pill-bg: #e2e8f0;
}
.my-autocomplete[data-mode="dark"] {
--aia-pill-bg: #334155;
}Selector Hooks
For styling beyond the CSS variables, target these stable data-aia-* attributes (component-internal class names are not part of the public API):
| Attribute | Element |
|---|---|
| [data-aia-editor] | Editor area wrapping the contentEditable + inline pill list |
| [data-aia-input] | The Tier 1 contentEditable <div> that owns typed text and bold completed params |
| [data-aia-pill-list-container] | Inline sibling of the editor that holds unfilled-suggestion pills |
| [data-aia-submit] | Submit button |
| [data-aia-pill] | Each unfilled-suggestion pill |
| [data-aia-pillbar] | Pill bar container inside the dropdown |
| [data-aia-option] | Each suggestion option |
| [data-aia-dropdown] | The dropdown root (listbox) |
Completed params render as inline <strong> elements inside the editor. Override their weight with [data-aia-input] strong { font-weight: 700; }.
/* Solid (non-glass) dropdown */
.my-autocomplete [data-aia-dropdown] {
background: #fff;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
backdrop-filter: none;
}Sessions
Every /api/suggest request carries a meta.session_id UUID. A session runs from mount (or the last reset()) until the next reset(). All requests in one session share the same session_id; calling reset() starts a new one.
The contract is simple: after the user submits the query, call reset(). That clears the input and rotates session_id so the next session begins clean.
Why it matters: the server uses
session_idto track each session's history — what the user has been typing sequentially, which options they've selected, and how the query evolved. That context lets the model produce better, more relevant suggestions on subsequent requests within the same session.
- Tier 1
<ai-autocomplete>does this automatically — it callsreset()for you aftersubmittedfires, for both Enter-key and built-in-button submits. - Tier 2
AIAutocompleteController— you own the submit flow, so callcontroller.reset()from youronSubmithandler (see the Tier 2 example above) or from your custom button after firing your submit handler.
Option Overrides
@Component({
template: `
<ai-autocomplete
[apiConfig]="apiConfig"
[optionOverrides]="overrides"
(submitted)="handleSubmit($event)"
></ai-autocomplete>
`,
})
export class SearchComponent {
overrides = {
account: () => [
{ text: "Savings", is_tappable: true, kind: null },
{ text: "Checking", is_tappable: true, kind: null },
],
value: (query: string) => {
const digits = query.replace(/\D/g, "");
if (!digits) return [{ text: "$100", is_tappable: true, kind: null }];
return [{ text: `$${digits}`, is_tappable: true, kind: null }];
},
};
}Server-side rendering
The package is render-blocking-safe — the Tier 1 component renders a static shell during SSR / Angular Universal, and the live autocomplete only mounts on the client. For Tier 2 (new AIAutocompleteController(...)), gate construction with isPlatformBrowser:
import { Inject, PLATFORM_ID } from "@angular/core";
import { isPlatformBrowser } from "@angular/common";
constructor(@Inject(PLATFORM_ID) private platformId: object) {
if (isPlatformBrowser(platformId)) {
this.ac = new AIAutocompleteController({ ... });
}
}License
MIT.
