create-agents24-app 0.2.30 → 0.2.31
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/dist/scaffold/host-styles.css +3 -0
- package/dist/scaffold/manifest.json +2 -2
- package/dist/scaffold/src/app.tsx +4 -1
- package/dist/scaffold/src/chat.css +11 -1
- package/dist/scaffold/src/components/chat/chat-composer.tsx +36 -19
- package/dist/scaffold/src/components/chat/chat-shell.tsx +5 -4
- package/dist/scaffold/src/components/chat/external-link-dialog.tsx +6 -5
- package/dist/scaffold/src/components/chat/identity-control.tsx +0 -1
- package/dist/scaffold/src/components/chat/subagent-tasks.tsx +109 -0
- package/dist/scaffold/src/components/theme-provider.tsx +30 -27
- package/dist/scaffold/src/components/ui/tooltip.tsx +3 -0
- package/dist/scaffold/src/host.css +11 -0
- package/dist/scaffold/src/index.css +1 -213
- package/dist/scaffold/src/index.tsx +4 -2
- package/dist/scaffold/src/theme.css +215 -0
- package/dist/scaffold-manifest.json +7 -3
- package/package.json +2 -2
- package/templates/integrations/nextjs/app/api/agents24/[[...path]]/route.ts +28 -7
- package/versions.json +4 -4
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"@shadcn/react": "^0.2.1",
|
|
15
15
|
"class-variance-authority": "^0.7.1",
|
|
16
16
|
"clsx": "^2.1.1",
|
|
17
|
-
"lucide-react": "0.542.0",
|
|
18
|
-
"radix-ui": "1.4.3",
|
|
17
|
+
"lucide-react": ">=0.542.0 <2.0.0",
|
|
18
|
+
"radix-ui": ">=1.4.3 <2.0.0",
|
|
19
19
|
"streamdown": "^2.5.0",
|
|
20
20
|
"tailwind-merge": "^3.6.0",
|
|
21
21
|
"tw-animate-css": "^1.4.0"
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import * as React from "react"
|
|
17
17
|
|
|
18
18
|
import type { Agents24ChatComposerProps } from "./components/chat/chat-composer"
|
|
19
|
+
|
|
19
20
|
import { ChatShell } from "./components/chat/chat-shell"
|
|
20
21
|
import { GUEST_CHAT_IDENTITY, type Agents24ChatIdentity } from "./identity"
|
|
21
22
|
|
|
@@ -36,9 +37,11 @@ export type Agents24ChatAppProps = {
|
|
|
36
37
|
sidebar?: React.ReactNode
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
export type Agents24ChatComposerSlotProps = Agents24ChatComposerProps
|
|
41
|
+
|
|
39
42
|
export type Agents24ChatScaffoldComponents = {
|
|
43
|
+
Composer?: React.ComponentType<Agents24ChatComposerSlotProps>
|
|
40
44
|
Timeline?: React.ComponentType<Agents24ChatTimelineSlotProps>
|
|
41
|
-
Composer?: React.ComponentType<Agents24ChatComposerProps>
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
export type ChatHitlResponse = HitlResponse
|
|
@@ -50,10 +50,20 @@
|
|
|
50
50
|
grid-template-areas: "textarea textarea textarea" "attach spacer submit";
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
[data-agents24-theme-root] .agents24-composer-grid:not([data-expanded]) {
|
|
53
|
+
[data-agents24-theme-root] .agents24-composer-grid:not([data-expanded]):not([data-attachments]) {
|
|
54
54
|
border-radius: var(--a24-composer-single-row-radius);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/* Draft attachments stay within the same surface and precede its input rows. */
|
|
58
|
+
.agents24-composer-grid[data-attachments] {
|
|
59
|
+
grid-template-areas: "attachments attachments attachments" "attach textarea submit";
|
|
60
|
+
border-radius: 1.125rem;
|
|
61
|
+
}
|
|
62
|
+
.agents24-composer-grid[data-attachments][data-expanded] {
|
|
63
|
+
grid-template-areas: "attachments attachments attachments" "textarea textarea textarea" "attach spacer submit";
|
|
64
|
+
}
|
|
65
|
+
.agents24-composer-attachments { grid-area: attachments; }
|
|
66
|
+
|
|
57
67
|
.agents24-composer-attach { grid-area: attach; }
|
|
58
68
|
.agents24-composer-textarea { grid-area: textarea; }
|
|
59
69
|
.agents24-composer-actions { grid-area: submit; }
|
|
@@ -20,6 +20,8 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"
|
|
|
20
20
|
import { cn } from "../../lib/utils"
|
|
21
21
|
|
|
22
22
|
export type Agents24ChatComposerProps = {
|
|
23
|
+
queue?: React.ReactNode
|
|
24
|
+
onRecordingStreamChange?: (stream: MediaStream | null) => void
|
|
23
25
|
inputContract: AgentChatInputContract
|
|
24
26
|
disabled?: boolean
|
|
25
27
|
contextWindow?: ContextWindow | null
|
|
@@ -41,6 +43,8 @@ export type Agents24ChatComposerProps = {
|
|
|
41
43
|
|
|
42
44
|
export function ChatComposer({
|
|
43
45
|
inputContract,
|
|
46
|
+
queue,
|
|
47
|
+
onRecordingStreamChange,
|
|
44
48
|
disabled,
|
|
45
49
|
contextWindow,
|
|
46
50
|
focusKey,
|
|
@@ -59,12 +63,12 @@ export function ChatComposer({
|
|
|
59
63
|
utilityRow = "inline",
|
|
60
64
|
}: Agents24ChatComposerProps) {
|
|
61
65
|
const inputRef = React.useRef<HTMLInputElement>(null)
|
|
62
|
-
const textareaRef
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
const { text, setText, files, removeFile, addFiles, textareaRef, submit,
|
|
67
|
+
attachmentError, attachmentMimeTypes, allowAttachments, recordingAllowed,
|
|
68
|
+
recorder, isExpanded, validationError, action, canSubmit } = useAgentChatComposer({
|
|
69
|
+
inputContract, disabled, focusKey, forceExpanded, isCancelling, isRunning,
|
|
70
|
+
onSubmit, restoredDraft, onRecordingStreamChange,
|
|
71
|
+
})
|
|
68
72
|
|
|
69
73
|
const selectedAgent = agents.find((item) => item.id === agentId)
|
|
70
74
|
const selectedModel = models.find((item) => item.id === modelId)
|
|
@@ -129,22 +133,28 @@ export function ChatComposer({
|
|
|
129
133
|
className="relative z-10 w-full pb-[max(1rem,env(safe-area-inset-bottom))]"
|
|
130
134
|
data-agents24-chat-composer=""
|
|
131
135
|
>
|
|
132
|
-
{
|
|
133
|
-
<div className="mb-2 min-w-0">
|
|
134
|
-
<ChatAttachmentRows
|
|
135
|
-
attachments={files}
|
|
136
|
-
onRemove={(attachment) => { if (attachment.id) composer.removeFile(attachment.id) }}
|
|
137
|
-
/>
|
|
138
|
-
</div>
|
|
139
|
-
)}
|
|
136
|
+
{queue}
|
|
140
137
|
{attachmentError || recorder.error ? <p className="mb-2 text-xs text-destructive" role="alert">{attachmentError || recorder.error}</p> : null}
|
|
141
138
|
<div
|
|
142
139
|
data-expanded={isExpanded || undefined}
|
|
140
|
+
data-attachments={files.length > 0 || undefined}
|
|
143
141
|
className={cn(
|
|
144
142
|
"agents24-composer-grid grid min-w-0 items-center gap-2 rounded-xl border bg-background shadow-sm",
|
|
145
143
|
isExpanded ? "p-2" : "p-1.5",
|
|
146
144
|
)}
|
|
147
145
|
>
|
|
146
|
+
{allowAttachments && files.length > 0 && (
|
|
147
|
+
<div className="agents24-composer-attachments min-w-0">
|
|
148
|
+
<ChatAttachmentRows
|
|
149
|
+
layout="composer"
|
|
150
|
+
attachments={files}
|
|
151
|
+
onRemove={(attachment) => {
|
|
152
|
+
if (!attachment.id) return
|
|
153
|
+
removeFile(attachment.id)
|
|
154
|
+
}}
|
|
155
|
+
/>
|
|
156
|
+
</div>
|
|
157
|
+
)}
|
|
148
158
|
{allowAttachments ? (
|
|
149
159
|
<>
|
|
150
160
|
<input
|
|
@@ -152,9 +162,9 @@ export function ChatComposer({
|
|
|
152
162
|
className="sr-only"
|
|
153
163
|
type="file"
|
|
154
164
|
multiple
|
|
155
|
-
accept={
|
|
165
|
+
accept={attachmentMimeTypes.join(",")}
|
|
156
166
|
onChange={(event) => {
|
|
157
|
-
|
|
167
|
+
addFiles(Array.from(event.target.files ?? []))
|
|
158
168
|
event.currentTarget.value = ""
|
|
159
169
|
}}
|
|
160
170
|
/>
|
|
@@ -176,8 +186,15 @@ export function ChatComposer({
|
|
|
176
186
|
"agents24-composer-textarea max-h-56 min-h-9 field-sizing-content resize-none overflow-y-auto border-0 bg-transparent px-2 shadow-none focus-visible:ring-0 dark:bg-transparent",
|
|
177
187
|
isExpanded ? "py-2" : "h-9 py-2",
|
|
178
188
|
)}
|
|
179
|
-
onChange={(event) =>
|
|
180
|
-
|
|
189
|
+
onChange={(event) => {
|
|
190
|
+
setText(event.target.value)
|
|
191
|
+
}}
|
|
192
|
+
onKeyDown={(event) => {
|
|
193
|
+
if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
|
|
194
|
+
event.preventDefault()
|
|
195
|
+
void submit()
|
|
196
|
+
}
|
|
197
|
+
}}
|
|
181
198
|
/>
|
|
182
199
|
<div className="agents24-composer-actions flex items-center gap-1">
|
|
183
200
|
{utilityRow === "inline" ? <AgentChatContextStatus contextWindow={contextWindow} /> : null}
|
|
@@ -208,7 +225,7 @@ export function ChatComposer({
|
|
|
208
225
|
<TooltipContent>{validationError}</TooltipContent>
|
|
209
226
|
</Tooltip>
|
|
210
227
|
) : (
|
|
211
|
-
<Button size="icon" aria-label={isRunning ? "Queue message" : "Send message"} disabled={
|
|
228
|
+
<Button size="icon" aria-label={isRunning ? "Queue message" : "Send message"} disabled={!canSubmit} onClick={() => void submit()}><ArrowUp /></Button>
|
|
212
229
|
)}
|
|
213
230
|
</div>
|
|
214
231
|
</div>
|
|
@@ -11,6 +11,7 @@ import type { Agents24ChatIdentity } from "../../identity"
|
|
|
11
11
|
|
|
12
12
|
import { ChatComposer } from "./chat-composer"
|
|
13
13
|
import { ChatMessage } from "./chat-message"
|
|
14
|
+
import { SubagentTasks } from "./subagent-tasks"
|
|
14
15
|
import { ThemeMenu } from "./theme-menu"
|
|
15
16
|
import { ThreadSidebar } from "./thread-sidebar"
|
|
16
17
|
import { TurnOutline } from "./turn-outline"
|
|
@@ -168,6 +169,7 @@ export function ChatShell({
|
|
|
168
169
|
data-agents24-chat-transcript=""
|
|
169
170
|
>
|
|
170
171
|
<OperationErrorNotice runtime={runtime} />
|
|
172
|
+
<SubagentTasks threadId={controller.activeThreadId} />
|
|
171
173
|
<LatestThreadScroller.Provider autoScroll defaultScrollPosition="last-anchor" scrollPreviousItemPeek={64}>
|
|
172
174
|
{!isEmptyState && (
|
|
173
175
|
<LatestThreadScroller.Root className="relative min-h-0 flex-1">
|
|
@@ -240,11 +242,10 @@ export function ChatShell({
|
|
|
240
242
|
<LatestThreadScroller.Button className="!relative !inset-auto !translate-x-0 rtl:!translate-x-0" />
|
|
241
243
|
</div>}
|
|
242
244
|
<div className="pointer-events-auto mx-auto w-full max-w-3xl px-4 sm:px-6">
|
|
243
|
-
<AgentChatQueue pending={controller.pendingMessages} queue={controller.messageQueue}
|
|
244
|
-
canSteer={controller.runState === "streaming"}
|
|
245
|
-
onSteer={controller.steerPendingMessage} onRemove={controller.removePendingMessage}
|
|
246
|
-
onEdit={retractForEdit} onResume={controller.resumeQueue} />
|
|
247
245
|
<Composer
|
|
246
|
+
queue={<AgentChatQueue pending={controller.pendingMessages} queue={controller.messageQueue}
|
|
247
|
+
onRemove={controller.removePendingMessage}
|
|
248
|
+
onEdit={retractForEdit} onResume={controller.resumeQueue} />}
|
|
248
249
|
restoredDraft={restoredDraft}
|
|
249
250
|
inputContract={runtime.inputContract || LOADING_INPUT_CONTRACT}
|
|
250
251
|
contextWindow={runtime.contextWindow}
|
|
@@ -14,9 +14,10 @@ import {
|
|
|
14
14
|
export function ExternalLinkDialog({ isOpen, onClose, onConfirm, url }: LinkSafetyModalProps) {
|
|
15
15
|
const [copied, setCopied] = React.useState(false)
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
const close = () => {
|
|
18
|
+
setCopied(false)
|
|
19
|
+
onClose()
|
|
20
|
+
}
|
|
20
21
|
|
|
21
22
|
const copyUrl = async () => {
|
|
22
23
|
await navigator.clipboard.writeText(url)
|
|
@@ -25,11 +26,11 @@ export function ExternalLinkDialog({ isOpen, onClose, onConfirm, url }: LinkSafe
|
|
|
25
26
|
|
|
26
27
|
const openUrl = () => {
|
|
27
28
|
onConfirm()
|
|
28
|
-
|
|
29
|
+
close()
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
return (
|
|
32
|
-
<Dialog open={isOpen} onOpenChange={(open) => { if (!open)
|
|
33
|
+
<Dialog open={isOpen} onOpenChange={(open) => { if (!open) close() }}>
|
|
33
34
|
<DialogContent className="max-h-[calc(100dvh-2rem)] min-w-0 overflow-hidden sm:max-w-md">
|
|
34
35
|
<DialogHeader>
|
|
35
36
|
<DialogTitle>Open external link?</DialogTitle>
|
|
@@ -21,7 +21,6 @@ function identityLabel(identity: Agents24ChatIdentity): string {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
function IdentityAvatar({ identity }: { identity: Agents24ChatIdentity }) {
|
|
24
|
-
const label = identityLabel(identity)
|
|
25
24
|
if (identity.avatarUrl) {
|
|
26
25
|
return <img alt="" className="size-6 rounded-full object-cover" src={identity.avatarUrl} />
|
|
27
26
|
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { useAgentClient, partsFromResponseBlocks } from "@agents24/react"
|
|
3
|
+
import { AgentResponseTimeline } from "@agents24/chat-react/ui"
|
|
4
|
+
import { useDelegationTasks, type DelegationHistory, type DelegationTask } from "@agents24/chat-react/runtime"
|
|
5
|
+
import { Button } from "../ui/button"
|
|
6
|
+
|
|
7
|
+
export function SubagentTasks({ threadId }: { threadId: string | null }) {
|
|
8
|
+
const client = useAgentClient()
|
|
9
|
+
const source = React.useMemo(() => ({ events: (input: { threadId: string; after: number; signal: AbortSignal }) =>
|
|
10
|
+
client.threads.delegations.events(input) }), [client])
|
|
11
|
+
const { tasks, error, refresh } = useDelegationTasks({ threadId, source })
|
|
12
|
+
const [selection, setSelection] = React.useState<{ threadId: string | null; handle: string | null }>({ threadId: null, handle: null })
|
|
13
|
+
const selected = selection.threadId === threadId ? selection.handle : null
|
|
14
|
+
if (!threadId || (!tasks.length && !error)) return null
|
|
15
|
+
return <section aria-label="Subagent tasks" className="mx-auto w-full max-w-3xl px-4 py-2 text-sm">
|
|
16
|
+
<details>
|
|
17
|
+
<summary className="cursor-pointer font-medium">Tasks ({tasks.length})</summary>
|
|
18
|
+
{error ? <p role="alert">Task updates are unavailable. Retrying…</p> : null}
|
|
19
|
+
<ul className="max-h-56 overflow-y-auto">
|
|
20
|
+
{tasks.map(task => <li key={task.handle} style={{ paddingInlineStart: Math.max(0, task.depth - 1) * 12 }}>
|
|
21
|
+
<button type="button" className="flex w-full items-center justify-between gap-3 rounded-md px-2 py-2 text-start hover:bg-muted"
|
|
22
|
+
onClick={() => setSelection({ threadId, handle: selected === task.handle ? null : task.handle })} aria-expanded={selected === task.handle}>
|
|
23
|
+
<span className="min-w-0 truncate">{task.name}{task.background ? " · Background" : ""}</span>
|
|
24
|
+
<span className="shrink-0 text-xs text-muted-foreground">{task.status.replaceAll("_", " ")}</span>
|
|
25
|
+
</button>
|
|
26
|
+
</li>)}
|
|
27
|
+
</ul>
|
|
28
|
+
{tasks.filter(task => task.handle === selected).map(task =>
|
|
29
|
+
<TaskConversation key={`${threadId}:${task.handle}`} threadId={threadId} task={task} onChanged={refresh} />)}
|
|
30
|
+
</details>
|
|
31
|
+
</section>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function TaskConversation({ threadId, task, onChanged }: {
|
|
35
|
+
threadId: string; task: DelegationTask; onChanged: () => void
|
|
36
|
+
}) {
|
|
37
|
+
const client = useAgentClient()
|
|
38
|
+
const [history, setHistory] = React.useState<DelegationHistory | null>(null)
|
|
39
|
+
const [earlier, setEarlier] = React.useState<DelegationHistory["turns"]>([])
|
|
40
|
+
const historyInitialized = React.useRef(false)
|
|
41
|
+
const [before, setBefore] = React.useState<number | null>(null)
|
|
42
|
+
const [error, setError] = React.useState<string | null>(null)
|
|
43
|
+
const [busy, setBusy] = React.useState(false)
|
|
44
|
+
const operation = React.useRef<{ action: "interrupt" | "cancel"; id: string } | null>(null)
|
|
45
|
+
React.useEffect(() => {
|
|
46
|
+
const controller = new AbortController()
|
|
47
|
+
let timer: ReturnType<typeof setTimeout>
|
|
48
|
+
const refresh = async () => {
|
|
49
|
+
try {
|
|
50
|
+
const page = await client.threads.delegations.history({ threadId, handle: task.handle, signal: controller.signal })
|
|
51
|
+
if (controller.signal.aborted) return
|
|
52
|
+
setHistory(page)
|
|
53
|
+
if (!historyInitialized.current) {
|
|
54
|
+
historyInitialized.current = true
|
|
55
|
+
setBefore(page.paging.next_before_turn_index)
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
if (!controller.signal.aborted) setError("Task history could not be loaded.")
|
|
59
|
+
} finally {
|
|
60
|
+
if (!controller.signal.aborted) timer = setTimeout(refresh, 2000)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
void refresh()
|
|
64
|
+
return () => { controller.abort(); clearTimeout(timer) }
|
|
65
|
+
}, [client, threadId, task.handle])
|
|
66
|
+
const loadEarlier = async () => {
|
|
67
|
+
if (before === null) return
|
|
68
|
+
setBusy(true)
|
|
69
|
+
try {
|
|
70
|
+
const page = await client.threads.delegations.history({ threadId, handle: task.handle, before })
|
|
71
|
+
setEarlier(current => [...page.turns, ...current])
|
|
72
|
+
setBefore(page.paging.next_before_turn_index)
|
|
73
|
+
} catch { setError("Earlier task history could not be loaded.") }
|
|
74
|
+
finally { setBusy(false) }
|
|
75
|
+
}
|
|
76
|
+
const act = async (action: "interrupt" | "cancel") => {
|
|
77
|
+
setBusy(true); setError(null)
|
|
78
|
+
if (operation.current?.action !== action) operation.current = { action, id: crypto.randomUUID() }
|
|
79
|
+
try {
|
|
80
|
+
await client.threads.delegations.act({ threadId, handle: task.handle,
|
|
81
|
+
action: { action, operation_id: operation.current.id } })
|
|
82
|
+
operation.current = null
|
|
83
|
+
onChanged()
|
|
84
|
+
} catch { setError("The task action could not be completed. Try again.") }
|
|
85
|
+
finally { setBusy(false) }
|
|
86
|
+
}
|
|
87
|
+
const turns = Array.from(new Map([...earlier, ...(history?.turns || [])].map(turn => [turn.id, turn])).values())
|
|
88
|
+
.sort((a, b) => a.turn_index - b.turn_index)
|
|
89
|
+
return <div className="space-y-3 rounded-md bg-muted/30 p-3">
|
|
90
|
+
<h3 className="font-medium">{task.name}</h3>
|
|
91
|
+
{error ? <p role="alert">{error}</p> : null}
|
|
92
|
+
<div className="flex flex-wrap gap-2">
|
|
93
|
+
{task.capabilities.interrupt ? <Button variant="secondary" size="sm" disabled={busy} onClick={() => void act("interrupt")}>Interrupt</Button> : null}
|
|
94
|
+
{task.capabilities.cancel ? <Button variant="secondary" size="sm" disabled={busy} onClick={() => void act("cancel")}>Cancel task</Button> : null}
|
|
95
|
+
{before !== null ? <Button variant="ghost" size="sm" disabled={busy} onClick={() => void loadEarlier()}>Earlier exchanges</Button> : null}
|
|
96
|
+
</div>
|
|
97
|
+
<div className="max-h-[55dvh] space-y-4 overflow-y-auto break-words">
|
|
98
|
+
{turns.map(turn => <article key={turn.id} className="space-y-2">
|
|
99
|
+
{turn.assignment_instruction ? <p className="whitespace-pre-wrap text-muted-foreground">{turn.assignment_instruction}</p> : null}
|
|
100
|
+
{turn.user_input_text ? <p className="whitespace-pre-wrap">{turn.user_input_text}</p> : null}
|
|
101
|
+
{turn.exchanges?.map(exchange => <div key={exchange.id} className="whitespace-pre-wrap break-words text-sm">
|
|
102
|
+
<p className="text-xs font-medium">{exchange.sender === "parent" ? "Parent Agent" : "Subagent update"}</p>
|
|
103
|
+
{exchange.text}
|
|
104
|
+
</div>)}
|
|
105
|
+
<AgentResponseTimeline message={{ id: turn.id, role: "assistant", createdAt: turn.created_at, content: turn.assistant_output_text || "", parts: partsFromResponseBlocks(turn.response_blocks) }} />
|
|
106
|
+
</article>)}
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from "react"
|
|
2
2
|
|
|
3
3
|
export type Agents24ChatAppearance = "system" | "light" | "dark"
|
|
4
|
-
export type Agents24ChatTheme = "soft" | "sharp"
|
|
4
|
+
export type Agents24ChatTheme = "soft" | "sharp"
|
|
5
5
|
|
|
6
6
|
export type Agents24ChatAppearanceController = {
|
|
7
7
|
appearance: Agents24ChatAppearance
|
|
@@ -9,13 +9,28 @@ export type Agents24ChatAppearanceController = {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
type ThemeContextValue = {
|
|
12
|
-
appearance: Agents24ChatAppearance
|
|
13
12
|
inherited: boolean
|
|
13
|
+
appearance: Agents24ChatAppearance
|
|
14
14
|
portalContainer: HTMLElement | null
|
|
15
15
|
setAppearance: (appearance: Agents24ChatAppearance) => void
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
const ThemeContext = React.createContext<ThemeContextValue | null>(null)
|
|
19
|
+
const appearanceEvent = "agents24-chat-appearance-change"
|
|
20
|
+
const subscribeAppearance = (notify: () => void) => {
|
|
21
|
+
window.addEventListener("storage", notify)
|
|
22
|
+
window.addEventListener(appearanceEvent, notify)
|
|
23
|
+
return () => {
|
|
24
|
+
window.removeEventListener("storage", notify)
|
|
25
|
+
window.removeEventListener(appearanceEvent, notify)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const subscribeDarkMode = (notify: () => void) => {
|
|
29
|
+
const media = window.matchMedia?.("(prefers-color-scheme: dark)")
|
|
30
|
+
media?.addEventListener("change", notify)
|
|
31
|
+
return () => media?.removeEventListener("change", notify)
|
|
32
|
+
}
|
|
33
|
+
const getDarkMode = () => window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? false
|
|
19
34
|
const storageKey = "agents24-chat-appearance"
|
|
20
35
|
|
|
21
36
|
function storedAppearance(): Agents24ChatAppearance {
|
|
@@ -31,48 +46,36 @@ export function ThemeProvider({
|
|
|
31
46
|
children,
|
|
32
47
|
controller,
|
|
33
48
|
theme = "soft",
|
|
49
|
+
mode = "isolated",
|
|
34
50
|
}: {
|
|
35
51
|
children: React.ReactNode
|
|
36
52
|
controller?: Agents24ChatAppearanceController
|
|
37
53
|
theme?: Agents24ChatTheme
|
|
54
|
+
mode?: "isolated" | "inherit"
|
|
38
55
|
}) {
|
|
39
|
-
const [
|
|
40
|
-
const
|
|
41
|
-
const
|
|
56
|
+
const [sessionAppearance, setSessionAppearance] = React.useState<Agents24ChatAppearance | null>(null)
|
|
57
|
+
const stored = React.useSyncExternalStore(subscribeAppearance, storedAppearance, () => "system" as const)
|
|
58
|
+
const systemDark = React.useSyncExternalStore(subscribeDarkMode, getDarkMode, () => false)
|
|
42
59
|
const [portalContainer, setPortalContainer] = React.useState<HTMLDivElement | null>(null)
|
|
43
|
-
const appearance = controller?.appearance ??
|
|
44
|
-
|
|
45
|
-
React.useEffect(() => {
|
|
46
|
-
if (theme !== "inherit" && !controller) setInternalAppearance(storedAppearance())
|
|
47
|
-
setMounted(true)
|
|
48
|
-
}, [controller, theme])
|
|
49
|
-
|
|
50
|
-
React.useEffect(() => {
|
|
51
|
-
if (theme === "inherit" || !mounted || typeof window === "undefined" || typeof window.matchMedia !== "function") return
|
|
52
|
-
const media = window.matchMedia("(prefers-color-scheme: dark)")
|
|
53
|
-
const apply = () => setResolvedAppearance(
|
|
54
|
-
appearance === "system" ? media.matches ? "dark" : "light" : appearance,
|
|
55
|
-
)
|
|
56
|
-
apply()
|
|
57
|
-
media.addEventListener("change", apply)
|
|
58
|
-
return () => media.removeEventListener("change", apply)
|
|
59
|
-
}, [appearance, mounted, theme])
|
|
60
|
+
const appearance = controller?.appearance ?? sessionAppearance ?? stored
|
|
61
|
+
const resolvedAppearance = appearance === "system" ? (systemDark ? "dark" : "light") : appearance
|
|
60
62
|
|
|
61
63
|
const change = React.useCallback((next: Agents24ChatAppearance) => {
|
|
62
|
-
if (theme === "inherit") return
|
|
63
64
|
if (controller) {
|
|
64
65
|
controller.setAppearance(next)
|
|
65
66
|
return
|
|
66
67
|
}
|
|
68
|
+
setSessionAppearance(next)
|
|
67
69
|
try { window.localStorage.setItem(storageKey, next) } catch { /* storage is optional */ }
|
|
68
|
-
|
|
69
|
-
}, [controller
|
|
70
|
+
window.dispatchEvent(new Event(appearanceEvent))
|
|
71
|
+
}, [controller])
|
|
70
72
|
|
|
71
73
|
return (
|
|
72
|
-
<ThemeContext.Provider value={{ appearance, inherited:
|
|
74
|
+
<ThemeContext.Provider value={{ appearance, portalContainer, setAppearance: change, inherited: mode === "inherit" }}>
|
|
73
75
|
<div
|
|
74
76
|
ref={setPortalContainer}
|
|
75
|
-
className={
|
|
77
|
+
className={mode === "inherit" ? "size-full" : `${resolvedAppearance} size-full`}
|
|
78
|
+
data-agents24-theme-mode={mode}
|
|
76
79
|
data-agents24-theme={theme}
|
|
77
80
|
data-agents24-theme-root=""
|
|
78
81
|
>
|
|
@@ -21,6 +21,7 @@ function TooltipTrigger({
|
|
|
21
21
|
asChild: _asChild,
|
|
22
22
|
...props
|
|
23
23
|
}: React.ComponentProps<typeof Slot.Root> & { asChild?: boolean }) {
|
|
24
|
+
void _asChild
|
|
24
25
|
return <Slot.Root data-slot="tooltip-trigger" {...props} />
|
|
25
26
|
}
|
|
26
27
|
|
|
@@ -35,6 +36,8 @@ function TooltipContent({
|
|
|
35
36
|
sideOffset?: number
|
|
36
37
|
align?: "start" | "center" | "end"
|
|
37
38
|
}) {
|
|
39
|
+
void _align
|
|
40
|
+
void _sideOffset
|
|
38
41
|
return (
|
|
39
42
|
<span
|
|
40
43
|
role="tooltip"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
@layer theme, base, components, utilities;
|
|
2
|
+
@import "tailwindcss/theme.css" layer(theme);
|
|
3
|
+
@import "tailwindcss/utilities.css" layer(utilities);
|
|
4
|
+
@import "tw-animate-css";
|
|
5
|
+
@import "@fontsource-variable/jetbrains-mono";
|
|
6
|
+
@import "@fontsource-variable/merriweather";
|
|
7
|
+
@import "@fontsource-variable/inter";
|
|
8
|
+
|
|
9
|
+
@source "./**/*.{ts,tsx}";
|
|
10
|
+
|
|
11
|
+
@import "./theme.css";
|
|
@@ -6,216 +6,4 @@
|
|
|
6
6
|
|
|
7
7
|
@source "./**/*.{ts,tsx}";
|
|
8
8
|
|
|
9
|
-
@
|
|
10
|
-
|
|
11
|
-
@theme inline {
|
|
12
|
-
--font-heading: var(--a24-font-heading);
|
|
13
|
-
--font-sans: var(--a24-font-sans);
|
|
14
|
-
--color-sidebar-ring: var(--sidebar-ring);
|
|
15
|
-
--color-sidebar-border: var(--sidebar-border);
|
|
16
|
-
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
17
|
-
--color-sidebar-accent: var(--sidebar-accent);
|
|
18
|
-
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
19
|
-
--color-sidebar-primary: var(--sidebar-primary);
|
|
20
|
-
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
21
|
-
--color-sidebar: var(--sidebar);
|
|
22
|
-
--color-ring: var(--ring);
|
|
23
|
-
--color-input: var(--input);
|
|
24
|
-
--color-border: var(--border);
|
|
25
|
-
--color-destructive: var(--destructive);
|
|
26
|
-
--color-accent-foreground: var(--accent-foreground);
|
|
27
|
-
--color-accent: var(--accent);
|
|
28
|
-
--color-muted-foreground: var(--muted-foreground);
|
|
29
|
-
--color-muted: var(--muted);
|
|
30
|
-
--color-secondary-foreground: var(--secondary-foreground);
|
|
31
|
-
--color-secondary: var(--secondary);
|
|
32
|
-
--color-primary-foreground: var(--primary-foreground);
|
|
33
|
-
--color-primary: var(--primary);
|
|
34
|
-
--color-popover-foreground: var(--popover-foreground);
|
|
35
|
-
--color-popover: var(--popover);
|
|
36
|
-
--color-card-foreground: var(--card-foreground);
|
|
37
|
-
--color-card: var(--card);
|
|
38
|
-
--color-foreground: var(--foreground);
|
|
39
|
-
--color-background: var(--background);
|
|
40
|
-
--radius-sm: calc(var(--radius) * 0.6);
|
|
41
|
-
--radius-md: calc(var(--radius) * 0.8);
|
|
42
|
-
--radius-lg: var(--radius);
|
|
43
|
-
--radius-xl: calc(var(--radius) * 1.4);
|
|
44
|
-
--radius-2xl: calc(var(--radius) * 1.8);
|
|
45
|
-
--radius-3xl: calc(var(--radius) * 2.2);
|
|
46
|
-
--radius-4xl: calc(var(--radius) * 2.6);
|
|
47
|
-
--a24-composer-single-row-radius: 9999px;
|
|
48
|
-
--color-chart-5: var(--chart-5);
|
|
49
|
-
--color-chart-4: var(--chart-4);
|
|
50
|
-
--color-chart-3: var(--chart-3);
|
|
51
|
-
--color-chart-2: var(--chart-2);
|
|
52
|
-
--color-chart-1: var(--chart-1);
|
|
53
|
-
--font-serif: var(--a24-font-heading);
|
|
54
|
-
--font-mono: var(--a24-font-mono);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
[data-agents24-theme-root]:not([data-agents24-theme="inherit"]) {
|
|
58
|
-
color-scheme: light;
|
|
59
|
-
--a24-font-heading: 'Inter Variable', sans-serif;
|
|
60
|
-
--a24-font-sans: 'Inter Variable', sans-serif;
|
|
61
|
-
--a24-font-mono: 'JetBrains Mono Variable', monospace;
|
|
62
|
-
--radius-sm: calc(var(--radius) * 0.6);
|
|
63
|
-
--radius-md: calc(var(--radius) * 0.8);
|
|
64
|
-
--radius-lg: var(--radius);
|
|
65
|
-
--radius-xl: calc(var(--radius) * 1.4);
|
|
66
|
-
--radius-2xl: calc(var(--radius) * 1.8);
|
|
67
|
-
--radius-3xl: calc(var(--radius) * 2.2);
|
|
68
|
-
--radius-4xl: calc(var(--radius) * 2.6);
|
|
69
|
-
--background: oklch(1 0 0);
|
|
70
|
-
--foreground: oklch(0.147 0.004 49.25);
|
|
71
|
-
--card: oklch(1 0 0);
|
|
72
|
-
--card-foreground: oklch(0.147 0.004 49.25);
|
|
73
|
-
--popover: oklch(1 0 0);
|
|
74
|
-
--popover-foreground: oklch(0.147 0.004 49.25);
|
|
75
|
-
--primary: oklch(0.52 0.105 223.128);
|
|
76
|
-
--primary-foreground: oklch(0.984 0.019 200.873);
|
|
77
|
-
--secondary: oklch(0.967 0.001 286.375);
|
|
78
|
-
--secondary-foreground: oklch(0.21 0.006 285.885);
|
|
79
|
-
--muted: oklch(0.97 0.001 106.424);
|
|
80
|
-
--muted-foreground: oklch(0.553 0.013 58.071);
|
|
81
|
-
--accent: oklch(0.97 0.001 106.424);
|
|
82
|
-
--accent-foreground: oklch(0.216 0.006 56.043);
|
|
83
|
-
--destructive: oklch(0.577 0.245 27.325);
|
|
84
|
-
--border: oklch(0.923 0.003 48.717);
|
|
85
|
-
--input: oklch(0.923 0.003 48.717);
|
|
86
|
-
--ring: oklch(0.709 0.01 56.259);
|
|
87
|
-
--radius: 0.875rem;
|
|
88
|
-
--sidebar: oklch(0.985 0.001 106.423);
|
|
89
|
-
--sidebar-foreground: oklch(0.147 0.004 49.25);
|
|
90
|
-
--sidebar-primary: oklch(0.609 0.126 221.723);
|
|
91
|
-
--sidebar-primary-foreground: oklch(0.984 0.019 200.873);
|
|
92
|
-
--sidebar-accent: oklch(0.97 0.001 106.424);
|
|
93
|
-
--sidebar-accent-foreground: oklch(0.216 0.006 56.043);
|
|
94
|
-
--sidebar-border: oklch(0.923 0.003 48.717);
|
|
95
|
-
--sidebar-ring: oklch(0.709 0.01 56.259);
|
|
96
|
-
--chart-1: oklch(0.845 0.143 164.978);
|
|
97
|
-
--chart-2: oklch(0.696 0.17 162.48);
|
|
98
|
-
--chart-3: oklch(0.596 0.145 163.225);
|
|
99
|
-
--chart-4: oklch(0.508 0.118 165.612);
|
|
100
|
-
--chart-5: oklch(0.432 0.095 166.913);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
[data-agents24-theme-root]:not([data-agents24-theme="inherit"]).dark {
|
|
104
|
-
color-scheme: dark;
|
|
105
|
-
--background: oklch(0.147 0.004 49.25);
|
|
106
|
-
--foreground: oklch(0.985 0.001 106.423);
|
|
107
|
-
--card: oklch(0.216 0.006 56.043);
|
|
108
|
-
--card-foreground: oklch(0.985 0.001 106.423);
|
|
109
|
-
--popover: oklch(0.216 0.006 56.043);
|
|
110
|
-
--popover-foreground: oklch(0.985 0.001 106.423);
|
|
111
|
-
--primary: oklch(0.45 0.085 224.283);
|
|
112
|
-
--primary-foreground: oklch(0.984 0.019 200.873);
|
|
113
|
-
--secondary: oklch(0.274 0.006 286.033);
|
|
114
|
-
--secondary-foreground: oklch(0.985 0 0);
|
|
115
|
-
--muted: oklch(0.268 0.007 34.298);
|
|
116
|
-
--muted-foreground: oklch(0.709 0.01 56.259);
|
|
117
|
-
--accent: oklch(0.268 0.007 34.298);
|
|
118
|
-
--accent-foreground: oklch(0.985 0.001 106.423);
|
|
119
|
-
--destructive: oklch(0.704 0.191 22.216);
|
|
120
|
-
--border: oklch(1 0 0 / 10%);
|
|
121
|
-
--input: oklch(1 0 0 / 15%);
|
|
122
|
-
--ring: oklch(0.553 0.013 58.071);
|
|
123
|
-
--sidebar: oklch(0.216 0.006 56.043);
|
|
124
|
-
--sidebar-foreground: oklch(0.985 0.001 106.423);
|
|
125
|
-
--sidebar-primary: oklch(0.715 0.143 215.221);
|
|
126
|
-
--sidebar-primary-foreground: oklch(0.302 0.056 229.695);
|
|
127
|
-
--sidebar-accent: oklch(0.268 0.007 34.298);
|
|
128
|
-
--sidebar-accent-foreground: oklch(0.985 0.001 106.423);
|
|
129
|
-
--sidebar-border: oklch(1 0 0 / 10%);
|
|
130
|
-
--sidebar-ring: oklch(0.553 0.013 58.071);
|
|
131
|
-
--chart-1: oklch(0.845 0.143 164.978);
|
|
132
|
-
--chart-2: oklch(0.696 0.17 162.48);
|
|
133
|
-
--chart-3: oklch(0.596 0.145 163.225);
|
|
134
|
-
--chart-4: oklch(0.508 0.118 165.612);
|
|
135
|
-
--chart-5: oklch(0.432 0.095 166.913);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
[data-agents24-theme-root]:not([data-agents24-theme="inherit"])[data-agents24-theme="sharp"] {
|
|
139
|
-
--a24-composer-single-row-radius: 0;
|
|
140
|
-
--a24-font-heading: 'Merriweather Variable', serif;
|
|
141
|
-
--a24-font-sans: 'JetBrains Mono Variable', monospace;
|
|
142
|
-
--a24-font-mono: 'JetBrains Mono Variable', monospace;
|
|
143
|
-
--background: oklch(1 0 0);
|
|
144
|
-
--foreground: oklch(0.141 0.005 285.823);
|
|
145
|
-
--card: oklch(1 0 0);
|
|
146
|
-
--card-foreground: oklch(0.141 0.005 285.823);
|
|
147
|
-
--popover: oklch(1 0 0);
|
|
148
|
-
--popover-foreground: oklch(0.141 0.005 285.823);
|
|
149
|
-
--primary: oklch(0.21 0.006 285.885);
|
|
150
|
-
--primary-foreground: oklch(0.985 0 0);
|
|
151
|
-
--secondary: oklch(0.967 0.001 286.375);
|
|
152
|
-
--secondary-foreground: oklch(0.21 0.006 285.885);
|
|
153
|
-
--muted: oklch(0.967 0.001 286.375);
|
|
154
|
-
--muted-foreground: oklch(0.552 0.016 285.938);
|
|
155
|
-
--accent: oklch(0.967 0.001 286.375);
|
|
156
|
-
--accent-foreground: oklch(0.21 0.006 285.885);
|
|
157
|
-
--destructive: oklch(0.577 0.245 27.325);
|
|
158
|
-
--border: oklch(0.92 0.004 286.32);
|
|
159
|
-
--input: oklch(0.92 0.004 286.32);
|
|
160
|
-
--ring: oklch(0.705 0.015 286.067);
|
|
161
|
-
--radius: 0;
|
|
162
|
-
--sidebar: oklch(0.985 0 0);
|
|
163
|
-
--sidebar-foreground: oklch(0.141 0.005 285.823);
|
|
164
|
-
--sidebar-primary: oklch(0.21 0.006 285.885);
|
|
165
|
-
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
166
|
-
--sidebar-accent: oklch(0.967 0.001 286.375);
|
|
167
|
-
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
|
|
168
|
-
--sidebar-border: oklch(0.92 0.004 286.32);
|
|
169
|
-
--sidebar-ring: oklch(0.705 0.015 286.067);
|
|
170
|
-
--chart-1: oklch(0.865 0.127 207.078);
|
|
171
|
-
--chart-2: oklch(0.715 0.143 215.221);
|
|
172
|
-
--chart-3: oklch(0.609 0.126 221.723);
|
|
173
|
-
--chart-4: oklch(0.52 0.105 223.128);
|
|
174
|
-
--chart-5: oklch(0.45 0.085 224.283);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
[data-agents24-theme-root]:not([data-agents24-theme="inherit"])[data-agents24-theme="sharp"].dark {
|
|
178
|
-
--background: oklch(0.141 0.005 285.823);
|
|
179
|
-
--foreground: oklch(0.985 0 0);
|
|
180
|
-
--card: oklch(0.21 0.006 285.885);
|
|
181
|
-
--card-foreground: oklch(0.985 0 0);
|
|
182
|
-
--popover: oklch(0.21 0.006 285.885);
|
|
183
|
-
--popover-foreground: oklch(0.985 0 0);
|
|
184
|
-
--primary: oklch(0.92 0.004 286.32);
|
|
185
|
-
--primary-foreground: oklch(0.21 0.006 285.885);
|
|
186
|
-
--secondary: oklch(0.274 0.006 286.033);
|
|
187
|
-
--secondary-foreground: oklch(0.985 0 0);
|
|
188
|
-
--muted: oklch(0.274 0.006 286.033);
|
|
189
|
-
--muted-foreground: oklch(0.705 0.015 286.067);
|
|
190
|
-
--accent: oklch(0.274 0.006 286.033);
|
|
191
|
-
--accent-foreground: oklch(0.985 0 0);
|
|
192
|
-
--destructive: oklch(0.704 0.191 22.216);
|
|
193
|
-
--border: oklch(1 0 0 / 10%);
|
|
194
|
-
--input: oklch(1 0 0 / 15%);
|
|
195
|
-
--ring: oklch(0.552 0.016 285.938);
|
|
196
|
-
--sidebar: oklch(0.21 0.006 285.885);
|
|
197
|
-
--sidebar-foreground: oklch(0.985 0 0);
|
|
198
|
-
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
199
|
-
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
200
|
-
--sidebar-accent: oklch(0.274 0.006 286.033);
|
|
201
|
-
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
202
|
-
--sidebar-border: oklch(1 0 0 / 10%);
|
|
203
|
-
--sidebar-ring: oklch(0.552 0.016 285.938);
|
|
204
|
-
--chart-1: oklch(0.865 0.127 207.078);
|
|
205
|
-
--chart-2: oklch(0.715 0.143 215.221);
|
|
206
|
-
--chart-3: oklch(0.609 0.126 221.723);
|
|
207
|
-
--chart-4: oklch(0.52 0.105 223.128);
|
|
208
|
-
--chart-5: oklch(0.45 0.085 224.283);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
@layer base {
|
|
212
|
-
[data-agents24-theme-root]:not([data-agents24-theme="inherit"]), [data-agents24-theme-root]:not([data-agents24-theme="inherit"]) * {
|
|
213
|
-
@apply border-border outline-ring/50;
|
|
214
|
-
box-sizing: border-box;
|
|
215
|
-
}
|
|
216
|
-
[data-agents24-theme-root]:not([data-agents24-theme="inherit"]) {
|
|
217
|
-
@apply size-full overflow-hidden bg-background font-sans text-foreground antialiased;
|
|
218
|
-
}
|
|
219
|
-
[data-agents24-theme-root]:not([data-agents24-theme="inherit"]) button,
|
|
220
|
-
[data-agents24-theme-root]:not([data-agents24-theme="inherit"]) textarea { font: inherit; }
|
|
221
|
-
}
|
|
9
|
+
@import "./theme.css";
|
|
@@ -13,11 +13,12 @@ import { TooltipProvider } from "./components/ui/tooltip"
|
|
|
13
13
|
export type Agents24ChatScaffoldProps = Agents24ChatAppProps & {
|
|
14
14
|
appearanceController?: Agents24ChatAppearanceController
|
|
15
15
|
theme?: Agents24ChatTheme
|
|
16
|
+
themeMode?: "isolated" | "inherit"
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
export function Agents24ChatScaffold({ appearanceController, theme, ...props }: Agents24ChatScaffoldProps) {
|
|
19
|
+
export function Agents24ChatScaffold({ appearanceController, theme, themeMode, ...props }: Agents24ChatScaffoldProps) {
|
|
19
20
|
return (
|
|
20
|
-
<ThemeProvider controller={appearanceController} theme={theme}>
|
|
21
|
+
<ThemeProvider controller={appearanceController} theme={theme} mode={themeMode}>
|
|
21
22
|
<TooltipProvider>
|
|
22
23
|
<App {...props} />
|
|
23
24
|
</TooltipProvider>
|
|
@@ -37,6 +38,7 @@ export type {
|
|
|
37
38
|
ChatHitlResponse,
|
|
38
39
|
Agents24ChatScaffoldComponents,
|
|
39
40
|
Agents24ChatTimelineSlotProps,
|
|
41
|
+
Agents24ChatComposerSlotProps,
|
|
40
42
|
} from "./app"
|
|
41
43
|
export { TooltipProvider as Agents24ChatTooltipProvider }
|
|
42
44
|
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
@custom-variant dark (&:is(.dark *));
|
|
2
|
+
|
|
3
|
+
@theme inline {
|
|
4
|
+
--font-heading: var(--a24-font-heading);
|
|
5
|
+
--font-sans: var(--a24-font-sans);
|
|
6
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
7
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
8
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
9
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
10
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
11
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
12
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
13
|
+
--color-sidebar: var(--sidebar);
|
|
14
|
+
--color-ring: var(--ring);
|
|
15
|
+
--color-input: var(--input);
|
|
16
|
+
--color-border: var(--border);
|
|
17
|
+
--color-destructive: var(--destructive);
|
|
18
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
19
|
+
--color-accent: var(--accent);
|
|
20
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
21
|
+
--color-muted: var(--muted);
|
|
22
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
23
|
+
--color-secondary: var(--secondary);
|
|
24
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
25
|
+
--color-primary: var(--primary);
|
|
26
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
27
|
+
--color-popover: var(--popover);
|
|
28
|
+
--color-card-foreground: var(--card-foreground);
|
|
29
|
+
--color-card: var(--card);
|
|
30
|
+
--color-foreground: var(--foreground);
|
|
31
|
+
--color-background: var(--background);
|
|
32
|
+
--radius-sm: calc(var(--radius) * 0.6);
|
|
33
|
+
--radius-md: calc(var(--radius) * 0.8);
|
|
34
|
+
--radius-lg: var(--radius);
|
|
35
|
+
--radius-xl: calc(var(--radius) * 1.4);
|
|
36
|
+
--radius-2xl: calc(var(--radius) * 1.8);
|
|
37
|
+
--radius-3xl: calc(var(--radius) * 2.2);
|
|
38
|
+
--radius-4xl: calc(var(--radius) * 2.6);
|
|
39
|
+
--a24-composer-single-row-radius: 9999px;
|
|
40
|
+
--color-chart-5: var(--chart-5);
|
|
41
|
+
--color-chart-4: var(--chart-4);
|
|
42
|
+
--color-chart-3: var(--chart-3);
|
|
43
|
+
--color-chart-2: var(--chart-2);
|
|
44
|
+
--color-chart-1: var(--chart-1);
|
|
45
|
+
--font-serif: var(--a24-font-heading);
|
|
46
|
+
--font-mono: var(--a24-font-mono);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
[data-agents24-theme-root] {
|
|
50
|
+
color-scheme: light;
|
|
51
|
+
--a24-font-heading: 'Inter Variable', sans-serif;
|
|
52
|
+
--a24-font-sans: 'Inter Variable', sans-serif;
|
|
53
|
+
--a24-font-mono: 'JetBrains Mono Variable', monospace;
|
|
54
|
+
--radius-sm: calc(var(--radius) * 0.6);
|
|
55
|
+
--radius-md: calc(var(--radius) * 0.8);
|
|
56
|
+
--radius-lg: var(--radius);
|
|
57
|
+
--radius-xl: calc(var(--radius) * 1.4);
|
|
58
|
+
--radius-2xl: calc(var(--radius) * 1.8);
|
|
59
|
+
--radius-3xl: calc(var(--radius) * 2.2);
|
|
60
|
+
--radius-4xl: calc(var(--radius) * 2.6);
|
|
61
|
+
--background: oklch(1 0 0);
|
|
62
|
+
--foreground: oklch(0.147 0.004 49.25);
|
|
63
|
+
--card: oklch(1 0 0);
|
|
64
|
+
--card-foreground: oklch(0.147 0.004 49.25);
|
|
65
|
+
--popover: oklch(1 0 0);
|
|
66
|
+
--popover-foreground: oklch(0.147 0.004 49.25);
|
|
67
|
+
--primary: oklch(0.52 0.105 223.128);
|
|
68
|
+
--primary-foreground: oklch(0.984 0.019 200.873);
|
|
69
|
+
--secondary: oklch(0.967 0.001 286.375);
|
|
70
|
+
--secondary-foreground: oklch(0.21 0.006 285.885);
|
|
71
|
+
--muted: oklch(0.97 0.001 106.424);
|
|
72
|
+
--muted-foreground: oklch(0.553 0.013 58.071);
|
|
73
|
+
--accent: oklch(0.97 0.001 106.424);
|
|
74
|
+
--accent-foreground: oklch(0.216 0.006 56.043);
|
|
75
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
76
|
+
--border: oklch(0.923 0.003 48.717);
|
|
77
|
+
--input: oklch(0.923 0.003 48.717);
|
|
78
|
+
--ring: oklch(0.709 0.01 56.259);
|
|
79
|
+
--radius: 0.875rem;
|
|
80
|
+
--sidebar: oklch(0.985 0.001 106.423);
|
|
81
|
+
--sidebar-foreground: oklch(0.147 0.004 49.25);
|
|
82
|
+
--sidebar-primary: oklch(0.609 0.126 221.723);
|
|
83
|
+
--sidebar-primary-foreground: oklch(0.984 0.019 200.873);
|
|
84
|
+
--sidebar-accent: oklch(0.97 0.001 106.424);
|
|
85
|
+
--sidebar-accent-foreground: oklch(0.216 0.006 56.043);
|
|
86
|
+
--sidebar-border: oklch(0.923 0.003 48.717);
|
|
87
|
+
--sidebar-ring: oklch(0.709 0.01 56.259);
|
|
88
|
+
--chart-1: oklch(0.845 0.143 164.978);
|
|
89
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
90
|
+
--chart-3: oklch(0.596 0.145 163.225);
|
|
91
|
+
--chart-4: oklch(0.508 0.118 165.612);
|
|
92
|
+
--chart-5: oklch(0.432 0.095 166.913);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
[data-agents24-theme-root].dark,
|
|
96
|
+
.dark [data-agents24-theme-root][data-agents24-theme-mode="inherit"] {
|
|
97
|
+
color-scheme: dark;
|
|
98
|
+
--background: oklch(0.147 0.004 49.25);
|
|
99
|
+
--foreground: oklch(0.985 0.001 106.423);
|
|
100
|
+
--card: oklch(0.216 0.006 56.043);
|
|
101
|
+
--card-foreground: oklch(0.985 0.001 106.423);
|
|
102
|
+
--popover: oklch(0.216 0.006 56.043);
|
|
103
|
+
--popover-foreground: oklch(0.985 0.001 106.423);
|
|
104
|
+
--primary: oklch(0.45 0.085 224.283);
|
|
105
|
+
--primary-foreground: oklch(0.984 0.019 200.873);
|
|
106
|
+
--secondary: oklch(0.274 0.006 286.033);
|
|
107
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
108
|
+
--muted: oklch(0.268 0.007 34.298);
|
|
109
|
+
--muted-foreground: oklch(0.709 0.01 56.259);
|
|
110
|
+
--accent: oklch(0.268 0.007 34.298);
|
|
111
|
+
--accent-foreground: oklch(0.985 0.001 106.423);
|
|
112
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
113
|
+
--border: oklch(1 0 0 / 10%);
|
|
114
|
+
--input: oklch(1 0 0 / 15%);
|
|
115
|
+
--ring: oklch(0.553 0.013 58.071);
|
|
116
|
+
--sidebar: oklch(0.216 0.006 56.043);
|
|
117
|
+
--sidebar-foreground: oklch(0.985 0.001 106.423);
|
|
118
|
+
--sidebar-primary: oklch(0.715 0.143 215.221);
|
|
119
|
+
--sidebar-primary-foreground: oklch(0.302 0.056 229.695);
|
|
120
|
+
--sidebar-accent: oklch(0.268 0.007 34.298);
|
|
121
|
+
--sidebar-accent-foreground: oklch(0.985 0.001 106.423);
|
|
122
|
+
--sidebar-border: oklch(1 0 0 / 10%);
|
|
123
|
+
--sidebar-ring: oklch(0.553 0.013 58.071);
|
|
124
|
+
--chart-1: oklch(0.845 0.143 164.978);
|
|
125
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
126
|
+
--chart-3: oklch(0.596 0.145 163.225);
|
|
127
|
+
--chart-4: oklch(0.508 0.118 165.612);
|
|
128
|
+
--chart-5: oklch(0.432 0.095 166.913);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
[data-agents24-theme-root][data-agents24-theme="sharp"] {
|
|
132
|
+
--a24-composer-single-row-radius: 0;
|
|
133
|
+
--a24-font-heading: 'Merriweather Variable', serif;
|
|
134
|
+
--a24-font-sans: 'JetBrains Mono Variable', monospace;
|
|
135
|
+
--a24-font-mono: 'JetBrains Mono Variable', monospace;
|
|
136
|
+
--background: oklch(1 0 0);
|
|
137
|
+
--foreground: oklch(0.141 0.005 285.823);
|
|
138
|
+
--card: oklch(1 0 0);
|
|
139
|
+
--card-foreground: oklch(0.141 0.005 285.823);
|
|
140
|
+
--popover: oklch(1 0 0);
|
|
141
|
+
--popover-foreground: oklch(0.141 0.005 285.823);
|
|
142
|
+
--primary: oklch(0.21 0.006 285.885);
|
|
143
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
144
|
+
--secondary: oklch(0.967 0.001 286.375);
|
|
145
|
+
--secondary-foreground: oklch(0.21 0.006 285.885);
|
|
146
|
+
--muted: oklch(0.967 0.001 286.375);
|
|
147
|
+
--muted-foreground: oklch(0.552 0.016 285.938);
|
|
148
|
+
--accent: oklch(0.967 0.001 286.375);
|
|
149
|
+
--accent-foreground: oklch(0.21 0.006 285.885);
|
|
150
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
151
|
+
--border: oklch(0.92 0.004 286.32);
|
|
152
|
+
--input: oklch(0.92 0.004 286.32);
|
|
153
|
+
--ring: oklch(0.705 0.015 286.067);
|
|
154
|
+
--radius: 0;
|
|
155
|
+
--sidebar: oklch(0.985 0 0);
|
|
156
|
+
--sidebar-foreground: oklch(0.141 0.005 285.823);
|
|
157
|
+
--sidebar-primary: oklch(0.21 0.006 285.885);
|
|
158
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
159
|
+
--sidebar-accent: oklch(0.967 0.001 286.375);
|
|
160
|
+
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
|
|
161
|
+
--sidebar-border: oklch(0.92 0.004 286.32);
|
|
162
|
+
--sidebar-ring: oklch(0.705 0.015 286.067);
|
|
163
|
+
--chart-1: oklch(0.865 0.127 207.078);
|
|
164
|
+
--chart-2: oklch(0.715 0.143 215.221);
|
|
165
|
+
--chart-3: oklch(0.609 0.126 221.723);
|
|
166
|
+
--chart-4: oklch(0.52 0.105 223.128);
|
|
167
|
+
--chart-5: oklch(0.45 0.085 224.283);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
[data-agents24-theme-root][data-agents24-theme="sharp"].dark,
|
|
171
|
+
.dark [data-agents24-theme-root][data-agents24-theme="sharp"][data-agents24-theme-mode="inherit"] {
|
|
172
|
+
--background: oklch(0.141 0.005 285.823);
|
|
173
|
+
--foreground: oklch(0.985 0 0);
|
|
174
|
+
--card: oklch(0.21 0.006 285.885);
|
|
175
|
+
--card-foreground: oklch(0.985 0 0);
|
|
176
|
+
--popover: oklch(0.21 0.006 285.885);
|
|
177
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
178
|
+
--primary: oklch(0.92 0.004 286.32);
|
|
179
|
+
--primary-foreground: oklch(0.21 0.006 285.885);
|
|
180
|
+
--secondary: oklch(0.274 0.006 286.033);
|
|
181
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
182
|
+
--muted: oklch(0.274 0.006 286.033);
|
|
183
|
+
--muted-foreground: oklch(0.705 0.015 286.067);
|
|
184
|
+
--accent: oklch(0.274 0.006 286.033);
|
|
185
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
186
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
187
|
+
--border: oklch(1 0 0 / 10%);
|
|
188
|
+
--input: oklch(1 0 0 / 15%);
|
|
189
|
+
--ring: oklch(0.552 0.016 285.938);
|
|
190
|
+
--sidebar: oklch(0.21 0.006 285.885);
|
|
191
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
192
|
+
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
193
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
194
|
+
--sidebar-accent: oklch(0.274 0.006 286.033);
|
|
195
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
196
|
+
--sidebar-border: oklch(1 0 0 / 10%);
|
|
197
|
+
--sidebar-ring: oklch(0.552 0.016 285.938);
|
|
198
|
+
--chart-1: oklch(0.865 0.127 207.078);
|
|
199
|
+
--chart-2: oklch(0.715 0.143 215.221);
|
|
200
|
+
--chart-3: oklch(0.609 0.126 221.723);
|
|
201
|
+
--chart-4: oklch(0.52 0.105 223.128);
|
|
202
|
+
--chart-5: oklch(0.45 0.085 224.283);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
@layer base {
|
|
206
|
+
[data-agents24-theme-root], [data-agents24-theme-root] * {
|
|
207
|
+
@apply border-border outline-ring/50;
|
|
208
|
+
box-sizing: border-box;
|
|
209
|
+
}
|
|
210
|
+
[data-agents24-theme-root] {
|
|
211
|
+
@apply size-full overflow-hidden bg-background font-sans text-foreground antialiased;
|
|
212
|
+
}
|
|
213
|
+
[data-agents24-theme-root] button,
|
|
214
|
+
[data-agents24-theme-root] textarea { font: inherit; }
|
|
215
|
+
}
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"@shadcn/react": "^0.2.1",
|
|
9
9
|
"class-variance-authority": "^0.7.1",
|
|
10
10
|
"clsx": "^2.1.1",
|
|
11
|
-
"lucide-react": "0.542.0",
|
|
12
|
-
"radix-ui": "1.4.3",
|
|
11
|
+
"lucide-react": ">=0.542.0 <2.0.0",
|
|
12
|
+
"radix-ui": ">=1.4.3 <2.0.0",
|
|
13
13
|
"streamdown": "^2.5.0",
|
|
14
14
|
"tailwind-merge": "^3.6.0",
|
|
15
15
|
"tw-animate-css": "^1.4.0"
|
|
@@ -20,9 +20,10 @@
|
|
|
20
20
|
"tailwindcss": "^4.1.16"
|
|
21
21
|
},
|
|
22
22
|
"source": "packages/agents24-chat-react/scaffold",
|
|
23
|
-
"sha256": "
|
|
23
|
+
"sha256": "c601ce4b1ce003ee92b6f59710ac74d4afa2f8630d7e1f86eda993ea7e7e4de4",
|
|
24
24
|
"files": [
|
|
25
25
|
"components.json",
|
|
26
|
+
"host-styles.css",
|
|
26
27
|
"inherit.css",
|
|
27
28
|
"manifest.json",
|
|
28
29
|
"src/app.tsx",
|
|
@@ -34,6 +35,7 @@
|
|
|
34
35
|
"src/components/chat/external-link-dialog.tsx",
|
|
35
36
|
"src/components/chat/identity-control.tsx",
|
|
36
37
|
"src/components/chat/message-parts.tsx",
|
|
38
|
+
"src/components/chat/subagent-tasks.tsx",
|
|
37
39
|
"src/components/chat/theme-menu.tsx",
|
|
38
40
|
"src/components/chat/thread-sidebar.tsx",
|
|
39
41
|
"src/components/chat/timeline-loading.ts",
|
|
@@ -57,10 +59,12 @@
|
|
|
57
59
|
"src/components/ui/textarea.tsx",
|
|
58
60
|
"src/components/ui/tooltip.tsx",
|
|
59
61
|
"src/hooks/use-mobile.ts",
|
|
62
|
+
"src/host.css",
|
|
60
63
|
"src/identity.ts",
|
|
61
64
|
"src/index.css",
|
|
62
65
|
"src/index.tsx",
|
|
63
66
|
"src/lib/utils.ts",
|
|
67
|
+
"src/theme.css",
|
|
64
68
|
"styles.css"
|
|
65
69
|
]
|
|
66
70
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-agents24-app",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.31",
|
|
4
4
|
"description": "Create a production-ready Agents24 React application.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"provenance": false
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@agents24/cli": "0.3.
|
|
36
|
+
"@agents24/cli": "0.3.26",
|
|
37
37
|
"@clack/prompts": "1.7.0",
|
|
38
38
|
"shadcn": "4.13.1"
|
|
39
39
|
},
|
|
@@ -5,10 +5,31 @@ export const runtime = "nodejs";
|
|
|
5
5
|
export const dynamic = "force-dynamic";
|
|
6
6
|
export const maxDuration = 300;
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
// Next development may construct Request URLs from its wildcard bind address.
|
|
9
|
+
// Restore only that address from Host; the BFF still checks both Origin and Host.
|
|
10
|
+
function handle(request: Request): Promise<Response> {
|
|
11
|
+
const url = new URL(request.url);
|
|
12
|
+
if (["0.0.0.0", "[::]"].includes(url.hostname)) {
|
|
13
|
+
const host = request.headers.get("host");
|
|
14
|
+
try {
|
|
15
|
+
if (!host) throw new Error("Missing Host");
|
|
16
|
+
const external = new URL(`${url.protocol}//${host}`);
|
|
17
|
+
if (external.username || external.password || external.pathname !== "/" || external.search || external.hash) {
|
|
18
|
+
throw new Error("Invalid Host");
|
|
19
|
+
}
|
|
20
|
+
url.host = external.host;
|
|
21
|
+
request = new Request(url, request);
|
|
22
|
+
} catch {
|
|
23
|
+
return Promise.resolve(new Response(null, { status: 403 }));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return handleAgents24(request);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const GET = handle;
|
|
30
|
+
export const POST = handle;
|
|
31
|
+
export const PUT = handle;
|
|
32
|
+
export const PATCH = handle;
|
|
33
|
+
export const DELETE = handle;
|
|
34
|
+
export const HEAD = handle;
|
|
35
|
+
export const OPTIONS = handle;
|
package/versions.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"agents24": {
|
|
3
|
-
"@agents24/client": "^0.2.
|
|
4
|
-
"@agents24/react": "^0.2.
|
|
5
|
-
"@agents24/chat-react": "^0.5.
|
|
6
|
-
"@agents24/node": "^0.5.
|
|
3
|
+
"@agents24/client": "^0.2.15",
|
|
4
|
+
"@agents24/react": "^0.2.19",
|
|
5
|
+
"@agents24/chat-react": "^0.5.31",
|
|
6
|
+
"@agents24/node": "^0.5.23"
|
|
7
7
|
},
|
|
8
8
|
"runtime": {
|
|
9
9
|
"next": "^16.3.4",
|