@neuronection/assistant-ui 0.12.1 → 0.13.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # @neuronection/assistant-ui
2
2
 
3
+ ## 0.13.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`aff5f8d`](https://github.com/neuronection/assistant-ui/commit/aff5f8df4834e704a0d329ed621a3fcac6df96ad) Thanks [@constLiakos](https://github.com/constLiakos)! - UX clarity from live testing: `ModelRegistry` gains `capsHint` (muted explainer under the capability chips in the catalog modal), and `TaskAssignmentPicker` renders a visible uppercase fallback marker before the secondary picker so the primary/fallback pair is self-explanatory.
8
+
9
+ - [`aff5f8d`](https://github.com/neuronection/assistant-ui/commit/aff5f8df4834e704a0d329ed621a3fcac6df96ad) Thanks [@constLiakos](https://github.com/constLiakos)! - `TaskAssignmentPicker` marks the primary/fallback pickers with compact badges (`primaryLabel`, default "Primary"; the fallback uses `secondaryLabel`) and optional `primaryInfo`/`fallbackInfo` popups (via the library InfoButton) explaining when each is used.
10
+
11
+ - [`ab46892`](https://github.com/neuronection/assistant-ui/commit/ab468925f37d6bc4a56c91cff4541fab8f8ace22) Thanks [@constLiakos](https://github.com/constLiakos)! - `TaskAssignmentPicker` defaults rows use a column layout: the Primary and Fallback badges act as column headers with their pickers aligned beneath (fixed widths keep the columns stuck across rows); override rows render a single unbadged picker; the task title block can no longer be squeezed out by the action columns.
12
+
13
+ - [`67fed2e`](https://github.com/neuronection/assistant-ui/commit/67fed2ea4a85b96c46cbdf524fcc7590ad7675f8) Thanks [@constLiakos](https://github.com/constLiakos)! - `TaskAssignmentPicker` fallback rows get their own clear button (visible when a fallback is assigned) alongside the primary one — each row's X clears exactly its own assignment.
14
+
15
+ - [`77913e3`](https://github.com/neuronection/assistant-ui/commit/77913e369c260bad9370a1a93376d45efebb3305) Thanks [@constLiakos](https://github.com/constLiakos)! - `TaskAssignmentPicker` info buttons are always visible and their popups open on click only — no hover-triggered description popups while moving across the rows.
16
+
17
+ - [`783ae31`](https://github.com/neuronection/assistant-ui/commit/783ae317463213a084ab95f8d558bf18d5d1af7f) Thanks [@constLiakos](https://github.com/constLiakos)! - `TaskAssignmentPicker` defaults cards stack the pickers in rows instead of columns — the fallback picker sits on the first row, the primary picker (with the assignment clear button) on the second; override rows keep a single unbadged picker row.
18
+
19
+ ## 0.13.0
20
+
21
+ ### Minor Changes
22
+
23
+ - [`f684e2c`](https://github.com/neuronection/assistant-ui/commit/f684e2cece799d89c97de2b334315373e0dacbbf) Thanks [@constLiakos](https://github.com/constLiakos)! - `TaskAssignmentPicker` row polish: every task tile falls back to a default `Cpu` icon, the duplicated "Model: provider / model" status line is removed (the row pickers show the selection themselves), the pickers no longer carry an internal clear button (the row-level X is the single remove affordance, vertically centered), accessible names are now just the task label, and `unassignedLabel`/`modelLabel` props are gone. New `@neuronection/assistant-ui/fuzzy` subpath exports `beautifyId` (display-name beautifier) alongside `fuzzyScore`/`searchScore`.
24
+
25
+ ### Patch Changes
26
+
27
+ - [`f684e2c`](https://github.com/neuronection/assistant-ui/commit/f684e2cece799d89c97de2b334315373e0dacbbf) Thanks [@constLiakos](https://github.com/constLiakos)! - `Combobox`/`ModelPicker` gain `hideLabel` — keeps the accessible name derived from `label` while suppressing the visible label element, for surfaces where the surrounding component already titles the row (e.g. `TaskAssignmentPicker` rows).
28
+
3
29
  ## 0.12.1
4
30
 
5
31
  ### Patch Changes
package/README.md CHANGED
@@ -60,36 +60,38 @@ dialogs clear your app chrome. Full guide: [docs/theming.md](./docs/theming.md).
60
60
 
61
61
  Every module has its own entry point (`import { Menu } from
62
62
  '@neuronection/assistant-ui/menu'`) and everything is re-exported from the
63
- package root. Authoritative props for your installed version: the `.d.ts`
64
- files in the package's `dist/`. Visual reference:
65
- [gallery](https://neuronection.github.io/assistant-ui/).
66
-
67
- | Module | Exports |
68
- |---|---|
69
- | `button` / `badge` / `card` | `Button`, `buttonVariants` · `Badge`, `badgeVariants` · `Card`, `CardHeader/Title/Description/Content/Footer` |
70
- | `modal` / `panel-modal` | `Modal` + parts (compound Radix dialog) · `PanelModal` (header/body/footer shell, full-screen on mobile) |
71
- | `confirmation-modal` / `form-modal` | `ConfirmationModal` · `FormModal` (form-in-modal shell: `headerActions`, reject button, busy states) |
72
- | `popover` / `popover-button` | compound Radix `Popover` + parts · `PopoverButton` (self-contained trigger + panel, hover-open, lazy children, `closeSignal`) |
73
- | `menu` / `context-menu` | `Menu`, `MenuTrigger/Content/Item/Separator/Label`, `ActionMenu` (items-driven) · `ContextMenu` (coordinate-anchored, `{x, y, items, onClose}`) |
74
- | `combobox` | `Combobox`, `ComboboxMulti` async mode (`onSearchChange` + `loading`), grouping, capability badges, full keyboard nav |
75
- | `tooltip` | `Tooltip` + parts, `InfoTooltip` |
76
- | `wizard` | `Wizard` (steps config + `renderStep`, per-step validation gates, modal/drawer variants), `Stepper` (dots/labels) |
77
- | `input` / `textarea` / `search-input` / `expandable-search` | `Input` (label/hint/error wiring) · `Textarea` · `SearchInput` · `ExpandableSearch` |
78
- | `chip-input` / `chip-list` | `ChipInput` (Enter/comma commit, paste-splits, Backspace-removes) · `ChipList` (variant pills, clickable, removable) |
79
- | `time-picker` / `time-list` | `TimePicker` (clock face, 24h value, 12h UI, editable fields) · `TimeList` (time chips with per-chip pickers) |
80
- | `date-picker` | `DatePicker` (popover calendar: days/months/years views, min/max, `allowClear`, arrow-key day grid, `unstyled` variant) |
81
- | `range-bar` / `scale-slider` | `RangeBar` (low–high band + value dot) · `ScaleSlider` (+ `scaleColorForValue`) |
82
- | `table` | `Table` (headers/rows, opt-in empty state) |
83
- | `check-indicator` / `selection-bar` / `view-toggle` | `CheckIndicator` (tri-state) · `SelectionBar` (bulk-select bar) · `ViewToggle` (grid/list) |
84
- | `error-banner` / `undo-notice` / `empty-state` / `spinner` | presentational feedback: alert banner with `action` slot · undo toast · `EmptyState` · `Spinner` |
85
- | `info-button` / `field-label` / `copy-button` / `breadcrumbs` | `InfoButton` · `FieldLabel` · `CopyButton` (clipboard + copied state) · `Breadcrumbs` (SPA links via `linkComponent`) |
86
- | `ai-button` / `ai-actions-dropdown` / `ai-magic-fill` | AI affordances: ask-with-suggestions (controlled open, icon-only mode) · action menu + custom prompt · describe-in-words fill modal API calls stay app-side |
87
- | `settings-shell` / `provider-form` / `connection-test-row` | `SettingsShell` (controlled two-pane nav) · `ProviderForm` (write-only API-key field) · `ConnectionTestRow` (idle/testing/ok/fail + latency, `inline` variant + meta slot) |
88
- | `model-picker` / `task-assignment-picker` / `capability-chips` | `ModelPicker` (grouped provider→model combobox, capability chips) · `TaskAssignmentPicker` (task model mapping rows, capability-filtered, sections + fallback assignment, meta slot) · `CapabilityChips` (cap toggle group / badges, min-selected) |
89
- | `model-registry` | `ModelRegistry` (provider cards registered model rows + remote catalog with two-stage add: quick-add or expandable draft panel with editable label/caps/reasoning-effort; cap filter + search + add-all; add/edit are one panel) |
90
- | `upload-dropzone` / `file-card` / `file-queue` | file attachment surface: dropzone (block/row) · card (status, include toggle) · queue (summary + reorder) |
91
- | `marquee` | `useMarquee`, `MarqueeSurface`, `MarqueeBand` (rubber-band selection) |
92
- | `portal` / `theme-scope` / `tokens` | `Portal` · `ThemeScope` · token name lists + types |
63
+ package root. Each module links a reference page under
64
+ [docs/components/](./docs/components/) below; the `.d.ts` files in the
65
+ package's `dist/` stay the authoritative API for your installed version.
66
+ Visual reference: [gallery](https://neuronection.github.io/assistant-ui/).
67
+
68
+ | Module | Exports | Gallery |
69
+ |---|---|---|
70
+ | [`button`](./docs/components/button.md) / [`badge`](./docs/components/badge.md) / [`card`](./docs/components/card.md) | `Button`, `buttonVariants` · `Badge`, `badgeVariants` · `Card`, `CardHeader/Title/Description/Content/Footer` | [stories](https://neuronection.github.io/assistant-ui/?story=button--variants) |
71
+ | [`modal`](./docs/components/modal.md) (`Modal` + parts, `PanelModal`) | `Modal` + parts (compound Radix dialog) · `PanelModal` (header/body/footer shell, full-screen on mobile) | [stories](https://neuronection.github.io/assistant-ui/?story=modal--sizes) |
72
+ | [`confirmation-modal`](./docs/components/confirmation-modal.md) / [`form-modal`](./docs/components/form-modal.md) | `ConfirmationModal` · `FormModal` (form-in-modal shell: `headerActions`, reject button, busy states) | [stories](https://neuronection.github.io/assistant-ui/?story=modal--confirmation) |
73
+ | [`popover`](./docs/components/popover.md) / [`popover-button`](./docs/components/popover-button.md) | compound Radix `Popover` + parts · `PopoverButton` (self-contained trigger + panel, hover-open, lazy children, `closeSignal`) | [stories](https://neuronection.github.io/assistant-ui/?story=popover-button--default) |
74
+ | [`menu`](./docs/components/menu.md) / [`context-menu`](./docs/components/context-menu.md) | `Menu`, `MenuTrigger/Content/Item/Separator/Label`, `ActionMenu` (items-driven) · `ContextMenu` (coordinate-anchored, `{x, y, items, onClose}`) | [stories](https://neuronection.github.io/assistant-ui/?story=menu--compound-menu) |
75
+ | [`combobox`](./docs/components/combobox.md) | `Combobox`, `ComboboxMulti` async mode (`onSearchChange` + `loading`), grouping, capability badges, full keyboard nav | [stories](https://neuronection.github.io/assistant-ui/?story=combobox--single-select) |
76
+ | [`tooltip`](./docs/components/tooltip.md) | `Tooltip` + parts, `InfoTooltip` | [stories](https://neuronection.github.io/assistant-ui/?story=overlay--tooltips) |
77
+ | [`wizard`](./docs/components/wizard.md) | `Wizard` (steps config + `renderStep`, per-step validation gates, modal/drawer variants), `Stepper` (dots/labels) | [stories](https://neuronection.github.io/assistant-ui/?story=wizard--modal-wizard) |
78
+ | [`input`](./docs/components/input.md) / [`textarea`](./docs/components/textarea.md) / [`search-input`](./docs/components/search-input.md) / [`expandable-search`](./docs/components/expandable-search.md) | `Input` (label/hint/error wiring) · `Textarea` · `SearchInput` · `ExpandableSearch` | [stories](https://neuronection.github.io/assistant-ui/?story=misc--inputs) |
79
+ | [`chip-input`](./docs/components/chip-input.md) / [`chip-list`](./docs/components/chip-list.md) | `ChipInput` (Enter/comma commit, paste-splits, Backspace-removes) · `ChipList` (variant pills, clickable, removable) | [stories](https://neuronection.github.io/assistant-ui/?story=phase2--chip-editing) |
80
+ | [`time-picker`](./docs/components/time-picker.md) / [`time-list`](./docs/components/time-list.md) | `TimePicker` (clock face, 24h value, 12h UI, editable fields) · `TimeList` (time chips with per-chip pickers) | [stories](https://neuronection.github.io/assistant-ui/?story=time-pickers--time-picker-story) |
81
+ | [`date-picker`](./docs/components/date-picker.md) | `DatePicker` (popover calendar: days/months/years views, min/max, `allowClear`, arrow-key day grid, `unstyled` variant) | [stories](https://neuronection.github.io/assistant-ui/?story=phase2--date-pickers) |
82
+ | [`range-bar`](./docs/components/range-bar.md) / [`scale-slider`](./docs/components/scale-slider.md) | `RangeBar` (low–high band + value dot) · `ScaleSlider` (+ `scaleColorForValue`) | [stories](https://neuronection.github.io/assistant-ui/?story=phase2--range-bars) |
83
+ | [`table`](./docs/components/table.md) | `Table` (headers/rows, opt-in empty state) | [stories](https://neuronection.github.io/assistant-ui/?story=phase2--data-table) |
84
+ | [`check-indicator`](./docs/components/check-indicator.md) / [`selection-bar`](./docs/components/selection-bar.md) / [`view-toggle`](./docs/components/view-toggle.md) | `CheckIndicator` (tri-state) · `SelectionBar` (bulk-select bar) · `ViewToggle` (grid/list) | [stories](https://neuronection.github.io/assistant-ui/?story=controls--selection) |
85
+ | [`error-banner`](./docs/components/error-banner.md) / [`undo-notice`](./docs/components/undo-notice.md) / [`empty-state`](./docs/components/empty-state.md) / [`spinner`](./docs/components/spinner.md) | presentational feedback: alert banner with `action` slot · undo toast · `EmptyState` · `Spinner` | [stories](https://neuronection.github.io/assistant-ui/?story=feedback--errors) |
86
+ | [`info-button`](./docs/components/info-button.md) / [`field-label`](./docs/components/field-label.md) / [`copy-button`](./docs/components/copy-button.md) / [`breadcrumbs`](./docs/components/breadcrumbs.md) | `InfoButton` · `FieldLabel` · `CopyButton` (clipboard + copied state) · `Breadcrumbs` (SPA links via `linkComponent`) | [stories](https://neuronection.github.io/assistant-ui/?story=phase2--copy-buttons) |
87
+ | [`ai-button`](./docs/components/ai-button.md) / [`ai-actions-dropdown`](./docs/components/ai-actions-dropdown.md) / [`ai-magic-fill`](./docs/components/ai-magic-fill.md) | AI affordances: ask-with-suggestions (controlled open, icon-only mode) · action menu + custom prompt · describe-in-words fill modal API calls stay app-side | [stories](https://neuronection.github.io/assistant-ui/?story=ai-patterns--ai-buttons) |
88
+ | [`settings-shell`](./docs/components/settings-shell.md) / [`provider-form`](./docs/components/provider-form.md) / [`connection-test-row`](./docs/components/connection-test-row.md) | `SettingsShell` (controlled two-pane nav) · `ProviderForm` (write-only API-key field) · `ConnectionTestRow` (idle/testing/ok/fail + latency, `inline` variant + meta slot) | [stories](https://neuronection.github.io/assistant-ui/?story=settings-trio--provider-form-story) |
89
+ | [`model-picker`](./docs/components/model-picker.md) / [`task-assignment-picker`](./docs/components/task-assignment-picker.md) / [`capability-chips`](./docs/components/capability-chips.md) | `ModelPicker` (grouped provider→model combobox, capability chips) · `TaskAssignmentPicker` (task model mapping rows, capability-filtered, sections + fallback assignment, meta slot) · `CapabilityChips` (cap toggle group / badges, min-selected) | [stories](https://neuronection.github.io/assistant-ui/?story=model-assignment--task-assignment-v2-story) |
90
+ | [`model-registry`](./docs/components/model-registry.md) | `ModelRegistry` (provider cards registered model rows; add/edit in one catalog modal fed by app-fetched remote models; manual id entry; add-all of pending ids) | [stories](https://neuronection.github.io/assistant-ui/?story=model-registry--model-registry-story) |
91
+ | [`upload-dropzone`](./docs/components/upload-dropzone.md) / [`file-card`](./docs/components/file-card.md) / [`file-queue`](./docs/components/file-queue.md) | file attachment surface: dropzone (block/row) · card (status, include toggle) · queue (summary + reorder) | [stories](https://neuronection.github.io/assistant-ui/?story=file-surface--dropzones) |
92
+ | [`marquee`](./docs/components/marquee.md) | `useMarquee`, `MarqueeSurface`, `MarqueeBand` (rubber-band selection) | [stories](https://neuronection.github.io/assistant-ui/?story=marquee--default) |
93
+ | [`portal`](./docs/components/portal.md) / [`theme-scope`](./docs/components/theme-scope.md) / tokens | `Portal` · `ThemeScope` · [token name lists + types](./docs/guides/utilities.md#tokens-entry-point) | [stories](https://neuronection.github.io/assistant-ui/?story=tokens--semantic-tokens) |
94
+ | [`logo`](./docs/components/logo.md) / [`about`](./docs/components/about.md) | `NeuronectionMark`, `NeuronectionWordmark`, `Career/Study/HealthMark` · `AboutPanel` + about-page building blocks (`AboutCard`, `AboutLinkList`, `FamilyBadge`, `SponsorCard`, …) | [stories](https://neuronection.github.io/assistant-ui/?story=about--health-about-page) |
93
95
 
94
96
  API rules: controlled-first, `className` merges (never replaces), `asChild`
95
97
  where it makes sense, refs forwarded everywhere, English label props with
@@ -98,6 +100,21 @@ no router — data in, events out.
98
100
 
99
101
  ## Documentation
100
102
 
103
+ Guides (`docs/guides/`):
104
+
105
+ - [Import & theming](./docs/guides/import-and-theming.md) — import paths
106
+ (subpath vs barrel vs app shims), CSS load order, `--as-*` tokens,
107
+ `data-as-*` styling hooks, Vite setup checklist
108
+ - [Utilities](./docs/guides/utilities.md) — `searchScore`/`fuzzyScore`
109
+ ranking model, `cn`, the tokens entry point
110
+ - [AI settings surfaces](./docs/guides/ai-settings.md) — the providers →
111
+ models → tasks recipe (`ProviderForm`, `ConnectionTestRow`,
112
+ `ModelRegistry`, `TaskAssignmentPicker`) with real app call-sites
113
+
114
+ Per-component reference (`docs/components/<module>.md`): purpose, import
115
+ line, props table, controlled contract, i18n contract, snippets,
116
+ accessibility notes — every module in the table above links its page.
117
+
101
118
  - [Adopting in an app](./docs/adoption.md) — the install/switchover recipe each family app follows (incl. the TW3 cascade notes)
102
119
  - [Theming](./docs/theming.md) — tokens, app themes, dark mode, styling hooks
103
120
  - [Accessibility](./docs/accessibility.md) — the keyboard + ARIA contract per module (test-asserted)
@@ -1,3 +1,4 @@
1
+ import { searchScore } from './chunk-B2G66OSQ.js';
1
2
  import { Spinner } from './chunk-INTST5VG.js';
2
3
  import { cn } from './chunk-BLJWR4ZV.js';
3
4
  import * as React from 'react';
@@ -5,139 +6,6 @@ import * as PopoverPrimitive from '@radix-ui/react-popover';
5
6
  import { ChevronDown, X, Search, Check } from 'lucide-react';
6
7
  import { jsxs, jsx } from 'react/jsx-runtime';
7
8
 
8
- // src/lib/fuzzy.ts
9
- var SEPARATORS = /* @__PURE__ */ new Set(["-", "_", ".", "/", " ", ":", "@"]);
10
- var BOUNDARY_START = 8;
11
- var BOUNDARY_SEPARATOR = 6;
12
- var CONSECUTIVE_STEP = 4;
13
- var EXACT_MATCH = 100;
14
- var SUBSTRING_MATCH = 80;
15
- var TOKEN_SUBSTRING = 60;
16
- var SUBSEQUENCE_BASE = 30;
17
- var TYPO_BASE = 20;
18
- function fuzzyScore(query, target) {
19
- if (query === "") {
20
- return 0;
21
- }
22
- const q = query.toLowerCase();
23
- const t = target.toLowerCase();
24
- if (q === t) {
25
- return EXACT_MATCH + q.length;
26
- }
27
- const substringIndex = t.indexOf(q);
28
- if (substringIndex !== -1) {
29
- let bonus = SUBSTRING_MATCH;
30
- if (substringIndex === 0) bonus += BOUNDARY_START;
31
- else if (SEPARATORS.has(t[substringIndex - 1] ?? "")) bonus += BOUNDARY_SEPARATOR;
32
- return bonus + q.length;
33
- }
34
- let score = SUBSEQUENCE_BASE;
35
- let matched = false;
36
- for (const token of q.split(/\s+/).filter(Boolean)) {
37
- const tokenScore = subsequenceScore(token, t);
38
- if (tokenScore === null) {
39
- return null;
40
- }
41
- score += tokenScore;
42
- matched = true;
43
- }
44
- if (!matched) {
45
- return null;
46
- }
47
- return score;
48
- }
49
- function subsequenceScore(query, target) {
50
- if (target.includes(query)) {
51
- let bonus = TOKEN_SUBSTRING;
52
- const index = target.indexOf(query);
53
- if (index === 0) bonus += BOUNDARY_START;
54
- else if (SEPARATORS.has(target[index - 1] ?? "")) bonus += BOUNDARY_SEPARATOR;
55
- return bonus;
56
- }
57
- let score = 0;
58
- let qi = 0;
59
- let consecutive = 0;
60
- let prevIndex = -2;
61
- for (let ti = 0; ti < target.length && qi < query.length; ti += 1) {
62
- if (target[ti] !== query[qi]) {
63
- continue;
64
- }
65
- let bonus = 0;
66
- if (ti === 0) {
67
- bonus += BOUNDARY_START;
68
- } else if (SEPARATORS.has(target[ti - 1] ?? "")) {
69
- bonus += BOUNDARY_SEPARATOR;
70
- }
71
- if (ti === prevIndex + 1) {
72
- consecutive += 1;
73
- bonus += CONSECUTIVE_STEP * consecutive;
74
- } else {
75
- consecutive = 0;
76
- }
77
- score += 1 + bonus;
78
- prevIndex = ti;
79
- qi += 1;
80
- }
81
- if (qi < query.length) {
82
- return null;
83
- }
84
- return score;
85
- }
86
- function typoDistance(a, b, max = 2) {
87
- if (Math.abs(a.length - b.length) > max) {
88
- return null;
89
- }
90
- let previous = Array.from({ length: b.length + 1 }, (_, index) => index);
91
- for (let i = 1; i <= a.length; i += 1) {
92
- const current = [i];
93
- let rowMin = i;
94
- for (let j = 1; j <= b.length; j += 1) {
95
- const substitution = previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1);
96
- const insertion = previous[j] + 1;
97
- const deletion = current[j - 1] + 1;
98
- let value = Math.min(substitution, insertion, deletion);
99
- if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
100
- value = Math.min(value, previous[j - 2] + 1);
101
- }
102
- current[j] = value;
103
- if (value < rowMin) {
104
- rowMin = value;
105
- }
106
- }
107
- if (rowMin > max) {
108
- return null;
109
- }
110
- previous = current;
111
- }
112
- return previous[b.length] ?? null;
113
- }
114
- function searchScore(query, target) {
115
- const direct = fuzzyScore(query, target);
116
- if (direct !== null) {
117
- return direct;
118
- }
119
- const q = query.toLowerCase().trim();
120
- const t = target.toLowerCase();
121
- if (q.length < 3) {
122
- return null;
123
- }
124
- const allowed = q.length >= 6 ? 2 : 1;
125
- const whole = typoDistance(q, t, allowed);
126
- if (whole !== null) {
127
- return TYPO_BASE + q.length - whole;
128
- }
129
- let best = null;
130
- for (const word of t.split(/[^a-z0-9.]+/).filter(Boolean)) {
131
- const distance = typoDistance(q, word, allowed);
132
- if (distance !== null) {
133
- const score = TYPO_BASE + q.length - distance + BOUNDARY_SEPARATOR;
134
- if (best === null || score > best) {
135
- best = score;
136
- }
137
- }
138
- }
139
- return best;
140
- }
141
9
  function defaultFilter(options, term) {
142
10
  const needle = term.trim();
143
11
  if (!needle) {
@@ -371,6 +239,7 @@ var Combobox = React.forwardRef(function Combobox2({
371
239
  onSearchChange,
372
240
  onOpenChange,
373
241
  label,
242
+ hideLabel = false,
374
243
  error,
375
244
  id: idProp,
376
245
  className,
@@ -395,7 +264,7 @@ var Combobox = React.forwardRef(function Combobox2({
395
264
  }
396
265
  };
397
266
  return /* @__PURE__ */ jsxs("div", { className: cn("flex w-full flex-col gap-1.5", className), children: [
398
- label ? /* @__PURE__ */ jsx("label", { htmlFor: id, className: "text-sm font-medium leading-none text-[var(--as-fg)]", children: label }) : null,
267
+ label && !hideLabel ? /* @__PURE__ */ jsx("label", { htmlFor: id, className: "text-sm font-medium leading-none text-[var(--as-fg)]", children: label }) : null,
399
268
  /* @__PURE__ */ jsxs(PopoverPrimitive.Root, { modal: true, open, onOpenChange: handleOpenChange, children: [
400
269
  /* @__PURE__ */ jsxs("span", { className: "relative inline-flex w-full", children: [
401
270
  /* @__PURE__ */ jsx(PopoverPrimitive.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
@@ -595,5 +464,5 @@ var ComboboxMulti = React.forwardRef(
595
464
  );
596
465
 
597
466
  export { Combobox, ComboboxMulti };
598
- //# sourceMappingURL=chunk-LC7CYUY4.js.map
599
- //# sourceMappingURL=chunk-LC7CYUY4.js.map
467
+ //# sourceMappingURL=chunk-AATZUVW4.js.map
468
+ //# sourceMappingURL=chunk-AATZUVW4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/combobox/Combobox.tsx"],"names":["Combobox","ComboboxMulti"],"mappings":"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","file":"chunk-AATZUVW4.js","sourcesContent":["import * as React from 'react'\nimport * as PopoverPrimitive from '@radix-ui/react-popover'\nimport { Check, ChevronDown, Search, X } from 'lucide-react'\nimport { cn } from '../../lib/utils'\nimport { searchScore } from '../../lib/fuzzy'\nimport { Spinner } from '../spinner/Spinner'\n\nexport interface ComboboxOption {\n value: string\n label: string\n description?: string\n group?: string\n badge?: string\n disabled?: boolean\n}\n\nfunction defaultFilter(options: ComboboxOption[], term: string): ComboboxOption[] {\n const needle = term.trim()\n if (!needle) {\n return options\n }\n const scored = options.flatMap((option) => {\n let best: number | null = null\n for (const target of [option.label, option.value, option.description ?? '']) {\n const score = searchScore(needle, target)\n if (score !== null && (best === null || score > best)) {\n best = score\n }\n }\n return best === null ? [] : [{ option, score: best as number }]\n })\n scored.sort(\n (a, b) => b.score - a.score || a.option.label.localeCompare(b.option.label),\n )\n return scored.map((entry) => entry.option)\n}\n\ninterface ComboboxListProps {\n id: string\n options: ComboboxOption[]\n activeIndex: number\n onActiveIndexChange: (index: number) => void\n selected: (value: string) => boolean\n onPick: (option: ComboboxOption) => void\n emptyLabel?: string\n loading?: boolean\n loadingLabel?: string\n listClassName?: string\n}\n\nfunction ComboboxList({\n id,\n options,\n activeIndex,\n onActiveIndexChange,\n selected,\n onPick,\n emptyLabel = 'No matches',\n loading = false,\n loadingLabel = 'Loading…',\n listClassName,\n}: ComboboxListProps) {\n const rows: React.ReactNode[] = []\n let currentGroup: string | undefined\n options.forEach((option, index) => {\n if (option.group !== undefined && option.group !== currentGroup) {\n currentGroup = option.group\n rows.push(\n <div\n key={`group-${option.group}-${index}`}\n role=\"presentation\"\n className=\"px-2 pb-0.5 pt-2 text-xs font-medium tracking-wide text-[var(--as-muted-fg)] uppercase\"\n >\n {option.group}\n </div>,\n )\n }\n const isSelected = selected(option.value)\n const isActive = index === activeIndex\n rows.push(\n <div\n key={`${option.value}-${index}`}\n id={`${id}-opt-${index}`}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled || undefined}\n data-state={isSelected ? 'checked' : 'unchecked'}\n data-active={isActive || undefined}\n tabIndex={-1}\n className={cn(\n 'flex cursor-pointer items-center justify-between gap-2 rounded-[calc(var(--as-radius-sm)-2px)] px-2 py-1.5 text-sm text-[var(--as-fg)] outline-none',\n isActive && 'bg-[var(--as-secondary)]',\n option.disabled && 'pointer-events-none opacity-50',\n )}\n onMouseMove={() => onActiveIndexChange(index)}\n onClick={() => !option.disabled && onPick(option)}\n >\n <span className=\"flex min-w-0 flex-1 items-center gap-2 text-left\">\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"truncate\">{option.label}</span>\n {option.description ? (\n <span className=\"truncate text-xs text-[var(--as-muted-fg)]\">\n {option.description}\n </span>\n ) : null}\n </span>\n {option.badge ? (\n <span className=\"shrink-0 rounded-full bg-[var(--as-secondary)] px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-[var(--as-secondary-fg)]\">\n {option.badge}\n </span>\n ) : null}\n </span>\n {isSelected ? (\n <Check className=\"size-4 shrink-0 text-[var(--as-primary)]\" aria-hidden />\n ) : null}\n </div>,\n )\n })\n return (\n <div\n id={id}\n role=\"listbox\"\n className={cn('max-h-60 overflow-y-auto p-1', listClassName)}\n >\n {loading ? (\n <div className=\"flex items-center gap-2 px-2 py-3 text-sm text-[var(--as-muted-fg)]\">\n <Spinner />\n {loadingLabel}\n </div>\n ) : options.length === 0 ? (\n <div className=\"px-2 py-3 text-center text-sm text-[var(--as-muted-fg)]\">\n {emptyLabel}\n </div>\n ) : (\n rows\n )}\n </div>\n )\n}\n\ninterface ComboboxPanelProps {\n listId: string\n options: ComboboxOption[]\n activeIndex: number\n setActiveIndex: (index: number) => void\n selected: (value: string) => boolean\n onPick: (option: ComboboxOption) => void\n search: string\n setSearch: (term: string) => void\n onOpenChange: (open: boolean) => void\n searchPlaceholder: string\n searchLabel?: string\n emptyLabel?: string\n loading?: boolean\n loadingLabel?: string\n multi?: boolean\n panelClassName?: string\n}\n\nfunction ComboboxPanel(props: ComboboxPanelProps) {\n const {\n listId,\n options,\n activeIndex,\n setActiveIndex,\n selected,\n onPick,\n search,\n setSearch,\n onOpenChange,\n searchPlaceholder,\n searchLabel = 'Search options',\n emptyLabel,\n loading,\n loadingLabel,\n multi,\n panelClassName,\n } = props\n\n const nextEnabled = React.useCallback(\n (from: number, delta: 1 | -1) => {\n for (let step = 1; step <= options.length; step++) {\n const index =\n delta === 1\n ? (from + step) % Math.max(options.length, 1)\n : (from - step + options.length * 2) % Math.max(options.length, 1)\n if (index < options.length && !options[index]?.disabled) {\n return index\n }\n }\n return from\n },\n [options],\n )\n\n const onKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'ArrowDown') {\n event.preventDefault()\n setActiveIndex(nextEnabled(activeIndex, 1))\n } else if (event.key === 'ArrowUp') {\n event.preventDefault()\n setActiveIndex(nextEnabled(activeIndex, -1))\n } else if (event.key === 'Home') {\n event.preventDefault()\n setActiveIndex(options.findIndex((option) => !option.disabled))\n } else if (event.key === 'End') {\n event.preventDefault()\n const last = [...options].reverse().find((option) => !option.disabled)\n if (last) {\n setActiveIndex(options.lastIndexOf(last))\n }\n } else if (event.key === 'Enter') {\n event.preventDefault()\n const option = options[activeIndex]\n if (option && !option.disabled) {\n onPick(option)\n }\n } else if (event.key === 'Tab') {\n onOpenChange(false)\n }\n }\n\n return (\n <PopoverPrimitive.Content\n align=\"start\"\n collisionPadding={8}\n onOpenAutoFocus={(event) => {\n event.preventDefault()\n const panel = event.currentTarget as HTMLElement | null\n const input = panel?.querySelector('input')\n if (input instanceof HTMLInputElement) {\n input.focus()\n }\n }}\n className={cn(\n 'as-anim-pop pointer-events-auto z-[var(--as-z-popover)] w-[var(--radix-popover-trigger-width)] min-w-48 overflow-hidden rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] text-[var(--as-fg)] shadow-[var(--as-shadow-3)]',\n panelClassName,\n )}\n data-as=\"combobox-panel\"\n >\n <div className=\"flex items-center gap-2 border-b border-[var(--as-border)] p-2\">\n <Search className=\"size-4 shrink-0 text-[var(--as-muted-fg)]\" aria-hidden />\n <input\n type=\"text\"\n role=\"combobox\"\n aria-expanded=\"true\"\n aria-controls={listId}\n aria-activedescendant={activeIndex >= 0 ? `${listId}-opt-${activeIndex}` : undefined}\n aria-label={searchLabel}\n placeholder={searchPlaceholder}\n className=\"h-7 w-full bg-transparent text-sm outline-none placeholder:text-[var(--as-muted-fg)]\"\n value={search}\n onChange={(event) => {\n setSearch(event.target.value)\n setActiveIndex(options.findIndex((option) => !option.disabled))\n }}\n onKeyDown={onKeyDown}\n />\n </div>\n <ComboboxList\n id={listId}\n options={options}\n activeIndex={activeIndex}\n onActiveIndexChange={setActiveIndex}\n selected={selected}\n onPick={onPick}\n emptyLabel={emptyLabel}\n loading={loading}\n loadingLabel={loadingLabel}\n />\n {multi ? (\n <div className=\"sr-only\" role=\"status\">\n {options.length} options\n </div>\n ) : null}\n </PopoverPrimitive.Content>\n )\n}\n\nexport interface ComboboxProps {\n options: ComboboxOption[]\n value?: string\n onChange: (value: string) => void\n placeholder?: string\n searchPlaceholder?: string\n searchLabel?: string\n emptyLabel?: string\n loadingLabel?: string\n loading?: boolean\n disabled?: boolean\n clearable?: boolean\n clearLabel?: string\n onSearchChange?: (term: string) => void\n onOpenChange?: (open: boolean) => void\n label?: string\n /** Keep the accessible name from `label` but do not render it visibly. */\n hideLabel?: boolean\n error?: string\n id?: string\n className?: string\n panelClassName?: string\n}\n\nexport const Combobox = React.forwardRef<HTMLButtonElement, ComboboxProps>(function Combobox(\n {\n options,\n value,\n onChange,\n placeholder = 'Select an option…',\n searchPlaceholder = 'Search…',\n searchLabel,\n emptyLabel,\n loadingLabel,\n loading = false,\n disabled = false,\n clearable = false,\n clearLabel = 'Clear',\n onSearchChange,\n onOpenChange,\n label,\n hideLabel = false,\n error,\n id: idProp,\n className,\n panelClassName,\n },\n ref,\n) {\n const autoId = React.useId()\n const id = idProp ?? (label || error ? autoId : undefined)\n const listId = `${autoId}-list`\n const [open, setOpen] = React.useState(false)\n const [search, setSearch] = React.useState('')\n const [activeIndex, setActiveIndex] = React.useState(-1)\n\n const filtered = onSearchChange ? options : defaultFilter(options, search)\n const selectedOption = options.find((option) => option.value === value)\n\n const handleOpenChange = (next: boolean) => {\n setOpen(next)\n onOpenChange?.(next)\n if (next) {\n setActiveIndex(options.findIndex((option) => !option.disabled))\n } else {\n setSearch('')\n setActiveIndex(-1)\n }\n }\n\n return (\n <div className={cn('flex w-full flex-col gap-1.5', className)}>\n {label && !hideLabel ? (\n <label htmlFor={id} className=\"text-sm font-medium leading-none text-[var(--as-fg)]\">\n {label}\n </label>\n ) : null}\n <PopoverPrimitive.Root modal open={open} onOpenChange={handleOpenChange}>\n <span className=\"relative inline-flex w-full\">\n <PopoverPrimitive.Trigger asChild>\n <button\n ref={ref}\n type=\"button\"\n id={id}\n data-as=\"combobox\"\n role=\"combobox\"\n aria-label={typeof label === 'string' ? label : placeholder}\n aria-haspopup=\"listbox\"\n aria-controls={open ? listId : undefined}\n aria-invalid={error ? true : undefined}\n disabled={disabled}\n onKeyDown={(event) => {\n if (!open && (event.key === 'ArrowDown' || event.key === 'ArrowUp')) {\n event.preventDefault()\n handleOpenChange(true)\n }\n }}\n className={cn(\n 'flex h-9 w-full cursor-pointer items-center justify-between gap-2 rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-2 text-sm text-[var(--as-fg)] transition-colors hover:border-[var(--as-fg)]/30 focus-visible:border-[var(--as-focus-ring)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50',\n error && 'border-[var(--as-danger)]',\n clearable && selectedOption && 'pr-14',\n )}\n >\n <span className=\"flex min-w-0 flex-1 items-center gap-2 text-left\">\n {selectedOption ? (\n <span className=\"truncate font-medium\">{selectedOption.label}</span>\n ) : (\n <span className=\"text-[var(--as-muted-fg)]\">{placeholder}</span>\n )}\n </span>\n <ChevronDown\n aria-hidden\n className={cn(\n 'size-4 shrink-0 text-[var(--as-muted-fg)] transition-transform',\n open && 'rotate-180',\n )}\n />\n </button>\n </PopoverPrimitive.Trigger>\n {clearable && selectedOption ? (\n <button\n type=\"button\"\n aria-label={clearLabel}\n className=\"absolute right-7 top-1/2 [translate:0_-50%] cursor-pointer rounded-[var(--as-radius-sm)] p-0.5 text-[var(--as-muted-fg)] hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n onClick={(event) => {\n event.stopPropagation()\n onChange('')\n }}\n >\n <X className=\"size-4\" aria-hidden />\n </button>\n ) : null}\n </span>\n <PopoverPrimitive.Portal>\n {open ? (\n <ComboboxPanel\n listId={listId}\n options={filtered}\n activeIndex={activeIndex}\n setActiveIndex={setActiveIndex}\n selected={(optionValue) => optionValue === value}\n onPick={(option) => {\n onChange(option.value)\n handleOpenChange(false)\n }}\n search={search}\n setSearch={(term) => {\n setSearch(term)\n onSearchChange?.(term)\n }}\n onOpenChange={handleOpenChange}\n searchPlaceholder={searchPlaceholder}\n searchLabel={searchLabel}\n emptyLabel={emptyLabel}\n loading={loading}\n loadingLabel={loadingLabel}\n panelClassName={panelClassName}\n />\n ) : null}\n </PopoverPrimitive.Portal>\n </PopoverPrimitive.Root>\n {error ? (\n <p role=\"alert\" className=\"text-xs font-medium text-[var(--as-danger)]\">\n {error}\n </p>\n ) : null}\n </div>\n )\n})\n\nexport interface ComboboxMultiProps {\n options: ComboboxOption[]\n value: string[]\n onChange: (value: string[]) => void\n placeholder?: string\n searchPlaceholder?: string\n searchLabel?: string\n emptyLabel?: string\n loadingLabel?: string\n loading?: boolean\n disabled?: boolean\n maxTriggerLabels?: number\n onSearchChange?: (term: string) => void\n onOpenChange?: (open: boolean) => void\n label?: string\n error?: string\n id?: string\n className?: string\n panelClassName?: string\n}\n\nexport const ComboboxMulti = React.forwardRef<HTMLButtonElement, ComboboxMultiProps>(\n function ComboboxMulti(\n {\n options,\n value,\n onChange,\n placeholder = 'Select options…',\n searchPlaceholder = 'Search…',\n searchLabel,\n emptyLabel,\n loadingLabel,\n loading = false,\n disabled = false,\n maxTriggerLabels = 3,\n onSearchChange,\n onOpenChange,\n label,\n error,\n id: idProp,\n className,\n panelClassName,\n },\n ref,\n ) {\n const autoId = React.useId()\n const id = idProp ?? (label || error ? autoId : undefined)\n const listId = `${autoId}-list`\n const [open, setOpen] = React.useState(false)\n const [search, setSearch] = React.useState('')\n const [activeIndex, setActiveIndex] = React.useState(-1)\n\n const filtered = onSearchChange ? options : defaultFilter(options, search)\n const selectedOptions = options.filter((option) => value.includes(option.value))\n const triggerLabel =\n selectedOptions.length === 0\n ? placeholder\n : selectedOptions.length > maxTriggerLabels\n ? `${selectedOptions.length} selected`\n : selectedOptions.map((option) => option.label).join(', ')\n\n const handleOpenChange = (next: boolean) => {\n setOpen(next)\n onOpenChange?.(next)\n if (next) {\n setActiveIndex(options.findIndex((option) => !option.disabled))\n } else {\n setSearch('')\n setActiveIndex(-1)\n }\n }\n\n const toggle = (optionValue: string) => {\n onChange(\n value.includes(optionValue)\n ? value.filter((entry) => entry !== optionValue)\n : [...value, optionValue],\n )\n }\n\n return (\n <div className={cn('flex w-full flex-col gap-1.5', className)}>\n {label ? (\n <label htmlFor={id} className=\"text-sm font-medium leading-none text-[var(--as-fg)]\">\n {label}\n </label>\n ) : null}\n <PopoverPrimitive.Root open={open} onOpenChange={handleOpenChange}>\n <PopoverPrimitive.Trigger asChild>\n <button\n ref={ref}\n type=\"button\"\n id={id}\n data-as=\"combobox-multi\"\n role=\"combobox\"\n aria-label={typeof label === 'string' ? label : placeholder}\n aria-haspopup=\"listbox\"\n aria-controls={open ? listId : undefined}\n aria-invalid={error ? true : undefined}\n disabled={disabled}\n onKeyDown={(event) => {\n if (!open && (event.key === 'ArrowDown' || event.key === 'ArrowUp')) {\n event.preventDefault()\n handleOpenChange(true)\n }\n }}\n className=\"flex h-9 w-full cursor-pointer items-center justify-between gap-2 rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-2 text-sm text-[var(--as-fg)] transition-colors hover:border-[var(--as-fg)]/30 focus-visible:border-[var(--as-focus-ring)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"min-w-0 flex-1 truncate text-left\">\n {selectedOptions.length === 0 ? (\n <span className=\"text-[var(--as-muted-fg)]\">{placeholder}</span>\n ) : (\n triggerLabel\n )}\n </span>\n <ChevronDown\n aria-hidden\n className={cn(\n 'size-4 shrink-0 text-[var(--as-muted-fg)] transition-transform',\n open && 'rotate-180',\n )}\n />\n </button>\n </PopoverPrimitive.Trigger>\n <PopoverPrimitive.Portal>\n {open ? (\n <ComboboxPanel\n listId={listId}\n options={filtered}\n activeIndex={activeIndex}\n setActiveIndex={setActiveIndex}\n selected={(optionValue) => value.includes(optionValue)}\n onPick={(option) => toggle(option.value)}\n search={search}\n setSearch={(term) => {\n setSearch(term)\n onSearchChange?.(term)\n }}\n onOpenChange={handleOpenChange}\n searchPlaceholder={searchPlaceholder}\n searchLabel={searchLabel}\n emptyLabel={emptyLabel}\n loading={loading}\n loadingLabel={loadingLabel}\n multi\n panelClassName={panelClassName}\n />\n ) : null}\n </PopoverPrimitive.Portal>\n </PopoverPrimitive.Root>\n {error ? (\n <p role=\"alert\" className=\"text-xs font-medium text-[var(--as-danger)]\">\n {error}\n </p>\n ) : null}\n </div>\n )\n },\n)\n"]}
@@ -0,0 +1,143 @@
1
+ // src/lib/fuzzy.ts
2
+ var SEPARATORS = /* @__PURE__ */ new Set(["-", "_", ".", "/", " ", ":", "@"]);
3
+ var BOUNDARY_START = 8;
4
+ var BOUNDARY_SEPARATOR = 6;
5
+ var CONSECUTIVE_STEP = 4;
6
+ var EXACT_MATCH = 100;
7
+ var SUBSTRING_MATCH = 80;
8
+ var TOKEN_SUBSTRING = 60;
9
+ var SUBSEQUENCE_BASE = 30;
10
+ var TYPO_BASE = 20;
11
+ function fuzzyScore(query, target) {
12
+ if (query === "") {
13
+ return 0;
14
+ }
15
+ const q = query.toLowerCase();
16
+ const t = target.toLowerCase();
17
+ if (q === t) {
18
+ return EXACT_MATCH + q.length;
19
+ }
20
+ const substringIndex = t.indexOf(q);
21
+ if (substringIndex !== -1) {
22
+ let bonus = SUBSTRING_MATCH;
23
+ if (substringIndex === 0) bonus += BOUNDARY_START;
24
+ else if (SEPARATORS.has(t[substringIndex - 1] ?? "")) bonus += BOUNDARY_SEPARATOR;
25
+ return bonus + q.length;
26
+ }
27
+ let score = SUBSEQUENCE_BASE;
28
+ let matched = false;
29
+ for (const token of q.split(/\s+/).filter(Boolean)) {
30
+ const tokenScore = subsequenceScore(token, t);
31
+ if (tokenScore === null) {
32
+ return null;
33
+ }
34
+ score += tokenScore;
35
+ matched = true;
36
+ }
37
+ if (!matched) {
38
+ return null;
39
+ }
40
+ return score;
41
+ }
42
+ function subsequenceScore(query, target) {
43
+ if (target.includes(query)) {
44
+ let bonus = TOKEN_SUBSTRING;
45
+ const index = target.indexOf(query);
46
+ if (index === 0) bonus += BOUNDARY_START;
47
+ else if (SEPARATORS.has(target[index - 1] ?? "")) bonus += BOUNDARY_SEPARATOR;
48
+ return bonus;
49
+ }
50
+ let score = 0;
51
+ let qi = 0;
52
+ let consecutive = 0;
53
+ let prevIndex = -2;
54
+ for (let ti = 0; ti < target.length && qi < query.length; ti += 1) {
55
+ if (target[ti] !== query[qi]) {
56
+ continue;
57
+ }
58
+ let bonus = 0;
59
+ if (ti === 0) {
60
+ bonus += BOUNDARY_START;
61
+ } else if (SEPARATORS.has(target[ti - 1] ?? "")) {
62
+ bonus += BOUNDARY_SEPARATOR;
63
+ }
64
+ if (ti === prevIndex + 1) {
65
+ consecutive += 1;
66
+ bonus += CONSECUTIVE_STEP * consecutive;
67
+ } else {
68
+ consecutive = 0;
69
+ }
70
+ score += 1 + bonus;
71
+ prevIndex = ti;
72
+ qi += 1;
73
+ }
74
+ if (qi < query.length) {
75
+ return null;
76
+ }
77
+ return score;
78
+ }
79
+ function typoDistance(a, b, max = 2) {
80
+ if (Math.abs(a.length - b.length) > max) {
81
+ return null;
82
+ }
83
+ let previous = Array.from({ length: b.length + 1 }, (_, index) => index);
84
+ for (let i = 1; i <= a.length; i += 1) {
85
+ const current = [i];
86
+ let rowMin = i;
87
+ for (let j = 1; j <= b.length; j += 1) {
88
+ const substitution = previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1);
89
+ const insertion = previous[j] + 1;
90
+ const deletion = current[j - 1] + 1;
91
+ let value = Math.min(substitution, insertion, deletion);
92
+ if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
93
+ value = Math.min(value, previous[j - 2] + 1);
94
+ }
95
+ current[j] = value;
96
+ if (value < rowMin) {
97
+ rowMin = value;
98
+ }
99
+ }
100
+ if (rowMin > max) {
101
+ return null;
102
+ }
103
+ previous = current;
104
+ }
105
+ return previous[b.length] ?? null;
106
+ }
107
+ function beautifyId(externalId) {
108
+ const acronyms = ["gpt", "nlp", "ocr", "llm", "stt", "tts", "vl", "ai"];
109
+ return externalId.replace(/[-:]/g, " ").replace(/(?<!\d)\.|\.(?!\d)/g, " ").split(" ").filter(Boolean).map(
110
+ (word) => acronyms.includes(word.toLowerCase()) ? word.toUpperCase() : word.charAt(0).toUpperCase() + word.slice(1)
111
+ ).join(" ");
112
+ }
113
+ function searchScore(query, target) {
114
+ const direct = fuzzyScore(query, target);
115
+ if (direct !== null) {
116
+ return direct;
117
+ }
118
+ const q = query.toLowerCase().trim();
119
+ const t = target.toLowerCase();
120
+ if (q.length < 3) {
121
+ return null;
122
+ }
123
+ const allowed = q.length >= 6 ? 2 : 1;
124
+ const whole = typoDistance(q, t, allowed);
125
+ if (whole !== null) {
126
+ return TYPO_BASE + q.length - whole;
127
+ }
128
+ let best = null;
129
+ for (const word of t.split(/[^a-z0-9.]+/).filter(Boolean)) {
130
+ const distance = typoDistance(q, word, allowed);
131
+ if (distance !== null) {
132
+ const score = TYPO_BASE + q.length - distance + BOUNDARY_SEPARATOR;
133
+ if (best === null || score > best) {
134
+ best = score;
135
+ }
136
+ }
137
+ }
138
+ return best;
139
+ }
140
+
141
+ export { beautifyId, fuzzyScore, searchScore, typoDistance };
142
+ //# sourceMappingURL=chunk-B2G66OSQ.js.map
143
+ //# sourceMappingURL=chunk-B2G66OSQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/fuzzy.ts"],"names":[],"mappings":"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","file":"chunk-B2G66OSQ.js","sourcesContent":["const SEPARATORS = new Set(['-', '_', '.', '/', ' ', ':', '@'])\n\nconst BOUNDARY_START = 8\nconst BOUNDARY_SEPARATOR = 6\nconst CONSECUTIVE_STEP = 4\nconst EXACT_MATCH = 100\nconst SUBSTRING_MATCH = 80\nconst TOKEN_SUBSTRING = 60\nconst SUBSEQUENCE_BASE = 30\nconst TYPO_BASE = 20\n\nexport function fuzzyScore(query: string, target: string): number | null {\n if (query === '') {\n return 0\n }\n const q = query.toLowerCase()\n const t = target.toLowerCase()\n if (q === t) {\n return EXACT_MATCH + q.length\n }\n const substringIndex = t.indexOf(q)\n if (substringIndex !== -1) {\n let bonus = SUBSTRING_MATCH\n if (substringIndex === 0) bonus += BOUNDARY_START\n else if (SEPARATORS.has(t[substringIndex - 1] ?? '')) bonus += BOUNDARY_SEPARATOR\n return bonus + q.length\n }\n let score = SUBSEQUENCE_BASE\n let matched = false\n for (const token of q.split(/\\s+/).filter(Boolean)) {\n const tokenScore = subsequenceScore(token, t)\n if (tokenScore === null) {\n return null\n }\n score += tokenScore\n matched = true\n }\n if (!matched) {\n return null\n }\n return score\n}\n\nfunction subsequenceScore(query: string, target: string): number | null {\n if (target.includes(query)) {\n let bonus = TOKEN_SUBSTRING\n const index = target.indexOf(query)\n if (index === 0) bonus += BOUNDARY_START\n else if (SEPARATORS.has(target[index - 1] ?? '')) bonus += BOUNDARY_SEPARATOR\n return bonus\n }\n let score = 0\n let qi = 0\n let consecutive = 0\n let prevIndex = -2\n for (let ti = 0; ti < target.length && qi < query.length; ti += 1) {\n if (target[ti] !== query[qi]) {\n continue\n }\n let bonus = 0\n if (ti === 0) {\n bonus += BOUNDARY_START\n } else if (SEPARATORS.has(target[ti - 1] ?? '')) {\n bonus += BOUNDARY_SEPARATOR\n }\n if (ti === prevIndex + 1) {\n consecutive += 1\n bonus += CONSECUTIVE_STEP * consecutive\n } else {\n consecutive = 0\n }\n score += 1 + bonus\n prevIndex = ti\n qi += 1\n }\n if (qi < query.length) {\n return null\n }\n return score\n}\n\nexport function typoDistance(a: string, b: string, max = 2): number | null {\n if (Math.abs(a.length - b.length) > max) {\n return null\n }\n let previous: number[] = Array.from({ length: b.length + 1 }, (_, index) => index)\n for (let i = 1; i <= a.length; i += 1) {\n const current: number[] = [i]\n let rowMin = i\n for (let j = 1; j <= b.length; j += 1) {\n const substitution = previous[j - 1]! + (a[i - 1] === b[j - 1] ? 0 : 1)\n const insertion = previous[j]! + 1\n const deletion = current[j - 1]! + 1\n let value = Math.min(substitution, insertion, deletion)\n if (\n i > 1 &&\n j > 1 &&\n a[i - 1] === b[j - 2] &&\n a[i - 2] === b[j - 1]\n ) {\n value = Math.min(value, previous[j - 2]! + 1)\n }\n current[j] = value\n if (value < rowMin) {\n rowMin = value\n }\n }\n if (rowMin > max) {\n return null\n }\n previous = current\n }\n return previous[b.length] ?? null\n}\n\nexport function beautifyId(externalId: string): string {\n const acronyms = ['gpt', 'nlp', 'ocr', 'llm', 'stt', 'tts', 'vl', 'ai']\n return externalId\n .replace(/[-:]/g, ' ')\n .replace(/(?<!\\d)\\.|\\.(?!\\d)/g, ' ')\n .split(' ')\n .filter(Boolean)\n .map((word) =>\n acronyms.includes(word.toLowerCase())\n ? word.toUpperCase()\n : word.charAt(0).toUpperCase() + word.slice(1),\n )\n .join(' ')\n}\n\nexport function searchScore(query: string, target: string): number | null {\n const direct = fuzzyScore(query, target)\n if (direct !== null) {\n return direct\n }\n const q = query.toLowerCase().trim()\n const t = target.toLowerCase()\n if (q.length < 3) {\n return null\n }\n const allowed = q.length >= 6 ? 2 : 1\n const whole = typoDistance(q, t, allowed)\n if (whole !== null) {\n return TYPO_BASE + q.length - whole\n }\n let best: number | null = null\n for (const word of t.split(/[^a-z0-9.]+/).filter(Boolean)) {\n const distance = typoDistance(q, word, allowed)\n if (distance !== null) {\n const score = TYPO_BASE + q.length - distance + BOUNDARY_SEPARATOR\n if (best === null || score > best) {\n best = score\n }\n }\n }\n return best\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import { CapabilityChips } from './chunk-G3DWN2DJ.js';
2
- import { ModelPicker } from './chunk-WHOJXXGI.js';
2
+ import { ModelPicker } from './chunk-SVQK7TXU.js';
3
+ import { beautifyId } from './chunk-B2G66OSQ.js';
3
4
  import { Modal, ModalContent, ModalHeader, ModalTitle, ModalFooter } from './chunk-Y7BKBG57.js';
4
5
  import { Button } from './chunk-GTXGTQ6F.js';
5
6
  import { Spinner } from './chunk-INTST5VG.js';
@@ -9,13 +10,6 @@ import { ChevronDown, ChevronRight, Pencil, X, Plus, Check, ChevronUp } from 'lu
9
10
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
10
11
 
11
12
  var CUSTOM = "__custom__";
12
- var ACRONYMS = ["gpt", "nlp", "ocr", "llm", "stt", "tts", "vl", "ai"];
13
- function beautifyModelId(externalId) {
14
- return externalId.replace(/[-:]/g, " ").replace(/(?<!\d)\.|\.(?!\d)/g, " ").split(" ").filter(Boolean).map((word) => {
15
- if (ACRONYMS.includes(word.toLowerCase())) return word.toUpperCase();
16
- return word.charAt(0).toUpperCase() + word.slice(1);
17
- }).join(" ");
18
- }
19
13
  function NumberField({
20
14
  label,
21
15
  value,
@@ -109,6 +103,7 @@ var ModelRegistry = React.forwardRef(
109
103
  removeLabel = "Remove",
110
104
  missingLabel = "Missing",
111
105
  capsLabel = "Capabilities",
106
+ capsHint,
112
107
  searchPlaceholder = "Search models\u2026",
113
108
  searchLabel = "Search models",
114
109
  emptyProviderLabel = "No models yet \u2014 use Add model to pick from the live catalog.",
@@ -181,7 +176,7 @@ var ModelRegistry = React.forwardRef(
181
176
  ...current,
182
177
  externalId: remoteId,
183
178
  caps: remote ? [...remote.caps] : current.caps,
184
- label: current.labelTouched ? current.label : beautifyModelId(remoteId)
179
+ label: current.labelTouched ? current.label : beautifyId(remoteId)
185
180
  } : current
186
181
  );
187
182
  };
@@ -337,7 +332,8 @@ var ModelRegistry = React.forwardRef(
337
332
  onToggle: toggleDraftCap,
338
333
  ariaLabel: capsLabel
339
334
  }
340
- )
335
+ ),
336
+ capsHint ? /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--as-muted-fg)]", children: capsHint }) : null
341
337
  ] }),
342
338
  reasoningEffortOptions ? /* @__PURE__ */ jsxs("label", { className: "block space-y-1 text-sm", children: [
343
339
  /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--as-fg)]", children: reasoningEffortLabel }),
@@ -570,5 +566,5 @@ var ModelRegistry = React.forwardRef(
570
566
  );
571
567
 
572
568
  export { ModelRegistry };
573
- //# sourceMappingURL=chunk-QIXRJMCL.js.map
574
- //# sourceMappingURL=chunk-QIXRJMCL.js.map
569
+ //# sourceMappingURL=chunk-JSPXF5YG.js.map
570
+ //# sourceMappingURL=chunk-JSPXF5YG.js.map