@agents24/chat-react 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -12
- package/dist/chunk-E3TJ3I6G.js +358 -0
- package/dist/chunk-E3TJ3I6G.js.map +1 -0
- package/dist/chunk-EWZO4QJI.js +140 -0
- package/dist/chunk-EWZO4QJI.js.map +1 -0
- package/dist/chunk-UU7OXHL3.js +11 -0
- package/dist/chunk-UU7OXHL3.js.map +1 -0
- package/dist/context-window.d.ts +38 -0
- package/dist/controller.d.ts +22 -0
- package/dist/index.cjs +653 -336
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +10 -466
- package/dist/index.js +290 -455
- package/dist/index.js.map +1 -1
- package/dist/latest-thread-scroller.d.ts +54 -0
- package/dist/model.d.ts +29 -0
- package/dist/renderers.d.ts +10 -0
- package/dist/sse.d.ts +3 -0
- package/dist/streaming-text.d.ts +24 -0
- package/dist/templates/agent-chat-shell.d.ts +25 -0
- package/dist/templates/index.cjs +523 -0
- package/dist/templates/index.cjs.map +1 -0
- package/dist/templates/index.d.ts +1 -0
- package/dist/templates/index.js +140 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/transport.d.ts +29 -0
- package/dist/types.d.ts +312 -0
- package/dist/ui/adapters.d.ts +36 -0
- package/dist/ui/attachment.d.ts +23 -0
- package/dist/ui/bubble.d.ts +17 -0
- package/dist/ui/index.cjs +943 -0
- package/dist/ui/index.cjs.map +1 -0
- package/dist/ui/index.d.ts +7 -0
- package/dist/ui/index.js +734 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/marker.d.ts +11 -0
- package/dist/ui/message-response.d.ts +7 -0
- package/dist/ui/message.d.ts +10 -0
- package/dist/ui/utils.d.ts +2 -0
- package/dist/viewport.d.ts +2 -0
- package/package.json +22 -4
- package/dist/index.d.cts +0 -466
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ChatPart, ChatContextWindow, ChatMessage, ChatReasoningStep, ChatThreadDetail, ChatThreadSummary, ChatToolState } from "./types";
|
|
2
|
+
export declare const DEFAULT_THREAD_PAGE_SIZE = 5;
|
|
3
|
+
export declare const isRunningThreadStatus: (status?: string | null) => boolean;
|
|
4
|
+
export declare const createChatId: () => string;
|
|
5
|
+
export declare const titleFromMessage: (text: string, files?: Array<{
|
|
6
|
+
filename?: string;
|
|
7
|
+
}>) => string;
|
|
8
|
+
export declare const threadActivityDate: (thread: ChatThreadSummary) => string;
|
|
9
|
+
export declare const assistantTextFromResponseBlocks: (blocks?: Array<Record<string, unknown>>) => string;
|
|
10
|
+
export declare const assistantTextFromParts: (parts?: ChatPart[]) => string;
|
|
11
|
+
export declare const textFromFinalOutput: (value: unknown) => string;
|
|
12
|
+
export declare const toolStateFromStatus: (status?: unknown) => ChatToolState;
|
|
13
|
+
export declare const partsFromResponseBlocks: (blocks?: Array<Record<string, unknown>>, fallbackText?: string) => ChatPart[];
|
|
14
|
+
export declare const mergeReasoningSteps: (steps?: ChatReasoningStep[], options?: {
|
|
15
|
+
finalize?: boolean;
|
|
16
|
+
}) => ChatReasoningStep[];
|
|
17
|
+
export declare const reasoningStepsFromParts: (parts?: ChatPart[]) => ChatReasoningStep[];
|
|
18
|
+
export declare const threadDetailToMessages: (thread: ChatThreadDetail) => ChatMessage[];
|
|
19
|
+
export declare const threadPaging: (thread: ChatThreadDetail) => {
|
|
20
|
+
hasOlderTurns: boolean;
|
|
21
|
+
nextBeforeTurnIndex: number | null;
|
|
22
|
+
};
|
|
23
|
+
export declare const latestContextWindowFromThread: (thread: ChatThreadDetail) => ChatContextWindow | null;
|
|
24
|
+
export declare const activeRunIdFromThread: (thread?: Partial<ChatThreadSummary> | null) => string | null;
|
|
25
|
+
export declare const hasUnfinishedAssistantMessage: (messages?: ChatMessage[]) => boolean;
|
|
26
|
+
export declare const hasStaleUnfinishedAssistantCache: (thread?: (Partial<ChatThreadSummary> & {
|
|
27
|
+
messages?: ChatMessage[];
|
|
28
|
+
}) | null) => boolean;
|
|
29
|
+
export declare const activeRunIdFromThreadDetail: (thread?: ChatThreadDetail | null) => string | null;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Agents24ChatRenderOptions, ChatMessage, ChatPart, ChatToolPart } from "./types";
|
|
2
|
+
export declare const DefaultToolPart: ({ part }: {
|
|
3
|
+
part: ChatToolPart;
|
|
4
|
+
}) => import("react").JSX.Element;
|
|
5
|
+
export declare const DefaultChatPart: ({ part, message, renderOptions, }: {
|
|
6
|
+
part: ChatPart;
|
|
7
|
+
message: ChatMessage;
|
|
8
|
+
renderOptions?: Agents24ChatRenderOptions;
|
|
9
|
+
}) => import("react").JSX.Element;
|
|
10
|
+
export declare const renderChatPart: (part: ChatPart, message: ChatMessage, renderOptions?: Agents24ChatRenderOptions) => import("react").JSX.Element;
|
package/dist/sse.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ChatRuntimeEvent, ChatStreamResult } from "./types";
|
|
2
|
+
export declare const parseSseBlock: (block: string) => ChatRuntimeEvent | null;
|
|
3
|
+
export declare const consumeSseResponse: (response: Response, onEvent: (event: ChatRuntimeEvent) => void | Promise<void>) => Promise<ChatStreamResult>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ChatMessage, ChatPart } from "./types";
|
|
2
|
+
export type StreamingTextMode = "streaming" | "static";
|
|
3
|
+
export type StreamingTextCache = {
|
|
4
|
+
get: (id: string) => string | undefined;
|
|
5
|
+
set: (id: string, text: string) => void;
|
|
6
|
+
};
|
|
7
|
+
export type UseStreamingTextOptions = {
|
|
8
|
+
id: string;
|
|
9
|
+
isStreaming: boolean;
|
|
10
|
+
text: string;
|
|
11
|
+
cache?: StreamingTextCache | false;
|
|
12
|
+
charsPerSecond?: number;
|
|
13
|
+
catchupThreshold?: number;
|
|
14
|
+
maxCatchupChars?: number;
|
|
15
|
+
};
|
|
16
|
+
export type UseStreamingTextResult = {
|
|
17
|
+
displayedText: string;
|
|
18
|
+
isAnimating: boolean;
|
|
19
|
+
mode: StreamingTextMode;
|
|
20
|
+
parseIncompleteMarkdown: boolean;
|
|
21
|
+
};
|
|
22
|
+
export declare const getActiveStreamingTextPartId: (message: ChatMessage, streamingMessageId: string | null) => string | null;
|
|
23
|
+
export declare const isActiveStreamingTextPart: (message: ChatMessage, part: ChatPart, streamingMessageId: string | null) => boolean;
|
|
24
|
+
export declare function useStreamingText({ id, isStreaming, text, cache, charsPerSecond, catchupThreshold, maxCatchupChars, }: UseStreamingTextOptions): UseStreamingTextResult;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type LatestThreadScrollerOutlineItem } from "../latest-thread-scroller";
|
|
3
|
+
import type { ChatMessage } from "../types";
|
|
4
|
+
export type AgentChatShellMessage = Pick<ChatMessage, "id" | "role" | "content"> & {
|
|
5
|
+
[key: string]: unknown;
|
|
6
|
+
};
|
|
7
|
+
export type AgentChatShellProps<TMessage extends AgentChatShellMessage> = {
|
|
8
|
+
className?: string;
|
|
9
|
+
contentClassName?: string;
|
|
10
|
+
composer?: React.ReactNode;
|
|
11
|
+
composerOverlayClassName?: string;
|
|
12
|
+
composerOverlayHeight?: number;
|
|
13
|
+
defaultComposerOverlayHeight?: number;
|
|
14
|
+
dir?: "ltr" | "rtl";
|
|
15
|
+
hideComposer?: boolean;
|
|
16
|
+
isLoadingOlder?: boolean;
|
|
17
|
+
messages: TMessage[];
|
|
18
|
+
onLoadOlder?: (() => Promise<unknown> | unknown) | null;
|
|
19
|
+
outlineItems?: LatestThreadScrollerOutlineItem[];
|
|
20
|
+
outlineSide?: "left" | "right";
|
|
21
|
+
renderMessage: (message: TMessage, index: number) => React.ReactNode;
|
|
22
|
+
renderOlderLoader?: () => React.ReactNode;
|
|
23
|
+
};
|
|
24
|
+
export declare function buildAgentChatOutlineItems<TMessage extends AgentChatShellMessage>(messages: TMessage[], getLabel?: (message: TMessage, index: number) => string, getResponsePreview?: (message: TMessage) => string): LatestThreadScrollerOutlineItem[];
|
|
25
|
+
export declare function AgentChatShell<TMessage extends AgentChatShellMessage>({ className, composer, composerOverlayClassName, composerOverlayHeight, contentClassName, defaultComposerOverlayHeight, dir, hideComposer, isLoadingOlder, messages, onLoadOlder, outlineItems, outlineSide, renderMessage, renderOlderLoader, }: AgentChatShellProps<TMessage>): React.JSX.Element;
|
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/templates/index.ts
|
|
31
|
+
var templates_exports = {};
|
|
32
|
+
__export(templates_exports, {
|
|
33
|
+
AgentChatShell: () => AgentChatShell,
|
|
34
|
+
buildAgentChatOutlineItems: () => buildAgentChatOutlineItems
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(templates_exports);
|
|
37
|
+
|
|
38
|
+
// src/templates/agent-chat-shell.tsx
|
|
39
|
+
var React2 = __toESM(require("react"), 1);
|
|
40
|
+
|
|
41
|
+
// src/latest-thread-scroller.tsx
|
|
42
|
+
var React = __toESM(require("react"), 1);
|
|
43
|
+
var import_message_scroller = require("@shadcn/react/message-scroller");
|
|
44
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
45
|
+
var cx = (...values) => values.filter(Boolean).join(" ");
|
|
46
|
+
var LatestThreadScrollerLayoutContext = React.createContext(null);
|
|
47
|
+
var useLatestThreadScrollerLayout = () => React.useContext(LatestThreadScrollerLayoutContext);
|
|
48
|
+
function LatestThreadScrollerProvider({
|
|
49
|
+
autoScroll = true,
|
|
50
|
+
defaultScrollPosition = "last-anchor",
|
|
51
|
+
scrollPreviousItemPeek = 64,
|
|
52
|
+
...props
|
|
53
|
+
}) {
|
|
54
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
55
|
+
import_message_scroller.MessageScroller.Provider,
|
|
56
|
+
{
|
|
57
|
+
autoScroll,
|
|
58
|
+
defaultScrollPosition,
|
|
59
|
+
scrollPreviousItemPeek,
|
|
60
|
+
...props
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
function LatestThreadScrollerRoot({
|
|
65
|
+
className,
|
|
66
|
+
ref,
|
|
67
|
+
...props
|
|
68
|
+
}) {
|
|
69
|
+
const [rootElement, setRootElement] = React.useState(null);
|
|
70
|
+
const [contentElement, setContentElement] = React.useState(null);
|
|
71
|
+
const layoutContext = React.useMemo(
|
|
72
|
+
() => ({
|
|
73
|
+
contentElement,
|
|
74
|
+
rootElement,
|
|
75
|
+
setContentElement,
|
|
76
|
+
setRootElement
|
|
77
|
+
}),
|
|
78
|
+
[contentElement, rootElement]
|
|
79
|
+
);
|
|
80
|
+
const setRef = React.useCallback(
|
|
81
|
+
(node) => {
|
|
82
|
+
setRootElement(node);
|
|
83
|
+
if (typeof ref === "function") {
|
|
84
|
+
ref(node);
|
|
85
|
+
} else if (ref) {
|
|
86
|
+
ref.current = node;
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
[ref]
|
|
90
|
+
);
|
|
91
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LatestThreadScrollerLayoutContext.Provider, { value: layoutContext, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
92
|
+
import_message_scroller.MessageScroller.Root,
|
|
93
|
+
{
|
|
94
|
+
ref: setRef,
|
|
95
|
+
className: cx(
|
|
96
|
+
"relative flex size-full min-h-0 flex-1 flex-col overflow-hidden",
|
|
97
|
+
className
|
|
98
|
+
),
|
|
99
|
+
...props
|
|
100
|
+
}
|
|
101
|
+
) });
|
|
102
|
+
}
|
|
103
|
+
function LatestThreadScrollerViewport({
|
|
104
|
+
className,
|
|
105
|
+
onViewportChange,
|
|
106
|
+
preserveScrollOnPrepend = true,
|
|
107
|
+
ref,
|
|
108
|
+
...props
|
|
109
|
+
}) {
|
|
110
|
+
const setRef = React.useCallback(
|
|
111
|
+
(node) => {
|
|
112
|
+
if (typeof ref === "function") {
|
|
113
|
+
ref(node);
|
|
114
|
+
} else if (ref) {
|
|
115
|
+
ref.current = node;
|
|
116
|
+
}
|
|
117
|
+
onViewportChange?.(node);
|
|
118
|
+
},
|
|
119
|
+
[onViewportChange, ref]
|
|
120
|
+
);
|
|
121
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
122
|
+
import_message_scroller.MessageScroller.Viewport,
|
|
123
|
+
{
|
|
124
|
+
ref: setRef,
|
|
125
|
+
className: cx(
|
|
126
|
+
"size-full min-h-0 min-w-0 flex-1 overflow-y-auto overscroll-contain",
|
|
127
|
+
className
|
|
128
|
+
),
|
|
129
|
+
preserveScrollOnPrepend,
|
|
130
|
+
...props
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
function LatestThreadScrollerContent({
|
|
135
|
+
className,
|
|
136
|
+
ref,
|
|
137
|
+
...props
|
|
138
|
+
}) {
|
|
139
|
+
const layout = useLatestThreadScrollerLayout();
|
|
140
|
+
const setRef = React.useCallback(
|
|
141
|
+
(node) => {
|
|
142
|
+
layout?.setContentElement(node);
|
|
143
|
+
if (typeof ref === "function") {
|
|
144
|
+
ref(node);
|
|
145
|
+
} else if (ref) {
|
|
146
|
+
ref.current = node;
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
[layout, ref]
|
|
150
|
+
);
|
|
151
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
152
|
+
import_message_scroller.MessageScroller.Content,
|
|
153
|
+
{
|
|
154
|
+
ref: setRef,
|
|
155
|
+
"data-agents24-latest-thread-content": "",
|
|
156
|
+
className: cx("flex h-max min-h-full flex-col gap-8 p-4", className),
|
|
157
|
+
...props
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
function LatestThreadScrollerItem({
|
|
162
|
+
className,
|
|
163
|
+
messageId,
|
|
164
|
+
...props
|
|
165
|
+
}) {
|
|
166
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
167
|
+
import_message_scroller.MessageScroller.Item,
|
|
168
|
+
{
|
|
169
|
+
className: cx(
|
|
170
|
+
"min-w-0 shrink-0 overflow-visible",
|
|
171
|
+
className
|
|
172
|
+
),
|
|
173
|
+
messageId,
|
|
174
|
+
...props
|
|
175
|
+
}
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
function LatestThreadScrollerButton({
|
|
179
|
+
children,
|
|
180
|
+
className,
|
|
181
|
+
direction = "end",
|
|
182
|
+
render,
|
|
183
|
+
...props
|
|
184
|
+
}) {
|
|
185
|
+
if (render) {
|
|
186
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
187
|
+
import_message_scroller.MessageScroller.Button,
|
|
188
|
+
{
|
|
189
|
+
direction,
|
|
190
|
+
render,
|
|
191
|
+
...props
|
|
192
|
+
}
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
196
|
+
import_message_scroller.MessageScroller.Button,
|
|
197
|
+
{
|
|
198
|
+
direction,
|
|
199
|
+
render: (buttonProps) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
200
|
+
"button",
|
|
201
|
+
{
|
|
202
|
+
...buttonProps,
|
|
203
|
+
"aria-label": direction === "end" ? "Scroll to latest message" : "Scroll to first message",
|
|
204
|
+
className: cx(
|
|
205
|
+
"absolute bottom-4 left-1/2 z-50 flex size-9 -translate-x-1/2 items-center justify-center rounded-full border border-border bg-background text-foreground shadow-md transition-all duration-200 data-[active=false]:pointer-events-none data-[active=false]:translate-y-2 data-[active=false]:opacity-0 data-[active=true]:translate-y-0 data-[active=true]:opacity-100",
|
|
206
|
+
className
|
|
207
|
+
),
|
|
208
|
+
type: "button",
|
|
209
|
+
children: children ?? (direction === "end" ? "\u2193" : "\u2191")
|
|
210
|
+
}
|
|
211
|
+
),
|
|
212
|
+
...props
|
|
213
|
+
}
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
function LatestThreadScrollerOutline({
|
|
217
|
+
cardClassName,
|
|
218
|
+
className,
|
|
219
|
+
contentMaxWidth = 768,
|
|
220
|
+
dir = "ltr",
|
|
221
|
+
items,
|
|
222
|
+
minContainerWidthForRail = 0,
|
|
223
|
+
minSideGutterForRail = 48,
|
|
224
|
+
side
|
|
225
|
+
}) {
|
|
226
|
+
const navRef = React.useRef(null);
|
|
227
|
+
const layout = useLatestThreadScrollerLayout();
|
|
228
|
+
const [hasSideRoom, setHasSideRoom] = React.useState(false);
|
|
229
|
+
const [hoveredIndex, setHoveredIndex] = React.useState(null);
|
|
230
|
+
const { scrollToMessage } = (0, import_message_scroller.useMessageScroller)();
|
|
231
|
+
const { currentAnchorId, visibleMessageIds } = (0, import_message_scroller.useMessageScrollerVisibility)();
|
|
232
|
+
const visibleMessageIdSet = React.useMemo(
|
|
233
|
+
() => new Set(visibleMessageIds),
|
|
234
|
+
[visibleMessageIds]
|
|
235
|
+
);
|
|
236
|
+
const focusIndex = hoveredIndex ?? -1;
|
|
237
|
+
const railSide = side ?? (dir === "rtl" ? "right" : "left");
|
|
238
|
+
React.useEffect(() => {
|
|
239
|
+
const root = layout?.rootElement ?? navRef.current?.parentElement;
|
|
240
|
+
if (!root) return;
|
|
241
|
+
const updateSideRoom = () => {
|
|
242
|
+
const rootRect = root.getBoundingClientRect();
|
|
243
|
+
const rootWidth = rootRect.width;
|
|
244
|
+
const content2 = layout?.contentElement;
|
|
245
|
+
const sideGutter = content2 ? Math.max(
|
|
246
|
+
0,
|
|
247
|
+
railSide === "left" ? content2.getBoundingClientRect().left - rootRect.left : rootRect.right - content2.getBoundingClientRect().right
|
|
248
|
+
) : Math.max(0, (rootWidth - contentMaxWidth) / 2);
|
|
249
|
+
const nextHasSideRoom = rootWidth >= minContainerWidthForRail && sideGutter >= minSideGutterForRail;
|
|
250
|
+
setHasSideRoom(
|
|
251
|
+
(current) => current === nextHasSideRoom ? current : nextHasSideRoom
|
|
252
|
+
);
|
|
253
|
+
};
|
|
254
|
+
updateSideRoom();
|
|
255
|
+
const resizeObserver = new ResizeObserver(updateSideRoom);
|
|
256
|
+
resizeObserver.observe(root);
|
|
257
|
+
const content = layout?.contentElement;
|
|
258
|
+
if (content) {
|
|
259
|
+
resizeObserver.observe(content);
|
|
260
|
+
}
|
|
261
|
+
return () => resizeObserver.disconnect();
|
|
262
|
+
}, [
|
|
263
|
+
contentMaxWidth,
|
|
264
|
+
layout?.contentElement,
|
|
265
|
+
layout?.rootElement,
|
|
266
|
+
minContainerWidthForRail,
|
|
267
|
+
minSideGutterForRail,
|
|
268
|
+
railSide
|
|
269
|
+
]);
|
|
270
|
+
if (items.length < 2) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
274
|
+
"nav",
|
|
275
|
+
{
|
|
276
|
+
ref: navRef,
|
|
277
|
+
"aria-label": "Transcript outline",
|
|
278
|
+
className: cx(
|
|
279
|
+
"pointer-events-none absolute top-[46%] z-10 max-h-[min(18rem,calc(100%-10rem))] w-16 -translate-y-1/2 overflow-visible",
|
|
280
|
+
hasSideRoom ? "hidden md:block" : "hidden",
|
|
281
|
+
railSide === "left" ? "left-1" : "right-1",
|
|
282
|
+
className
|
|
283
|
+
),
|
|
284
|
+
dir,
|
|
285
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pointer-events-auto flex flex-col gap-0 py-1", children: items.map((item, index) => {
|
|
286
|
+
const itemMessageIds = item.messageIds && item.messageIds.length > 0 ? item.messageIds : [item.id];
|
|
287
|
+
const active = itemMessageIds.some(
|
|
288
|
+
(messageId) => visibleMessageIdSet.has(messageId) || messageId === currentAnchorId
|
|
289
|
+
);
|
|
290
|
+
const distance = focusIndex >= 0 ? Math.abs(index - focusIndex) : Number.POSITIVE_INFINITY;
|
|
291
|
+
const isFocused = distance === 0;
|
|
292
|
+
const isNear = distance === 1;
|
|
293
|
+
const isNextNear = distance === 2;
|
|
294
|
+
const barWidth = isFocused ? 50 : isNear ? 36 : isNextNear ? 23 : 10;
|
|
295
|
+
const barOpacity = isFocused ? 1 : isNear ? 0.78 : isNextNear ? 0.58 : 0.36;
|
|
296
|
+
const fallbackLabel = `Turn ${index + 1}`;
|
|
297
|
+
const previewLabel = item.label && item.label !== fallbackLabel ? item.label : "";
|
|
298
|
+
const responsePreview = (item.responsePreview || "").trim();
|
|
299
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
300
|
+
"button",
|
|
301
|
+
{
|
|
302
|
+
type: "button",
|
|
303
|
+
"data-active": active,
|
|
304
|
+
dir: "ltr",
|
|
305
|
+
className: cx(
|
|
306
|
+
"group relative flex h-2 w-16 items-center rounded-sm outline-none",
|
|
307
|
+
railSide === "left" ? "justify-start" : "justify-end"
|
|
308
|
+
),
|
|
309
|
+
"aria-label": `Jump to ${item.label || fallbackLabel}`,
|
|
310
|
+
onBlur: () => setHoveredIndex(null),
|
|
311
|
+
onFocus: () => setHoveredIndex(index),
|
|
312
|
+
onMouseEnter: () => setHoveredIndex(index),
|
|
313
|
+
onMouseLeave: () => setHoveredIndex(null),
|
|
314
|
+
onClick: () => scrollToMessage(item.id, {
|
|
315
|
+
align: "start",
|
|
316
|
+
behavior: "smooth"
|
|
317
|
+
}),
|
|
318
|
+
children: [
|
|
319
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
320
|
+
"span",
|
|
321
|
+
{
|
|
322
|
+
className: "h-0.5 rounded-full bg-muted-foreground/55 transition-[width,background-color,opacity] duration-150 ease-out group-hover:bg-foreground group-focus-visible:bg-foreground group-data-[active=true]:bg-foreground",
|
|
323
|
+
style: {
|
|
324
|
+
opacity: barOpacity,
|
|
325
|
+
width: barWidth
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
),
|
|
329
|
+
previewLabel ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
330
|
+
"span",
|
|
331
|
+
{
|
|
332
|
+
dir,
|
|
333
|
+
className: cx(
|
|
334
|
+
"pointer-events-none absolute top-1/2 z-20 max-h-32 w-64 -translate-y-1/2 overflow-hidden rounded-lg border border-border/80 bg-background p-3 text-start opacity-0 shadow-lg transition-all duration-200 group-hover:pointer-events-auto group-hover:opacity-100 group-focus-visible:pointer-events-auto group-focus-visible:opacity-100",
|
|
335
|
+
railSide === "left" ? "left-full ml-2 -translate-x-1 group-hover:translate-x-0 group-focus-visible:translate-x-0" : "right-full mr-2 translate-x-1 group-hover:translate-x-0 group-focus-visible:translate-x-0",
|
|
336
|
+
cardClassName
|
|
337
|
+
),
|
|
338
|
+
children: [
|
|
339
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
340
|
+
"span",
|
|
341
|
+
{
|
|
342
|
+
className: "block overflow-hidden text-sm leading-5 text-foreground",
|
|
343
|
+
style: {
|
|
344
|
+
display: "-webkit-box",
|
|
345
|
+
WebkitBoxOrient: "vertical",
|
|
346
|
+
WebkitLineClamp: responsePreview ? 2 : 4
|
|
347
|
+
},
|
|
348
|
+
children: previewLabel
|
|
349
|
+
}
|
|
350
|
+
),
|
|
351
|
+
responsePreview ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
352
|
+
"span",
|
|
353
|
+
{
|
|
354
|
+
className: "mt-1 block overflow-hidden text-sm leading-5 text-muted-foreground",
|
|
355
|
+
style: {
|
|
356
|
+
display: "-webkit-box",
|
|
357
|
+
WebkitBoxOrient: "vertical",
|
|
358
|
+
WebkitLineClamp: 2
|
|
359
|
+
},
|
|
360
|
+
children: responsePreview
|
|
361
|
+
}
|
|
362
|
+
) : null
|
|
363
|
+
]
|
|
364
|
+
}
|
|
365
|
+
) : null
|
|
366
|
+
]
|
|
367
|
+
},
|
|
368
|
+
item.id
|
|
369
|
+
);
|
|
370
|
+
}) })
|
|
371
|
+
}
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
var LatestThreadScroller = {
|
|
375
|
+
Provider: LatestThreadScrollerProvider,
|
|
376
|
+
Root: LatestThreadScrollerRoot,
|
|
377
|
+
Viewport: LatestThreadScrollerViewport,
|
|
378
|
+
Content: LatestThreadScrollerContent,
|
|
379
|
+
Item: LatestThreadScrollerItem,
|
|
380
|
+
Button: LatestThreadScrollerButton,
|
|
381
|
+
Outline: LatestThreadScrollerOutline
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
// src/ui/utils.ts
|
|
385
|
+
var import_clsx = require("clsx");
|
|
386
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
387
|
+
function cn(...values) {
|
|
388
|
+
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(values));
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// src/templates/agent-chat-shell.tsx
|
|
392
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
393
|
+
var defaultOutlineLabel = (content, index) => {
|
|
394
|
+
const label = String(content || "").replace(/\s+/g, " ").trim();
|
|
395
|
+
return label || `Turn ${index + 1}`;
|
|
396
|
+
};
|
|
397
|
+
var defaultOutlinePreview = (content) => String(content || "").replace(/\s+/g, " ").trim();
|
|
398
|
+
function buildAgentChatOutlineItems(messages, getLabel = (message, index) => defaultOutlineLabel(message.content, index), getResponsePreview = (message) => defaultOutlinePreview(message.content)) {
|
|
399
|
+
return messages.reduce((items, message) => {
|
|
400
|
+
if (message.role === "user") {
|
|
401
|
+
items.push({
|
|
402
|
+
id: message.id,
|
|
403
|
+
label: getLabel(message, items.length),
|
|
404
|
+
messageIds: [message.id]
|
|
405
|
+
});
|
|
406
|
+
return items;
|
|
407
|
+
}
|
|
408
|
+
const currentItem = items[items.length - 1];
|
|
409
|
+
currentItem?.messageIds?.push(message.id);
|
|
410
|
+
if (currentItem && !currentItem.responsePreview && message.role === "assistant") {
|
|
411
|
+
currentItem.responsePreview = getResponsePreview(message);
|
|
412
|
+
}
|
|
413
|
+
return items;
|
|
414
|
+
}, []);
|
|
415
|
+
}
|
|
416
|
+
function AgentChatShell({
|
|
417
|
+
className,
|
|
418
|
+
composer,
|
|
419
|
+
composerOverlayClassName,
|
|
420
|
+
composerOverlayHeight,
|
|
421
|
+
contentClassName,
|
|
422
|
+
defaultComposerOverlayHeight = 192,
|
|
423
|
+
dir = "ltr",
|
|
424
|
+
hideComposer = false,
|
|
425
|
+
isLoadingOlder = false,
|
|
426
|
+
messages,
|
|
427
|
+
onLoadOlder,
|
|
428
|
+
outlineItems,
|
|
429
|
+
outlineSide = "left",
|
|
430
|
+
renderMessage,
|
|
431
|
+
renderOlderLoader
|
|
432
|
+
}) {
|
|
433
|
+
const overlayRef = React2.useRef(null);
|
|
434
|
+
const [measuredOverlayHeight, setMeasuredOverlayHeight] = React2.useState(
|
|
435
|
+
defaultComposerOverlayHeight
|
|
436
|
+
);
|
|
437
|
+
const olderPageRequestRef = React2.useRef(false);
|
|
438
|
+
const items = React2.useMemo(
|
|
439
|
+
() => outlineItems || buildAgentChatOutlineItems(messages),
|
|
440
|
+
[messages, outlineItems]
|
|
441
|
+
);
|
|
442
|
+
React2.useEffect(() => {
|
|
443
|
+
const overlay = overlayRef.current;
|
|
444
|
+
if (!overlay || hideComposer) return;
|
|
445
|
+
const updateHeight = () => {
|
|
446
|
+
setMeasuredOverlayHeight(Math.ceil(overlay.getBoundingClientRect().height));
|
|
447
|
+
};
|
|
448
|
+
updateHeight();
|
|
449
|
+
const observer = new ResizeObserver(updateHeight);
|
|
450
|
+
observer.observe(overlay);
|
|
451
|
+
return () => observer.disconnect();
|
|
452
|
+
}, [hideComposer]);
|
|
453
|
+
const handleScroll = React2.useCallback(
|
|
454
|
+
(event) => {
|
|
455
|
+
if (!onLoadOlder || isLoadingOlder || olderPageRequestRef.current || event.currentTarget.scrollTop > 48) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
olderPageRequestRef.current = true;
|
|
459
|
+
void Promise.resolve(onLoadOlder()).finally(() => {
|
|
460
|
+
olderPageRequestRef.current = false;
|
|
461
|
+
});
|
|
462
|
+
},
|
|
463
|
+
[isLoadingOlder, onLoadOlder]
|
|
464
|
+
);
|
|
465
|
+
const bottomPadding = hideComposer ? 32 : (composerOverlayHeight ?? measuredOverlayHeight) + 72;
|
|
466
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: cn("relative flex h-full min-h-0 w-full flex-col", className), dir, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(LatestThreadScroller.Provider, { children: [
|
|
467
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(LatestThreadScroller.Root, { children: [
|
|
468
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LatestThreadScroller.Viewport, { onScroll: handleScroll, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
469
|
+
LatestThreadScroller.Content,
|
|
470
|
+
{
|
|
471
|
+
className: cn(
|
|
472
|
+
"mx-auto flex min-h-full w-full max-w-3xl flex-col gap-8 px-4 pt-12",
|
|
473
|
+
contentClassName
|
|
474
|
+
),
|
|
475
|
+
style: { paddingBottom: bottomPadding },
|
|
476
|
+
children: [
|
|
477
|
+
isLoadingOlder ? renderOlderLoader?.() : null,
|
|
478
|
+
messages.map((message, index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
479
|
+
LatestThreadScroller.Item,
|
|
480
|
+
{
|
|
481
|
+
messageId: message.id,
|
|
482
|
+
scrollAnchor: message.role === "user",
|
|
483
|
+
children: renderMessage(message, index)
|
|
484
|
+
},
|
|
485
|
+
message.id
|
|
486
|
+
))
|
|
487
|
+
]
|
|
488
|
+
}
|
|
489
|
+
) }),
|
|
490
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
491
|
+
LatestThreadScroller.Outline,
|
|
492
|
+
{
|
|
493
|
+
dir,
|
|
494
|
+
items,
|
|
495
|
+
side: outlineSide
|
|
496
|
+
}
|
|
497
|
+
)
|
|
498
|
+
] }),
|
|
499
|
+
!hideComposer && composer ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
500
|
+
"div",
|
|
501
|
+
{
|
|
502
|
+
ref: overlayRef,
|
|
503
|
+
className: cn(
|
|
504
|
+
"pointer-events-none absolute inset-x-0 bottom-0 z-20 overflow-visible px-4 pb-4 pt-10",
|
|
505
|
+
composerOverlayClassName
|
|
506
|
+
),
|
|
507
|
+
style: {
|
|
508
|
+
background: "linear-gradient(to bottom, transparent 0%, color-mix(in oklch, var(--chat-background, var(--background)) 85%, transparent) 45%, var(--chat-background, var(--background)) 65%, var(--chat-background, var(--background)) 100%)"
|
|
509
|
+
},
|
|
510
|
+
children: [
|
|
511
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "pointer-events-auto absolute inset-x-0 top-0 flex -translate-y-full justify-center pb-3", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LatestThreadScroller.Button, {}) }),
|
|
512
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "pointer-events-auto mx-auto w-full max-w-3xl", children: composer })
|
|
513
|
+
]
|
|
514
|
+
}
|
|
515
|
+
) : null
|
|
516
|
+
] }) });
|
|
517
|
+
}
|
|
518
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
519
|
+
0 && (module.exports = {
|
|
520
|
+
AgentChatShell,
|
|
521
|
+
buildAgentChatOutlineItems
|
|
522
|
+
});
|
|
523
|
+
//# sourceMappingURL=index.cjs.map
|