@neuronection/assistant-ui 0.7.1 → 0.8.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 +28 -0
- package/README.md +43 -52
- package/dist/ai-button.d.ts +8 -0
- package/dist/ai-button.js +1 -1
- package/dist/{chunk-L56KGPGS.js → chunk-3VCFRIIK.js} +16 -4
- package/dist/chunk-3VCFRIIK.js.map +1 -0
- package/dist/chunk-CR42EAF4.js +463 -0
- package/dist/chunk-CR42EAF4.js.map +1 -0
- package/dist/chunk-GVOC3ARM.js +163 -0
- package/dist/chunk-GVOC3ARM.js.map +1 -0
- package/dist/chunk-KLWKZSYM.js +3 -0
- package/dist/chunk-KLWKZSYM.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/time-list.d.ts +30 -0
- package/dist/time-list.js +3 -0
- package/dist/time-list.js.map +1 -0
- package/dist/time-picker.d.ts +39 -0
- package/dist/time-picker.js +3 -0
- package/dist/time-picker.js.map +1 -0
- package/package.json +9 -1
- package/dist/chunk-L56KGPGS.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @neuronection/assistant-ui
|
|
2
2
|
|
|
3
|
+
## 0.8.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`0421e0e`](https://github.com/neuronection/assistant-ui/commit/0421e0e02ed994182ac3d2e79c23f786aff87b36) Thanks [@constLiakos](https://github.com/constLiakos)! - README: family showcase — neuronection.com, the three Assistant apps
|
|
8
|
+
(career / study / health-assistant.io) front and center; complete component
|
|
9
|
+
index incl. PanelModal, Textarea and the dark-mode / z-token theming notes.
|
|
10
|
+
Docs: correct CSS import order in theming.md, TW3 translate-collision +
|
|
11
|
+
z-index notes in adoption.md, positioning/z-index/audit-import rules in
|
|
12
|
+
adding-a-component.md, AGENTS.md gotchas updated.
|
|
13
|
+
|
|
14
|
+
## 0.8.0
|
|
15
|
+
|
|
16
|
+
### Minor Changes
|
|
17
|
+
|
|
18
|
+
- [`8f92588`](https://github.com/neuronection/assistant-ui/commit/8f92588f6b294c8661fd56d57991c394a1ac9dc1) Thanks [@constLiakos](https://github.com/constLiakos)! - New `TimePicker` (+ standalone `TimePickerContent`): clock-face time picker
|
|
19
|
+
with a 12h AM/PM UI over a 24-hour `HH:MM` value, editable HH:MM fields
|
|
20
|
+
(typing 13–23 or 0 normalizes the period), keyboard-navigable dial, and
|
|
21
|
+
default/unstyled trigger variants. New `TimeList`: chip-based editor for
|
|
22
|
+
`HH:MM` lists with per-chip pickers, remove-on-hover and a max cap.
|
|
23
|
+
Ported from health-assistant (i18n → label props, Radix popover, tokens).
|
|
24
|
+
|
|
25
|
+
### Patch Changes
|
|
26
|
+
|
|
27
|
+
- [`077def0`](https://github.com/neuronection/assistant-ui/commit/077def0835ab7d962b7dec04fd4e2cd26ff08bc3) Thanks [@constLiakos](https://github.com/constLiakos)! - `AiButton`: new `showLabel` (icon-only trigger), `closeOnSubmit`
|
|
28
|
+
(auto-close after submit) and controlled `open`/`onOpenChange` props
|
|
29
|
+
(close-on-success form-fill flows).
|
|
30
|
+
|
|
3
31
|
## 0.7.1
|
|
4
32
|
|
|
5
33
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
# assistant-ui
|
|
2
2
|
|
|
3
|
-
Shared React component library for the **Assistant family**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
Shared React component library for the **Assistant family** — three sibling
|
|
4
|
+
apps that AI-help people with career, study, and health. One package, three
|
|
5
|
+
apps: family look by default, app flavor via CSS-variable design tokens.
|
|
6
|
+
Works in React 18 & 19 and alongside Tailwind 3 or 4 — the library ships
|
|
7
|
+
**precompiled CSS**, so your app's Tailwind never compiles library classes.
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
**[neuronection.com](https://neuronection.com)** — the family hub.
|
|
10
|
+
|
|
11
|
+
| App | What it is |
|
|
12
|
+
|---|---|
|
|
13
|
+
| [**career-assistant**](https://github.com/neuronection/career-assistant) | Job discovery, AI matching and university pathways for students |
|
|
14
|
+
| [**study-assistant**](https://github.com/neuronection/study-assistant) | Study workspace: notes, materials, chat with math & code rendering |
|
|
15
|
+
| [**health-assistant**](https://health-assistant.io) | Universal health data platform — self-hosted, privacy-first, [open source](https://github.com/health-assistant-io/health-assistant) |
|
|
16
|
+
|
|
17
|
+
The family shares its UI DNA here: **40+ modules, 260+ tests** (keyboard-nav
|
|
18
|
+
+ axe per component), built on Radix, token-themed so every app looks like a
|
|
19
|
+
sibling without sharing a stack. Patterns the apps actually use — provider &
|
|
20
|
+
model settings blocks, AI affordances (ask/fill/act), date & clock pickers,
|
|
21
|
+
async comboboxes, file attachment surfaces — live in the package, not
|
|
22
|
+
copy-pasted in three repos.
|
|
12
23
|
|
|
13
24
|
## Install
|
|
14
25
|
|
|
@@ -30,8 +41,8 @@ import { Button, ConfirmationModal, Input } from '@neuronection/assistant-ui'
|
|
|
30
41
|
## Theming
|
|
31
42
|
|
|
32
43
|
All visuals flow through `--as-*` CSS variables. The defaults are the family
|
|
33
|
-
look (extracted from study-assistant); each app overrides identity tokens in
|
|
34
|
-
small `theme.css`:
|
|
44
|
+
look (extracted from study-assistant); each app overrides identity tokens in
|
|
45
|
+
a small `theme.css`:
|
|
35
46
|
|
|
36
47
|
```css
|
|
37
48
|
:root {
|
|
@@ -40,28 +51,10 @@ small `theme.css`:
|
|
|
40
51
|
}
|
|
41
52
|
```
|
|
42
53
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
## Peer requirements
|
|
48
|
-
|
|
49
|
-
| Package | Range |
|
|
50
|
-
|---|---|
|
|
51
|
-
| react / react-dom | `>=18 <20` |
|
|
52
|
-
| lucide-react | `>=0.460 <2` |
|
|
53
|
-
|
|
54
|
-
Tailwind is **not** required — but if your app uses Tailwind 3 or 4, both work
|
|
55
|
-
without configuration. Do **not** add this package to your Tailwind
|
|
56
|
-
`content`/`@source`.
|
|
57
|
-
|
|
58
|
-
## Documentation
|
|
59
|
-
|
|
60
|
-
- [Local development](./docs/local-development.md) — live-test library changes in the family apps without publishing
|
|
61
|
-
- [Adopting in an app](./docs/adoption.md) — the install/switchover recipe each family app follows
|
|
62
|
-
- [Theming](./docs/theming.md) — tokens, app themes, styling hooks
|
|
63
|
-
- [Publishing](./docs/publishing.md) — the Changesets release flow and CI setup
|
|
64
|
-
- [Adding a component](./docs/adding-a-component.md) — rules, checklist, required companions
|
|
54
|
+
Dark mode is the same story: remap the tokens under `.dark` (health-assistant
|
|
55
|
+
ships a class-based dark theme this way) — no component rewrites. Overlay
|
|
56
|
+
stacking is token-driven too (`--as-z-modal`, `--as-z-popover`) so library
|
|
57
|
+
dialogs clear your app chrome. Full guide: [docs/theming.md](./docs/theming.md).
|
|
65
58
|
|
|
66
59
|
## Components
|
|
67
60
|
|
|
@@ -74,25 +67,25 @@ files in the package's `dist/`. Visual reference:
|
|
|
74
67
|
| Module | Exports |
|
|
75
68
|
|---|---|
|
|
76
69
|
| `button` / `badge` / `card` | `Button`, `buttonVariants` · `Badge`, `badgeVariants` · `Card`, `CardHeader/Title/Description/Content/Footer` |
|
|
77
|
-
| `modal` / `
|
|
70
|
+
| `modal` / `panel-modal` | `Modal` + parts (compound Radix dialog) · `PanelModal` (header/body/footer shell, full-screen on mobile) |
|
|
71
|
+
| `confirmation-modal` / `form-modal` | `ConfirmationModal` · `FormModal` (form-in-modal shell: `headerActions`, reject button, busy states) |
|
|
78
72
|
| `popover` / `popover-button` | compound Radix `Popover` + parts · `PopoverButton` (self-contained trigger + panel, hover-open, lazy children, `closeSignal`) |
|
|
79
73
|
| `menu` / `context-menu` | `Menu`, `MenuTrigger/Content/Item/Separator/Label`, `ActionMenu` (items-driven) · `ContextMenu` (coordinate-anchored, `{x, y, items, onClose}`) |
|
|
80
|
-
| `combobox` | `Combobox`, `ComboboxMulti` — async mode (`onSearchChange` + `loading`), grouping, full keyboard nav |
|
|
74
|
+
| `combobox` | `Combobox`, `ComboboxMulti` — async mode (`onSearchChange` + `loading`), grouping, capability badges, full keyboard nav |
|
|
81
75
|
| `tooltip` | `Tooltip` + parts, `InfoTooltip` |
|
|
82
76
|
| `wizard` | `Wizard` (steps config + `renderStep`, per-step validation gates, modal/drawer variants), `Stepper` (dots/labels) |
|
|
83
|
-
| `input` / `search-input` / `expandable-search` | `Input` (label/hint/error wiring) · `SearchInput` · `ExpandableSearch` |
|
|
77
|
+
| `input` / `textarea` / `search-input` / `expandable-search` | `Input` (label/hint/error wiring) · `Textarea` · `SearchInput` · `ExpandableSearch` |
|
|
84
78
|
| `chip-input` / `chip-list` | `ChipInput` (Enter/comma commit, paste-splits, Backspace-removes) · `ChipList` (variant pills, clickable, removable) |
|
|
85
|
-
| `
|
|
79
|
+
| `time-picker` / `time-list` | `TimePicker` (clock face, 24h value, 12h UI, editable fields) · `TimeList` (time chips with per-chip pickers) |
|
|
80
|
+
| `date-picker` | `DatePicker` (popover calendar: days/months/years views, min/max, `allowClear`, arrow-key day grid, `unstyled` variant) |
|
|
86
81
|
| `range-bar` / `scale-slider` | `RangeBar` (low–high band + value dot) · `ScaleSlider` (+ `scaleColorForValue`) |
|
|
87
|
-
| `table` | `Table` (headers/rows,
|
|
82
|
+
| `table` | `Table` (headers/rows, opt-in empty state) |
|
|
88
83
|
| `check-indicator` / `selection-bar` / `view-toggle` | `CheckIndicator` (tri-state) · `SelectionBar` (bulk-select bar) · `ViewToggle` (grid/list) |
|
|
89
84
|
| `error-banner` / `undo-notice` / `empty-state` / `spinner` | presentational feedback: alert banner with `action` slot · undo toast · `EmptyState` · `Spinner` |
|
|
90
|
-
| `info-button` / `field-label` | `InfoButton` (
|
|
85
|
+
| `info-button` / `field-label` / `copy-button` / `breadcrumbs` | `InfoButton` · `FieldLabel` · `CopyButton` (clipboard + copied state) · `Breadcrumbs` (SPA links via `linkComponent`) |
|
|
86
|
+
| `ai-button` / `ai-actions-dropdown` / `ai-magic-fill` | AI affordances: ask-with-suggestions (controlled open, icon-only mode) · action menu + custom prompt · describe-in-words fill modal — API calls stay app-side |
|
|
87
|
+
| `settings-shell` / `provider-form` / `connection-test-row` | `SettingsShell` (controlled two-pane nav) · `ProviderForm` (write-only API-key field) · `ConnectionTestRow` (idle/testing/ok/fail + latency) |
|
|
91
88
|
| `model-picker` / `task-assignment-picker` | `ModelPicker` (grouped provider→model combobox, capability chips) · `TaskAssignmentPicker` (task → model mapping rows) |
|
|
92
|
-
| `settings-shell` / `provider-form` / `connection-test-row` | `SettingsShell` (controlled two-pane nav) · `ProviderForm` (write-only key field) · `ConnectionTestRow` (idle/testing/ok/fail) |
|
|
93
|
-
| `copy-button` / `breadcrumbs` | `CopyButton` (clipboard + copied state) · `Breadcrumbs` (SPA links via `linkComponent`) |
|
|
94
|
-
| `ai-magic-fill` | `AiMagicFill` (describe-in-words → structured data prompt modal; app applies the data) |
|
|
95
|
-
| `ai-button` / `ai-actions-dropdown` | `AiButton` (suggestions + prompt + response slot) · `AiActionsDropdown` (action list + custom prompt) |
|
|
96
89
|
| `upload-dropzone` / `file-card` / `file-queue` | file attachment surface: dropzone (block/row) · card (status, include toggle) · queue (summary + reorder) |
|
|
97
90
|
| `marquee` | `useMarquee`, `MarqueeSurface`, `MarqueeBand` (rubber-band selection) |
|
|
98
91
|
| `portal` / `theme-scope` / `tokens` | `Portal` · `ThemeScope` · token name lists + types |
|
|
@@ -102,6 +95,14 @@ where it makes sense, refs forwarded everywhere, English label props with
|
|
|
102
95
|
defaults (translate at call sites), icons as props. No fetching, no stores,
|
|
103
96
|
no router — data in, events out.
|
|
104
97
|
|
|
98
|
+
## Documentation
|
|
99
|
+
|
|
100
|
+
- [Adopting in an app](./docs/adoption.md) — the install/switchover recipe each family app follows (incl. the TW3 cascade notes)
|
|
101
|
+
- [Theming](./docs/theming.md) — tokens, app themes, dark mode, styling hooks
|
|
102
|
+
- [Adding a component](./docs/adding-a-component.md) — rules, checklist, required companions
|
|
103
|
+
- [Local development](./docs/local-development.md) — live-test library changes in the family apps without publishing
|
|
104
|
+
- [Publishing](./docs/publishing.md) — the Changesets release flow and CI setup
|
|
105
|
+
|
|
105
106
|
## Development
|
|
106
107
|
|
|
107
108
|
```bash
|
|
@@ -111,16 +112,6 @@ pnpm test --watch
|
|
|
111
112
|
pnpm verify # lint + typecheck + test + build
|
|
112
113
|
```
|
|
113
114
|
|
|
114
|
-
Testing library changes inside a real app (no publishing needed) — full
|
|
115
|
-
guide in [docs/local-development.md](./docs/local-development.md):
|
|
116
|
-
|
|
117
|
-
```bash
|
|
118
|
-
pnpm watch # library: rebuild TS + CSS on change
|
|
119
|
-
node scripts/dev-link.mjs link <app-dir> # wire app (pnpm override / npm file:)
|
|
120
|
-
# ...run the app dev server; reload picks up library changes
|
|
121
|
-
node scripts/dev-link.mjs unlink <app-dir> # restore before committing in the app
|
|
122
|
-
```
|
|
123
|
-
|
|
124
115
|
Releases happen via [Changesets](./CONTRIBUTING.md) — CI publishes, never a
|
|
125
116
|
laptop.
|
|
126
117
|
|
package/dist/ai-button.d.ts
CHANGED
|
@@ -12,6 +12,14 @@ interface AiButtonProps {
|
|
|
12
12
|
loadingLabel?: string;
|
|
13
13
|
placeholder?: string;
|
|
14
14
|
disabled?: boolean;
|
|
15
|
+
/** Controlled open state (defaults to internal). */
|
|
16
|
+
open?: boolean;
|
|
17
|
+
defaultOpen?: boolean;
|
|
18
|
+
onOpenChange?: (open: boolean) => void;
|
|
19
|
+
/** Show the label text next to the icon (default true; icon-only when false). */
|
|
20
|
+
showLabel?: boolean;
|
|
21
|
+
/** Close the panel after a submit (form-fill flows; Q&A keeps it open). */
|
|
22
|
+
closeOnSubmit?: boolean;
|
|
15
23
|
side?: 'top' | 'bottom' | 'left' | 'right';
|
|
16
24
|
align?: 'start' | 'center' | 'end';
|
|
17
25
|
className?: string;
|
package/dist/ai-button.js
CHANGED
|
@@ -17,18 +17,30 @@ var AiButton = React.forwardRef(
|
|
|
17
17
|
loadingLabel = "Thinking\u2026",
|
|
18
18
|
placeholder,
|
|
19
19
|
disabled = false,
|
|
20
|
+
open: openProp,
|
|
21
|
+
defaultOpen = false,
|
|
22
|
+
onOpenChange,
|
|
23
|
+
showLabel = true,
|
|
24
|
+
closeOnSubmit = false,
|
|
20
25
|
side = "bottom",
|
|
21
26
|
align = "end",
|
|
22
27
|
className,
|
|
23
28
|
panelClassName
|
|
24
29
|
}, ref) {
|
|
25
|
-
const [
|
|
30
|
+
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen);
|
|
31
|
+
const isControlled = openProp !== void 0;
|
|
32
|
+
const open = isControlled ? openProp : uncontrolledOpen;
|
|
26
33
|
const [prompt, setPrompt] = React.useState("");
|
|
34
|
+
const setOpen = (next) => {
|
|
35
|
+
if (!isControlled) setUncontrolledOpen(next);
|
|
36
|
+
onOpenChange?.(next);
|
|
37
|
+
};
|
|
27
38
|
const send = (value) => {
|
|
28
39
|
const trimmed = value.trim();
|
|
29
40
|
if (!trimmed || loading) return;
|
|
30
41
|
onSubmit(trimmed);
|
|
31
42
|
setPrompt("");
|
|
43
|
+
if (closeOnSubmit) setOpen(false);
|
|
32
44
|
};
|
|
33
45
|
return /* @__PURE__ */ jsx(Popover, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs("span", { className: "relative inline-flex", children: [
|
|
34
46
|
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, disabled, children: /* @__PURE__ */ jsxs(
|
|
@@ -44,7 +56,7 @@ var AiButton = React.forwardRef(
|
|
|
44
56
|
),
|
|
45
57
|
children: [
|
|
46
58
|
/* @__PURE__ */ jsx(Sparkles, { className: "size-3.5", "aria-hidden": true }),
|
|
47
|
-
label
|
|
59
|
+
showLabel ? label : null
|
|
48
60
|
]
|
|
49
61
|
}
|
|
50
62
|
) }),
|
|
@@ -111,5 +123,5 @@ var AiButton = React.forwardRef(
|
|
|
111
123
|
);
|
|
112
124
|
|
|
113
125
|
export { AiButton };
|
|
114
|
-
//# sourceMappingURL=chunk-
|
|
115
|
-
//# sourceMappingURL=chunk-
|
|
126
|
+
//# sourceMappingURL=chunk-3VCFRIIK.js.map
|
|
127
|
+
//# sourceMappingURL=chunk-3VCFRIIK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ai-button/AiButton.tsx"],"names":["AiButton"],"mappings":"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","file":"chunk-3VCFRIIK.js","sourcesContent":["import * as React from 'react'\nimport { Send, Sparkles } from 'lucide-react'\nimport { cn } from '../../lib/utils'\nimport { Popover, PopoverContent, PopoverTrigger } from '../popover'\n\nexport interface AiButtonProps {\n onSubmit: (prompt: string) => void\n suggestions?: string[]\n onResponse?: React.ReactNode\n loading?: boolean\n error?: string | null\n label?: string\n promptLabel?: string\n submitLabel?: string\n loadingLabel?: string\n placeholder?: string\n disabled?: boolean\n /** Controlled open state (defaults to internal). */\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n /** Show the label text next to the icon (default true; icon-only when false). */\n showLabel?: boolean\n /** Close the panel after a submit (form-fill flows; Q&A keeps it open). */\n closeOnSubmit?: boolean\n side?: 'top' | 'bottom' | 'left' | 'right'\n align?: 'start' | 'center' | 'end'\n className?: string\n panelClassName?: string\n}\n\nexport const AiButton = React.forwardRef<HTMLButtonElement, AiButtonProps>(\n function AiButton(\n {\n onSubmit,\n suggestions = [],\n onResponse,\n loading = false,\n error,\n label = 'Ask AI',\n promptLabel = 'Ask a question',\n submitLabel = 'Send',\n loadingLabel = 'Thinking…',\n placeholder,\n disabled = false,\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n showLabel = true,\n closeOnSubmit = false,\n side = 'bottom',\n align = 'end',\n className,\n panelClassName,\n },\n ref,\n ) {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n const isControlled = openProp !== undefined\n const open = isControlled ? openProp : uncontrolledOpen\n const [prompt, setPrompt] = React.useState('')\n\n const setOpen = (next: boolean) => {\n if (!isControlled) setUncontrolledOpen(next)\n onOpenChange?.(next)\n }\n\n const send = (value: string) => {\n const trimmed = value.trim()\n if (!trimmed || loading) return\n onSubmit(trimmed)\n setPrompt('')\n if (closeOnSubmit) setOpen(false)\n }\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <span className=\"relative inline-flex\">\n <PopoverTrigger asChild disabled={disabled}>\n <button\n ref={ref}\n type=\"button\"\n data-as=\"ai-button\"\n aria-label={label}\n className={cn(\n 'inline-flex cursor-pointer items-center gap-1 rounded-full bg-[color-mix(in_srgb,var(--as-ai)_12%,transparent)] px-2.5 py-1 text-xs font-medium text-[var(--as-ai)] transition-[filter] hover:brightness-95 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)] disabled:pointer-events-none disabled:opacity-50',\n className,\n )}\n >\n <Sparkles className=\"size-3.5\" aria-hidden />\n {showLabel ? label : null}\n </button>\n </PopoverTrigger>\n <PopoverContent\n side={side}\n align={align}\n aria-label={label}\n className={cn('w-72 p-3', panelClassName)}\n >\n <form\n data-as=\"ai-button-panel\"\n onSubmit={(event) => {\n event.preventDefault()\n send(prompt)\n }}\n >\n {suggestions.length > 0 ? (\n <div className=\"mb-2 flex flex-wrap gap-1\">\n {suggestions.map((suggestion) => (\n <button\n key={suggestion}\n type=\"button\"\n disabled={loading}\n onClick={() => send(suggestion)}\n className=\"cursor-pointer rounded-[var(--as-radius-sm)] bg-[var(--as-secondary)] px-2 py-1 text-xs transition-colors hover:bg-[color-mix(in_srgb,var(--as-ai)_14%,var(--as-secondary))] disabled:pointer-events-none disabled:opacity-50\"\n >\n {suggestion}\n </button>\n ))}\n </div>\n ) : null}\n <div className=\"mb-2 flex items-center gap-2\">\n <input\n type=\"text\"\n aria-label={promptLabel}\n placeholder={placeholder ?? promptLabel}\n value={prompt}\n onChange={(event) => setPrompt(event.target.value)}\n className=\"h-8 w-full rounded-[var(--as-radius-sm)] border border-[var(--as-border)] bg-[var(--as-surface)] px-2 text-sm outline-none placeholder:text-[var(--as-muted-fg)] focus-visible:border-[var(--as-focus-ring)]\"\n />\n <button\n type=\"submit\"\n aria-label={submitLabel}\n disabled={loading || !prompt.trim()}\n className=\"shrink-0 cursor-pointer rounded-[var(--as-radius-sm)] bg-[var(--as-ai)] p-1.5 text-[var(--as-ai-fg)] transition-opacity hover:opacity-90 disabled:pointer-events-none disabled:opacity-40\"\n >\n <Send className=\"size-3.5\" aria-hidden />\n </button>\n </div>\n {loading ? (\n <p className=\"text-sm text-[var(--as-muted-fg)]\" role=\"status\">\n {loadingLabel}\n </p>\n ) : null}\n {error ? (\n <p role=\"alert\" className=\"text-sm text-[var(--as-danger)]\">\n {error}\n </p>\n ) : null}\n {onResponse}\n </form>\n </PopoverContent>\n </span>\n </Popover>\n )\n },\n)\n"]}
|