@neuronection/assistant-ui 0.29.1 → 0.30.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 +19 -0
- package/README.md +1 -1
- package/dist/chat-composer.js +1 -1
- package/dist/chat-launcher.d.ts +8 -0
- package/dist/chat-launcher.js +1 -1
- package/dist/{chunk-743FJDRA.js → chunk-IN6ORI3V.js} +9 -2
- package/dist/chunk-IN6ORI3V.js.map +1 -0
- package/dist/{chunk-RPLEI6LQ.js → chunk-OUJJLZ6C.js} +5 -4
- package/dist/chunk-OUJJLZ6C.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-743FJDRA.js.map +0 -1
- package/dist/chunk-RPLEI6LQ.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @neuronection/assistant-ui
|
|
2
2
|
|
|
3
|
+
## 0.30.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`815aecb`](https://github.com/neuronection/assistant-ui/commit/815aecbbbde173d2b569a2552d1773d7eaca84c2) Thanks [@constLiakos](https://github.com/constLiakos)! - ChatComposer: the input row now exposes styling hooks —
|
|
8
|
+
`data-as="chat-composer-row"` plus a `data-multiline` attribute that is
|
|
9
|
+
present whenever the auto-growing textarea renders more than one line
|
|
10
|
+
(measured in the same layout pass as the auto-grow height). Layout stays
|
|
11
|
+
unchanged by default; apps opt in via the hook (first consumer:
|
|
12
|
+
desktop-assistant wraps the toolbars into a footer row under the
|
|
13
|
+
full-width textarea once a draft is multiline).
|
|
14
|
+
|
|
15
|
+
- [`068b817`](https://github.com/neuronection/assistant-ui/commit/068b8174e2701a20d9b4beae411127cbaf0f826c) Thanks [@constLiakos](https://github.com/constLiakos)! - ChatLauncher: new `showClose` prop (default `true`). Set it to `false`
|
|
16
|
+
when the panel body renders its own header actions — the overlaid
|
|
17
|
+
close button pinned to the panel's top-right corner intercepted clicks
|
|
18
|
+
meant for top-right header buttons (career's bubble panel: the
|
|
19
|
+
"New chat" action was unclickable with a mouse). Compose the close
|
|
20
|
+
into the header actions via `onOpenChange(false)` instead.
|
|
21
|
+
|
|
3
22
|
## 0.29.1
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -91,7 +91,7 @@ Visual reference: [gallery](https://neuronection.github.io/assistant-ui/).
|
|
|
91
91
|
| [`chat-core`](./docs/components/chat-core.md) | `useChatStream` (transport-injected live-turn state machine), `liveTurnReducer`, `ChatMessageView`, family `ChatStreamEvent` vocabulary, branch-tree utils (`buildBranchTree`, `walkActivePath`, `variantInfo`) | [stories](https://neuronection.github.io/assistant-ui/?story=chat-core--chat-core-story) |
|
|
92
92
|
| [`chat-markdown`](./docs/components/chat-markdown.md) | `MarkdownSurface` (read-only streaming markdown: GFM + KaTeX + lazy mermaid + copyable code blocks; no raw HTML) · `MarkdownCodeBlock` · `MermaidDiagram` | [stories](https://neuronection.github.io/assistant-ui/?story=chat-markdown--default) |
|
|
93
93
|
| [`chat-message`](./docs/components/chat-message.md) / [`chat-reasoning`](./docs/components/chat-reasoning.md) / [`chat-tool-card`](./docs/components/chat-tool-card.md) / [`chat-turn-status`](./docs/components/chat-turn-status.md) | `ChatMessage` (bubble: actions row, variant switcher, edit-and-resend, error/interrupted states) · `ChatReasoning` (collapsible thinking block) · `ChatToolCard` (tool observation: status, duration, args/result, app-provided result views via `renderResult`) · `ChatTurnStatus` (live pre-text turn status: dots, phase label, elapsed timer) | [stories](https://neuronection.github.io/assistant-ui/?story=chat-message--assistant) |
|
|
94
|
-
| [`chat-composer`](./docs/components/chat-composer.md) | `ChatComposer` — auto-growing IME-safe input with send/stop states, toolbar/attachment/suggestion slots, drag-drop + paste wiring | [stories](https://neuronection.github.io/assistant-ui/?story=chat-composer--default) |
|
|
94
|
+
| [`chat-composer`](./docs/components/chat-composer.md) | `ChatComposer` — auto-growing IME-safe input with send/stop states, toolbar/attachment/suggestion slots, drag-drop + paste wiring, `data-multiline` row styling hook | [stories](https://neuronection.github.io/assistant-ui/?story=chat-composer--default) |
|
|
95
95
|
| [`chat-transcript`](./docs/components/chat-transcript.md) / [`chat-branch-tree`](./docs/components/chat-branch-tree.md) / [`chat-session-list`](./docs/components/chat-session-list.md) | `ChatTranscript` (log semantics, polite announcements, stick-to-bottom + jump pill, optional virtualization) · `ChatBranchTree` (OpenWebUI-style branch rail, keyboard tree nav) · `ChatSessionList` (fuzzy search, date groups, row actions) | [stories](https://neuronection.github.io/assistant-ui/?story=chat-surfaces--transcript) |
|
|
96
96
|
| [`chat-panel`](./docs/components/chat-panel.md) / [`chat-drawer`](./docs/components/chat-drawer.md) / [`chat-launcher`](./docs/components/chat-launcher.md) | the three chat surfaces from one assembly — `ChatPanel` host (`page` \| `sidebar` \| `bubble`) · `ChatDrawer` (resizable sidepanel on Radix Dialog, keyboard resize) · `ChatLauncher` (floating bubble → anchored panel, badge) | [stories](https://neuronection.github.io/assistant-ui/?story=chat-surfaces--transcript) |
|
|
97
97
|
| [`chat-tools-catalog`](./docs/components/chat-tools-catalog.md) | `ChatToolsCatalog` — searchable disclosure catalog of the tools an assistant can use (name, title, scope, arguments, example, response); fetching stays app-side | [stories](https://neuronection.github.io/assistant-ui/?story=chat-tools-catalog--default) |
|
package/dist/chat-composer.js
CHANGED
package/dist/chat-launcher.d.ts
CHANGED
|
@@ -14,6 +14,14 @@ interface ChatLauncherProps {
|
|
|
14
14
|
closeIcon?: LucideIcon;
|
|
15
15
|
/** Unread / activity badge. */
|
|
16
16
|
badge?: number | string;
|
|
17
|
+
/**
|
|
18
|
+
* Render the floating close button pinned to the panel's top-right
|
|
19
|
+
* corner (default). Turn this off when the panel body renders its own
|
|
20
|
+
* header actions — an overlaid close intercepts clicks meant for
|
|
21
|
+
* top-right header buttons; compose the close into those actions
|
|
22
|
+
* (calling `onOpenChange(false)`) instead.
|
|
23
|
+
*/
|
|
24
|
+
showClose?: boolean;
|
|
17
25
|
container?: HTMLElement | null;
|
|
18
26
|
panelClassName?: string;
|
|
19
27
|
className?: string;
|
package/dist/chat-launcher.js
CHANGED
|
@@ -29,6 +29,7 @@ var ChatComposer = React.forwardRef(
|
|
|
29
29
|
}, ref) {
|
|
30
30
|
const internalTextareaRef = React.useRef(null);
|
|
31
31
|
const [dragging, setDragging] = React.useState(false);
|
|
32
|
+
const [multiline, setMultiline] = React.useState(false);
|
|
32
33
|
const dragDepth = React.useRef(0);
|
|
33
34
|
const setTextarea = (element) => {
|
|
34
35
|
internalTextareaRef.current = element;
|
|
@@ -47,6 +48,10 @@ var ChatComposer = React.forwardRef(
|
|
|
47
48
|
const cap = maxRows * 22;
|
|
48
49
|
element.style.height = element.scrollHeight > 0 ? `${Math.min(element.scrollHeight, cap)}px` : "auto";
|
|
49
50
|
element.style.overflowY = element.scrollHeight > cap ? "auto" : "hidden";
|
|
51
|
+
const style = window.getComputedStyle(element);
|
|
52
|
+
const lineHeight = Number.parseFloat(style.lineHeight) || 22;
|
|
53
|
+
const verticalPadding = (Number.parseFloat(style.paddingTop) || 0) + (Number.parseFloat(style.paddingBottom) || 0);
|
|
54
|
+
setMultiline(element.scrollHeight > lineHeight + verticalPadding + 1);
|
|
50
55
|
}, [value, maxRows]);
|
|
51
56
|
const submit = () => {
|
|
52
57
|
if (disabled || sending) {
|
|
@@ -124,6 +129,8 @@ var ChatComposer = React.forwardRef(
|
|
|
124
129
|
/* @__PURE__ */ jsxs(
|
|
125
130
|
"div",
|
|
126
131
|
{
|
|
132
|
+
"data-as": "chat-composer-row",
|
|
133
|
+
"data-multiline": multiline || void 0,
|
|
127
134
|
"data-dragging": dragging || void 0,
|
|
128
135
|
className: cn(
|
|
129
136
|
"flex items-end gap-1.5 rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface)] px-2 py-1.5 transition-colors focus-within:border-[var(--as-primary)]",
|
|
@@ -180,5 +187,5 @@ var ChatComposer = React.forwardRef(
|
|
|
180
187
|
);
|
|
181
188
|
|
|
182
189
|
export { ChatComposer };
|
|
183
|
-
//# sourceMappingURL=chunk-
|
|
184
|
-
//# sourceMappingURL=chunk-
|
|
190
|
+
//# sourceMappingURL=chunk-IN6ORI3V.js.map
|
|
191
|
+
//# sourceMappingURL=chunk-IN6ORI3V.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/chat-composer/ChatComposer.tsx"],"names":["ChatComposer"],"mappings":"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","file":"chunk-IN6ORI3V.js","sourcesContent":["import * as React from 'react'\nimport { ArrowUp, Square } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\n\nimport { cn } from '../../lib/utils'\n\nexport interface ChatComposerLabels {\n send: string\n stop: string\n placeholder: string\n ariaLabel: string\n dropFiles: string\n}\n\nexport interface ChatComposerIcons {\n send: LucideIcon\n stop: LucideIcon\n}\n\nexport interface ChatComposerProps {\n value: string\n onValueChange: (value: string) => void\n /** Enter / Send — IME-safe (composition and keyCode 229 are ignored). */\n onSubmit: () => void\n /** A turn is in flight — renders the stop control when `onStop` is provided. */\n sending?: boolean\n onStop?: () => void\n disabled?: boolean\n placeholder?: string\n ariaLabel?: string\n /** Auto-grow cap before the textarea scrolls. Default 8. */\n maxRows?: number\n /** Leading slot inside the input row (attach menu, equation, draw…). */\n toolbarStart?: React.ReactNode\n /** Trailing slot inside the input row (dictation, tools…). */\n toolbarEnd?: React.ReactNode\n /** Attachment rail above the input (usually `FileQueue` / chips). */\n attachments?: React.ReactNode\n /** Suggestion chips / context strip above everything. */\n suggestions?: React.ReactNode\n /** Drag-drop + paste wiring for file attachments. */\n onAttachFiles?: (files: File[]) => void\n labels?: Partial<ChatComposerLabels>\n icons?: Partial<ChatComposerIcons>\n textareaRef?: React.Ref<HTMLTextAreaElement>\n className?: string\n}\n\nfunction isIMEComposition(event: React.KeyboardEvent<HTMLTextAreaElement>): boolean {\n return event.nativeEvent.isComposing || event.keyCode === 229\n}\n\n/**\n * The family chat input: auto-growing IME-safe textarea, send/stop\n * states, attachment/dictation/toolbar slots, drag-drop + paste file\n * wiring. Presentational + controlled; transports stay app-side.\n */\nexport const ChatComposer = React.forwardRef<HTMLFormElement, ChatComposerProps>(\n function ChatComposer(\n {\n value,\n onValueChange,\n onSubmit,\n sending = false,\n onStop,\n disabled = false,\n placeholder,\n ariaLabel,\n maxRows = 8,\n toolbarStart,\n toolbarEnd,\n attachments,\n suggestions,\n onAttachFiles,\n labels,\n icons,\n textareaRef,\n className,\n },\n ref,\n ) {\n const internalTextareaRef = React.useRef<HTMLTextAreaElement | null>(null)\n const [dragging, setDragging] = React.useState(false)\n const [multiline, setMultiline] = React.useState(false)\n const dragDepth = React.useRef(0)\n\n const setTextarea = (element: HTMLTextAreaElement | null) => {\n internalTextareaRef.current = element\n if (typeof textareaRef === 'function') {\n textareaRef(element)\n } else if (textareaRef && typeof textareaRef === 'object') {\n ;(textareaRef as React.MutableRefObject<HTMLTextAreaElement | null>).current = element\n }\n }\n\n React.useLayoutEffect(() => {\n const element = internalTextareaRef.current\n if (element === null) {\n return\n }\n element.style.height = 'auto'\n const cap = maxRows * 22\n element.style.height = element.scrollHeight > 0 ? `${Math.min(element.scrollHeight, cap)}px` : 'auto'\n element.style.overflowY = element.scrollHeight > cap ? 'auto' : 'hidden'\n const style = window.getComputedStyle(element)\n const lineHeight = Number.parseFloat(style.lineHeight) || 22\n const verticalPadding =\n (Number.parseFloat(style.paddingTop) || 0) + (Number.parseFloat(style.paddingBottom) || 0)\n setMultiline(element.scrollHeight > lineHeight + verticalPadding + 1)\n }, [value, maxRows])\n\n const submit = () => {\n if (disabled || sending) {\n return\n }\n onSubmit()\n }\n\n const onKeyDown: React.KeyboardEventHandler<HTMLTextAreaElement> = (event) => {\n if (event.key !== 'Enter' || event.shiftKey) {\n return\n }\n if (isIMEComposition(event)) {\n return\n }\n event.preventDefault()\n submit()\n }\n\n const emitFiles = (fileList: FileList | null | undefined) => {\n const files = fileList ? Array.from(fileList) : []\n if (files.length > 0) {\n onAttachFiles?.(files)\n }\n }\n\n const dragCounterHandlers = {\n onDragEnter: (event: React.DragEvent) => {\n if (onAttachFiles === undefined || !event.dataTransfer.types.includes('Files')) {\n return\n }\n event.preventDefault()\n dragDepth.current += 1\n setDragging(true)\n },\n onDragOver: (event: React.DragEvent) => {\n if (onAttachFiles === undefined || !event.dataTransfer.types.includes('Files')) {\n return\n }\n event.preventDefault()\n },\n onDragLeave: () => {\n if (dragDepth.current > 0) {\n dragDepth.current -= 1\n if (dragDepth.current === 0) {\n setDragging(false)\n }\n }\n },\n onDrop: (event: React.DragEvent) => {\n if (onAttachFiles === undefined) {\n return\n }\n event.preventDefault()\n dragDepth.current = 0\n setDragging(false)\n emitFiles(event.dataTransfer.files)\n },\n }\n\n const SendIcon = icons?.send ?? ArrowUp\n const StopIcon = icons?.stop ?? Square\n const showStop = sending && onStop !== undefined\n\n return (\n <form\n ref={ref}\n data-as=\"chat-composer\"\n data-sending={sending}\n onSubmit={(event) => {\n event.preventDefault()\n submit()\n }}\n className={cn('flex w-full flex-col gap-2', className)}\n {...dragCounterHandlers}\n >\n {suggestions ? <div className=\"flex flex-wrap gap-1.5\">{suggestions}</div> : null}\n {attachments ? <div className=\"flex flex-wrap gap-1.5\">{attachments}</div> : null}\n <div\n data-as=\"chat-composer-row\"\n data-multiline={multiline || undefined}\n data-dragging={dragging || undefined}\n className={cn(\n 'flex items-end gap-1.5 rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface)] px-2 py-1.5 transition-colors focus-within:border-[var(--as-primary)]',\n dragging && 'border-dashed border-[var(--as-primary)] bg-[var(--as-primary)]/5',\n disabled && 'opacity-50',\n )}\n >\n {toolbarStart ? <div className=\"flex shrink-0 items-center gap-0.5 pb-0.5\">{toolbarStart}</div> : null}\n <textarea\n ref={setTextarea}\n value={value}\n rows={1}\n disabled={disabled}\n placeholder={placeholder ?? labels?.placeholder ?? 'Send a message…'}\n aria-label={ariaLabel ?? labels?.ariaLabel ?? 'Message'}\n onChange={(event) => onValueChange(event.target.value)}\n onKeyDown={onKeyDown}\n onPaste={(event) => emitFiles(event.clipboardData.files)}\n className=\"max-h-40 min-h-[1.5rem] w-full flex-1 resize-none bg-transparent px-1 py-1 text-sm leading-relaxed text-[var(--as-fg)] placeholder:text-[var(--as-muted-fg)] focus-visible:outline-none disabled:cursor-not-allowed\"\n />\n {toolbarEnd ? <div className=\"flex shrink-0 items-center gap-0.5 pb-0.5\">{toolbarEnd}</div> : null}\n {showStop ? (\n <button\n type=\"button\"\n aria-label={labels?.stop ?? 'Stop generating'}\n title={labels?.stop ?? 'Stop generating'}\n onClick={onStop}\n className=\"mb-0.5 flex size-7 shrink-0 items-center justify-center rounded-full border border-[var(--as-border)] bg-[var(--as-surface-raised)] text-[var(--as-fg)] transition-colors hover:bg-[var(--as-secondary)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <StopIcon className=\"size-3.5\" aria-hidden />\n </button>\n ) : (\n <button\n type=\"submit\"\n aria-label={labels?.send ?? 'Send message'}\n title={labels?.send ?? 'Send message'}\n disabled={disabled || value.trim() === '' || sending}\n className=\"mb-0.5 flex size-7 shrink-0 items-center justify-center rounded-full bg-[var(--as-primary)] text-[var(--as-primary-fg)] transition-opacity hover:opacity-90 disabled:pointer-events-none disabled:opacity-30 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <SendIcon className=\"size-4\" aria-hidden />\n </button>\n )}\n </div>\n {dragging && onAttachFiles ? (\n <p className=\"text-xs text-[var(--as-muted-fg)]\" role=\"status\">\n {labels?.dropFiles ?? 'Drop files to attach'}\n </p>\n ) : null}\n </form>\n )\n },\n)\n"]}
|
|
@@ -16,6 +16,7 @@ var ChatLauncher = React.forwardRef(
|
|
|
16
16
|
icon: Icon = MessageCircle,
|
|
17
17
|
closeIcon: CloseIcon = X,
|
|
18
18
|
badge,
|
|
19
|
+
showClose = true,
|
|
19
20
|
container,
|
|
20
21
|
panelClassName,
|
|
21
22
|
className
|
|
@@ -58,7 +59,7 @@ var ChatLauncher = React.forwardRef(
|
|
|
58
59
|
"aria-label": label,
|
|
59
60
|
children: [
|
|
60
61
|
panel,
|
|
61
|
-
/* @__PURE__ */ jsx(
|
|
62
|
+
showClose ? /* @__PURE__ */ jsx(
|
|
62
63
|
"button",
|
|
63
64
|
{
|
|
64
65
|
type: "button",
|
|
@@ -67,7 +68,7 @@ var ChatLauncher = React.forwardRef(
|
|
|
67
68
|
className: "absolute right-2 top-2 z-10 rounded-[var(--as-radius-sm)] p-1 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-secondary)] hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]",
|
|
68
69
|
children: /* @__PURE__ */ jsx(CloseIcon, { "aria-hidden": true, className: "size-4" })
|
|
69
70
|
}
|
|
70
|
-
)
|
|
71
|
+
) : null
|
|
71
72
|
]
|
|
72
73
|
}
|
|
73
74
|
) }) : null,
|
|
@@ -98,5 +99,5 @@ var ChatLauncher = React.forwardRef(
|
|
|
98
99
|
);
|
|
99
100
|
|
|
100
101
|
export { ChatLauncher };
|
|
101
|
-
//# sourceMappingURL=chunk-
|
|
102
|
-
//# sourceMappingURL=chunk-
|
|
102
|
+
//# sourceMappingURL=chunk-OUJJLZ6C.js.map
|
|
103
|
+
//# sourceMappingURL=chunk-OUJJLZ6C.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/chat-launcher/ChatLauncher.tsx"],"names":["ChatLauncher"],"mappings":"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","file":"chunk-OUJJLZ6C.js","sourcesContent":["import * as React from 'react'\nimport { MessageCircle, X } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\n\nimport { cn } from '../../lib/utils'\nimport { Portal } from '../portal/Portal'\n\nexport interface ChatLauncherProps {\n /** The anchored panel body — usually `ChatPanel variant=\"bubble\"`. */\n panel: React.ReactNode\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n position?: 'bottom-right' | 'bottom-left'\n label?: string\n closeLabel?: string\n icon?: LucideIcon\n closeIcon?: LucideIcon\n /** Unread / activity badge. */\n badge?: number | string\n /**\n * Render the floating close button pinned to the panel's top-right\n * corner (default). Turn this off when the panel body renders its own\n * header actions — an overlaid close intercepts clicks meant for\n * top-right header buttons; compose the close into those actions\n * (calling `onOpenChange(false)`) instead.\n */\n showClose?: boolean\n container?: HTMLElement | null\n panelClassName?: string\n className?: string\n}\n\n/**\n * The floating chat bubble (career's `ChatWidget` launcher,\n * family-standard): fixed launcher button opening an anchored, non-modal\n * panel. `aria-expanded` wiring, Escape closes while focus is inside.\n */\nexport const ChatLauncher = React.forwardRef<HTMLButtonElement, ChatLauncherProps>(\n function ChatLauncher(\n {\n panel,\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n position = 'bottom-right',\n label = 'Chat',\n closeLabel = 'Close chat',\n icon: Icon = MessageCircle,\n closeIcon: CloseIcon = X,\n badge,\n showClose = true,\n container,\n panelClassName,\n className,\n },\n ref,\n ) {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n const isControlled = openProp !== undefined\n const open = isControlled ? openProp : uncontrolledOpen\n const panelRef = React.useRef<HTMLDivElement | null>(null)\n\n const setOpen = (next: boolean) => {\n if (!isControlled) {\n setUncontrolledOpen(next)\n }\n onOpenChange?.(next)\n }\n\n React.useEffect(() => {\n if (!open) {\n return\n }\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && panelRef.current?.contains(document.activeElement)) {\n setOpen(false)\n }\n }\n document.addEventListener('keydown', onKeyDown)\n return () => document.removeEventListener('keydown', onKeyDown)\n })\n\n const showBadge = badge !== undefined && badge !== 0 && badge !== ''\n\n return (\n <>\n {open ? (\n <Portal container={container}>\n <div\n ref={panelRef}\n data-as=\"chat-launcher-panel\"\n className={cn(\n 'as-anim-pop fixed bottom-20 z-[var(--as-z-modal)] flex h-[min(36rem,calc(100dvh-11rem))] w-[min(26rem,calc(100vw-2rem))] flex-col overflow-hidden rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface)] text-[var(--as-fg)] shadow-[var(--as-shadow-3)]',\n position === 'bottom-right' ? 'right-4' : 'left-4',\n panelClassName,\n )}\n role=\"complementary\"\n aria-label={label}\n >\n {panel}\n {showClose ? (\n <button\n type=\"button\"\n aria-label={closeLabel}\n onClick={() => setOpen(false)}\n className=\"absolute right-2 top-2 z-10 rounded-[var(--as-radius-sm)] p-1 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-secondary)] hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <CloseIcon aria-hidden className=\"size-4\" />\n </button>\n ) : null}\n </div>\n </Portal>\n ) : null}\n <button\n ref={ref}\n type=\"button\"\n aria-expanded={open}\n aria-label={open ? closeLabel : label}\n title={open ? closeLabel : label}\n onClick={() => setOpen(!open)}\n data-as=\"chat-launcher\"\n data-open={open || undefined}\n className={cn(\n 'fixed bottom-4 z-[var(--as-z-popover)] flex size-12 items-center justify-center rounded-full bg-[var(--as-primary)] text-[var(--as-primary-fg)] shadow-[var(--as-shadow-2)] transition-transform hover:scale-105 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]',\n position === 'bottom-right' ? 'right-4' : 'left-4',\n className,\n )}\n >\n <Icon className=\"size-5\" aria-hidden />\n {showBadge ? (\n <span className=\"absolute -right-0.5 -top-0.5 flex h-5 min-w-5 items-center justify-center rounded-full border-2 border-[var(--as-surface)] bg-[var(--as-danger)] px-1 text-[10px] font-semibold text-[var(--as-danger-fg)]\">\n {badge}\n </span>\n ) : null}\n </button>\n </>\n )\n },\n)\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -2539,6 +2539,14 @@ interface ChatLauncherProps {
|
|
|
2539
2539
|
closeIcon?: LucideIcon;
|
|
2540
2540
|
/** Unread / activity badge. */
|
|
2541
2541
|
badge?: number | string;
|
|
2542
|
+
/**
|
|
2543
|
+
* Render the floating close button pinned to the panel's top-right
|
|
2544
|
+
* corner (default). Turn this off when the panel body renders its own
|
|
2545
|
+
* header actions — an overlaid close intercepts clicks meant for
|
|
2546
|
+
* top-right header buttons; compose the close into those actions
|
|
2547
|
+
* (calling `onOpenChange(false)`) instead.
|
|
2548
|
+
*/
|
|
2549
|
+
showClose?: boolean;
|
|
2542
2550
|
container?: HTMLElement | null;
|
|
2543
2551
|
panelClassName?: string;
|
|
2544
2552
|
className?: string;
|
package/dist/index.js
CHANGED
|
@@ -56,9 +56,9 @@ export { ChatTurnStatus } from './chunk-P75QKU5J.js';
|
|
|
56
56
|
export { buildChatMarkdown, chatExportFileName, downloadChatMarkdown } from './chunk-DRJGZ5TB.js';
|
|
57
57
|
export { initialLiveTurnState, liveTurnReducer, useChatStream } from './chunk-6GNLY2EB.js';
|
|
58
58
|
export { activePathSet, buildBranchTree, linearTree, variantInfo, walkActivePath } from './chunk-Z4IBQ53C.js';
|
|
59
|
-
export { ChatLauncher } from './chunk-
|
|
59
|
+
export { ChatLauncher } from './chunk-OUJJLZ6C.js';
|
|
60
60
|
export { Portal } from './chunk-XZWLTTHA.js';
|
|
61
|
-
export { ChatComposer } from './chunk-
|
|
61
|
+
export { ChatComposer } from './chunk-IN6ORI3V.js';
|
|
62
62
|
export { ChatPanel } from './chunk-MVT4F6TA.js';
|
|
63
63
|
export { ChatTranscript } from './chunk-FS7M7PNS.js';
|
|
64
64
|
export { ChatMessage, ChatMessageEditor, MessageVariantSwitcher } from './chunk-FGYGWZ6S.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neuronection/assistant-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "Shared React component library for the Assistant family (career-, study-, health-assistant). Family look by default, app-flavored via CSS-variable tokens.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/chat-composer/ChatComposer.tsx"],"names":["ChatComposer"],"mappings":"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","file":"chunk-743FJDRA.js","sourcesContent":["import * as React from 'react'\nimport { ArrowUp, Square } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\n\nimport { cn } from '../../lib/utils'\n\nexport interface ChatComposerLabels {\n send: string\n stop: string\n placeholder: string\n ariaLabel: string\n dropFiles: string\n}\n\nexport interface ChatComposerIcons {\n send: LucideIcon\n stop: LucideIcon\n}\n\nexport interface ChatComposerProps {\n value: string\n onValueChange: (value: string) => void\n /** Enter / Send — IME-safe (composition and keyCode 229 are ignored). */\n onSubmit: () => void\n /** A turn is in flight — renders the stop control when `onStop` is provided. */\n sending?: boolean\n onStop?: () => void\n disabled?: boolean\n placeholder?: string\n ariaLabel?: string\n /** Auto-grow cap before the textarea scrolls. Default 8. */\n maxRows?: number\n /** Leading slot inside the input row (attach menu, equation, draw…). */\n toolbarStart?: React.ReactNode\n /** Trailing slot inside the input row (dictation, tools…). */\n toolbarEnd?: React.ReactNode\n /** Attachment rail above the input (usually `FileQueue` / chips). */\n attachments?: React.ReactNode\n /** Suggestion chips / context strip above everything. */\n suggestions?: React.ReactNode\n /** Drag-drop + paste wiring for file attachments. */\n onAttachFiles?: (files: File[]) => void\n labels?: Partial<ChatComposerLabels>\n icons?: Partial<ChatComposerIcons>\n textareaRef?: React.Ref<HTMLTextAreaElement>\n className?: string\n}\n\nfunction isIMEComposition(event: React.KeyboardEvent<HTMLTextAreaElement>): boolean {\n return event.nativeEvent.isComposing || event.keyCode === 229\n}\n\n/**\n * The family chat input: auto-growing IME-safe textarea, send/stop\n * states, attachment/dictation/toolbar slots, drag-drop + paste file\n * wiring. Presentational + controlled; transports stay app-side.\n */\nexport const ChatComposer = React.forwardRef<HTMLFormElement, ChatComposerProps>(\n function ChatComposer(\n {\n value,\n onValueChange,\n onSubmit,\n sending = false,\n onStop,\n disabled = false,\n placeholder,\n ariaLabel,\n maxRows = 8,\n toolbarStart,\n toolbarEnd,\n attachments,\n suggestions,\n onAttachFiles,\n labels,\n icons,\n textareaRef,\n className,\n },\n ref,\n ) {\n const internalTextareaRef = React.useRef<HTMLTextAreaElement | null>(null)\n const [dragging, setDragging] = React.useState(false)\n const dragDepth = React.useRef(0)\n\n const setTextarea = (element: HTMLTextAreaElement | null) => {\n internalTextareaRef.current = element\n if (typeof textareaRef === 'function') {\n textareaRef(element)\n } else if (textareaRef && typeof textareaRef === 'object') {\n ;(textareaRef as React.MutableRefObject<HTMLTextAreaElement | null>).current = element\n }\n }\n\n React.useLayoutEffect(() => {\n const element = internalTextareaRef.current\n if (element === null) {\n return\n }\n element.style.height = 'auto'\n const cap = maxRows * 22\n element.style.height = element.scrollHeight > 0 ? `${Math.min(element.scrollHeight, cap)}px` : 'auto'\n element.style.overflowY = element.scrollHeight > cap ? 'auto' : 'hidden'\n }, [value, maxRows])\n\n const submit = () => {\n if (disabled || sending) {\n return\n }\n onSubmit()\n }\n\n const onKeyDown: React.KeyboardEventHandler<HTMLTextAreaElement> = (event) => {\n if (event.key !== 'Enter' || event.shiftKey) {\n return\n }\n if (isIMEComposition(event)) {\n return\n }\n event.preventDefault()\n submit()\n }\n\n const emitFiles = (fileList: FileList | null | undefined) => {\n const files = fileList ? Array.from(fileList) : []\n if (files.length > 0) {\n onAttachFiles?.(files)\n }\n }\n\n const dragCounterHandlers = {\n onDragEnter: (event: React.DragEvent) => {\n if (onAttachFiles === undefined || !event.dataTransfer.types.includes('Files')) {\n return\n }\n event.preventDefault()\n dragDepth.current += 1\n setDragging(true)\n },\n onDragOver: (event: React.DragEvent) => {\n if (onAttachFiles === undefined || !event.dataTransfer.types.includes('Files')) {\n return\n }\n event.preventDefault()\n },\n onDragLeave: () => {\n if (dragDepth.current > 0) {\n dragDepth.current -= 1\n if (dragDepth.current === 0) {\n setDragging(false)\n }\n }\n },\n onDrop: (event: React.DragEvent) => {\n if (onAttachFiles === undefined) {\n return\n }\n event.preventDefault()\n dragDepth.current = 0\n setDragging(false)\n emitFiles(event.dataTransfer.files)\n },\n }\n\n const SendIcon = icons?.send ?? ArrowUp\n const StopIcon = icons?.stop ?? Square\n const showStop = sending && onStop !== undefined\n\n return (\n <form\n ref={ref}\n data-as=\"chat-composer\"\n data-sending={sending}\n onSubmit={(event) => {\n event.preventDefault()\n submit()\n }}\n className={cn('flex w-full flex-col gap-2', className)}\n {...dragCounterHandlers}\n >\n {suggestions ? <div className=\"flex flex-wrap gap-1.5\">{suggestions}</div> : null}\n {attachments ? <div className=\"flex flex-wrap gap-1.5\">{attachments}</div> : null}\n <div\n data-dragging={dragging || undefined}\n className={cn(\n 'flex items-end gap-1.5 rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface)] px-2 py-1.5 transition-colors focus-within:border-[var(--as-primary)]',\n dragging && 'border-dashed border-[var(--as-primary)] bg-[var(--as-primary)]/5',\n disabled && 'opacity-50',\n )}\n >\n {toolbarStart ? <div className=\"flex shrink-0 items-center gap-0.5 pb-0.5\">{toolbarStart}</div> : null}\n <textarea\n ref={setTextarea}\n value={value}\n rows={1}\n disabled={disabled}\n placeholder={placeholder ?? labels?.placeholder ?? 'Send a message…'}\n aria-label={ariaLabel ?? labels?.ariaLabel ?? 'Message'}\n onChange={(event) => onValueChange(event.target.value)}\n onKeyDown={onKeyDown}\n onPaste={(event) => emitFiles(event.clipboardData.files)}\n className=\"max-h-40 min-h-[1.5rem] w-full flex-1 resize-none bg-transparent px-1 py-1 text-sm leading-relaxed text-[var(--as-fg)] placeholder:text-[var(--as-muted-fg)] focus-visible:outline-none disabled:cursor-not-allowed\"\n />\n {toolbarEnd ? <div className=\"flex shrink-0 items-center gap-0.5 pb-0.5\">{toolbarEnd}</div> : null}\n {showStop ? (\n <button\n type=\"button\"\n aria-label={labels?.stop ?? 'Stop generating'}\n title={labels?.stop ?? 'Stop generating'}\n onClick={onStop}\n className=\"mb-0.5 flex size-7 shrink-0 items-center justify-center rounded-full border border-[var(--as-border)] bg-[var(--as-surface-raised)] text-[var(--as-fg)] transition-colors hover:bg-[var(--as-secondary)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <StopIcon className=\"size-3.5\" aria-hidden />\n </button>\n ) : (\n <button\n type=\"submit\"\n aria-label={labels?.send ?? 'Send message'}\n title={labels?.send ?? 'Send message'}\n disabled={disabled || value.trim() === '' || sending}\n className=\"mb-0.5 flex size-7 shrink-0 items-center justify-center rounded-full bg-[var(--as-primary)] text-[var(--as-primary-fg)] transition-opacity hover:opacity-90 disabled:pointer-events-none disabled:opacity-30 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <SendIcon className=\"size-4\" aria-hidden />\n </button>\n )}\n </div>\n {dragging && onAttachFiles ? (\n <p className=\"text-xs text-[var(--as-muted-fg)]\" role=\"status\">\n {labels?.dropFiles ?? 'Drop files to attach'}\n </p>\n ) : null}\n </form>\n )\n },\n)\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/chat-launcher/ChatLauncher.tsx"],"names":["ChatLauncher"],"mappings":"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","file":"chunk-RPLEI6LQ.js","sourcesContent":["import * as React from 'react'\nimport { MessageCircle, X } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\n\nimport { cn } from '../../lib/utils'\nimport { Portal } from '../portal/Portal'\n\nexport interface ChatLauncherProps {\n /** The anchored panel body — usually `ChatPanel variant=\"bubble\"`. */\n panel: React.ReactNode\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n position?: 'bottom-right' | 'bottom-left'\n label?: string\n closeLabel?: string\n icon?: LucideIcon\n closeIcon?: LucideIcon\n /** Unread / activity badge. */\n badge?: number | string\n container?: HTMLElement | null\n panelClassName?: string\n className?: string\n}\n\n/**\n * The floating chat bubble (career's `ChatWidget` launcher,\n * family-standard): fixed launcher button opening an anchored, non-modal\n * panel. `aria-expanded` wiring, Escape closes while focus is inside.\n */\nexport const ChatLauncher = React.forwardRef<HTMLButtonElement, ChatLauncherProps>(\n function ChatLauncher(\n {\n panel,\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n position = 'bottom-right',\n label = 'Chat',\n closeLabel = 'Close chat',\n icon: Icon = MessageCircle,\n closeIcon: CloseIcon = X,\n badge,\n container,\n panelClassName,\n className,\n },\n ref,\n ) {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n const isControlled = openProp !== undefined\n const open = isControlled ? openProp : uncontrolledOpen\n const panelRef = React.useRef<HTMLDivElement | null>(null)\n\n const setOpen = (next: boolean) => {\n if (!isControlled) {\n setUncontrolledOpen(next)\n }\n onOpenChange?.(next)\n }\n\n React.useEffect(() => {\n if (!open) {\n return\n }\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && panelRef.current?.contains(document.activeElement)) {\n setOpen(false)\n }\n }\n document.addEventListener('keydown', onKeyDown)\n return () => document.removeEventListener('keydown', onKeyDown)\n })\n\n const showBadge = badge !== undefined && badge !== 0 && badge !== ''\n\n return (\n <>\n {open ? (\n <Portal container={container}>\n <div\n ref={panelRef}\n data-as=\"chat-launcher-panel\"\n className={cn(\n 'as-anim-pop fixed bottom-20 z-[var(--as-z-modal)] flex h-[min(36rem,calc(100dvh-11rem))] w-[min(26rem,calc(100vw-2rem))] flex-col overflow-hidden rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface)] text-[var(--as-fg)] shadow-[var(--as-shadow-3)]',\n position === 'bottom-right' ? 'right-4' : 'left-4',\n panelClassName,\n )}\n role=\"complementary\"\n aria-label={label}\n >\n {panel}\n <button\n type=\"button\"\n aria-label={closeLabel}\n onClick={() => setOpen(false)}\n className=\"absolute right-2 top-2 z-10 rounded-[var(--as-radius-sm)] p-1 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-secondary)] hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <CloseIcon aria-hidden className=\"size-4\" />\n </button>\n </div>\n </Portal>\n ) : null}\n <button\n ref={ref}\n type=\"button\"\n aria-expanded={open}\n aria-label={open ? closeLabel : label}\n title={open ? closeLabel : label}\n onClick={() => setOpen(!open)}\n data-as=\"chat-launcher\"\n data-open={open || undefined}\n className={cn(\n 'fixed bottom-4 z-[var(--as-z-popover)] flex size-12 items-center justify-center rounded-full bg-[var(--as-primary)] text-[var(--as-primary-fg)] shadow-[var(--as-shadow-2)] transition-transform hover:scale-105 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]',\n position === 'bottom-right' ? 'right-4' : 'left-4',\n className,\n )}\n >\n <Icon className=\"size-5\" aria-hidden />\n {showBadge ? (\n <span className=\"absolute -right-0.5 -top-0.5 flex h-5 min-w-5 items-center justify-center rounded-full border-2 border-[var(--as-surface)] bg-[var(--as-danger)] px-1 text-[10px] font-semibold text-[var(--as-danger-fg)]\">\n {badge}\n </span>\n ) : null}\n </button>\n </>\n )\n },\n)\n"]}
|