@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 +26 -0
- package/README.md +47 -30
- package/dist/{chunk-LC7CYUY4.js → chunk-AATZUVW4.js} +5 -136
- package/dist/chunk-AATZUVW4.js.map +1 -0
- package/dist/chunk-B2G66OSQ.js +143 -0
- package/dist/chunk-B2G66OSQ.js.map +1 -0
- package/dist/{chunk-QIXRJMCL.js → chunk-JSPXF5YG.js} +8 -12
- package/dist/chunk-JSPXF5YG.js.map +1 -0
- package/dist/{chunk-WHOJXXGI.js → chunk-SVQK7TXU.js} +5 -3
- package/dist/chunk-SVQK7TXU.js.map +1 -0
- package/dist/chunk-YGYKYELC.js +182 -0
- package/dist/chunk-YGYKYELC.js.map +1 -0
- package/dist/combobox.d.ts +2 -0
- package/dist/combobox.js +1 -1
- package/dist/fuzzy.d.ts +6 -0
- package/dist/fuzzy.js +3 -0
- package/dist/fuzzy.js.map +1 -0
- package/dist/index.js +5 -5
- package/dist/model-picker.d.ts +2 -0
- package/dist/model-picker.js +1 -1
- package/dist/model-registry.d.ts +2 -0
- package/dist/model-registry.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/task-assignment-picker.d.ts +6 -2
- package/dist/task-assignment-picker.js +1 -1
- package/package.json +6 -2
- package/dist/chunk-LC7CYUY4.js.map +0 -1
- package/dist/chunk-QIXRJMCL.js.map +0 -1
- package/dist/chunk-WHOJXXGI.js.map +0 -1
- package/dist/chunk-YU6YF4YH.js +0 -133
- package/dist/chunk-YU6YF4YH.js.map +0 -1
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.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
78
|
-
| `
|
|
79
|
-
| `
|
|
80
|
-
| `
|
|
81
|
-
| `
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `model-
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
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-
|
|
599
|
-
//# sourceMappingURL=chunk-
|
|
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-
|
|
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 :
|
|
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-
|
|
574
|
-
//# sourceMappingURL=chunk-
|
|
569
|
+
//# sourceMappingURL=chunk-JSPXF5YG.js.map
|
|
570
|
+
//# sourceMappingURL=chunk-JSPXF5YG.js.map
|