Skip to content
This repository was archived by the owner on Mar 7, 2026. It is now read-only.

Commit fbcfc6a

Browse files
committed
Merge branch 'development'
2 parents b763c7c + 8f3b444 commit fbcfc6a

8 files changed

Lines changed: 228 additions & 155 deletions

File tree

packages/bytebot-ui/src/app/tasks/[id]/page.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,17 @@ export default function TaskPage() {
3030
const chatContainerRef = useRef<HTMLDivElement>(null);
3131
const {
3232
messages,
33+
groupedMessages,
3334
taskStatus,
3435
control,
36+
input,
37+
setInput,
38+
isLoading,
39+
isLoadingSession,
40+
isLoadingMoreMessages,
41+
hasMoreMessages,
42+
loadMoreMessages,
43+
handleAddMessage,
3544
handleTakeOverTask,
3645
handleResumeTask,
3746
handleCancelTask,
@@ -79,6 +88,13 @@ export default function TaskPage() {
7988
scrollContainerRef: chatContainerRef,
8089
});
8190

91+
// For inactive tasks, auto-load all messages for proper screenshot navigation
92+
useEffect(() => {
93+
if (isTaskInactive() && hasMoreMessages && !isLoadingMoreMessages) {
94+
loadMoreMessages();
95+
}
96+
}, [isTaskInactive(), hasMoreMessages, isLoadingMoreMessages, loadMoreMessages]);
97+
8298
// Map each message ID to its flat index for screenshot scroll logic
8399
const messageIdToIndex = React.useMemo(() => {
84100
const map: Record<string, number> = {};
@@ -178,6 +194,17 @@ export default function TaskPage() {
178194
scrollRef={chatContainerRef}
179195
messageIdToIndex={messageIdToIndex}
180196
taskId={taskId}
197+
input={input}
198+
setInput={setInput}
199+
isLoading={isLoading}
200+
handleAddMessage={handleAddMessage}
201+
groupedMessages={groupedMessages}
202+
taskStatus={taskStatus}
203+
control={control}
204+
isLoadingSession={isLoadingSession}
205+
isLoadingMoreMessages={isLoadingMoreMessages}
206+
hasMoreMessages={hasMoreMessages}
207+
loadMoreMessages={loadMoreMessages}
181208
/>
182209
</div>
183210
</div>

packages/bytebot-ui/src/components/messages/AssistantMessage.tsx

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export function AssistantMessage({
2121
<div className={
2222
cn(
2323
"bg-bytebot-bronze-light-3 flex items-start justify-start gap-2 px-4 py-3 border-x border-bytebot-bronze-light-7",
24-
![TaskStatus.RUNNING, TaskStatus.NEEDS_HELP].includes(taskStatus) && "border-b border-bytebot-bronze-light-7 rounded-b-lg"
24+
![TaskStatus.RUNNING, TaskStatus.NEEDS_HELP].includes(taskStatus) && !group.take_over && "border-b border-bytebot-bronze-light-7 rounded-b-lg"
2525
)}
2626
>
2727
<MessageAvatar role={group.role} />
@@ -52,13 +52,16 @@ export function AssistantMessage({
5252
block.content &&
5353
block.content.length > 0
5454
) {
55-
block.content.map((contentBlock) => {
56-
if (isImageContentBlock(contentBlock)) {
57-
return (
55+
// Check ALL content items in the tool result, not just the first one
56+
const markers: React.ReactNode[] = [];
57+
block.content.forEach((contentItem, contentIndex) => {
58+
if (isImageContentBlock(contentItem)) {
59+
markers.push(
5860
<div
59-
key={blockIndex}
61+
key={`${blockIndex}-${contentIndex}`}
6062
data-message-index={messageIdToIndex[message.id]}
6163
data-block-index={blockIndex}
64+
data-content-index={contentIndex}
6265
style={{
6366
position: "absolute",
6467
width: 0,
@@ -69,6 +72,7 @@ export function AssistantMessage({
6972
);
7073
}
7174
});
75+
return markers;
7276
}
7377
return null;
7478
})}
@@ -95,22 +99,27 @@ export function AssistantMessage({
9599
block.content &&
96100
block.content.length > 0
97101
) {
98-
const imageBlock = block.content[0];
99-
if (isImageContentBlock(imageBlock)) {
100-
return (
101-
<div
102-
key={blockIndex}
103-
data-message-index={messageIdToIndex[message.id]}
104-
data-block-index={blockIndex}
105-
style={{
106-
position: "absolute",
107-
width: 0,
108-
height: 0,
109-
overflow: "hidden",
110-
}}
111-
/>
112-
);
113-
}
102+
// Check ALL content items in the tool result, not just the first one
103+
const markers: React.ReactNode[] = [];
104+
block.content.forEach((contentItem, contentIndex) => {
105+
if (isImageContentBlock(contentItem)) {
106+
markers.push(
107+
<div
108+
key={`${blockIndex}-${contentIndex}`}
109+
data-message-index={messageIdToIndex[message.id]}
110+
data-block-index={blockIndex}
111+
data-content-index={contentIndex}
112+
style={{
113+
position: "absolute",
114+
width: 0,
115+
height: 0,
116+
overflow: "hidden",
117+
}}
118+
/>
119+
);
120+
}
121+
});
122+
return markers;
114123
}
115124
return null;
116125
})}

packages/bytebot-ui/src/components/messages/ChatContainer.tsx

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,43 @@
11
import React, { useRef, useEffect, useCallback, Fragment } from "react";
2-
import { Role, TaskStatus } from "@/types";
2+
import { Role, TaskStatus, GroupedMessages } from "@/types";
33
import { MessageGroup } from "./MessageGroup";
44
import { TextShimmer } from "../ui/text-shimmer";
55
import { MessageAvatar } from "./MessageAvatar";
66
import { Loader } from "../ui/loader";
7-
import { useChatSession } from "@/hooks/useChatSession";
87
import { ChatInput } from "./ChatInput";
98

109
interface ChatContainerProps {
1110
scrollRef?: React.RefObject<HTMLDivElement | null>;
1211
messageIdToIndex: Record<string, number>;
1312
taskId: string;
13+
input: string;
14+
setInput: (value: string) => void;
15+
isLoading: boolean;
16+
handleAddMessage: () => Promise<void>;
17+
groupedMessages: GroupedMessages[];
18+
taskStatus: TaskStatus;
19+
control: Role;
20+
isLoadingSession: boolean;
21+
isLoadingMoreMessages: boolean;
22+
hasMoreMessages: boolean;
23+
loadMoreMessages: () => Promise<void>;
1424
}
1525

1626
export function ChatContainer({
1727
scrollRef,
18-
taskId,
1928
messageIdToIndex,
29+
input,
30+
setInput,
31+
isLoading,
32+
handleAddMessage,
33+
groupedMessages,
34+
taskStatus,
35+
control,
36+
isLoadingSession,
37+
isLoadingMoreMessages,
38+
hasMoreMessages,
39+
loadMoreMessages,
2040
}: ChatContainerProps) {
21-
const {
22-
input,
23-
setInput,
24-
isLoading,
25-
handleAddMessage,
26-
groupedMessages,
27-
taskStatus,
28-
control,
29-
isLoadingSession,
30-
isLoadingMoreMessages,
31-
hasMoreMessages,
32-
loadMoreMessages,
33-
} = useChatSession({ initialTaskId: taskId });
34-
3541
const messagesEndRef = useRef<HTMLDivElement>(null);
3642

3743
// Infinite scroll handler
@@ -82,8 +88,8 @@ export function ChatContainer({
8288
</div>
8389
) : groupedMessages.length > 0 ? (
8490
<>
85-
{/* Scrollable content area */}
86-
<div className="flex-1 overflow-y-auto">
91+
{/* Content area - scrolling handled by parent */}
92+
<div className="flex-1">
8793
{groupedMessages.map((group, groupIndex) => (
8894
<Fragment key={groupIndex}>
8995
<MessageGroup

packages/bytebot-ui/src/components/messages/UserMessage.tsx

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,27 @@ export function UserMessage({ group, messageIdToIndex }: UserMessageProps) {
3333
block.content &&
3434
block.content.length > 0
3535
) {
36-
const imageBlock = block.content[0];
37-
if (isImageContentBlock(imageBlock)) {
38-
return (
39-
<div
40-
key={blockIndex}
41-
data-message-index={messageIdToIndex[message.id]}
42-
data-block-index={blockIndex}
43-
style={{
44-
position: "absolute",
45-
width: 0,
46-
height: 0,
47-
overflow: "hidden",
48-
}}
49-
/>
50-
);
51-
}
36+
// Check ALL content items in the tool result, not just the first one
37+
const markers: React.ReactNode[] = [];
38+
block.content.forEach((contentItem, contentIndex) => {
39+
if (isImageContentBlock(contentItem)) {
40+
markers.push(
41+
<div
42+
key={`${blockIndex}-${contentIndex}`}
43+
data-message-index={messageIdToIndex[message.id]}
44+
data-block-index={blockIndex}
45+
data-content-index={contentIndex}
46+
style={{
47+
position: "absolute",
48+
width: 0,
49+
height: 0,
50+
overflow: "hidden",
51+
}}
52+
/>
53+
);
54+
}
55+
});
56+
return markers;
5257
}
5358
return null;
5459
})}
@@ -87,22 +92,27 @@ export function UserMessage({ group, messageIdToIndex }: UserMessageProps) {
8792
block.content &&
8893
block.content.length > 0
8994
) {
90-
const imageBlock = block.content[0];
91-
if (isImageContentBlock(imageBlock)) {
92-
return (
93-
<div
94-
key={blockIndex}
95-
data-message-index={messageIdToIndex[message.id]}
96-
data-block-index={blockIndex}
97-
style={{
98-
position: "absolute",
99-
width: 0,
100-
height: 0,
101-
overflow: "hidden",
102-
}}
103-
/>
104-
);
105-
}
95+
// Check ALL content items in the tool result, not just the first one
96+
const markers: React.ReactNode[] = [];
97+
block.content.forEach((contentItem, contentIndex) => {
98+
if (isImageContentBlock(contentItem)) {
99+
markers.push(
100+
<div
101+
key={`${blockIndex}-${contentIndex}`}
102+
data-message-index={messageIdToIndex[message.id]}
103+
data-block-index={blockIndex}
104+
data-content-index={contentIndex}
105+
style={{
106+
position: "absolute",
107+
width: 0,
108+
height: 0,
109+
overflow: "hidden",
110+
}}
111+
/>
112+
);
113+
}
114+
});
115+
return markers;
106116
}
107117
return null;
108118
})}

packages/bytebot-ui/src/components/screenshot/ScreenshotViewer.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useState, useEffect } from 'react';
22
import Image from 'next/image';
33
import { ScreenshotData } from '@/utils/screenshotUtils';
44

@@ -8,8 +8,15 @@ interface ScreenshotViewerProps {
88
}
99

1010
export function ScreenshotViewer({ screenshot, className = '' }: ScreenshotViewerProps) {
11-
12-
if (!screenshot) {
11+
const [currentScreenshot, setCurrentScreenshot] = useState(screenshot);
12+
13+
useEffect(() => {
14+
if (screenshot?.id !== currentScreenshot?.id) {
15+
setCurrentScreenshot(screenshot);
16+
}
17+
}, [screenshot, currentScreenshot]);
18+
19+
if (!currentScreenshot) {
1320
return (
1421
<div className={`flex items-center justify-center bg-gray-100 ${className}`}>
1522
<div className="text-center text-gray-500">
@@ -24,7 +31,7 @@ export function ScreenshotViewer({ screenshot, className = '' }: ScreenshotViewe
2431
return (
2532
<div className={`relative overflow-hidden ${className}`}>
2633
<Image
27-
src={`data:image/png;base64,${screenshot.base64Data}`}
34+
src={`data:image/png;base64,${currentScreenshot.base64Data}`}
2835
alt="Task screenshot"
2936
fill
3037
className="object-contain"

packages/bytebot-ui/src/hooks/useChatSession.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,28 +105,18 @@ export function useChatSession({ initialTaskId }: UseChatSessionProps = {}) {
105105

106106
// Load more messages function for infinite scroll
107107
const loadMoreMessages = useCallback(async () => {
108-
console.log("loadMoreMessages called with state:", {
109-
currentTaskId,
110-
isLoadingMoreMessages,
111-
hasMoreMessages,
112-
currentPage,
113-
});
114-
115108
if (!currentTaskId || isLoadingMoreMessages || !hasMoreMessages) {
116109
console.log("loadMoreMessages early return");
117110
return;
118111
}
119112

120-
console.log("Starting to load more messages");
121113
setIsLoadingMoreMessages(true);
122114
try {
123115
const nextPage = currentPage + 1;
124-
console.log("Fetching page:", nextPage);
125116
const newMessages = await fetchTaskMessages(currentTaskId, {
126117
limit: 10,
127118
page: nextPage,
128119
});
129-
console.log("Received messages:", newMessages.length);
130120

131121
if (newMessages.length === 0) {
132122
setHasMoreMessages(false);

0 commit comments

Comments
 (0)