@neuronection/assistant-ui 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/dist/capability-chips.d.ts +2 -0
- package/dist/capability-chips.js +1 -1
- package/dist/chunk-4WDXLXHL.js +145 -0
- package/dist/chunk-4WDXLXHL.js.map +1 -0
- package/dist/{chunk-XB7QWPHP.js → chunk-EKGZXIUN.js} +21 -18
- package/dist/chunk-EKGZXIUN.js.map +1 -0
- package/dist/chunk-G3DWN2DJ.js +80 -0
- package/dist/chunk-G3DWN2DJ.js.map +1 -0
- package/dist/{chunk-3RDFWXX6.js → chunk-LC7CYUY4.js} +152 -8
- package/dist/chunk-LC7CYUY4.js.map +1 -0
- package/dist/chunk-QIXRJMCL.js +574 -0
- package/dist/chunk-QIXRJMCL.js.map +1 -0
- package/dist/{chunk-RI6CEWWF.js → chunk-WHOJXXGI.js} +3 -3
- package/dist/{chunk-RI6CEWWF.js.map → chunk-WHOJXXGI.js.map} +1 -1
- package/dist/combobox.js +1 -1
- package/dist/index.js +6 -6
- package/dist/model-picker.js +1 -1
- package/dist/model-registry.d.ts +15 -10
- package/dist/model-registry.js +1 -1
- package/dist/provider-form.d.ts +19 -0
- package/dist/provider-form.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/task-assignment-picker.d.ts +5 -1
- package/dist/task-assignment-picker.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-3RDFWXX6.js.map +0 -1
- package/dist/chunk-HGJ5HINX.js +0 -500
- package/dist/chunk-HGJ5HINX.js.map +0 -1
- package/dist/chunk-MLQAA2QV.js +0 -68
- package/dist/chunk-MLQAA2QV.js.map +0 -1
- package/dist/chunk-RJSN4TVT.js +0 -89
- package/dist/chunk-RJSN4TVT.js.map +0 -1
- package/dist/chunk-XB7QWPHP.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# @neuronection/assistant-ui
|
|
2
2
|
|
|
3
|
+
## 0.12.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`7f69a7d`](https://github.com/neuronection/assistant-ui/commit/7f69a7d8f373e3bf66293d5eb423a2ad7b8115b6) Thanks [@constLiakos](https://github.com/constLiakos)! - `ModelRegistry` moves model add/edit into a **catalog modal** (replacing the inline draft panel and always-visible catalog zone): the picker is a searchable, scrollable `ModelPicker` over the provider's live catalog with a manual-id escape hatch; the draft form gains a **reasoning-effort dropdown with a Custom… option** (free-text), plus **clearable temperature and max-tokens fields** (empty = unset, `null` in drafts/patches — apps persist them per model). Registered rows keep edit/delete; Add-all moves to the modal footer. New labels: `addTitle`, `editTitle`, `selectModelLabel`, `manualIdToggleLabel`, `customOptionLabel`, `temperatureLabel`, `maxTokensLabel`, `emptyProviderLabel`, `externalIdRequiredLabel`; removed: `browseLabel`, `configureLabel`, `addedLabel`, `capFilterLabel`, `unclassifiedLabel`, `manualAddLabel`, `externalIdLabel`, `reasoningEffortPlaceholder`.
|
|
8
|
+
|
|
9
|
+
- [`7f69a7d`](https://github.com/neuronection/assistant-ui/commit/7f69a7d8f373e3bf66293d5eb423a2ad7b8115b6) Thanks [@constLiakos](https://github.com/constLiakos)! - `ProviderForm` gains optional, flag-gated metadata fields — `showLocationKind` renders a Local/Cloud toggle (`locationKind`/`onLocationKindChange`, icon tile chips) and `showCountry` renders a country select (`country`/`onCountryChange`, app-supplied `countryOptions`). Both default off; apps adopt only what they model.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [`7f69a7d`](https://github.com/neuronection/assistant-ui/commit/7f69a7d8f373e3bf66293d5eb423a2ad7b8115b6) Thanks [@constLiakos](https://github.com/constLiakos)! - `Combobox` search is resilient and accurate: the default filter now ranks matches (exact > substring > token subsequence > bounded typo) instead of plain substring filtering — misspelled queries (`gemni`, `gemeni`, `vicion`) still find their models, separator-agnostic subsequences work (`qwen vl` → `qwen2.5-vl:7b`), and unrelated queries return nothing. New `src/lib/fuzzy` exports `fuzzyScore`/`searchScore` for app reuse.
|
|
14
|
+
|
|
15
|
+
- [`7f69a7d`](https://github.com/neuronection/assistant-ui/commit/7f69a7d8f373e3bf66293d5eb423a2ad7b8115b6) Thanks [@constLiakos](https://github.com/constLiakos)! - The `Combobox` panel popover is now Radix-`modal` so its list scrolls with the mouse wheel inside dialogs — the dialog's scroll lock was swallowing wheel events on the portaled panel. Outside clicks still close the panel; behavior while closed is unchanged.
|
|
16
|
+
|
|
17
|
+
- [`7f69a7d`](https://github.com/neuronection/assistant-ui/commit/7f69a7d8f373e3bf66293d5eb423a2ad7b8115b6) Thanks [@constLiakos](https://github.com/constLiakos)! - Model-registry modal polish from live testing: the combobox panel re-enables `pointer-events` so catalogs scroll with the mouse inside dialogs (Radix Dialog blocks body-level pointer events on portaled panels); temperature/max-tokens fields use a dedicated number field with spinner chevrons at the far right (native spinners hidden) and an inline clear button; picking a catalog model auto-fills an editable display name (dashes/colons/dots → spaces, version dots preserved, acronym-aware title case — ports the health-assistant beautifier) unless the label was hand-edited.
|
|
18
|
+
|
|
19
|
+
## 0.11.0
|
|
20
|
+
|
|
21
|
+
### Minor Changes
|
|
22
|
+
|
|
23
|
+
- [`b69b713`](https://github.com/neuronection/assistant-ui/commit/b69b713f4a61887bf1ae1aee3c65f150f6e459e9) Thanks [@constLiakos](https://github.com/constLiakos)! - Model-registry family adopts the health-assistant settings design language, all on tokens: provider cards gain the chevron icon tile (primary-filled when expanded), tinted border + shadow on the open card, an entry fade, and an enabled/total count pill; `CapabilityDescriptor` gains `icon` and every chip/badge carries `data-as-cap` so apps can re-tint per capability; `TaskAssignmentTask` gains `icon` (leading tile). Behavior changes: the remote-catalog zone (search, capability filters, manual add, add-all, catalog rows) is collapsed behind a `browseLabel` trigger button (`aria-expanded`) instead of always visible, and the per-row enable checkbox is removed — apps filter the `models` prop themselves (disabled models re-enter via the catalog's normal add path). `enabledLabel` is gone; `tasks` is optional on `TaskAssignmentPicker`.
|
|
24
|
+
|
|
25
|
+
### Patch Changes
|
|
26
|
+
|
|
27
|
+
- [`4f9e9bb`](https://github.com/neuronection/assistant-ui/commit/4f9e9bb923ea09fb5f186312627b124779a0035f) Thanks [@constLiakos](https://github.com/constLiakos)! - `ModelRegistry` draft payloads always carry `reasoningEffort` (empty string when cleared) so apps can distinguish "unset" from "cleared" — previously clearing the field sent `undefined` and the stored value could never be removed.
|
|
28
|
+
|
|
29
|
+
- [`a93f657`](https://github.com/neuronection/assistant-ui/commit/a93f657ed7577bb347909e211c62a40a36fda87a) Thanks [@constLiakos](https://github.com/constLiakos)! - `ProviderForm` gains `hideBaseUrl` for provider types with a fixed endpoint (Google, Anthropic) — the base URL field is omitted instead of rendering an inert input.
|
|
30
|
+
|
|
31
|
+
- [`6d8c694`](https://github.com/neuronection/assistant-ui/commit/6d8c694ffdc5b1358d49cc2e596639fbab3818a2) Thanks [@constLiakos](https://github.com/constLiakos)! - `TaskAssignmentPicker` makes `tasks` optional — sections-only usage no longer needs an empty `tasks` array.
|
|
32
|
+
|
|
3
33
|
## 0.10.0
|
|
4
34
|
|
|
5
35
|
### Minor Changes
|
package/dist/capability-chips.js
CHANGED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { Input } from './chunk-M4V7XHL7.js';
|
|
2
|
+
import { cn } from './chunk-BLJWR4ZV.js';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { KeyRound, House, Building2 } from 'lucide-react';
|
|
5
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
var ProviderForm = React.forwardRef(
|
|
8
|
+
function ProviderForm2({
|
|
9
|
+
name,
|
|
10
|
+
onNameChange,
|
|
11
|
+
baseUrl,
|
|
12
|
+
onBaseUrlChange,
|
|
13
|
+
apiKey,
|
|
14
|
+
onApiKeyChange,
|
|
15
|
+
namePlaceholder = "Provider name",
|
|
16
|
+
baseUrlPlaceholder = "https://api.example.com/v1",
|
|
17
|
+
nameLabel = "Name",
|
|
18
|
+
baseUrlLabel = "API base URL",
|
|
19
|
+
hideBaseUrl = false,
|
|
20
|
+
apiKeyLabel = "API key",
|
|
21
|
+
apiKeyHelp,
|
|
22
|
+
hasStoredKey = false,
|
|
23
|
+
showLocationKind = false,
|
|
24
|
+
locationKind = "cloud",
|
|
25
|
+
onLocationKindChange,
|
|
26
|
+
locationLabel = "Hosting",
|
|
27
|
+
localLabel = "Local / on-premise",
|
|
28
|
+
cloudLabel = "Cloud",
|
|
29
|
+
showCountry = false,
|
|
30
|
+
country,
|
|
31
|
+
onCountryChange,
|
|
32
|
+
countryLabel = "Country",
|
|
33
|
+
countryOptions = [],
|
|
34
|
+
countryPlaceholder = "Select a country\u2026",
|
|
35
|
+
storedKeyLabel = "Stored \u2014 leave empty to keep",
|
|
36
|
+
keyPlaceholder = "sk-\u2026",
|
|
37
|
+
error,
|
|
38
|
+
children,
|
|
39
|
+
className,
|
|
40
|
+
...props
|
|
41
|
+
}, ref) {
|
|
42
|
+
return /* @__PURE__ */ jsxs(
|
|
43
|
+
"div",
|
|
44
|
+
{
|
|
45
|
+
ref,
|
|
46
|
+
"data-as": "provider-form",
|
|
47
|
+
className: cn("flex w-full flex-col gap-4", className),
|
|
48
|
+
...props,
|
|
49
|
+
children: [
|
|
50
|
+
/* @__PURE__ */ jsx(
|
|
51
|
+
Input,
|
|
52
|
+
{
|
|
53
|
+
label: nameLabel,
|
|
54
|
+
value: name,
|
|
55
|
+
onChange: (event) => onNameChange(event.target.value),
|
|
56
|
+
placeholder: namePlaceholder
|
|
57
|
+
}
|
|
58
|
+
),
|
|
59
|
+
hideBaseUrl ? null : /* @__PURE__ */ jsx(
|
|
60
|
+
Input,
|
|
61
|
+
{
|
|
62
|
+
label: baseUrlLabel,
|
|
63
|
+
value: baseUrl,
|
|
64
|
+
onChange: (event) => onBaseUrlChange(event.target.value),
|
|
65
|
+
placeholder: baseUrlPlaceholder,
|
|
66
|
+
className: "font-mono"
|
|
67
|
+
}
|
|
68
|
+
),
|
|
69
|
+
/* @__PURE__ */ jsx(
|
|
70
|
+
Input,
|
|
71
|
+
{
|
|
72
|
+
label: hasStoredKey ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
73
|
+
apiKeyLabel,
|
|
74
|
+
" ",
|
|
75
|
+
/* @__PURE__ */ jsxs("span", { className: "font-normal text-[var(--as-muted-fg)]", children: [
|
|
76
|
+
"(",
|
|
77
|
+
storedKeyLabel,
|
|
78
|
+
")"
|
|
79
|
+
] })
|
|
80
|
+
] }) : apiKeyLabel,
|
|
81
|
+
type: "password",
|
|
82
|
+
value: apiKey,
|
|
83
|
+
onChange: (event) => onApiKeyChange(event.target.value),
|
|
84
|
+
placeholder: hasStoredKey ? "\u2022\u2022\u2022" : keyPlaceholder,
|
|
85
|
+
autoComplete: "new-password",
|
|
86
|
+
hint: apiKeyHelp ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
87
|
+
/* @__PURE__ */ jsx(KeyRound, { className: "size-3", "aria-hidden": true }),
|
|
88
|
+
apiKeyHelp
|
|
89
|
+
] }) : void 0
|
|
90
|
+
}
|
|
91
|
+
),
|
|
92
|
+
showLocationKind && onLocationKindChange ? /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
93
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--as-fg)]", children: locationLabel }),
|
|
94
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-2", role: "group", "aria-label": locationLabel, children: [
|
|
95
|
+
{ kind: "local", label: localLabel, Icon: House },
|
|
96
|
+
{ kind: "cloud", label: cloudLabel, Icon: Building2 }
|
|
97
|
+
].map(({ kind, label, Icon }) => {
|
|
98
|
+
const on = locationKind === kind;
|
|
99
|
+
return /* @__PURE__ */ jsxs(
|
|
100
|
+
"button",
|
|
101
|
+
{
|
|
102
|
+
type: "button",
|
|
103
|
+
"data-as-location": kind,
|
|
104
|
+
"aria-pressed": on,
|
|
105
|
+
onClick: () => onLocationKindChange(kind),
|
|
106
|
+
className: cn(
|
|
107
|
+
"flex flex-1 cursor-pointer items-center justify-center gap-1.5 rounded-[var(--as-radius)] border px-3 py-2 text-xs font-semibold transition-all",
|
|
108
|
+
"focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
|
|
109
|
+
on ? "border-[var(--as-primary)]/30 bg-[var(--as-primary)]/10 text-[var(--as-primary)] shadow-[var(--as-shadow-1)]" : "border-[var(--as-border)] bg-[var(--as-surface)] text-[var(--as-muted-fg)] hover:border-[var(--as-muted-fg)]/40"
|
|
110
|
+
),
|
|
111
|
+
children: [
|
|
112
|
+
/* @__PURE__ */ jsx(Icon, { className: "size-3.5", "aria-hidden": true }),
|
|
113
|
+
label
|
|
114
|
+
]
|
|
115
|
+
},
|
|
116
|
+
kind
|
|
117
|
+
);
|
|
118
|
+
}) })
|
|
119
|
+
] }) : null,
|
|
120
|
+
showCountry && onCountryChange ? /* @__PURE__ */ jsxs("label", { className: "block space-y-1 text-sm", children: [
|
|
121
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--as-fg)]", children: countryLabel }),
|
|
122
|
+
/* @__PURE__ */ jsxs(
|
|
123
|
+
"select",
|
|
124
|
+
{
|
|
125
|
+
value: country ?? "",
|
|
126
|
+
onChange: (event) => onCountryChange(event.target.value),
|
|
127
|
+
className: "w-full rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-2 text-sm text-[var(--as-fg)]",
|
|
128
|
+
children: [
|
|
129
|
+
/* @__PURE__ */ jsx("option", { value: "", children: countryPlaceholder }),
|
|
130
|
+
countryOptions.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
)
|
|
134
|
+
] }) : null,
|
|
135
|
+
children,
|
|
136
|
+
error ? /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm font-medium text-[var(--as-danger)]", children: error }) : null
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
export { ProviderForm };
|
|
144
|
+
//# sourceMappingURL=chunk-4WDXLXHL.js.map
|
|
145
|
+
//# sourceMappingURL=chunk-4WDXLXHL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/provider-form/ProviderForm.tsx"],"names":["ProviderForm"],"mappings":"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","file":"chunk-4WDXLXHL.js","sourcesContent":["import * as React from 'react'\nimport { Building2, House, KeyRound } from 'lucide-react'\nimport { cn } from '../../lib/utils'\nimport { Input } from '../input/Input'\n\nexport interface ProviderFormProps extends React.ComponentProps<'div'> {\n name: string\n onNameChange: (value: string) => void\n baseUrl: string\n onBaseUrlChange: (value: string) => void\n apiKey: string\n onApiKeyChange: (value: string) => void\n namePlaceholder?: string\n baseUrlPlaceholder?: string\n nameLabel?: string\n baseUrlLabel?: string\n /** Hide the base URL field for provider types that have a fixed endpoint. */\n hideBaseUrl?: boolean\n apiKeyLabel?: string\n /** Optional Local/Cloud toggle — render only when `showLocationKind`. */\n showLocationKind?: boolean\n locationKind?: 'local' | 'cloud'\n onLocationKindChange?: (kind: 'local' | 'cloud') => void\n locationLabel?: string\n localLabel?: string\n cloudLabel?: string\n /** Optional country select — render only when `showCountry`. */\n showCountry?: boolean\n country?: string\n onCountryChange?: (country: string) => void\n countryLabel?: string\n countryOptions?: { value: string; label: string }[]\n countryPlaceholder?: string\n /** Hint under the key field; apps mention keyring storage here. */\n apiKeyHelp?: string\n /** The provider already stores a key (write-only field shows no value). */\n hasStoredKey?: boolean\n storedKeyLabel?: string\n keyPlaceholder?: string\n error?: string\n children?: React.ReactNode\n}\n\n/**\n * Standard provider credential fields: name, base URL and a **write-only**\n * masked API key (`onChange` only — the library never renders a stored key;\n * keyring/storage is the app's business, ADR-006).\n */\nexport const ProviderForm = React.forwardRef<HTMLDivElement, ProviderFormProps>(\n function ProviderForm(\n {\n name,\n onNameChange,\n baseUrl,\n onBaseUrlChange,\n apiKey,\n onApiKeyChange,\n namePlaceholder = 'Provider name',\n baseUrlPlaceholder = 'https://api.example.com/v1',\n nameLabel = 'Name',\n baseUrlLabel = 'API base URL',\n hideBaseUrl = false,\n apiKeyLabel = 'API key',\n apiKeyHelp,\n hasStoredKey = false,\n showLocationKind = false,\n locationKind = 'cloud',\n onLocationKindChange,\n locationLabel = 'Hosting',\n localLabel = 'Local / on-premise',\n cloudLabel = 'Cloud',\n showCountry = false,\n country,\n onCountryChange,\n countryLabel = 'Country',\n countryOptions = [],\n countryPlaceholder = 'Select a country…',\n storedKeyLabel = 'Stored — leave empty to keep',\n keyPlaceholder = 'sk-…',\n error,\n children,\n className,\n ...props\n },\n ref,\n ) {\n return (\n <div\n ref={ref}\n data-as=\"provider-form\"\n className={cn('flex w-full flex-col gap-4', className)}\n {...props}\n >\n <Input\n label={nameLabel}\n value={name}\n onChange={(event) => onNameChange(event.target.value)}\n placeholder={namePlaceholder}\n />\n {hideBaseUrl ? null : (\n <Input\n label={baseUrlLabel}\n value={baseUrl}\n onChange={(event) => onBaseUrlChange(event.target.value)}\n placeholder={baseUrlPlaceholder}\n className=\"font-mono\"\n />\n )}\n <Input\n label={\n hasStoredKey ? (\n <>\n {apiKeyLabel}{' '}\n <span className=\"font-normal text-[var(--as-muted-fg)]\">\n ({storedKeyLabel})\n </span>\n </>\n ) : (\n apiKeyLabel\n )\n }\n type=\"password\"\n value={apiKey}\n onChange={(event) => onApiKeyChange(event.target.value)}\n placeholder={hasStoredKey ? '•••' : keyPlaceholder}\n autoComplete=\"new-password\"\n hint={\n apiKeyHelp ? (\n <span className=\"inline-flex items-center gap-1\">\n <KeyRound className=\"size-3\" aria-hidden />\n {apiKeyHelp}\n </span>\n ) : undefined\n }\n />\n {showLocationKind && onLocationKindChange ? (\n <div className=\"space-y-1\">\n <span className=\"text-sm font-medium text-[var(--as-fg)]\">{locationLabel}</span>\n <div className=\"flex gap-2\" role=\"group\" aria-label={locationLabel}>\n {(\n [\n { kind: 'local' as const, label: localLabel, Icon: House },\n { kind: 'cloud' as const, label: cloudLabel, Icon: Building2 },\n ]\n ).map(({ kind, label, Icon }) => {\n const on = locationKind === kind\n return (\n <button\n key={kind}\n type=\"button\"\n data-as-location={kind}\n aria-pressed={on}\n onClick={() => onLocationKindChange(kind)}\n className={cn(\n 'flex flex-1 cursor-pointer items-center justify-center gap-1.5 rounded-[var(--as-radius)] border px-3 py-2 text-xs font-semibold transition-all',\n 'focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]',\n on\n ? 'border-[var(--as-primary)]/30 bg-[var(--as-primary)]/10 text-[var(--as-primary)] shadow-[var(--as-shadow-1)]'\n : 'border-[var(--as-border)] bg-[var(--as-surface)] text-[var(--as-muted-fg)] hover:border-[var(--as-muted-fg)]/40',\n )}\n >\n <Icon className=\"size-3.5\" aria-hidden />\n {label}\n </button>\n )\n })}\n </div>\n </div>\n ) : null}\n {showCountry && onCountryChange ? (\n <label className=\"block space-y-1 text-sm\">\n <span className=\"text-sm font-medium text-[var(--as-fg)]\">{countryLabel}</span>\n <select\n value={country ?? ''}\n onChange={(event) => onCountryChange(event.target.value)}\n className=\"w-full rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-2 text-sm text-[var(--as-fg)]\"\n >\n <option value=\"\">{countryPlaceholder}</option>\n {countryOptions.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </label>\n ) : null}\n {children}\n {error ? (\n <p role=\"alert\" className=\"text-sm font-medium text-[var(--as-danger)]\">\n {error}\n </p>\n ) : null}\n </div>\n )\n },\n)\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ModelPicker } from './chunk-
|
|
1
|
+
import { ModelPicker } from './chunk-WHOJXXGI.js';
|
|
2
2
|
import { cn } from './chunk-BLJWR4ZV.js';
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import { X } from 'lucide-react';
|
|
@@ -13,7 +13,7 @@ function filterProviders(providers, requires) {
|
|
|
13
13
|
})).filter((provider) => provider.models.length > 0);
|
|
14
14
|
}
|
|
15
15
|
var TaskAssignmentPicker = React.forwardRef(function TaskAssignmentPicker2({
|
|
16
|
-
tasks,
|
|
16
|
+
tasks = [],
|
|
17
17
|
sections,
|
|
18
18
|
providers,
|
|
19
19
|
value,
|
|
@@ -47,20 +47,23 @@ var TaskAssignmentPicker = React.forwardRef(function TaskAssignmentPicker2({
|
|
|
47
47
|
{
|
|
48
48
|
className: "flex flex-col gap-2 rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] p-3 sm:flex-row sm:items-center sm:justify-between",
|
|
49
49
|
children: [
|
|
50
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
51
|
-
/* @__PURE__ */ jsx("
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
":",
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
50
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [
|
|
51
|
+
task.icon ? /* @__PURE__ */ jsx("span", { className: "flex size-9 shrink-0 items-center justify-center rounded-[var(--as-radius)] bg-[var(--as-muted)] text-[var(--as-muted-fg)]", children: /* @__PURE__ */ jsx(task.icon, { className: "size-4", "aria-hidden": true }) }) : null,
|
|
52
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
53
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-semibold text-[var(--as-fg)]", children: task.label }),
|
|
54
|
+
task.description ? /* @__PURE__ */ jsx("p", { className: "truncate text-xs text-[var(--as-muted-fg)]", children: task.description }) : null,
|
|
55
|
+
renderMeta ? renderMeta(task) : null,
|
|
56
|
+
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-[var(--as-muted-fg)]", children: assigned ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
57
|
+
modelLabel,
|
|
58
|
+
":",
|
|
59
|
+
" ",
|
|
60
|
+
/* @__PURE__ */ jsxs("span", { className: "font-medium text-[var(--as-fg)]", children: [
|
|
61
|
+
assigned.provider,
|
|
62
|
+
" / ",
|
|
63
|
+
assigned.name
|
|
64
|
+
] })
|
|
65
|
+
] }) : unassignedLabel })
|
|
66
|
+
] })
|
|
64
67
|
] }),
|
|
65
68
|
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1.5", children: [
|
|
66
69
|
showSecondary && onAssignSecondary ? /* @__PURE__ */ jsx(
|
|
@@ -123,5 +126,5 @@ var TaskAssignmentPicker = React.forwardRef(function TaskAssignmentPicker2({
|
|
|
123
126
|
});
|
|
124
127
|
|
|
125
128
|
export { TaskAssignmentPicker };
|
|
126
|
-
//# sourceMappingURL=chunk-
|
|
127
|
-
//# sourceMappingURL=chunk-
|
|
129
|
+
//# sourceMappingURL=chunk-EKGZXIUN.js.map
|
|
130
|
+
//# sourceMappingURL=chunk-EKGZXIUN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/task-assignment-picker/TaskAssignmentPicker.tsx"],"names":["TaskAssignmentPicker"],"mappings":";;;;;;AAoDA,SAAS,eAAA,CACP,WACA,QAAA,EACuB;AACvB,EAAA,IAAI,CAAC,UAAU,OAAO,SAAA;AACtB,EAAA,MAAM,OAAA,GAAU,CAAC,KAAA,KACf,KAAA,CAAM,UAAA,KAAe,aAAa,KAAA,CAAM,YAAA,EAAc,QAAA,CAAS,QAAQ,CAAA,IAAK,KAAA,CAAA;AAC9E,EAAA,OAAO,SAAA,CACJ,GAAA,CAAI,CAAC,QAAA,MAAc;AAAA,IAClB,GAAG,QAAA;AAAA,IACH,MAAA,EAAQ,QAAA,CAAS,MAAA,CAAO,MAAA,CAAO,OAAO;AAAA,GACxC,CAAE,EACD,MAAA,CAAO,CAAC,aAAa,QAAA,CAAS,MAAA,CAAO,SAAS,CAAC,CAAA;AACpD;AAEO,IAAM,oBAAA,GAA6B,KAAA,CAAA,UAAA,CAGxC,SAASA,qBAAAA,CACT;AAAA,EACE,QAAQ,EAAC;AAAA,EACT,QAAA;AAAA,EACA,SAAA;AAAA,EACA,KAAA;AAAA,EACA,QAAA;AAAA,EACA,cAAA;AAAA,EACA,iBAAA;AAAA,EACA,cAAA,GAAiB,gBAAA;AAAA,EACjB,UAAA;AAAA,EACA,eAAA,GAAkB,cAAA;AAAA,EAClB,UAAA,GAAa,kBAAA;AAAA,EACb,UAAA,GAAa,OAAA;AAAA,EACb,QAAA,GAAW,KAAA;AAAA,EACX;AACF,CAAA,EACA,GAAA,EACA;AACA,EAAA,MAAM,SAAA,GAAkB,KAAA,CAAA,WAAA;AAAA,IACtB,CAAC,OAAA,KAAuC;AACtC,MAAA,IAAI,CAAC,SAAS,OAAO,IAAA;AACrB,MAAA,KAAA,MAAW,YAAY,SAAA,EAAW;AAChC,QAAA,MAAM,KAAA,GAAQ,SAAS,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,OAAO,CAAA;AAC1D,QAAA,IAAI,KAAA,SAAc,EAAE,IAAA,EAAM,MAAM,IAAA,EAAM,QAAA,EAAU,SAAS,IAAA,EAAK;AAAA,MAChE;AACA,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA,IACA,CAAC,SAAS;AAAA,GACZ;AAEA,EAAA,MAAM,SAAA,GAAY,CAAC,IAAA,EAA0B,aAAA,KAA2B;AACtE,IAAA,MAAM,QAAA,GAAW,SAAA,CAAU,KAAA,CAAM,IAAA,CAAK,EAAE,CAAC,CAAA;AACzC,IAAA,MAAM,OAAA,GAAU,eAAA,CAAgB,SAAA,EAAW,IAAA,CAAK,QAAQ,CAAA;AACxD,IAAA,uBACE,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QAEC,SAAA,EAAU,qKAAA;AAAA,QAEV,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,iCAAA,EACZ,QAAA,EAAA;AAAA,YAAA,IAAA,CAAK,IAAA,mBACJ,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,8HACd,QAAA,kBAAA,GAAA,CAAC,IAAA,CAAK,IAAA,EAAL,EAAU,SAAA,EAAU,QAAA,EAAS,aAAA,EAAW,IAAA,EAAC,GAC5C,CAAA,GACE,IAAA;AAAA,4BACJ,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,SAAA,EACf,QAAA,EAAA;AAAA,8BAAA,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,oDAAA,EACV,QAAA,EAAA,IAAA,CAAK,KAAA,EACR,CAAA;AAAA,cACC,IAAA,CAAK,8BACJ,GAAA,CAAC,GAAA,EAAA,EAAE,WAAU,4CAAA,EACV,QAAA,EAAA,IAAA,CAAK,aACR,CAAA,GACE,IAAA;AAAA,cACH,UAAA,GAAa,UAAA,CAAW,IAAI,CAAA,GAAI,IAAA;AAAA,8BACjC,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,0CAAA,EACV,qCACC,IAAA,CAAA,QAAA,EAAA,EACG,QAAA,EAAA;AAAA,gBAAA,UAAA;AAAA,gBAAW,GAAA;AAAA,gBAAE,GAAA;AAAA,gCACd,IAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,iCAAA,EACb,QAAA,EAAA;AAAA,kBAAA,QAAA,CAAS,QAAA;AAAA,kBAAS,KAAA;AAAA,kBAAI,QAAA,CAAS;AAAA,iBAAA,EAClC;AAAA,eAAA,EACF,IAEA,eAAA,EAEJ;AAAA,aAAA,EACA;AAAA,WAAA,EACF,CAAA;AAAA,0BACA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,oCAAA,EACZ,QAAA,EAAA;AAAA,YAAA,aAAA,IAAiB,iBAAA,mBAChB,GAAA;AAAA,cAAC,WAAA;AAAA,cAAA;AAAA,gBACC,SAAA,EAAW,OAAA;AAAA,gBACX,KAAA,EAAO,cAAA,GAAiB,IAAA,CAAK,EAAE,CAAA,IAAK,EAAA;AAAA,gBACpC,UAAU,CAAC,OAAA,KAAY,iBAAA,CAAkB,IAAA,CAAK,IAAI,OAAO,CAAA;AAAA,gBACzD,QAAA;AAAA,gBACA,SAAA,EAAW,OAAA,CAAQ,cAAA,GAAiB,IAAA,CAAK,EAAE,CAAC,CAAA;AAAA,gBAC5C,UAAA;AAAA,gBACA,KAAA,EAAO,CAAA,EAAG,cAAc,CAAA,QAAA,EAAM,KAAK,KAAK,CAAA,CAAA;AAAA,gBACxC,SAAA,EAAU;AAAA;AAAA,aACZ,GACE,IAAA;AAAA,4BACJ,GAAA;AAAA,cAAC,WAAA;AAAA,cAAA;AAAA,gBACC,SAAA,EAAW,OAAA;AAAA,gBACX,KAAA,EAAO,KAAA,CAAM,IAAA,CAAK,EAAE,CAAA,IAAK,EAAA;AAAA,gBACzB,UAAU,CAAC,OAAA,KAAY,QAAA,CAAS,IAAA,CAAK,IAAI,OAAO,CAAA;AAAA,gBAChD,QAAA;AAAA,gBACA,SAAA,EAAW,OAAA,CAAQ,KAAA,CAAM,IAAA,CAAK,EAAE,CAAC,CAAA;AAAA,gBACjC,UAAA;AAAA,gBACA,KAAA,EAAO,CAAA,EAAG,UAAU,CAAA,QAAA,EAAM,KAAK,KAAK,CAAA,CAAA;AAAA,gBACpC,SAAA,EAAU;AAAA;AAAA,aACZ;AAAA,YACC,KAAA,CAAM,IAAA,CAAK,EAAE,CAAA,mBACZ,GAAA;AAAA,cAAC,QAAA;AAAA,cAAA;AAAA,gBACC,IAAA,EAAK,QAAA;AAAA,gBACL,YAAA,EAAY,CAAA,EAAG,UAAU,CAAA,QAAA,EAAM,KAAK,KAAK,CAAA,CAAA;AAAA,gBACzC,QAAA;AAAA,gBACA,OAAA,EAAS,MAAM,QAAA,CAAS,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA,gBACrC,SAAA,EAAU,0SAAA;AAAA,gBAEV,QAAA,kBAAA,GAAA,CAAC,CAAA,EAAA,EAAE,SAAA,EAAU,QAAA,EAAS,eAAW,IAAA,EAAC;AAAA;AAAA,aACpC,GACE;AAAA,WAAA,EACN;AAAA;AAAA,OAAA;AAAA,MAnEK,IAAA,CAAK;AAAA,KAoEZ;AAAA,EAEJ,CAAA;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA;AAAA,MACA,SAAA,EAAQ,wBAAA;AAAA,MACR,SAAA,EAAW,EAAA,CAAG,4BAAA,EAA8B,SAAS,CAAA;AAAA,MAEpD,QAAA,EAAA,QAAA,GACG,SAAS,GAAA,CAAI,CAAC,4BACZ,IAAA,CAAC,KAAA,EAAA,EAAqB,WAAU,qBAAA,EAC9B,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,yEAAA,EACV,QAAA,EAAA,OAAA,CAAQ,KAAA,EACX,CAAA;AAAA,QACC,QAAQ,KAAA,CAAM,GAAA;AAAA,UAAI,CAAC,SAClB,SAAA,CAAU,IAAA,EAAM,QAAQ,SAAA,IAAa,OAAA,CAAQ,IAAA,CAAK,SAAS,CAAC;AAAA;AAC9D,OAAA,EAAA,EANQ,OAAA,CAAQ,EAOlB,CACD,CAAA,GACD,MAAM,GAAA,CAAI,CAAC,IAAA,KAAS,SAAA,CAAU,IAAA,EAAM,OAAA,CAAQ,IAAA,CAAK,SAAS,CAAC,CAAC;AAAA;AAAA,GAClE;AAEJ,CAAC","file":"chunk-EKGZXIUN.js","sourcesContent":["import * as React from 'react'\nimport { X } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '../../lib/utils'\nimport {\n ModelPicker,\n type ModelPickerModel,\n type ModelPickerProvider,\n} from '../model-picker/ModelPicker'\n\nexport interface TaskAssignmentTask {\n id: string\n label: string\n description?: string\n /** Rendered in an icon tile at the row's leading edge. */\n icon?: LucideIcon\n /** When set, the row's picker offers only models matching this capability. */\n requires?: string\n /** Render the fallback-model picker for this row (needs `onAssignSecondary`). */\n secondary?: boolean\n}\n\nexport interface TaskAssignmentSection {\n id: string\n label: string\n /** Show the fallback picker on every task in the section (task-level wins). */\n secondary?: boolean\n tasks: TaskAssignmentTask[]\n}\n\nexport interface TaskAssignmentPickerProps {\n /** Flat rendering; ignored when `sections` is set. */\n tasks?: TaskAssignmentTask[]\n /** Grouped rendering; when set, `tasks` is ignored. */\n sections?: TaskAssignmentSection[]\n providers: ModelPickerProvider[]\n /** Current mapping: taskId → modelId (null/undefined = unassigned). */\n value: Record<string, string | null>\n onAssign: (taskId: string, modelId: string | null) => void\n /** Enables fallback-model rows for tasks flagged (directly or via section). */\n secondaryValue?: Record<string, string | null>\n onAssignSecondary?: (taskId: string, modelId: string | null) => void\n secondaryLabel?: string\n /** App-rendered per-row context (inherit notes, spend, badges). */\n renderMeta?: (task: TaskAssignmentTask) => React.ReactNode\n unassignedLabel?: string\n clearLabel?: string\n modelLabel?: string\n disabled?: boolean\n className?: string\n}\n\nfunction filterProviders(\n providers: ModelPickerProvider[],\n requires: string | undefined,\n): ModelPickerProvider[] {\n if (!requires) return providers\n const matches = (model: ModelPickerModel) =>\n model.capability === requires || (model.capabilities?.includes(requires) ?? false)\n return providers\n .map((provider) => ({\n ...provider,\n models: provider.models.filter(matches),\n }))\n .filter((provider) => provider.models.length > 0)\n}\n\nexport const TaskAssignmentPicker = React.forwardRef<\n HTMLDivElement,\n TaskAssignmentPickerProps\n>(function TaskAssignmentPicker(\n {\n tasks = [],\n sections,\n providers,\n value,\n onAssign,\n secondaryValue,\n onAssignSecondary,\n secondaryLabel = 'Fallback model',\n renderMeta,\n unassignedLabel = 'Not assigned',\n clearLabel = 'Clear assignment',\n modelLabel = 'Model',\n disabled = false,\n className,\n },\n ref,\n) {\n const modelName = React.useCallback(\n (modelId: string | null | undefined) => {\n if (!modelId) return null\n for (const provider of providers) {\n const model = provider.models.find((m) => m.id === modelId)\n if (model) return { name: model.name, provider: provider.name }\n }\n return null\n },\n [providers],\n )\n\n const renderRow = (task: TaskAssignmentTask, showSecondary: boolean) => {\n const assigned = modelName(value[task.id])\n const catalog = filterProviders(providers, task.requires)\n return (\n <div\n key={task.id}\n className=\"flex flex-col gap-2 rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] p-3 sm:flex-row sm:items-center sm:justify-between\"\n >\n <div className=\"flex min-w-0 items-center gap-3\">\n {task.icon ? (\n <span className=\"flex size-9 shrink-0 items-center justify-center rounded-[var(--as-radius)] bg-[var(--as-muted)] text-[var(--as-muted-fg)]\">\n <task.icon className=\"size-4\" aria-hidden />\n </span>\n ) : null}\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-[var(--as-fg)]\">\n {task.label}\n </p>\n {task.description ? (\n <p className=\"truncate text-xs text-[var(--as-muted-fg)]\">\n {task.description}\n </p>\n ) : null}\n {renderMeta ? renderMeta(task) : null}\n <p className=\"mt-0.5 text-xs text-[var(--as-muted-fg)]\">\n {assigned ? (\n <>\n {modelLabel}:{' '}\n <span className=\"font-medium text-[var(--as-fg)]\">\n {assigned.provider} / {assigned.name}\n </span>\n </>\n ) : (\n unassignedLabel\n )}\n </p>\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-1.5\">\n {showSecondary && onAssignSecondary ? (\n <ModelPicker\n providers={catalog}\n value={secondaryValue?.[task.id] ?? ''}\n onChange={(modelId) => onAssignSecondary(task.id, modelId)}\n disabled={disabled}\n clearable={Boolean(secondaryValue?.[task.id])}\n clearLabel={clearLabel}\n label={`${secondaryLabel} — ${task.label}`}\n className=\"w-48\"\n />\n ) : null}\n <ModelPicker\n providers={catalog}\n value={value[task.id] ?? ''}\n onChange={(modelId) => onAssign(task.id, modelId)}\n disabled={disabled}\n clearable={Boolean(value[task.id])}\n clearLabel={clearLabel}\n label={`${modelLabel} — ${task.label}`}\n className=\"w-56\"\n />\n {value[task.id] ? (\n <button\n type=\"button\"\n aria-label={`${clearLabel} — ${task.label}`}\n disabled={disabled}\n onClick={() => onAssign(task.id, null)}\n className=\"cursor-pointer rounded-[var(--as-radius-sm)] p-1.5 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-muted)] hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)] disabled:pointer-events-none disabled:opacity-50\"\n >\n <X className=\"size-4\" aria-hidden />\n </button>\n ) : null}\n </div>\n </div>\n )\n }\n\n return (\n <div\n ref={ref}\n data-as=\"task-assignment-picker\"\n className={cn('flex w-full flex-col gap-3', className)}\n >\n {sections\n ? sections.map((section) => (\n <div key={section.id} className=\"flex flex-col gap-2\">\n <p className=\"text-xs font-semibold uppercase tracking-wide text-[var(--as-muted-fg)]\">\n {section.label}\n </p>\n {section.tasks.map((task) =>\n renderRow(task, section.secondary || Boolean(task.secondary)),\n )}\n </div>\n ))\n : tasks.map((task) => renderRow(task, Boolean(task.secondary)))}\n </div>\n )\n})\n"]}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { cn } from './chunk-BLJWR4ZV.js';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { Check } from 'lucide-react';
|
|
4
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
var CapabilityChips = React.forwardRef(
|
|
7
|
+
function CapabilityChips2({
|
|
8
|
+
caps,
|
|
9
|
+
selected,
|
|
10
|
+
onToggle,
|
|
11
|
+
variant = "toggle",
|
|
12
|
+
minSelected = 0,
|
|
13
|
+
disabled = false,
|
|
14
|
+
ariaLabel,
|
|
15
|
+
className
|
|
16
|
+
}, ref) {
|
|
17
|
+
const interactive = variant === "toggle" && Boolean(onToggle);
|
|
18
|
+
const locked = (value) => interactive && !disabled && selected.includes(value) && selected.length <= minSelected;
|
|
19
|
+
return /* @__PURE__ */ jsx(
|
|
20
|
+
"div",
|
|
21
|
+
{
|
|
22
|
+
ref,
|
|
23
|
+
"data-as": "capability-chips",
|
|
24
|
+
"data-variant": variant,
|
|
25
|
+
role: interactive ? "group" : void 0,
|
|
26
|
+
"aria-label": interactive ? ariaLabel : void 0,
|
|
27
|
+
className: cn("flex flex-wrap items-center gap-1.5", className),
|
|
28
|
+
children: caps.map((cap) => {
|
|
29
|
+
const on = selected.includes(cap.value);
|
|
30
|
+
const Icon = cap.icon;
|
|
31
|
+
if (!interactive) {
|
|
32
|
+
return /* @__PURE__ */ jsxs(
|
|
33
|
+
"span",
|
|
34
|
+
{
|
|
35
|
+
"data-as-cap": cap.value,
|
|
36
|
+
"data-active": on || void 0,
|
|
37
|
+
title: `${cap.label} capability`,
|
|
38
|
+
className: cn(
|
|
39
|
+
"inline-flex items-center gap-1 rounded-[var(--as-radius-sm)] px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide",
|
|
40
|
+
on ? "bg-[var(--as-primary)]/10 text-[var(--as-primary)]" : "bg-[var(--as-muted)] text-[var(--as-muted-fg)]"
|
|
41
|
+
),
|
|
42
|
+
children: [
|
|
43
|
+
Icon ? /* @__PURE__ */ jsx(Icon, { className: "size-2.5", "aria-hidden": true }) : null,
|
|
44
|
+
cap.label
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
cap.value
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
return /* @__PURE__ */ jsxs(
|
|
51
|
+
"button",
|
|
52
|
+
{
|
|
53
|
+
type: "button",
|
|
54
|
+
"data-as-cap": cap.value,
|
|
55
|
+
"aria-pressed": on,
|
|
56
|
+
disabled: disabled || locked(cap.value),
|
|
57
|
+
onClick: () => onToggle?.(cap.value),
|
|
58
|
+
className: cn(
|
|
59
|
+
"flex cursor-pointer items-center gap-1.5 rounded-[var(--as-radius)] border px-3 py-2 text-xs font-semibold uppercase tracking-wider transition-all",
|
|
60
|
+
"focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
|
|
61
|
+
"disabled:pointer-events-none disabled:opacity-60",
|
|
62
|
+
on ? "border-[var(--as-primary)]/30 bg-[var(--as-primary)]/10 text-[var(--as-primary)] shadow-[var(--as-shadow-1)]" : "border-[var(--as-border)] bg-[var(--as-surface)] text-[var(--as-muted-fg)] hover:border-[var(--as-muted-fg)]/40"
|
|
63
|
+
),
|
|
64
|
+
children: [
|
|
65
|
+
Icon ? /* @__PURE__ */ jsx(Icon, { className: "size-3.5", "aria-hidden": true }) : null,
|
|
66
|
+
cap.label,
|
|
67
|
+
on ? /* @__PURE__ */ jsx(Check, { className: "size-3", "aria-hidden": true }) : null
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
cap.value
|
|
71
|
+
);
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
export { CapabilityChips };
|
|
79
|
+
//# sourceMappingURL=chunk-G3DWN2DJ.js.map
|
|
80
|
+
//# sourceMappingURL=chunk-G3DWN2DJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/capability-chips/CapabilityChips.tsx"],"names":["CapabilityChips"],"mappings":";;;;;AAsBO,IAAM,eAAA,GAAwB,KAAA,CAAA,UAAA;AAAA,EACnC,SAASA,gBAAAA,CACP;AAAA,IACE,IAAA;AAAA,IACA,QAAA;AAAA,IACA,QAAA;AAAA,IACA,OAAA,GAAU,QAAA;AAAA,IACV,WAAA,GAAc,CAAA;AAAA,IACd,QAAA,GAAW,KAAA;AAAA,IACX,SAAA;AAAA,IACA;AAAA,KAEF,GAAA,EACA;AACA,IAAA,MAAM,WAAA,GAAc,OAAA,KAAY,QAAA,IAAY,OAAA,CAAQ,QAAQ,CAAA;AAC5D,IAAA,MAAM,MAAA,GAAS,CAAC,KAAA,KACd,WAAA,IAAe,CAAC,QAAA,IAAY,QAAA,CAAS,QAAA,CAAS,KAAK,CAAA,IAAK,QAAA,CAAS,MAAA,IAAU,WAAA;AAE7E,IAAA,uBACE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,SAAA,EAAQ,kBAAA;AAAA,QACR,cAAA,EAAc,OAAA;AAAA,QACd,IAAA,EAAM,cAAc,OAAA,GAAU,MAAA;AAAA,QAC9B,YAAA,EAAY,cAAc,SAAA,GAAY,MAAA;AAAA,QACtC,SAAA,EAAW,EAAA,CAAG,qCAAA,EAAuC,SAAS,CAAA;AAAA,QAE7D,QAAA,EAAA,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,KAAQ;AACjB,UAAA,MAAM,EAAA,GAAK,QAAA,CAAS,QAAA,CAAS,GAAA,CAAI,KAAK,CAAA;AACtC,UAAA,MAAM,OAAO,GAAA,CAAI,IAAA;AACjB,UAAA,IAAI,CAAC,WAAA,EAAa;AAChB,YAAA,uBACE,IAAA;AAAA,cAAC,MAAA;AAAA,cAAA;AAAA,gBAEC,eAAa,GAAA,CAAI,KAAA;AAAA,gBACjB,eAAa,EAAA,IAAM,MAAA;AAAA,gBACnB,KAAA,EAAO,CAAA,EAAG,GAAA,CAAI,KAAK,CAAA,WAAA,CAAA;AAAA,gBACnB,SAAA,EAAW,EAAA;AAAA,kBACT,8HAAA;AAAA,kBACA,KACI,oDAAA,GACA;AAAA,iBACN;AAAA,gBAEC,QAAA,EAAA;AAAA,kBAAA,IAAA,uBAAQ,IAAA,EAAA,EAAK,SAAA,EAAU,UAAA,EAAW,aAAA,EAAW,MAAC,CAAA,GAAK,IAAA;AAAA,kBACnD,GAAA,CAAI;AAAA;AAAA,eAAA;AAAA,cAZA,GAAA,CAAI;AAAA,aAaX;AAAA,UAEJ;AACA,UAAA,uBACE,IAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cAEC,IAAA,EAAK,QAAA;AAAA,cACL,eAAa,GAAA,CAAI,KAAA;AAAA,cACjB,cAAA,EAAc,EAAA;AAAA,cACd,QAAA,EAAU,QAAA,IAAY,MAAA,CAAO,GAAA,CAAI,KAAK,CAAA;AAAA,cACtC,OAAA,EAAS,MAAM,QAAA,GAAW,GAAA,CAAI,KAAK,CAAA;AAAA,cACnC,SAAA,EAAW,EAAA;AAAA,gBACT,oJAAA;AAAA,gBACA,qGAAA;AAAA,gBACA,kDAAA;AAAA,gBACA,KACI,8GAAA,GACA;AAAA,eACN;AAAA,cAEC,QAAA,EAAA;AAAA,gBAAA,IAAA,uBAAQ,IAAA,EAAA,EAAK,SAAA,EAAU,UAAA,EAAW,aAAA,EAAW,MAAC,CAAA,GAAK,IAAA;AAAA,gBACnD,GAAA,CAAI,KAAA;AAAA,gBACJ,qBAAK,GAAA,CAAC,KAAA,EAAA,EAAM,WAAU,QAAA,EAAS,aAAA,EAAW,MAAC,CAAA,GAAK;AAAA;AAAA,aAAA;AAAA,YAjB5C,GAAA,CAAI;AAAA,WAkBX;AAAA,QAEJ,CAAC;AAAA;AAAA,KACH;AAAA,EAEJ;AACF","file":"chunk-G3DWN2DJ.js","sourcesContent":["import * as React from 'react'\nimport { Check } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '../../lib/utils'\n\nexport interface CapabilityDescriptor {\n value: string\n label: string\n icon?: LucideIcon\n}\n\nexport interface CapabilityChipsProps {\n caps: CapabilityDescriptor[]\n selected: string[]\n onToggle?: (value: string) => void\n variant?: 'toggle' | 'badge'\n minSelected?: number\n disabled?: boolean\n ariaLabel?: string\n className?: string\n}\n\nexport const CapabilityChips = React.forwardRef<HTMLDivElement, CapabilityChipsProps>(\n function CapabilityChips(\n {\n caps,\n selected,\n onToggle,\n variant = 'toggle',\n minSelected = 0,\n disabled = false,\n ariaLabel,\n className,\n },\n ref,\n ) {\n const interactive = variant === 'toggle' && Boolean(onToggle)\n const locked = (value: string) =>\n interactive && !disabled && selected.includes(value) && selected.length <= minSelected\n\n return (\n <div\n ref={ref}\n data-as=\"capability-chips\"\n data-variant={variant}\n role={interactive ? 'group' : undefined}\n aria-label={interactive ? ariaLabel : undefined}\n className={cn('flex flex-wrap items-center gap-1.5', className)}\n >\n {caps.map((cap) => {\n const on = selected.includes(cap.value)\n const Icon = cap.icon\n if (!interactive) {\n return (\n <span\n key={cap.value}\n data-as-cap={cap.value}\n data-active={on || undefined}\n title={`${cap.label} capability`}\n className={cn(\n 'inline-flex items-center gap-1 rounded-[var(--as-radius-sm)] px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide',\n on\n ? 'bg-[var(--as-primary)]/10 text-[var(--as-primary)]'\n : 'bg-[var(--as-muted)] text-[var(--as-muted-fg)]',\n )}\n >\n {Icon ? <Icon className=\"size-2.5\" aria-hidden /> : null}\n {cap.label}\n </span>\n )\n }\n return (\n <button\n key={cap.value}\n type=\"button\"\n data-as-cap={cap.value}\n aria-pressed={on}\n disabled={disabled || locked(cap.value)}\n onClick={() => onToggle?.(cap.value)}\n className={cn(\n 'flex cursor-pointer items-center gap-1.5 rounded-[var(--as-radius)] border px-3 py-2 text-xs font-semibold uppercase tracking-wider transition-all',\n 'focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]',\n 'disabled:pointer-events-none disabled:opacity-60',\n on\n ? 'border-[var(--as-primary)]/30 bg-[var(--as-primary)]/10 text-[var(--as-primary)] shadow-[var(--as-shadow-1)]'\n : 'border-[var(--as-border)] bg-[var(--as-surface)] text-[var(--as-muted-fg)] hover:border-[var(--as-muted-fg)]/40',\n )}\n >\n {Icon ? <Icon className=\"size-3.5\" aria-hidden /> : null}\n {cap.label}\n {on ? <Check className=\"size-3\" aria-hidden /> : null}\n </button>\n )\n })}\n </div>\n )\n },\n)\n"]}
|
|
@@ -5,14 +5,158 @@ import * as PopoverPrimitive from '@radix-ui/react-popover';
|
|
|
5
5
|
import { ChevronDown, X, Search, Check } from 'lucide-react';
|
|
6
6
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
7
7
|
|
|
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
|
+
}
|
|
8
141
|
function defaultFilter(options, term) {
|
|
9
|
-
|
|
142
|
+
const needle = term.trim();
|
|
143
|
+
if (!needle) {
|
|
10
144
|
return options;
|
|
11
145
|
}
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
(
|
|
146
|
+
const scored = options.flatMap((option) => {
|
|
147
|
+
let best = null;
|
|
148
|
+
for (const target of [option.label, option.value, option.description ?? ""]) {
|
|
149
|
+
const score = searchScore(needle, target);
|
|
150
|
+
if (score !== null && (best === null || score > best)) {
|
|
151
|
+
best = score;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return best === null ? [] : [{ option, score: best }];
|
|
155
|
+
});
|
|
156
|
+
scored.sort(
|
|
157
|
+
(a, b) => b.score - a.score || a.option.label.localeCompare(b.option.label)
|
|
15
158
|
);
|
|
159
|
+
return scored.map((entry) => entry.option);
|
|
16
160
|
}
|
|
17
161
|
function ComboboxList({
|
|
18
162
|
id,
|
|
@@ -162,7 +306,7 @@ function ComboboxPanel(props) {
|
|
|
162
306
|
}
|
|
163
307
|
},
|
|
164
308
|
className: cn(
|
|
165
|
-
"as-anim-pop 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)]",
|
|
309
|
+
"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)]",
|
|
166
310
|
panelClassName
|
|
167
311
|
),
|
|
168
312
|
"data-as": "combobox-panel",
|
|
@@ -252,7 +396,7 @@ var Combobox = React.forwardRef(function Combobox2({
|
|
|
252
396
|
};
|
|
253
397
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex w-full flex-col gap-1.5", className), children: [
|
|
254
398
|
label ? /* @__PURE__ */ jsx("label", { htmlFor: id, className: "text-sm font-medium leading-none text-[var(--as-fg)]", children: label }) : null,
|
|
255
|
-
/* @__PURE__ */ jsxs(PopoverPrimitive.Root, { open, onOpenChange: handleOpenChange, children: [
|
|
399
|
+
/* @__PURE__ */ jsxs(PopoverPrimitive.Root, { modal: true, open, onOpenChange: handleOpenChange, children: [
|
|
256
400
|
/* @__PURE__ */ jsxs("span", { className: "relative inline-flex w-full", children: [
|
|
257
401
|
/* @__PURE__ */ jsx(PopoverPrimitive.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
258
402
|
"button",
|
|
@@ -451,5 +595,5 @@ var ComboboxMulti = React.forwardRef(
|
|
|
451
595
|
);
|
|
452
596
|
|
|
453
597
|
export { Combobox, ComboboxMulti };
|
|
454
|
-
//# sourceMappingURL=chunk-
|
|
455
|
-
//# sourceMappingURL=chunk-
|
|
598
|
+
//# sourceMappingURL=chunk-LC7CYUY4.js.map
|
|
599
|
+
//# sourceMappingURL=chunk-LC7CYUY4.js.map
|