create-agents24-app 0.2.29 → 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.
@@ -0,0 +1,3 @@
1
+ @import "@agents24/chat-react/styles.css";
2
+ @import "./src/host.css";
3
+ @import "./src/chat.css";
@@ -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 = React.useRef<HTMLTextAreaElement>(null)
63
- const composer = useAgentChatComposer({ inputContract, disabled, isRunning, isCancelling,
64
- focusKey, restoredDraft, textareaRef, onSubmit: ({ text, files }) => onSubmit(text, files) })
65
- const { text, files, setText, validationError, attachmentError, recorder, recordingAllowed,
66
- allowAttachments, action, submit } = composer
67
- const isExpanded = forceExpanded || text.includes("\n") || text.length > 62
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
- {allowAttachments && files.length > 0 && (
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={composer.fileAccept}
165
+ accept={attachmentMimeTypes.join(",")}
156
166
  onChange={(event) => {
157
- composer.addFiles(Array.from(event.target.files ?? []))
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) => setText(event.target.value)}
180
- onKeyDown={composer.onKeyDown}
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={disabled || !composer.canSubmit} onClick={() => void submit()}><ArrowUp /></Button>
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
- React.useEffect(() => {
18
- if (!isOpen) setCopied(false)
19
- }, [isOpen])
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
- onClose()
29
+ close()
29
30
  }
30
31
 
31
32
  return (
32
- <Dialog open={isOpen} onOpenChange={(open) => { if (!open) onClose() }}>
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" | "inherit"
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 [internalAppearance, setInternalAppearance] = React.useState<Agents24ChatAppearance>("system")
40
- const [resolvedAppearance, setResolvedAppearance] = React.useState<"light" | "dark">("light")
41
- const [mounted, setMounted] = React.useState(false)
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 ?? internalAppearance
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
- setInternalAppearance(next)
69
- }, [controller, theme])
70
+ window.dispatchEvent(new Event(appearanceEvent))
71
+ }, [controller])
70
72
 
71
73
  return (
72
- <ThemeContext.Provider value={{ appearance, inherited: theme === "inherit", portalContainer, setAppearance: change }}>
74
+ <ThemeContext.Provider value={{ appearance, portalContainer, setAppearance: change, inherited: mode === "inherit" }}>
73
75
  <div
74
76
  ref={setPortalContainer}
75
- className={theme === "inherit" ? "size-full" : `${resolvedAppearance} size-full`}
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
- @custom-variant dark (&:is(.dark *));
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": "ff79a5c99abb98c547132058504c6404d1cf9513f63ad98aaf1dfafc2dad4688",
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.29",
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.24",
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
- export const GET = handleAgents24;
9
- export const POST = handleAgents24;
10
- export const PUT = handleAgents24;
11
- export const PATCH = handleAgents24;
12
- export const DELETE = handleAgents24;
13
- export const HEAD = handleAgents24;
14
- export const OPTIONS = handleAgents24;
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.14",
4
- "@agents24/react": "^0.2.18",
5
- "@agents24/chat-react": "^0.5.30",
6
- "@agents24/node": "^0.5.21"
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",