@agents24/chat-react 0.5.4 → 0.5.6
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/README.md +8 -2
- package/compatibility.json +4 -4
- package/dist/runtime/index.cjs +127 -18
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.js +125 -18
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/use-agent-chat-runtime.d.ts +17 -2
- package/dist/scaffold/app.d.ts +1 -0
- package/dist/scaffold/components/chat/chat-composer.d.ts +10 -2
- package/dist/scaffold/components/chat/chat-message.d.ts +3 -1
- package/dist/scaffold/components/chat/chat-shell.d.ts +3 -1
- package/dist/scaffold/index.cjs +415 -253
- package/dist/scaffold/index.cjs.map +1 -1
- package/dist/scaffold/index.js +408 -240
- package/dist/scaffold/index.js.map +1 -1
- package/dist/styles.css +18 -4
- package/dist/ui/adapters.d.ts +14 -2
- package/dist/ui/agent-chat-composer.d.ts +2 -9
- package/dist/ui/agent-chat-message.d.ts +3 -1
- package/dist/ui/composer-attachments.d.ts +11 -0
- package/dist/ui/index.cjs +653 -343
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +638 -328
- package/dist/ui/index.js.map +1 -1
- package/package.json +4 -3
- package/scaffold/src/app.tsx +2 -1
- package/scaffold/src/chat.css +13 -1
- package/scaffold/src/components/chat/chat-composer.tsx +107 -27
- package/scaffold/src/components/chat/chat-message.tsx +8 -9
- package/scaffold/src/components/chat/chat-shell.tsx +24 -11
- package/scaffold/src/components/ui/button.tsx +1 -1
- package/scaffold/src/components/ui/dropdown-menu.tsx +1 -1
- package/scaffold/src/components/ui/sidebar.tsx +4 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agents24/chat-react",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"description": "Accessible web UI and complete default layout for Agents24 chat applications.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -58,8 +58,9 @@
|
|
|
58
58
|
"prepack": "pnpm run build && pnpm run check:boundaries"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@
|
|
62
|
-
"@agents24/
|
|
61
|
+
"@untitledui/file-icons": "0.0.9",
|
|
62
|
+
"@agents24/client": "0.2.4",
|
|
63
|
+
"@agents24/react": "0.2.4",
|
|
63
64
|
"@fontsource-variable/inter": "^5.3.0",
|
|
64
65
|
"@fontsource-variable/jetbrains-mono": "^5.3.0",
|
|
65
66
|
"@fontsource-variable/merriweather": "^5.3.0",
|
package/scaffold/src/app.tsx
CHANGED
|
@@ -30,6 +30,7 @@ export type Agents24ChatAppProps = {
|
|
|
30
30
|
onModelChange?: (modelId: string) => void
|
|
31
31
|
identity?: Agents24ChatIdentity
|
|
32
32
|
components?: Agents24ChatScaffoldComponents
|
|
33
|
+
sidebar?: React.ReactNode
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
export type Agents24ChatScaffoldComponents = {
|
|
@@ -58,7 +59,7 @@ function RuntimeChat({
|
|
|
58
59
|
),
|
|
59
60
|
})
|
|
60
61
|
|
|
61
|
-
return <ChatShell components={components} identity={identity} runtime={runtime} />
|
|
62
|
+
return <ChatShell components={components} identity={identity} runtime={runtime} sidebar={props.sidebar} />
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
export function App(props: Agents24ChatAppProps) {
|
package/scaffold/src/chat.css
CHANGED
|
@@ -10,12 +10,24 @@
|
|
|
10
10
|
[data-agents24-theme-root],
|
|
11
11
|
[data-agents24-theme-root] * {
|
|
12
12
|
box-sizing: border-box;
|
|
13
|
+
-ms-overflow-style: none;
|
|
14
|
+
scrollbar-width: none;
|
|
15
|
+
--tw-inset-ring-shadow: 0 0 #0000 !important;
|
|
16
|
+
--tw-ring-shadow: 0 0 #0000 !important;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
[data-agents24-theme-root] ::-webkit-scrollbar {
|
|
20
|
+
display: none;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
[data-agents24-theme-root] :focus-visible {
|
|
24
|
+
outline: none !important;
|
|
13
25
|
}
|
|
14
26
|
|
|
15
27
|
[data-agents24-theme-root] .a24-message-response {
|
|
16
28
|
max-width: 100%;
|
|
17
29
|
min-width: 0;
|
|
18
|
-
padding-inline:
|
|
30
|
+
padding-inline: 6px;
|
|
19
31
|
width: 100%;
|
|
20
32
|
}
|
|
21
33
|
|
|
@@ -1,10 +1,21 @@
|
|
|
1
|
-
import { ArrowUp, LoaderCircle, Mic, Square
|
|
1
|
+
import { ArrowUp, ChevronDown, LoaderCircle, Mic, Square } from "lucide-react"
|
|
2
2
|
import * as React from "react"
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
AgentChatContextStatus,
|
|
5
|
+
ChatAttachmentRows,
|
|
6
|
+
createAgentChatComposerFile,
|
|
7
|
+
revokeAgentChatComposerFiles,
|
|
8
|
+
useAudioRecorder,
|
|
9
|
+
type AgentChatComposerFile,
|
|
10
|
+
} from "@agents24/chat-react/ui"
|
|
4
11
|
import { canonicalInputMimeType, inputMimeTypes, validateAgentChatSubmission, type AgentChatInputContract } from "@agents24/chat-react/runtime"
|
|
5
12
|
import type { ContextWindow } from "@agents24/client/protocol"
|
|
6
13
|
|
|
7
14
|
import { Button } from "../ui/button"
|
|
15
|
+
import {
|
|
16
|
+
DropdownMenu, DropdownMenuContent, DropdownMenuRadioGroup, DropdownMenuRadioItem,
|
|
17
|
+
DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger,
|
|
18
|
+
} from "../ui/dropdown-menu"
|
|
8
19
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select"
|
|
9
20
|
import { Textarea } from "../ui/textarea"
|
|
10
21
|
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"
|
|
@@ -14,14 +25,18 @@ type ChatComposerProps = {
|
|
|
14
25
|
inputContract: AgentChatInputContract
|
|
15
26
|
disabled?: boolean
|
|
16
27
|
contextWindow?: ContextWindow | null
|
|
28
|
+
focusKey?: string | null
|
|
17
29
|
forceExpanded?: boolean
|
|
18
30
|
isCancelling?: boolean
|
|
19
31
|
isRunning: boolean
|
|
32
|
+
agentId: string | null
|
|
33
|
+
agents: Array<{ id: string; label: string }>
|
|
20
34
|
modelId: string | null
|
|
21
35
|
models: Array<{ id: string; label: string }>
|
|
22
36
|
onModelChange: (modelId: string) => void
|
|
37
|
+
onAgentChange: (agentId: string) => void
|
|
23
38
|
onStop: () => void
|
|
24
|
-
onSubmit: (text: string, files:
|
|
39
|
+
onSubmit: (text: string, files: AgentChatComposerFile[]) => Promise<void>
|
|
25
40
|
utilityRow?: "inline" | "below"
|
|
26
41
|
}
|
|
27
42
|
|
|
@@ -29,45 +44,61 @@ export function ChatComposer({
|
|
|
29
44
|
inputContract,
|
|
30
45
|
disabled,
|
|
31
46
|
contextWindow,
|
|
47
|
+
focusKey,
|
|
32
48
|
forceExpanded = false,
|
|
33
49
|
isCancelling = false,
|
|
34
50
|
isRunning,
|
|
51
|
+
agentId,
|
|
52
|
+
agents,
|
|
35
53
|
modelId,
|
|
36
54
|
models,
|
|
37
55
|
onModelChange,
|
|
56
|
+
onAgentChange,
|
|
38
57
|
onStop,
|
|
39
58
|
onSubmit,
|
|
40
59
|
utilityRow = "inline",
|
|
41
60
|
}: ChatComposerProps) {
|
|
42
61
|
const [text, setText] = React.useState("")
|
|
43
|
-
const [files, setFiles] = React.useState<
|
|
62
|
+
const [files, setFiles] = React.useState<AgentChatComposerFile[]>([])
|
|
44
63
|
const [attachmentError, setAttachmentError] = React.useState<string | null>(null)
|
|
45
64
|
const inputRef = React.useRef<HTMLInputElement>(null)
|
|
46
65
|
const textareaRef = React.useRef<HTMLTextAreaElement>(null)
|
|
66
|
+
const filesRef = React.useRef<AgentChatComposerFile[]>([])
|
|
67
|
+
const inFlightFilesRef = React.useRef<AgentChatComposerFile[]>([])
|
|
47
68
|
const submittingRef = React.useRef(false)
|
|
48
69
|
const draftRevisionRef = React.useRef(0)
|
|
49
70
|
const attachmentMimeTypes = inputMimeTypes(inputContract)
|
|
50
71
|
const allowAttachments = attachmentMimeTypes.length > 0
|
|
51
72
|
const recordingAllowed = inputContract.modalities.audio.recording_enabled
|
|
73
|
+
React.useEffect(() => {
|
|
74
|
+
filesRef.current = files
|
|
75
|
+
}, [files])
|
|
76
|
+
React.useEffect(() => () => {
|
|
77
|
+
revokeAgentChatComposerFiles(filesRef.current)
|
|
78
|
+
revokeAgentChatComposerFiles(inFlightFilesRef.current)
|
|
79
|
+
}, [])
|
|
80
|
+
React.useEffect(() => {
|
|
81
|
+
if (disabled || isRunning) return
|
|
82
|
+
const frame = window.requestAnimationFrame(() => textareaRef.current?.focus({ preventScroll: true }))
|
|
83
|
+
return () => window.cancelAnimationFrame(frame)
|
|
84
|
+
}, [disabled, focusKey, isRunning])
|
|
52
85
|
const addRecording = React.useCallback((file: File) => {
|
|
53
86
|
draftRevisionRef.current += 1
|
|
54
|
-
setFiles((current) => [...current, file])
|
|
87
|
+
setFiles((current) => [...current, createAgentChatComposerFile(file)])
|
|
55
88
|
}, [])
|
|
56
89
|
const recorder = useAudioRecorder(addRecording)
|
|
57
90
|
const isExpanded = forceExpanded || text.includes("\n") || text.length > 62
|
|
58
91
|
const validationError = validateAgentChatSubmission(
|
|
59
92
|
inputContract,
|
|
60
93
|
text,
|
|
61
|
-
files.map((file) => ({ data: file, mediaType: file.
|
|
94
|
+
files.map((file) => ({ data: file.source, mediaType: file.mediaType })),
|
|
62
95
|
)
|
|
63
96
|
|
|
97
|
+
const selectedAgent = agents.find((item) => item.id === agentId)
|
|
98
|
+
const selectedModel = models.find((item) => item.id === modelId)
|
|
64
99
|
const modelSelector = models.length > 0 ? (
|
|
65
100
|
<div className="w-fit max-w-40">
|
|
66
|
-
<Select
|
|
67
|
-
disabled={disabled || isRunning}
|
|
68
|
-
onValueChange={onModelChange}
|
|
69
|
-
value={modelId || undefined}
|
|
70
|
-
>
|
|
101
|
+
<Select disabled={disabled || isRunning} onValueChange={onModelChange} value={modelId || undefined}>
|
|
71
102
|
<SelectTrigger aria-label="Runtime model">
|
|
72
103
|
<SelectValue placeholder="Model" />
|
|
73
104
|
</SelectTrigger>
|
|
@@ -77,6 +108,47 @@ export function ChatComposer({
|
|
|
77
108
|
</Select>
|
|
78
109
|
</div>
|
|
79
110
|
) : null
|
|
111
|
+
const runtimeSelector = agents.length > 1 ? (
|
|
112
|
+
<DropdownMenu>
|
|
113
|
+
<DropdownMenuTrigger asChild>
|
|
114
|
+
<Button
|
|
115
|
+
variant="ghost"
|
|
116
|
+
className="h-8 max-w-64 gap-1.5 bg-transparent px-2.5 text-xs font-normal hover:bg-muted/40 disabled:opacity-100"
|
|
117
|
+
disabled={disabled || isRunning}
|
|
118
|
+
aria-label="Runtime configuration"
|
|
119
|
+
>
|
|
120
|
+
<span className="truncate">
|
|
121
|
+
{[selectedAgent?.label, selectedModel?.label].filter(Boolean).join(" · ") || "Runtime"}
|
|
122
|
+
</span>
|
|
123
|
+
<ChevronDown className="size-3.5 text-muted-foreground" />
|
|
124
|
+
</Button>
|
|
125
|
+
</DropdownMenuTrigger>
|
|
126
|
+
<DropdownMenuContent align="start" className="w-64 rounded-lg">
|
|
127
|
+
<DropdownMenuSub>
|
|
128
|
+
<DropdownMenuSubTrigger className="rounded-lg">
|
|
129
|
+
<span>Agent</span><span className="ml-auto max-w-32 truncate text-muted-foreground">{selectedAgent?.label}</span>
|
|
130
|
+
</DropdownMenuSubTrigger>
|
|
131
|
+
<DropdownMenuSubContent className="w-56 rounded-lg">
|
|
132
|
+
<DropdownMenuRadioGroup value={agentId || undefined} onValueChange={onAgentChange}>
|
|
133
|
+
{agents.map((agent) => <DropdownMenuRadioItem className="rounded-lg" key={agent.id} value={agent.id}>{agent.label}</DropdownMenuRadioItem>)}
|
|
134
|
+
</DropdownMenuRadioGroup>
|
|
135
|
+
</DropdownMenuSubContent>
|
|
136
|
+
</DropdownMenuSub>
|
|
137
|
+
{models.length > 0 ? (
|
|
138
|
+
<DropdownMenuSub>
|
|
139
|
+
<DropdownMenuSubTrigger className="rounded-lg">
|
|
140
|
+
<span>Model</span><span className="ml-auto max-w-32 truncate text-muted-foreground">{selectedModel?.label}</span>
|
|
141
|
+
</DropdownMenuSubTrigger>
|
|
142
|
+
<DropdownMenuSubContent className="max-h-64 w-56 rounded-lg">
|
|
143
|
+
<DropdownMenuRadioGroup value={modelId || undefined} onValueChange={onModelChange}>
|
|
144
|
+
{models.map((model) => <DropdownMenuRadioItem className="rounded-lg" key={model.id} value={model.id}>{model.label}</DropdownMenuRadioItem>)}
|
|
145
|
+
</DropdownMenuRadioGroup>
|
|
146
|
+
</DropdownMenuSubContent>
|
|
147
|
+
</DropdownMenuSub>
|
|
148
|
+
) : null}
|
|
149
|
+
</DropdownMenuContent>
|
|
150
|
+
</DropdownMenu>
|
|
151
|
+
) : modelSelector
|
|
80
152
|
const hasContextWindow = Number.isFinite(Number(contextWindow?.max_tokens)) && Number(contextWindow?.max_tokens) > 0
|
|
81
153
|
const showUtilityRow = utilityRow === "below"
|
|
82
154
|
|
|
@@ -87,16 +159,23 @@ export function ChatComposer({
|
|
|
87
159
|
const clearedRevision = draftRevisionRef.current + 1
|
|
88
160
|
draftRevisionRef.current = clearedRevision
|
|
89
161
|
submittingRef.current = true
|
|
162
|
+
inFlightFilesRef.current = selectedFiles
|
|
90
163
|
setText("")
|
|
91
164
|
setFiles([])
|
|
92
165
|
textareaRef.current?.focus()
|
|
93
166
|
try {
|
|
94
167
|
await onSubmit(value, selectedFiles)
|
|
168
|
+
revokeAgentChatComposerFiles(selectedFiles)
|
|
169
|
+
inFlightFilesRef.current = []
|
|
95
170
|
} catch {
|
|
96
171
|
if (draftRevisionRef.current === clearedRevision) {
|
|
97
172
|
draftRevisionRef.current += 1
|
|
98
173
|
setText(value)
|
|
99
174
|
setFiles(selectedFiles)
|
|
175
|
+
inFlightFilesRef.current = []
|
|
176
|
+
} else {
|
|
177
|
+
revokeAgentChatComposerFiles(selectedFiles)
|
|
178
|
+
inFlightFilesRef.current = []
|
|
100
179
|
}
|
|
101
180
|
} finally {
|
|
102
181
|
submittingRef.current = false
|
|
@@ -109,18 +188,19 @@ export function ChatComposer({
|
|
|
109
188
|
data-agents24-chat-composer=""
|
|
110
189
|
>
|
|
111
190
|
{allowAttachments && files.length > 0 && (
|
|
112
|
-
<div className="mb-2
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
191
|
+
<div className="mb-2 min-w-0">
|
|
192
|
+
<ChatAttachmentRows
|
|
193
|
+
attachments={files}
|
|
194
|
+
onRemove={(attachment) => {
|
|
195
|
+
if (!attachment.id) return
|
|
196
|
+
draftRevisionRef.current += 1
|
|
197
|
+
setFiles((current) => {
|
|
198
|
+
const removed = current.find((file) => file.id === attachment.id)
|
|
199
|
+
if (removed) revokeAgentChatComposerFiles([removed])
|
|
200
|
+
return current.filter((file) => file.id !== attachment.id)
|
|
201
|
+
})
|
|
202
|
+
}}
|
|
203
|
+
/>
|
|
124
204
|
</div>
|
|
125
205
|
)}
|
|
126
206
|
{attachmentError || recorder.error ? <p className="mb-2 text-xs text-destructive" role="alert">{attachmentError || recorder.error}</p> : null}
|
|
@@ -145,7 +225,7 @@ export function ChatComposer({
|
|
|
145
225
|
attachmentMimeTypes.includes(canonicalInputMimeType(file.type))
|
|
146
226
|
))
|
|
147
227
|
draftRevisionRef.current += 1
|
|
148
|
-
setFiles((current) => [...current, ...accepted])
|
|
228
|
+
setFiles((current) => [...current, ...accepted.map(createAgentChatComposerFile)])
|
|
149
229
|
setAttachmentError(
|
|
150
230
|
accepted.length === selected.length
|
|
151
231
|
? null
|
|
@@ -185,7 +265,7 @@ export function ChatComposer({
|
|
|
185
265
|
/>
|
|
186
266
|
<div className="agents24-composer-actions flex items-center gap-1">
|
|
187
267
|
{utilityRow === "inline" ? <AgentChatContextStatus contextWindow={contextWindow} /> : null}
|
|
188
|
-
{utilityRow === "inline" ?
|
|
268
|
+
{utilityRow === "inline" ? runtimeSelector : null}
|
|
189
269
|
{recordingAllowed ? (
|
|
190
270
|
<Button
|
|
191
271
|
variant={recorder.isRecording ? "default" : "ghost"}
|
|
@@ -224,11 +304,11 @@ export function ChatComposer({
|
|
|
224
304
|
</div>
|
|
225
305
|
{showUtilityRow ? (
|
|
226
306
|
<div
|
|
227
|
-
aria-hidden={!
|
|
307
|
+
aria-hidden={!runtimeSelector && !hasContextWindow ? "true" : undefined}
|
|
228
308
|
className="mt-2 flex min-h-8 items-center justify-between gap-2 px-1"
|
|
229
309
|
data-agents24-chat-composer-utility=""
|
|
230
310
|
>
|
|
231
|
-
<div className="min-w-0">{
|
|
311
|
+
<div className="min-w-0">{runtimeSelector}</div>
|
|
232
312
|
<AgentChatContextStatus contextWindow={contextWindow} />
|
|
233
313
|
</div>
|
|
234
314
|
) : null}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ChatHitlPart, ChatMessage as ChatMessageModel } from "@agents24/react"
|
|
2
2
|
import type { FeedbackReason, HitlResponse } from "@agents24/client"
|
|
3
3
|
import type { AgentChatRuntimeHitlActionState } from "@agents24/chat-react/runtime"
|
|
4
|
-
import { AgentChatDefaultActions,
|
|
4
|
+
import { AgentChatDefaultActions, ChatAttachmentRows, type ChatAttachmentContentResolver } from "@agents24/chat-react/ui"
|
|
5
5
|
import * as React from "react"
|
|
6
6
|
|
|
7
7
|
import { MessageParts } from "./message-parts"
|
|
@@ -20,6 +20,7 @@ type Props = {
|
|
|
20
20
|
input: { rating: "like" | "dislike" | null; reason?: FeedbackReason | null; comment?: string | null },
|
|
21
21
|
) => Promise<void>
|
|
22
22
|
Timeline?: React.ComponentType<Agents24ChatTimelineSlotProps>
|
|
23
|
+
resolveAttachmentContent?: ChatAttachmentContentResolver
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export function ChatMessage({
|
|
@@ -30,6 +31,7 @@ export function ChatMessage({
|
|
|
30
31
|
onHitlAction,
|
|
31
32
|
onRegenerate,
|
|
32
33
|
onSetFeedback,
|
|
34
|
+
resolveAttachmentContent,
|
|
33
35
|
Timeline,
|
|
34
36
|
}: Props) {
|
|
35
37
|
const isUser = message.role === "user"
|
|
@@ -37,14 +39,11 @@ export function ChatMessage({
|
|
|
37
39
|
<Message align={isUser ? "end" : "start"}>
|
|
38
40
|
<MessageContent>
|
|
39
41
|
{isUser && message.attachments?.length ? (
|
|
40
|
-
<
|
|
41
|
-
{message.attachments
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
/>
|
|
46
|
-
))}
|
|
47
|
-
</ChatAttachments>
|
|
42
|
+
<ChatAttachmentRows
|
|
43
|
+
attachments={message.attachments}
|
|
44
|
+
className="mb-2"
|
|
45
|
+
resolveContent={resolveAttachmentContent}
|
|
46
|
+
/>
|
|
48
47
|
) : null}
|
|
49
48
|
<Bubble variant={isUser ? "secondary" : "ghost"} align={isUser ? "end" : "start"} className={isUser ? "max-w-[min(75%,42rem)]" : "max-w-full overflow-visible"}>
|
|
50
49
|
<BubbleContent className={isUser ? "px-4 py-2.5" : "w-full overflow-visible"}>
|
|
@@ -87,10 +87,12 @@ export function ChatShell({
|
|
|
87
87
|
components,
|
|
88
88
|
identity,
|
|
89
89
|
runtime,
|
|
90
|
+
sidebar,
|
|
90
91
|
}: {
|
|
91
92
|
components?: Agents24ChatScaffoldComponents
|
|
92
93
|
identity: Agents24ChatIdentity
|
|
93
94
|
runtime: AgentChatRuntime
|
|
95
|
+
sidebar?: React.ReactNode
|
|
94
96
|
}) {
|
|
95
97
|
const { controller } = runtime
|
|
96
98
|
const isRunning =
|
|
@@ -98,7 +100,7 @@ export function ChatShell({
|
|
|
98
100
|
|| controller.runState === "reconnecting"
|
|
99
101
|
|| controller.runState === "paused"
|
|
100
102
|
|| controller.runState === "cancelling"
|
|
101
|
-
|
|
103
|
+
const isUploading = controller.runState === "uploading" || runtime.isUploading
|
|
102
104
|
const isCancelling = controller.runState === "cancelling"
|
|
103
105
|
const activeTitle = controller.threads.find((thread) => thread.id === controller.activeThreadId)?.title
|
|
104
106
|
const hasBlockingError = Boolean(runtime.fatalError && controller.messages.length === 0)
|
|
@@ -111,7 +113,7 @@ export function ChatShell({
|
|
|
111
113
|
data-agents24-chat-layout=""
|
|
112
114
|
>
|
|
113
115
|
<PersistentSidebarTrigger />
|
|
114
|
-
<ThreadSidebar
|
|
116
|
+
{sidebar || <ThreadSidebar
|
|
115
117
|
activeThreadId={controller.activeThreadId}
|
|
116
118
|
hasMore={controller.hasMoreThreads}
|
|
117
119
|
identity={identity}
|
|
@@ -122,7 +124,7 @@ export function ChatShell({
|
|
|
122
124
|
onLoadMore={controller.loadMoreThreads}
|
|
123
125
|
onNew={runtime.newThread}
|
|
124
126
|
onOpen={runtime.openThread}
|
|
125
|
-
/>
|
|
127
|
+
/>}
|
|
126
128
|
<SidebarInset className="h-svh min-h-0 overflow-hidden bg-background">
|
|
127
129
|
<header
|
|
128
130
|
className="flex h-12 shrink-0 items-center gap-2 border-b border-border/30 px-3 sm:px-4"
|
|
@@ -149,10 +151,14 @@ export function ChatShell({
|
|
|
149
151
|
<ChatComposer
|
|
150
152
|
inputContract={runtime.inputContract || LOADING_INPUT_CONTRACT}
|
|
151
153
|
contextWindow={runtime.contextWindow}
|
|
152
|
-
disabled={runtime.isBootstrapping}
|
|
154
|
+
disabled={runtime.isBootstrapping || runtime.isChangingAgent || isUploading}
|
|
155
|
+
focusKey={controller.activeThreadId}
|
|
153
156
|
forceExpanded
|
|
154
157
|
isCancelling={isCancelling}
|
|
155
158
|
isRunning={isRunning}
|
|
159
|
+
agentId={runtime.agentId}
|
|
160
|
+
agents={runtime.agents}
|
|
161
|
+
onAgentChange={(agentId) => { void runtime.changeAgent(agentId) }}
|
|
156
162
|
onStop={() => { void controller.cancelRun().catch(() => undefined) }}
|
|
157
163
|
modelId={runtime.modelId}
|
|
158
164
|
models={runtime.models}
|
|
@@ -160,10 +166,11 @@ export function ChatShell({
|
|
|
160
166
|
onSubmit={(text, files) => runtime.submit({
|
|
161
167
|
text,
|
|
162
168
|
files: files.map((file) => ({
|
|
163
|
-
data: file,
|
|
164
|
-
mediaType: file.
|
|
165
|
-
name: file.
|
|
169
|
+
data: file.source,
|
|
170
|
+
mediaType: file.mediaType,
|
|
171
|
+
name: file.filename,
|
|
166
172
|
})),
|
|
173
|
+
attachments: files,
|
|
167
174
|
})}
|
|
168
175
|
/>
|
|
169
176
|
</div>
|
|
@@ -216,6 +223,7 @@ export function ChatShell({
|
|
|
216
223
|
onHitlAction={runtime.onHitlAction}
|
|
217
224
|
onRegenerate={() => controller.regenerate(message)}
|
|
218
225
|
onSetFeedback={(input) => controller.setFeedback(message, input)}
|
|
226
|
+
resolveAttachmentContent={runtime.resolveAttachmentContent}
|
|
219
227
|
Timeline={components?.Timeline}
|
|
220
228
|
/>
|
|
221
229
|
</MessageScrollerItem>
|
|
@@ -230,9 +238,13 @@ export function ChatShell({
|
|
|
230
238
|
<ChatComposer
|
|
231
239
|
inputContract={runtime.inputContract || LOADING_INPUT_CONTRACT}
|
|
232
240
|
contextWindow={runtime.contextWindow}
|
|
233
|
-
disabled={runtime.isBootstrapping || hasBlockingError}
|
|
241
|
+
disabled={runtime.isBootstrapping || runtime.isChangingAgent || hasBlockingError || isUploading}
|
|
242
|
+
focusKey={controller.activeThreadId}
|
|
234
243
|
isCancelling={isCancelling}
|
|
235
244
|
isRunning={isRunning}
|
|
245
|
+
agentId={runtime.agentId}
|
|
246
|
+
agents={runtime.agents}
|
|
247
|
+
onAgentChange={(agentId) => { void runtime.changeAgent(agentId) }}
|
|
236
248
|
onStop={() => { void controller.cancelRun().catch(() => undefined) }}
|
|
237
249
|
modelId={runtime.modelId}
|
|
238
250
|
models={runtime.models}
|
|
@@ -240,10 +252,11 @@ export function ChatShell({
|
|
|
240
252
|
onSubmit={(text, files) => runtime.submit({
|
|
241
253
|
text,
|
|
242
254
|
files: files.map((file) => ({
|
|
243
|
-
data: file,
|
|
244
|
-
mediaType: file.
|
|
245
|
-
name: file.
|
|
255
|
+
data: file.source,
|
|
256
|
+
mediaType: file.mediaType,
|
|
257
|
+
name: file.filename,
|
|
246
258
|
})),
|
|
259
|
+
attachments: files,
|
|
247
260
|
})}
|
|
248
261
|
utilityRow="below"
|
|
249
262
|
/>
|
|
@@ -5,7 +5,7 @@ import { Slot } from "radix-ui"
|
|
|
5
5
|
import { cn } from "../../lib/utils"
|
|
6
6
|
|
|
7
7
|
const buttonVariants = cva(
|
|
8
|
-
"group/button inline-flex shrink-0 items-center justify-center rounded-4xl border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none
|
|
8
|
+
"group/button inline-flex shrink-0 items-center justify-center rounded-4xl border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
9
9
|
{
|
|
10
10
|
variants: {
|
|
11
11
|
variant: {
|
|
@@ -246,7 +246,7 @@ function DropdownMenuSubContent({
|
|
|
246
246
|
return (
|
|
247
247
|
<DropdownMenuPrimitive.SubContent
|
|
248
248
|
data-slot="dropdown-menu-sub-content"
|
|
249
|
-
className={cn("z-50 min-w-36 origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-3xl bg-popover p-1.5 text-popover-foreground shadow-lg ring-1 ring-foreground/5 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:ring-foreground/10 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
|
|
249
|
+
className={cn("z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-36 origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-3xl bg-popover p-1.5 text-popover-foreground shadow-lg ring-1 ring-foreground/5 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:ring-foreground/10 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
|
|
250
250
|
{...props}
|
|
251
251
|
/>
|
|
252
252
|
)
|
|
@@ -451,7 +451,7 @@ function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
|
|
|
451
451
|
}
|
|
452
452
|
|
|
453
453
|
const sidebarMenuButtonVariants = cva(
|
|
454
|
-
"peer/menu-button group/menu-button flex w-full items-center gap-2 overflow-hidden rounded-xl px-3 py-2 text-left text-sm ring-sidebar-ring outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-accent-foreground data-active:bg-sidebar-accent data-active:font-medium data-active:text-sidebar-accent-foreground [&_svg]:size-4 [&_svg]:shrink-0 [&>span:last-child]:truncate",
|
|
454
|
+
"peer/menu-button group/menu-button flex w-full items-center gap-2 overflow-hidden rounded-xl px-3 py-2 text-left text-sm ring-sidebar-ring outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-accent-foreground data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground [&_svg]:size-4 [&_svg]:shrink-0 [&>span:last-child]:truncate",
|
|
455
455
|
{
|
|
456
456
|
variants: {
|
|
457
457
|
variant: {
|
|
@@ -540,7 +540,7 @@ function SidebarMenuAction({
|
|
|
540
540
|
className={cn(
|
|
541
541
|
"absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-xl p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[size=default]/menu-button:top-2 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 after:absolute after:-inset-2 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0",
|
|
542
542
|
showOnHover &&
|
|
543
|
-
"group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 peer-data-active/menu-button:text-sidebar-accent-foreground aria-expanded:opacity-100 md:opacity-0",
|
|
543
|
+
"group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground aria-expanded:opacity-100 md:opacity-0",
|
|
544
544
|
className
|
|
545
545
|
)}
|
|
546
546
|
{...props}
|
|
@@ -557,7 +557,7 @@ function SidebarMenuBadge({
|
|
|
557
557
|
data-slot="sidebar-menu-badge"
|
|
558
558
|
data-sidebar="menu-badge"
|
|
559
559
|
className={cn(
|
|
560
|
-
"pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-xl px-1 text-xs font-medium text-sidebar-foreground tabular-nums select-none group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 peer-data-active/menu-button:text-sidebar-accent-foreground",
|
|
560
|
+
"pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-xl px-1 text-xs font-medium text-sidebar-foreground tabular-nums select-none group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground",
|
|
561
561
|
className
|
|
562
562
|
)}
|
|
563
563
|
{...props}
|
|
@@ -641,7 +641,7 @@ function SidebarMenuSubButton({
|
|
|
641
641
|
data-size={size}
|
|
642
642
|
data-active={isActive}
|
|
643
643
|
className={cn(
|
|
644
|
-
"flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-xl px-3 text-sidebar-foreground ring-sidebar-ring outline-hidden group-data-[collapsible=icon]:hidden hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[size=md]:text-sm data-[size=sm]:text-xs data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground",
|
|
644
|
+
"flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-xl px-3 text-sidebar-foreground ring-sidebar-ring outline-hidden group-data-[collapsible=icon]:hidden hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[size=md]:text-sm data-[size=sm]:text-xs data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground",
|
|
645
645
|
className
|
|
646
646
|
)}
|
|
647
647
|
{...props}
|