Browse Source

Add title and logs; fix row expansion in browser group

Saoud Rizwan 1 year ago
parent
commit
3ba1eaf908

+ 104 - 22
webview-ui/src/components/chat/BrowserSessionRow.tsx

@@ -5,13 +5,13 @@ import { BrowserActionResult, ClineMessage, ClineSayBrowserAction } from "../../
 import { vscode } from "../../utils/vscode"
 import { vscode } from "../../utils/vscode"
 import CodeAccordian from "../common/CodeAccordian"
 import CodeAccordian from "../common/CodeAccordian"
 import CodeBlock, { CODE_BLOCK_BG_COLOR } from "../common/CodeBlock"
 import CodeBlock, { CODE_BLOCK_BG_COLOR } from "../common/CodeBlock"
-import { ChatRowContent } from "./ChatRow"
+import { ChatRowContent, ProgressIndicator } from "./ChatRow"
 import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
 
 
 interface BrowserSessionRowProps {
 interface BrowserSessionRowProps {
 	messages: ClineMessage[]
 	messages: ClineMessage[]
-	isExpanded: boolean
-	onToggleExpand: () => void
+	isExpanded: (messageTs: number) => boolean
+	onToggleExpand: (messageTs: number) => void
 	lastModifiedMessage?: ClineMessage
 	lastModifiedMessage?: ClineMessage
 	isLast: boolean
 	isLast: boolean
 	onHeightChange: (isTaller: boolean) => void
 	onHeightChange: (isTaller: boolean) => void
@@ -20,12 +20,20 @@ interface BrowserSessionRowProps {
 /*
 /*
 
 
 - console logs will be aggregate up to that current page
 - console logs will be aggregate up to that current page
+- while isbrowsing, disable next prev buttons and latest action if click use that as position instead of display state
 */
 */
 
 
 const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
 const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
-	const { messages, isLast, onHeightChange } = props
+	const { messages, isLast, onHeightChange, lastModifiedMessage, isExpanded } = props
 	const prevHeightRef = useRef(0)
 	const prevHeightRef = useRef(0)
-	const maxActionHeightRef = useRef(0) // Track max height of action section
+	const [maxActionHeight, setMaxActionHeight] = useState(0)
+	const [consoleLogsExpanded, setConsoleLogsExpanded] = useState(false)
+
+	const isBrowsing = useMemo(() => {
+		return (
+			isLast && lastModifiedMessage?.ask !== "resume_task" && lastModifiedMessage?.ask !== "resume_completed_task"
+		)
+	}, [isLast, lastModifiedMessage])
 
 
 	// Organize messages into pages with current state and next action
 	// Organize messages into pages with current state and next action
 	const pages = useMemo(() => {
 	const pages = useMemo(() => {
@@ -33,6 +41,8 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
 			currentState: {
 			currentState: {
 				url?: string
 				url?: string
 				screenshot?: string
 				screenshot?: string
+				mousePosition?: string
+				consoleLogs?: string
 				messages: ClineMessage[] // messages up to and including the result
 				messages: ClineMessage[] // messages up to and including the result
 			}
 			}
 			nextAction?: {
 			nextAction?: {
@@ -57,6 +67,8 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
 					currentState: {
 					currentState: {
 						url: resultData.currentUrl,
 						url: resultData.currentUrl,
 						screenshot: resultData.screenshot,
 						screenshot: resultData.screenshot,
+						mousePosition: resultData.currentMousePosition,
+						consoleLogs: resultData.logs,
 						messages: [...currentStateMessages],
 						messages: [...currentStateMessages],
 					},
 					},
 					nextAction:
 					nextAction:
@@ -120,11 +132,13 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
 			if (page.currentState.url || page.currentState.screenshot) {
 			if (page.currentState.url || page.currentState.screenshot) {
 				return {
 				return {
 					url: page.currentState.url,
 					url: page.currentState.url,
+					mousePosition: page.currentState.mousePosition,
+					consoleLogs: page.currentState.consoleLogs,
 					screenshot: page.currentState.screenshot,
 					screenshot: page.currentState.screenshot,
 				}
 				}
 			}
 			}
 		}
 		}
-		return { url: undefined, screenshot: undefined }
+		return { url: undefined, mousePosition: undefined, consoleLogs: undefined, screenshot: undefined }
 	}, [pages])
 	}, [pages])
 
 
 	const currentPage = pages[currentPageIndex]
 	const currentPage = pages[currentPageIndex]
@@ -134,17 +148,26 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
 	const displayState = isLastPage
 	const displayState = isLastPage
 		? {
 		? {
 				url: currentPage?.currentState.url || latestState.url || initialUrl,
 				url: currentPage?.currentState.url || latestState.url || initialUrl,
+				mousePosition: currentPage?.currentState.mousePosition || latestState.mousePosition || "400,300",
+				consoleLogs: currentPage?.currentState.consoleLogs,
 				screenshot: currentPage?.currentState.screenshot || latestState.screenshot,
 				screenshot: currentPage?.currentState.screenshot || latestState.screenshot,
 		  }
 		  }
 		: {
 		: {
 				url: currentPage?.currentState.url || initialUrl,
 				url: currentPage?.currentState.url || initialUrl,
+				mousePosition: currentPage?.currentState.mousePosition || "400,300",
+				consoleLogs: currentPage?.currentState.consoleLogs,
 				screenshot: currentPage?.currentState.screenshot,
 				screenshot: currentPage?.currentState.screenshot,
 		  }
 		  }
 
 
 	const [actionContent, { height: actionHeight }] = useSize(
 	const [actionContent, { height: actionHeight }] = useSize(
 		<div>
 		<div>
 			{currentPage?.nextAction?.messages.map((message) => (
 			{currentPage?.nextAction?.messages.map((message) => (
-				<BrowserSessionRowContent key={message.ts} {...props} message={message} />
+				<BrowserSessionRowContent
+					key={message.ts}
+					{...props}
+					message={message}
+					setMaxActionHeight={setMaxActionHeight}
+				/>
 			))}
 			))}
 		</div>
 		</div>
 	)
 	)
@@ -153,13 +176,31 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
 		if (actionHeight === 0 || actionHeight === Infinity) {
 		if (actionHeight === 0 || actionHeight === Infinity) {
 			return
 			return
 		}
 		}
-		if (actionHeight > maxActionHeightRef.current) {
-			maxActionHeightRef.current = actionHeight
+		if (actionHeight > maxActionHeight) {
+			setMaxActionHeight(actionHeight)
 		}
 		}
-	}, [actionHeight])
+	}, [actionHeight, maxActionHeight])
+
+	useEffect(() => {
+		if (!displayState.consoleLogs || displayState.consoleLogs.trim() === "") {
+			setConsoleLogsExpanded(false)
+		}
+	}, [displayState.consoleLogs])
 
 
 	const [browserSessionRow, { height }] = useSize(
 	const [browserSessionRow, { height }] = useSize(
 		<div style={{ padding: "10px 6px 10px 15px" }}>
 		<div style={{ padding: "10px 6px 10px 15px" }}>
+			<div style={{ display: "flex", alignItems: "center", gap: "10px", marginBottom: "10px" }}>
+				{isBrowsing ? (
+					<ProgressIndicator />
+				) : (
+					<span
+						className={`codicon codicon-inspect`}
+						style={{ color: "var(--vscode-foreground)", marginBottom: "-1.5px" }}></span>
+				)}
+				<span style={{ fontWeight: "bold" }}>
+					<>Cline wants to use the browser:</>
+				</span>
+			</div>
 			<div
 			<div
 				style={{
 				style={{
 					borderRadius: 3,
 					borderRadius: 3,
@@ -172,6 +213,7 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
 					style={{
 					style={{
 						margin: "5px auto",
 						margin: "5px auto",
 						width: "calc(100% - 10px)",
 						width: "calc(100% - 10px)",
+						boxSizing: "border-box", // includes padding in width calculation
 						backgroundColor: "var(--vscode-input-background)",
 						backgroundColor: "var(--vscode-input-background)",
 						border: "1px solid var(--vscode-input-border)",
 						border: "1px solid var(--vscode-input-border)",
 						borderRadius: "4px",
 						borderRadius: "4px",
@@ -228,11 +270,42 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
 							/>
 							/>
 						</div>
 						</div>
 					)}
 					)}
+					{displayState.mousePosition && (
+						<BrowserCursor
+							style={{
+								position: "absolute",
+								top: `${(parseInt(displayState.mousePosition.split(",")[1]) / 600) * 100}%`,
+								left: `${(parseInt(displayState.mousePosition.split(",")[0]) / 800) * 100}%`,
+							}}
+						/>
+					)}
+				</div>
+
+				<div style={{ width: "100%" }}>
+					<div
+						onClick={() => {
+							setConsoleLogsExpanded(!consoleLogsExpanded)
+						}}
+						style={{
+							display: "flex",
+							alignItems: "center",
+							gap: "4px",
+							width: "100%",
+							justifyContent: "flex-start",
+							cursor: "pointer",
+							padding: `9px 8px ${consoleLogsExpanded ? 0 : 8}px 8px`,
+						}}>
+						<span className={`codicon codicon-chevron-${consoleLogsExpanded ? "down" : "right"}`}></span>
+						<span style={{ fontSize: "0.8em" }}>Console Logs</span>
+					</div>
+					{consoleLogsExpanded && (
+						<CodeBlock source={`${"```"}shell\n${displayState.consoleLogs || "(No new logs)"}\n${"```"}`} />
+					)}
 				</div>
 				</div>
 			</div>
 			</div>
 
 
 			{/* Action content with min height */}
 			{/* Action content with min height */}
-			<div style={{ minHeight: maxActionHeightRef.current }}>{actionContent}</div>
+			<div style={{ minHeight: maxActionHeight }}>{actionContent}</div>
 
 
 			{/* Pagination moved to bottom */}
 			{/* Pagination moved to bottom */}
 			{pages.length > 1 && (
 			{pages.length > 1 && (
@@ -250,12 +323,12 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
 					</div>
 					</div>
 					<div style={{ display: "flex", gap: "4px" }}>
 					<div style={{ display: "flex", gap: "4px" }}>
 						<VSCodeButton
 						<VSCodeButton
-							disabled={currentPageIndex === 0}
+							disabled={currentPageIndex === 0 || isBrowsing}
 							onClick={() => setCurrentPageIndex((i) => i - 1)}>
 							onClick={() => setCurrentPageIndex((i) => i - 1)}>
 							Previous
 							Previous
 						</VSCodeButton>
 						</VSCodeButton>
 						<VSCodeButton
 						<VSCodeButton
-							disabled={currentPageIndex === pages.length - 1}
+							disabled={currentPageIndex === pages.length - 1 || isBrowsing}
 							onClick={() => setCurrentPageIndex((i) => i + 1)}>
 							onClick={() => setCurrentPageIndex((i) => i + 1)}>
 							Next
 							Next
 						</VSCodeButton>
 						</VSCodeButton>
@@ -281,6 +354,7 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
 
 
 interface BrowserSessionRowContentProps extends Omit<BrowserSessionRowProps, "messages"> {
 interface BrowserSessionRowContentProps extends Omit<BrowserSessionRowProps, "messages"> {
 	message: ClineMessage
 	message: ClineMessage
+	setMaxActionHeight: (height: number) => void
 }
 }
 
 
 const BrowserSessionRowContent = ({
 const BrowserSessionRowContent = ({
@@ -289,6 +363,7 @@ const BrowserSessionRowContent = ({
 	onToggleExpand,
 	onToggleExpand,
 	lastModifiedMessage,
 	lastModifiedMessage,
 	isLast,
 	isLast,
+	setMaxActionHeight,
 }: BrowserSessionRowContentProps) => {
 }: BrowserSessionRowContentProps) => {
 	const headerStyle: React.CSSProperties = {
 	const headerStyle: React.CSSProperties = {
 		display: "flex",
 		display: "flex",
@@ -307,13 +382,20 @@ const BrowserSessionRowContent = ({
 				case "api_req_started":
 				case "api_req_started":
 				case "text":
 				case "text":
 					return (
 					return (
-						<ChatRowContent
-							message={message}
-							isExpanded={isExpanded}
-							onToggleExpand={onToggleExpand}
-							lastModifiedMessage={lastModifiedMessage}
-							isLast={isLast}
-						/>
+						<div style={{ padding: "15px 0 5px 0" }}>
+							<ChatRowContent
+								message={message}
+								isExpanded={isExpanded(message.ts)}
+								onToggleExpand={() => {
+									if (message.say === "api_req_started") {
+										setMaxActionHeight(0)
+									}
+									onToggleExpand(message.ts)
+								}}
+								lastModifiedMessage={lastModifiedMessage}
+								isLast={isLast}
+							/>
+						</div>
 					)
 					)
 
 
 				case "browser_action":
 				case "browser_action":
@@ -355,8 +437,8 @@ const BrowserSessionRowContent = ({
 									code={logs}
 									code={logs}
 									language="shell"
 									language="shell"
 									isConsoleLogs={true}
 									isConsoleLogs={true}
-									isExpanded={isExpanded}
-									onToggleExpand={onToggleExpand}
+									isExpanded={isExpanded(message.ts)}
+									onToggleExpand={() => onToggleExpand(message.ts)}
 								/>
 								/>
 							)}
 							)}
 						</div>
 						</div>

+ 1 - 1
webview-ui/src/components/chat/ChatRow.tsx

@@ -754,7 +754,7 @@ export const ChatRowContent = ({
 	}
 	}
 }
 }
 
 
-const ProgressIndicator = () => (
+export const ProgressIndicator = () => (
 	<div
 	<div
 		style={{
 		style={{
 			width: "16px",
 			width: "16px",

+ 9 - 5
webview-ui/src/components/chat/ChatView.tsx

@@ -627,16 +627,20 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
 		(index: number, messageOrGroup: ClineMessage | ClineMessage[]) => {
 		(index: number, messageOrGroup: ClineMessage | ClineMessage[]) => {
 			// browser session group
 			// browser session group
 			if (Array.isArray(messageOrGroup)) {
 			if (Array.isArray(messageOrGroup)) {
-				const firstMessage = messageOrGroup[0]
 				return (
 				return (
 					<BrowserSessionRow
 					<BrowserSessionRow
-						key={firstMessage.ts}
 						messages={messageOrGroup}
 						messages={messageOrGroup}
-						isExpanded={expandedRows[firstMessage.ts] || false}
-						onToggleExpand={() => toggleRowExpansion(firstMessage.ts)}
-						lastModifiedMessage={modifiedMessages.at(-1)}
 						isLast={index === groupedMessages.length - 1}
 						isLast={index === groupedMessages.length - 1}
+						lastModifiedMessage={modifiedMessages.at(-1)}
 						onHeightChange={handleRowHeightChange}
 						onHeightChange={handleRowHeightChange}
+						// Pass handlers for each message in the group
+						isExpanded={(messageTs: number) => expandedRows[messageTs] ?? false}
+						onToggleExpand={(messageTs: number) => {
+							setExpandedRows((prev) => ({
+								...prev,
+								[messageTs]: !prev[messageTs],
+							}))
+						}}
 					/>
 					/>
 				)
 				)
 			}
 			}