@agents24/chat-react 0.5.6 → 0.5.7

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.
@@ -11,6 +11,7 @@ import {
11
11
  agentChatRuntimeErrorMessage
12
12
  } from "@agents24/chat-react/runtime";
13
13
  import { CircleAlert, LoaderCircle as LoaderCircle3, SquarePen as SquarePen2, X } from "lucide-react";
14
+ import * as React8 from "react";
14
15
 
15
16
  // scaffold/src/components/chat/chat-composer.tsx
16
17
  import { ArrowUp, ChevronDown, LoaderCircle, Mic, Square } from "lucide-react";
@@ -726,7 +727,7 @@ function ChatComposer({
726
727
  }
727
728
 
728
729
  // scaffold/src/components/chat/chat-message.tsx
729
- import { AgentChatDefaultActions, ChatAttachmentRows as ChatAttachmentRows2 } from "@agents24/chat-react/ui";
730
+ import { AgentChatDefaultActions, AgentChatUserMessageActionRow, ChatAttachmentRows as ChatAttachmentRows2 } from "@agents24/chat-react/ui";
730
731
 
731
732
  // scaffold/src/components/chat/message-parts.tsx
732
733
  import {
@@ -1116,7 +1117,7 @@ function MessageFooter({ className, ...props }) {
1116
1117
  {
1117
1118
  "data-slot": "message-footer",
1118
1119
  className: cn(
1119
- "flex max-w-full min-w-0 items-center px-3.5 text-xs font-medium text-muted-foreground group-has-data-[variant=ghost]/message:px-0 group-data-[align=end]/message:justify-end",
1120
+ "flex max-w-full min-w-0 items-center pl-3.5 text-xs font-medium text-muted-foreground group-has-data-[variant=ghost]/message:px-0 group-data-[align=end]/message:justify-end",
1120
1121
  className
1121
1122
  ),
1122
1123
  ...props
@@ -1138,7 +1139,7 @@ function ChatMessage({
1138
1139
  Timeline
1139
1140
  }) {
1140
1141
  const isUser = message.role === "user";
1141
- return /* @__PURE__ */ jsx14(Message, { align: isUser ? "end" : "start", children: /* @__PURE__ */ jsxs8(MessageContent, { children: [
1142
+ return /* @__PURE__ */ jsx14(Message, { align: isUser ? "end" : "start", children: /* @__PURE__ */ jsxs8(MessageContent, { className: isUser ? "gap-0" : void 0, children: [
1142
1143
  isUser && message.attachments?.length ? /* @__PURE__ */ jsx14(
1143
1144
  ChatAttachmentRows2,
1144
1145
  {
@@ -1157,6 +1158,17 @@ function ChatMessage({
1157
1158
  Timeline
1158
1159
  }
1159
1160
  ) }) }),
1161
+ isUser && /* @__PURE__ */ jsx14(AgentChatUserMessageActionRow, { children: /* @__PURE__ */ jsx14(
1162
+ AgentChatDefaultActions,
1163
+ {
1164
+ handlers: {
1165
+ onCopy: (content) => {
1166
+ void navigator.clipboard.writeText(content).catch(() => void 0);
1167
+ }
1168
+ },
1169
+ message
1170
+ }
1171
+ ) }),
1160
1172
  !isUser && message.isFinal !== false && /* @__PURE__ */ jsx14(MessageFooter, { className: "gap-1 opacity-70 transition-opacity group-hover/message:opacity-100", children: /* @__PURE__ */ jsx14(
1161
1173
  AgentChatDefaultActions,
1162
1174
  {
@@ -1559,7 +1571,7 @@ function Sidebar({
1559
1571
  className: cn(
1560
1572
  "fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear data-[side=left]:left-0 data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] data-[side=right]:right-0 data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)] md:flex",
1561
1573
  // Adjust the padding for floating and inset variants.
1562
- variant === "floating" || variant === "inset" ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
1574
+ variant === "floating" || variant === "inset" ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" : "border-border/50 group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
1563
1575
  className
1564
1576
  ),
1565
1577
  ...props,
@@ -1958,7 +1970,47 @@ function ThreadSidebar({ activeThreadId, hasMore, identity, isLoading, isLoading
1958
1970
  }
1959
1971
 
1960
1972
  // scaffold/src/components/chat/turn-outline.tsx
1961
- import { useState as useState8 } from "react";
1973
+ import { LatestThreadScroller } from "@agents24/chat-react";
1974
+ import { jsx as jsx23 } from "react/jsx-runtime";
1975
+ function turnsFromMessages(messages) {
1976
+ return messages.reduce((turns, message) => {
1977
+ if (message.role === "user") {
1978
+ turns.push({
1979
+ id: message.id,
1980
+ label: message.content.trim() || `Turn ${turns.length + 1}`,
1981
+ messageIds: [message.id],
1982
+ responsePreview: ""
1983
+ });
1984
+ } else if (turns.length) {
1985
+ const turn = turns[turns.length - 1];
1986
+ turn.messageIds?.push(message.id);
1987
+ if (!turn.responsePreview) turn.responsePreview = message.content.trim();
1988
+ }
1989
+ return turns;
1990
+ }, []);
1991
+ }
1992
+ function TurnOutline({ messages }) {
1993
+ return /* @__PURE__ */ jsx23(
1994
+ LatestThreadScroller.Outline,
1995
+ {
1996
+ items: turnsFromMessages(messages),
1997
+ minSideGutterForRail: 0,
1998
+ side: "left",
1999
+ verticalOffset: "3.5rem"
2000
+ }
2001
+ );
2002
+ }
2003
+
2004
+ // scaffold/src/components/chat/timeline-loading.ts
2005
+ function isScaffoldTimelineLoading({
2006
+ activeRunId,
2007
+ message,
2008
+ runState,
2009
+ streamingMessageId
2010
+ }) {
2011
+ if (runState !== "streaming" && runState !== "reconnecting") return false;
2012
+ return message.id === streamingMessageId || Boolean(message.runId && message.runId === activeRunId);
2013
+ }
1962
2014
 
1963
2015
  // scaffold/src/components/ui/message-scroller.tsx
1964
2016
  import {
@@ -1968,15 +2020,15 @@ import {
1968
2020
  useMessageScrollerVisibility
1969
2021
  } from "@shadcn/react/message-scroller";
1970
2022
  import { ArrowDownIcon } from "lucide-react";
1971
- import { Fragment as Fragment3, jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
2023
+ import { Fragment as Fragment3, jsx as jsx24, jsxs as jsxs14 } from "react/jsx-runtime";
1972
2024
  function MessageScrollerProvider(props) {
1973
- return /* @__PURE__ */ jsx23(MessageScrollerPrimitive.Provider, { ...props });
2025
+ return /* @__PURE__ */ jsx24(MessageScrollerPrimitive.Provider, { ...props });
1974
2026
  }
1975
2027
  function MessageScroller({
1976
2028
  className,
1977
2029
  ...props
1978
2030
  }) {
1979
- return /* @__PURE__ */ jsx23(
2031
+ return /* @__PURE__ */ jsx24(
1980
2032
  MessageScrollerPrimitive.Root,
1981
2033
  {
1982
2034
  "data-slot": "message-scroller",
@@ -1992,7 +2044,7 @@ function MessageScrollerViewport({
1992
2044
  className,
1993
2045
  ...props
1994
2046
  }) {
1995
- return /* @__PURE__ */ jsx23(
2047
+ return /* @__PURE__ */ jsx24(
1996
2048
  MessageScrollerPrimitive.Viewport,
1997
2049
  {
1998
2050
  "data-slot": "message-scroller-viewport",
@@ -2008,11 +2060,11 @@ function MessageScrollerContent({
2008
2060
  className,
2009
2061
  ...props
2010
2062
  }) {
2011
- return /* @__PURE__ */ jsx23(
2063
+ return /* @__PURE__ */ jsx24(
2012
2064
  MessageScrollerPrimitive.Content,
2013
2065
  {
2014
2066
  "data-slot": "message-scroller-content",
2015
- className: cn("flex h-max min-h-full flex-col gap-8", className),
2067
+ className: cn("flex h-max min-h-full flex-col gap-3", className),
2016
2068
  ...props
2017
2069
  }
2018
2070
  );
@@ -2022,7 +2074,7 @@ function MessageScrollerItem({
2022
2074
  scrollAnchor = false,
2023
2075
  ...props
2024
2076
  }) {
2025
- return /* @__PURE__ */ jsx23(
2077
+ return /* @__PURE__ */ jsx24(
2026
2078
  MessageScrollerPrimitive.Item,
2027
2079
  {
2028
2080
  "data-slot": "message-scroller-item",
@@ -2044,7 +2096,7 @@ function MessageScrollerButton({
2044
2096
  size = "icon-sm",
2045
2097
  ...props
2046
2098
  }) {
2047
- return /* @__PURE__ */ jsx23(
2099
+ return /* @__PURE__ */ jsx24(
2048
2100
  MessageScrollerPrimitive.Button,
2049
2101
  {
2050
2102
  "data-slot": "message-scroller-button",
@@ -2056,88 +2108,21 @@ function MessageScrollerButton({
2056
2108
  "absolute inset-s-1/2 -translate-x-1/2 border-border bg-background text-foreground transition-[translate,scale,opacity] duration-200 hover:bg-muted hover:text-foreground data-[active=false]:pointer-events-none data-[active=false]:scale-95 data-[active=false]:opacity-0 data-[active=false]:duration-400 data-[active=false]:ease-[cubic-bezier(0.7,0,0.84,0)] data-[active=true]:translate-y-0 data-[active=true]:scale-100 data-[active=true]:opacity-100 data-[active=true]:ease-[cubic-bezier(0.23,1,0.32,1)] data-[direction=end]:bottom-4 data-[direction=end]:data-[active=false]:translate-y-full data-[direction=start]:top-4 data-[direction=start]:data-[active=false]:-translate-y-full rtl:translate-x-1/2 data-[direction=start]:[&_svg]:rotate-180",
2057
2109
  className
2058
2110
  ),
2059
- render: render ?? /* @__PURE__ */ jsx23(Button, { variant, size }),
2111
+ render: render ?? /* @__PURE__ */ jsx24(Button, { variant, size }),
2060
2112
  ...props,
2061
2113
  children: children ?? /* @__PURE__ */ jsxs14(Fragment3, { children: [
2062
- /* @__PURE__ */ jsx23(
2114
+ /* @__PURE__ */ jsx24(
2063
2115
  ArrowDownIcon,
2064
2116
  {}
2065
2117
  ),
2066
- /* @__PURE__ */ jsx23("span", { className: "sr-only", children: direction === "end" ? "Scroll to end" : "Scroll to start" })
2118
+ /* @__PURE__ */ jsx24("span", { className: "sr-only", children: direction === "end" ? "Scroll to end" : "Scroll to start" })
2067
2119
  ] })
2068
2120
  }
2069
2121
  );
2070
2122
  }
2071
2123
 
2072
- // scaffold/src/components/chat/turn-outline.tsx
2073
- import { jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
2074
- function turnsFromMessages(messages) {
2075
- return messages.reduce((turns, message) => {
2076
- if (message.role === "user") {
2077
- turns.push({ id: message.id, label: message.content.trim() || `Turn ${turns.length + 1}`, messageIds: [message.id], response: "" });
2078
- } else if (turns.length) {
2079
- const turn = turns[turns.length - 1];
2080
- turn.messageIds.push(message.id);
2081
- if (!turn.response) turn.response = message.content.trim();
2082
- }
2083
- return turns;
2084
- }, []);
2085
- }
2086
- function TurnOutline({ messages }) {
2087
- const turns = turnsFromMessages(messages);
2088
- const { scrollToMessage } = useMessageScroller();
2089
- const { currentAnchorId, visibleMessageIds } = useMessageScrollerVisibility();
2090
- const visible = new Set(visibleMessageIds);
2091
- const [hoveredIndex, setHoveredIndex] = useState8(null);
2092
- const focusIndex = hoveredIndex ?? -1;
2093
- if (turns.length < 2) return null;
2094
- return /* @__PURE__ */ jsx24("nav", { "aria-label": "Transcript outline", className: "pointer-events-none absolute end-2 top-1/2 z-20 hidden max-h-72 w-14 -translate-y-1/2 md:block xl:end-5", children: /* @__PURE__ */ jsx24("div", { className: "pointer-events-auto flex flex-col items-end gap-0.5 py-2", children: turns.map((turn, index) => {
2095
- const active = turn.messageIds.some((id) => visible.has(id) || currentAnchorId === id);
2096
- const distance = focusIndex >= 0 ? Math.abs(index - focusIndex) : Number.POSITIVE_INFINITY;
2097
- const barWidth = focusIndex < 0 ? 12 : distance === 0 ? 50 : distance === 1 ? 36 : distance === 2 ? 23 : 10;
2098
- const barOpacity = focusIndex < 0 ? active ? 1 : 0.4 : distance === 0 ? 1 : distance === 1 ? 0.78 : distance === 2 ? 0.58 : 0.36;
2099
- return /* @__PURE__ */ jsxs15(Tooltip, { children: [
2100
- /* @__PURE__ */ jsx24(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx24(
2101
- "button",
2102
- {
2103
- type: "button",
2104
- "aria-label": `Jump to ${turn.label}`,
2105
- className: "group relative flex h-2 w-14 items-center justify-end outline-none after:pointer-events-auto after:absolute after:-inset-y-0.5 after:inset-x-0 after:content-['']",
2106
- onBlur: () => setHoveredIndex(null),
2107
- onFocus: () => setHoveredIndex(index),
2108
- onMouseEnter: () => setHoveredIndex(index),
2109
- onMouseLeave: () => setHoveredIndex(null),
2110
- onClick: () => scrollToMessage(turn.id, { align: "start", behavior: "smooth" }),
2111
- children: /* @__PURE__ */ jsx24(
2112
- "span",
2113
- {
2114
- className: cn("h-0.5 rounded-full bg-muted-foreground/40 transition-[width,background-color,opacity] duration-150 ease-out group-hover:bg-foreground group-focus-visible:bg-foreground", active && "bg-foreground"),
2115
- style: { opacity: barOpacity, width: barWidth }
2116
- }
2117
- )
2118
- }
2119
- ) }),
2120
- /* @__PURE__ */ jsxs15(TooltipContent, { side: "left", className: "w-64 flex-col items-stretch gap-0 overflow-hidden p-3", children: [
2121
- /* @__PURE__ */ jsx24("p", { className: "line-clamp-2 max-w-full wrap-break-word text-sm font-medium", children: turn.label }),
2122
- turn.response && /* @__PURE__ */ jsx24("p", { className: "mt-1 line-clamp-2 max-w-full wrap-break-word text-xs opacity-70", children: turn.response })
2123
- ] })
2124
- ] }, turn.id);
2125
- }) }) });
2126
- }
2127
-
2128
- // scaffold/src/components/chat/timeline-loading.ts
2129
- function isScaffoldTimelineLoading({
2130
- activeRunId,
2131
- message,
2132
- runState,
2133
- streamingMessageId
2134
- }) {
2135
- if (runState !== "streaming" && runState !== "reconnecting") return false;
2136
- return message.id === streamingMessageId || Boolean(message.runId && message.runId === activeRunId);
2137
- }
2138
-
2139
2124
  // scaffold/src/components/chat/chat-shell.tsx
2140
- import { Fragment as Fragment4, jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
2125
+ import { Fragment as Fragment4, jsx as jsx25, jsxs as jsxs15 } from "react/jsx-runtime";
2141
2126
  var LOADING_INPUT_CONTRACT = {
2142
2127
  version: "agents24.runtime-inputs.v2",
2143
2128
  modalities: {
@@ -2156,7 +2141,7 @@ function CollapsedHeaderActions({ onNew }) {
2156
2141
  const { isMobile, openMobile, state } = useSidebar();
2157
2142
  const sidebarVisible = isMobile ? openMobile : state === "expanded";
2158
2143
  if (sidebarVisible) return null;
2159
- return /* @__PURE__ */ jsxs16(Fragment4, { children: [
2144
+ return /* @__PURE__ */ jsxs15(Fragment4, { children: [
2160
2145
  /* @__PURE__ */ jsx25("div", { className: "h-8 w-17 shrink-0", "aria-hidden": "true" }),
2161
2146
  /* @__PURE__ */ jsx25("div", { className: "pointer-events-none fixed start-12 top-0 z-[60] flex h-12 items-center", children: /* @__PURE__ */ jsx25(Button, { className: "pointer-events-auto", variant: "ghost", size: "icon-sm", "aria-label": "New chat", onClick: onNew, children: /* @__PURE__ */ jsx25(SquarePen2, {}) }) })
2162
2147
  ] });
@@ -2164,7 +2149,7 @@ function CollapsedHeaderActions({ onNew }) {
2164
2149
  function OperationErrorNotice({ runtime }) {
2165
2150
  const error = runtime.operationError || runtime.backgroundError;
2166
2151
  if (!error) return null;
2167
- return /* @__PURE__ */ jsx25("div", { className: "pointer-events-none absolute inset-x-0 top-3 z-40 flex justify-center px-4", children: /* @__PURE__ */ jsxs16("div", { className: "pointer-events-auto flex max-w-lg items-start gap-2 rounded-lg border border-destructive/20 bg-background px-3 py-2 text-sm shadow-md", role: "alert", children: [
2152
+ return /* @__PURE__ */ jsx25("div", { className: "pointer-events-none absolute inset-x-0 top-3 z-40 flex justify-center px-4", children: /* @__PURE__ */ jsxs15("div", { className: "pointer-events-auto flex max-w-lg items-start gap-2 rounded-lg border border-destructive/20 bg-background px-3 py-2 text-sm shadow-md", role: "alert", children: [
2168
2153
  /* @__PURE__ */ jsx25(CircleAlert, { className: "mt-0.5 size-4 shrink-0 text-destructive" }),
2169
2154
  /* @__PURE__ */ jsx25("span", { className: "min-w-0 flex-1 wrap-break-word", children: agentChatRuntimeErrorMessage(error) || "The chat could not complete that action." }),
2170
2155
  /* @__PURE__ */ jsx25(
@@ -2192,7 +2177,20 @@ function ChatShell({
2192
2177
  const activeTitle = controller.threads.find((thread) => thread.id === controller.activeThreadId)?.title;
2193
2178
  const hasBlockingError = Boolean(runtime.fatalError && controller.messages.length === 0);
2194
2179
  const isEmptyState = controller.activeThreadId === null && controller.messages.length === 0 && !hasBlockingError;
2195
- return /* @__PURE__ */ jsxs16(
2180
+ const composerOverlayRef = React8.useRef(null);
2181
+ const [composerOverlayHeight, setComposerOverlayHeight] = React8.useState(192);
2182
+ React8.useEffect(() => {
2183
+ const overlay = composerOverlayRef.current;
2184
+ if (!overlay || isEmptyState) return;
2185
+ const updateHeight = () => {
2186
+ setComposerOverlayHeight(Math.ceil(overlay.getBoundingClientRect().height));
2187
+ };
2188
+ updateHeight();
2189
+ const resizeObserver = new ResizeObserver(updateHeight);
2190
+ resizeObserver.observe(overlay);
2191
+ return () => resizeObserver.disconnect();
2192
+ }, [isEmptyState]);
2193
+ return /* @__PURE__ */ jsxs15(
2196
2194
  SidebarProvider,
2197
2195
  {
2198
2196
  className: "h-svh min-h-0 overflow-hidden",
@@ -2215,11 +2213,11 @@ function ChatShell({
2215
2213
  onOpen: runtime.openThread
2216
2214
  }
2217
2215
  ),
2218
- /* @__PURE__ */ jsxs16(SidebarInset, { className: "h-svh min-h-0 overflow-hidden bg-background", children: [
2219
- /* @__PURE__ */ jsxs16(
2216
+ /* @__PURE__ */ jsxs15(SidebarInset, { className: "h-svh min-h-0 overflow-hidden bg-background", children: [
2217
+ /* @__PURE__ */ jsxs15(
2220
2218
  "header",
2221
2219
  {
2222
- className: "flex h-12 shrink-0 items-center gap-2 border-b border-border/30 px-3 sm:px-4",
2220
+ className: "flex h-12 shrink-0 items-center gap-2 border-b border-border/50 px-3 sm:px-4",
2223
2221
  "data-agents24-chat-header": "",
2224
2222
  children: [
2225
2223
  /* @__PURE__ */ jsx25(CollapsedHeaderActions, { onNew: runtime.newThread }),
@@ -2228,7 +2226,7 @@ function ChatShell({
2228
2226
  ]
2229
2227
  }
2230
2228
  ),
2231
- /* @__PURE__ */ jsxs16(
2229
+ /* @__PURE__ */ jsxs15(
2232
2230
  "section",
2233
2231
  {
2234
2232
  "aria-label": "Conversation",
@@ -2236,7 +2234,7 @@ function ChatShell({
2236
2234
  "data-agents24-chat-transcript": "",
2237
2235
  children: [
2238
2236
  /* @__PURE__ */ jsx25(OperationErrorNotice, { runtime }),
2239
- isEmptyState ? /* @__PURE__ */ jsxs16("div", { className: "absolute inset-0 flex min-h-0 flex-col justify-center pb-20", children: [
2237
+ isEmptyState ? /* @__PURE__ */ jsxs15("div", { className: "absolute inset-0 flex min-h-0 flex-col justify-center pb-20", children: [
2240
2238
  /* @__PURE__ */ jsx25("div", { className: "mx-auto mb-8 w-full max-w-3xl px-4 text-center sm:px-6", children: /* @__PURE__ */ jsx25("h2", { className: "text-3xl font-semibold tracking-tight sm:text-4xl", children: "Ready when you are." }) }),
2241
2239
  /* @__PURE__ */ jsx25(
2242
2240
  ChatComposer,
@@ -2270,29 +2268,30 @@ function ChatShell({
2270
2268
  })
2271
2269
  }
2272
2270
  )
2273
- ] }) : /* @__PURE__ */ jsxs16(Fragment4, { children: [
2274
- /* @__PURE__ */ jsx25(
2275
- MessageScrollerProvider,
2276
- {
2277
- autoScroll: true,
2278
- defaultScrollPosition: "last-anchor",
2279
- scrollPreviousItemPeek: 64,
2280
- children: /* @__PURE__ */ jsxs16(MessageScroller, { className: "relative min-h-0 flex-1", children: [
2281
- /* @__PURE__ */ jsx25(MessageScrollerViewport, { preserveScrollOnPrepend: true, children: /* @__PURE__ */ jsxs16(
2271
+ ] }) : /* @__PURE__ */ jsx25(Fragment4, { children: /* @__PURE__ */ jsxs15(
2272
+ MessageScrollerProvider,
2273
+ {
2274
+ autoScroll: true,
2275
+ defaultScrollPosition: "last-anchor",
2276
+ scrollPreviousItemPeek: 64,
2277
+ children: [
2278
+ /* @__PURE__ */ jsxs15(MessageScroller, { className: "relative min-h-0 flex-1", children: [
2279
+ /* @__PURE__ */ jsx25(MessageScrollerViewport, { preserveScrollOnPrepend: true, children: /* @__PURE__ */ jsxs15(
2282
2280
  MessageScrollerContent,
2283
2281
  {
2284
- className: "mx-auto w-full max-w-3xl gap-7 px-4 pt-8 pb-16 sm:px-6 sm:pt-12",
2282
+ className: "mx-auto w-full max-w-3xl gap-3 px-4 pt-8 pb-0 sm:px-6 sm:pt-12",
2283
+ style: { paddingBottom: composerOverlayHeight + 72 },
2285
2284
  children: [
2286
- controller.hasOlderTurns && /* @__PURE__ */ jsxs16(Button, { className: "self-center", variant: "ghost", size: "sm", disabled: controller.isLoadingOlder, onClick: () => {
2285
+ controller.hasOlderTurns && /* @__PURE__ */ jsxs15(Button, { className: "self-center", variant: "ghost", size: "sm", disabled: controller.isLoadingOlder, onClick: () => {
2287
2286
  void controller.loadOlder().catch(() => void 0);
2288
2287
  }, children: [
2289
2288
  controller.isLoadingOlder && /* @__PURE__ */ jsx25(LoaderCircle3, { className: "animate-spin" }),
2290
2289
  " Load earlier messages"
2291
2290
  ] }),
2292
2291
  runtime.isBootstrapping && /* @__PURE__ */ jsx25("div", { "aria-label": "Loading conversation", className: "flex flex-1 items-center justify-center py-24 text-muted-foreground", role: "status", children: /* @__PURE__ */ jsx25(LoaderCircle3, { className: "size-4 animate-spin" }) }),
2293
- hasBlockingError && /* @__PURE__ */ jsxs16("div", { className: "flex flex-1 flex-col items-center justify-center gap-3 py-24 text-center", children: [
2292
+ hasBlockingError && /* @__PURE__ */ jsxs15("div", { className: "flex flex-1 flex-col items-center justify-center gap-3 py-24 text-center", children: [
2294
2293
  /* @__PURE__ */ jsx25(CircleAlert, { className: "size-5 text-destructive" }),
2295
- /* @__PURE__ */ jsxs16("div", { children: [
2294
+ /* @__PURE__ */ jsxs15("div", { children: [
2296
2295
  /* @__PURE__ */ jsx25("h2", { className: "font-medium", children: "Couldn\u2019t connect" }),
2297
2296
  /* @__PURE__ */ jsx25("p", { className: "mt-1 max-w-md text-sm text-muted-foreground", children: runtime.errorMessage })
2298
2297
  ] }),
@@ -2329,44 +2328,56 @@ function ChatShell({
2329
2328
  ]
2330
2329
  }
2331
2330
  ) }),
2332
- /* @__PURE__ */ jsx25(TurnOutline, { messages: controller.messages }),
2333
- /* @__PURE__ */ jsx25(MessageScrollerButton, {})
2334
- ] })
2335
- }
2336
- ),
2337
- /* @__PURE__ */ jsx25("div", { className: "relative z-30 shrink-0 bg-background pt-3", children: /* @__PURE__ */ jsx25(
2338
- ChatComposer,
2339
- {
2340
- inputContract: runtime.inputContract || LOADING_INPUT_CONTRACT,
2341
- contextWindow: runtime.contextWindow,
2342
- disabled: runtime.isBootstrapping || runtime.isChangingAgent || hasBlockingError || isUploading,
2343
- focusKey: controller.activeThreadId,
2344
- isCancelling,
2345
- isRunning,
2346
- agentId: runtime.agentId,
2347
- agents: runtime.agents,
2348
- onAgentChange: (agentId) => {
2349
- void runtime.changeAgent(agentId);
2350
- },
2351
- onStop: () => {
2352
- void controller.cancelRun().catch(() => void 0);
2353
- },
2354
- modelId: runtime.modelId,
2355
- models: runtime.models,
2356
- onModelChange: runtime.changeModel,
2357
- onSubmit: (text, files) => runtime.submit({
2358
- text,
2359
- files: files.map((file) => ({
2360
- data: file.source,
2361
- mediaType: file.mediaType,
2362
- name: file.filename
2363
- })),
2364
- attachments: files
2365
- }),
2366
- utilityRow: "below"
2367
- }
2368
- ) })
2369
- ] })
2331
+ /* @__PURE__ */ jsx25(TurnOutline, { messages: controller.messages })
2332
+ ] }),
2333
+ /* @__PURE__ */ jsxs15(
2334
+ "div",
2335
+ {
2336
+ ref: composerOverlayRef,
2337
+ className: "pointer-events-none absolute inset-x-0 bottom-0 z-30 overflow-visible pt-10",
2338
+ style: {
2339
+ background: "linear-gradient(to bottom, transparent 0%, color-mix(in oklch, var(--background) 85%, transparent) 45%, var(--background) 65%, var(--background) 100%)"
2340
+ },
2341
+ children: [
2342
+ /* @__PURE__ */ jsx25("div", { className: "pointer-events-auto absolute inset-x-0 top-0 flex -translate-y-full justify-center pb-3", children: /* @__PURE__ */ jsx25(MessageScrollerButton, { className: "!relative !inset-auto !translate-x-0 rtl:!translate-x-0" }) }),
2343
+ /* @__PURE__ */ jsx25("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx25(
2344
+ ChatComposer,
2345
+ {
2346
+ inputContract: runtime.inputContract || LOADING_INPUT_CONTRACT,
2347
+ contextWindow: runtime.contextWindow,
2348
+ disabled: runtime.isBootstrapping || runtime.isChangingAgent || hasBlockingError || isUploading,
2349
+ focusKey: controller.activeThreadId,
2350
+ isCancelling,
2351
+ isRunning,
2352
+ agentId: runtime.agentId,
2353
+ agents: runtime.agents,
2354
+ onAgentChange: (agentId) => {
2355
+ void runtime.changeAgent(agentId);
2356
+ },
2357
+ onStop: () => {
2358
+ void controller.cancelRun().catch(() => void 0);
2359
+ },
2360
+ modelId: runtime.modelId,
2361
+ models: runtime.models,
2362
+ onModelChange: runtime.changeModel,
2363
+ onSubmit: (text, files) => runtime.submit({
2364
+ text,
2365
+ files: files.map((file) => ({
2366
+ data: file.source,
2367
+ mediaType: file.mediaType,
2368
+ name: file.filename
2369
+ })),
2370
+ attachments: files
2371
+ }),
2372
+ utilityRow: "below"
2373
+ }
2374
+ ) })
2375
+ ]
2376
+ }
2377
+ )
2378
+ ]
2379
+ }
2380
+ ) })
2370
2381
  ]
2371
2382
  }
2372
2383
  )