Browse Source

Cosmetic updates

Saoud Rizwan 1 year ago
parent
commit
3a4e8b8d96

BIN
icons/icon.png


+ 1 - 1
package.json

@@ -5,7 +5,7 @@
   "version": "1.6.2",
   "icon": "icons/icon.png",
   "galleryBanner": {
-    "color": "#C1DCEA",
+    "color": "#C7D1D9",
     "theme": "light"
   },
   "engines": {

+ 3 - 0
src/integrations/TerminalManager.ts

@@ -330,6 +330,9 @@ export class TerminalProcess extends EventEmitter<TerminalProcessEvents> {
 					data = lines.join("\n")
 				}
 
+				// sometimes chunks have leading/trailing commas which we'll remove too
+				data = data.replace(/^,+|,+$/g, "")
+
 				// For non-immediately returning commands we want to show loading spinner right away but this wouldnt happen until it emits a line break, so as soon as we get any output we emit "" to let webview know to show spinner
 				if (!didEmitEmptyLine && !this.fullOutput && data) {
 					this.emit("line", "") // empty line to indicate start of command output stream

+ 23 - 23
webview-ui/src/components/ChatRow.tsx

@@ -1,4 +1,4 @@
-import { VSCodeBadge, VSCodeButton, VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react"
+import { VSCodeBadge, VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react"
 import deepEqual from "fast-deep-equal"
 import React, { memo, useMemo } from "react"
 import ReactMarkdown from "react-markdown"
@@ -103,11 +103,11 @@ const ChatRowContent = ({ message, isExpanded, onToggleExpand, lastModifiedMessa
 						<ProgressIndicator />
 					),
 					cost != null ? (
-						<span style={{ color: normalColor, fontWeight: "bold" }}>API Request Complete</span>
+						<span style={{ color: normalColor, fontWeight: "bold" }}>API Request</span>
 					) : apiRequestFailedMessage ? (
 						<span style={{ color: errorColor, fontWeight: "bold" }}>API Request Failed</span>
 					) : (
-						<span style={{ color: normalColor, fontWeight: "bold" }}>Making API Request...</span>
+						<span style={{ color: normalColor, fontWeight: "bold" }}>API Request...</span>
 					),
 				]
 			case "followup":
@@ -299,15 +299,19 @@ const ChatRowContent = ({ message, isExpanded, onToggleExpand, lastModifiedMessa
 									...headerStyle,
 									marginBottom: cost == null && apiRequestFailedMessage ? 10 : 0,
 									justifyContent: "space-between",
-								}}>
+									cursor: "pointer",
+									userSelect: "none",
+									WebkitUserSelect: "none",
+									MozUserSelect: "none",
+									msUserSelect: "none",
+								}}
+								onClick={onToggleExpand}>
 								<div style={{ display: "flex", alignItems: "center", gap: "10px" }}>
 									{icon}
 									{title}
 									{cost != null && cost > 0 && <VSCodeBadge>${Number(cost)?.toFixed(4)}</VSCodeBadge>}
 								</div>
-								<VSCodeButton appearance="icon" aria-label="Toggle Details" onClick={onToggleExpand}>
-									<span className={`codicon codicon-chevron-${isExpanded ? "up" : "down"}`}></span>
-								</VSCodeButton>
+								<span className={`codicon codicon-chevron-${isExpanded ? "up" : "down"}`}></span>
 							</div>
 							{cost == null && apiRequestFailedMessage && (
 								<>
@@ -391,24 +395,12 @@ const ChatRowContent = ({ message, isExpanded, onToggleExpand, lastModifiedMessa
 					return (
 						<div
 							style={{
-								backgroundColor: "var(--vscode-editor-inactiveSelectionBackground)",
-								borderRadius: "3px",
-								padding: "8px",
-								whiteSpace: "pre-line",
-								wordWrap: "break-word",
+								marginTop: -10,
+								width: "100%",
 							}}>
-							<span
-								style={{
-									display: "block",
-									fontStyle: "italic",
-									marginBottom: "8px",
-									opacity: 0.8,
-								}}>
-								The user made the following changes:
-							</span>
 							<CodeAccordian
 								diff={tool.diff!}
-								path={tool.path!}
+								isFeedback={true}
 								isExpanded={isExpanded}
 								onToggleExpand={onToggleExpand}
 							/>
@@ -627,7 +619,8 @@ const Markdown = memo(({ markdown }: { markdown?: string }) => {
 	// react-markdown lets us customize elements, so here we're using their example of replacing code blocks with SyntaxHighlighter. However when there are no language matches (` or ``` without a language specifier) then we default to a normal code element for inline code. Code blocks without a language specifier shouldn't be a common occurrence as we prompt Claude to always use a language specifier.
 	// when claude wraps text in thinking tags, he doesnt use line breaks so we need to insert those ourselves to render markdown correctly
 	const parsed = markdown?.replace(/<thinking>([\s\S]*?)<\/thinking>/g, (match, content) => {
-		return `_<thinking>_\n\n${content}\n\n_</thinking>_`
+		return content
+		// return `_<thinking>_\n\n${content}\n\n_</thinking>_`
 	})
 	return (
 		<div style={{ wordBreak: "break-word", overflowWrap: "anywhere" }}>
@@ -704,6 +697,13 @@ const Markdown = memo(({ markdown }: { markdown?: string }) => {
 									whiteSpace: "pre-line",
 									wordBreak: "break-word",
 									overflowWrap: "anywhere",
+									backgroundColor: "var(--vscode-textCodeBlock-background)",
+									color: "var(--vscode-textPreformat-foreground)",
+									fontFamily: "var(--vscode-editor-font-family)",
+									fontSize: "var(--vscode-editor-font-size)",
+									borderRadius: "3px",
+									border: "1px solid var(--vscode-textSeparator-foreground)",
+									// padding: "2px 4px",
 								}}>
 								{children}
 							</code>

+ 4 - 3
webview-ui/src/components/ChatView.tsx

@@ -602,11 +602,12 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
 						// Since we have maxRows, when text is long enough it starts to overflow the bottom padding, appearing behind the thumbnails. To fix this, we use a transparent border to push the text up instead. (https://stackoverflow.com/questions/42631947/maintaining-a-padding-inside-of-text-area/52538410#52538410)
 						borderTop: "9px solid transparent",
 						borderBottom: `${thumbnailsHeight + 9}px solid transparent`,
-						borderRight: "54px solid transparent",
-						borderLeft: "9px solid transparent",
+						borderColor: "transparent",
+						// borderRight: "54px solid transparent",
+						// borderLeft: "9px solid transparent", // NOTE: react-textarea-autosize doesn't calculate correct height when using borderLeft/borderRight so we need to use horizontal padding instead
 						// Instead of using boxShadow, we use a div with a border to better replicate the behavior when the textarea is focused
 						// boxShadow: "0px 0px 0px 1px var(--vscode-input-border)",
-						padding: 0,
+						padding: "0 50px 0 9px",
 						cursor: textAreaDisabled ? "not-allowed" : undefined,
 						flex: 1,
 					}}

+ 26 - 16
webview-ui/src/components/CodeAccordian.tsx

@@ -7,6 +7,7 @@ interface CodeAccordianProps {
 	diff?: string
 	language?: string | undefined
 	path?: string
+	isFeedback?: boolean
 	isExpanded: boolean
 	onToggleExpand: () => void
 }
@@ -19,7 +20,7 @@ The replace method removes these matched characters, effectively trimming the st
 */
 const removeLeadingNonAlphanumeric = (path: string): string => path.replace(/^[^a-zA-Z0-9]+/, "")
 
-const CodeAccordian = ({ code, diff, language, path, isExpanded, onToggleExpand }: CodeAccordianProps) => {
+const CodeAccordian = ({ code, diff, language, path, isFeedback, isExpanded, onToggleExpand }: CodeAccordianProps) => {
 	const inferredLanguage = useMemo(
 		() => code && (language ?? (path ? getLanguageFromPath(path) : undefined)),
 		[path, language, code]
@@ -33,7 +34,7 @@ const CodeAccordian = ({ code, diff, language, path, isExpanded, onToggleExpand
 				overflow: "hidden", // This ensures the inner scrollable area doesn't overflow the rounded corners
 				border: "1px solid var(--vscode-editorGroup-border)",
 			}}>
-			{path && (
+			{(path || isFeedback) && (
 				<div
 					style={{
 						color: "var(--vscode-descriptionForeground)",
@@ -42,25 +43,34 @@ const CodeAccordian = ({ code, diff, language, path, isExpanded, onToggleExpand
 						alignItems: "center",
 						padding: "6px 10px",
 						cursor: "pointer",
+						userSelect: "none",
+						WebkitUserSelect: "none",
+						MozUserSelect: "none",
+						msUserSelect: "none",
 					}}
 					onClick={onToggleExpand}>
-					<span
-						style={{
-							whiteSpace: "nowrap",
-							overflow: "hidden",
-							textOverflow: "ellipsis",
-							marginRight: "8px",
-							fontSize: "11px",
-							// trick to get ellipsis at beginning of string
-							direction: "rtl",
-							textAlign: "left",
-						}}>
-						{removeLeadingNonAlphanumeric(path) + "\u200E"}
-					</span>
+					<div style={{ display: "flex", alignItems: "center" }}>
+						{isFeedback && (
+							<span className="codicon codicon-feedback" style={{ marginRight: "6px" }}></span>
+						)}
+						<span
+							style={{
+								whiteSpace: "nowrap",
+								overflow: "hidden",
+								textOverflow: "ellipsis",
+								marginRight: "8px",
+								fontSize: "11px",
+								// trick to get ellipsis at beginning of string
+								direction: "rtl",
+								textAlign: "left",
+							}}>
+							{isFeedback ? "User Edits" : removeLeadingNonAlphanumeric(path ?? "") + "\u200E"}
+						</span>
+					</div>
 					<span className={`codicon codicon-chevron-${isExpanded ? "up" : "down"}`}></span>
 				</div>
 			)}
-			{(!path || isExpanded) && (
+			{(!(path || isFeedback) || isExpanded) && (
 				<div
 					//className="code-block-scrollable" this doesn't seem to be necessary anymore, on silicon macs it shows the native mac scrollbar instead of the vscode styled one
 					style={{

+ 181 - 131
webview-ui/src/components/TaskHeader.tsx

@@ -28,7 +28,8 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
 	onClose,
 }) => {
 	const { apiConfiguration } = useExtensionState()
-	const [isExpanded, setIsExpanded] = useState(false)
+	const [isTaskExpanded, setIsTaskExpanded] = useState(true)
+	const [isTextExpanded, setIsTextExpanded] = useState(false)
 	const [showSeeMore, setShowSeeMore] = useState(false)
 	const textContainerRef = useRef<HTMLDivElement>(null)
 	const textRef = useRef<HTMLDivElement>(null)
@@ -68,11 +69,11 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
 	const { height: windowHeight, width: windowWidth } = useWindowSize()
 
 	useEffect(() => {
-		if (isExpanded && textContainerRef.current) {
+		if (isTextExpanded && textContainerRef.current) {
 			const maxHeight = windowHeight * (1 / 2)
 			textContainerRef.current.style.maxHeight = `${maxHeight}px`
 		}
-	}, [isExpanded, windowHeight])
+	}, [isTextExpanded, windowHeight])
 
 	useEffect(() => {
 		if (textRef.current && textContainerRef.current) {
@@ -83,7 +84,7 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
 			const isOverflowing = textRef.current.scrollHeight > textContainerHeight
 			// necessary to show see more button again if user resizes window to expand and then back to collapse
 			if (!isOverflowing) {
-				setIsExpanded(false)
+				setIsTextExpanded(false)
 			}
 			setShowSeeMore(isOverflowing)
 		}
@@ -96,10 +97,10 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
 					backgroundColor: "var(--vscode-badge-background)",
 					color: "var(--vscode-badge-foreground)",
 					borderRadius: "3px",
-					padding: "12px",
+					padding: "9px 10px 9px 14px",
 					display: "flex",
 					flexDirection: "column",
-					gap: "8px",
+					gap: 6,
 					position: "relative",
 					zIndex: 1,
 				}}>
@@ -109,144 +110,191 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
 						justifyContent: "space-between",
 						alignItems: "center",
 					}}>
-					<span style={{ fontWeight: "bold" }}>Task</span>
-					<VSCodeButton
-						appearance="icon"
-						onClick={onClose}
-						style={{ marginTop: "-6px", marginRight: "-4px" }}>
-						<span className="codicon codicon-close"></span>
-					</VSCodeButton>
-				</div>
-				<div
-					ref={textContainerRef}
-					style={{
-						fontSize: "var(--vscode-font-size)",
-						overflowY: isExpanded ? "auto" : "hidden",
-						wordBreak: "break-word",
-						overflowWrap: "anywhere",
-						position: "relative",
-					}}>
 					<div
-						ref={textRef}
 						style={{
-							display: "-webkit-box",
-							WebkitLineClamp: isExpanded ? "unset" : 3,
-							WebkitBoxOrient: "vertical",
-							overflow: "hidden",
-							whiteSpace: "pre-wrap",
-							wordBreak: "break-word",
-							overflowWrap: "anywhere",
-						}}>
-						{task.text}
+							display: "flex",
+							alignItems: "center",
+							cursor: "pointer",
+							marginLeft: -2,
+							userSelect: "none",
+							WebkitUserSelect: "none",
+							MozUserSelect: "none",
+							msUserSelect: "none",
+							flexGrow: 1,
+							minWidth: 0, // This allows the div to shrink below its content size
+						}}
+						onClick={() => setIsTaskExpanded(!isTaskExpanded)}>
+						<div style={{ display: "flex", alignItems: "center", flexShrink: 0 }}>
+							<span className={`codicon codicon-chevron-${isTaskExpanded ? "down" : "right"}`}></span>
+						</div>
+						<div
+							style={{
+								marginLeft: 6,
+								whiteSpace: "nowrap",
+								overflow: "hidden",
+								textOverflow: "ellipsis",
+								flexGrow: 1,
+								minWidth: 0, // This allows the div to shrink below its content size
+							}}>
+							<span style={{ fontWeight: "bold" }}>Task{!isTaskExpanded && ":"}</span>
+							{!isTaskExpanded && <span style={{ marginLeft: 4 }}>{task.text}</span>}
+						</div>
 					</div>
-					{!isExpanded && showSeeMore && (
+					{!isTaskExpanded && (
+						<div
+							style={{
+								marginLeft: 10,
+								backgroundColor: "color-mix(in srgb, var(--vscode-badge-foreground) 70%, transparent)",
+								color: "var(--vscode-badge-background)",
+								padding: "2px 4px",
+								borderRadius: "500px",
+								fontSize: "11px",
+								fontWeight: 500,
+								display: "inline-block",
+								flexShrink: 0,
+							}}>
+							${totalCost?.toFixed(4)}
+						</div>
+					)}
+					<VSCodeButton appearance="icon" onClick={onClose} style={{ marginLeft: 10, flexShrink: 0 }}>
+						<span className="codicon codicon-close"></span>
+					</VSCodeButton>
+				</div>
+				{isTaskExpanded && (
+					<>
 						<div
+							ref={textContainerRef}
 							style={{
-								position: "absolute",
-								right: 0,
-								bottom: 0,
-								display: "flex",
-								alignItems: "center",
+								marginTop: -2,
+								fontSize: "var(--vscode-font-size)",
+								overflowY: isTextExpanded ? "auto" : "hidden",
+								wordBreak: "break-word",
+								overflowWrap: "anywhere",
+								position: "relative",
 							}}>
 							<div
+								ref={textRef}
 								style={{
-									width: 30,
-									height: "1.2em",
-									background:
-										"linear-gradient(to right, transparent, var(--vscode-badge-background))",
-								}}
-							/>
+									display: "-webkit-box",
+									WebkitLineClamp: isTextExpanded ? "unset" : 3,
+									WebkitBoxOrient: "vertical",
+									overflow: "hidden",
+									whiteSpace: "pre-wrap",
+									wordBreak: "break-word",
+									overflowWrap: "anywhere",
+								}}>
+								{task.text}
+							</div>
+							{!isTextExpanded && showSeeMore && (
+								<div
+									style={{
+										position: "absolute",
+										right: 0,
+										bottom: 0,
+										display: "flex",
+										alignItems: "center",
+									}}>
+									<div
+										style={{
+											width: 30,
+											height: "1.2em",
+											background:
+												"linear-gradient(to right, transparent, var(--vscode-badge-background))",
+										}}
+									/>
+									<div
+										style={{
+											cursor: "pointer",
+											color: "var(--vscode-textLink-foreground)",
+											paddingRight: 0,
+											paddingLeft: 3,
+											backgroundColor: "var(--vscode-badge-background)",
+										}}
+										onClick={() => setIsTextExpanded(!isTextExpanded)}>
+										See more
+									</div>
+								</div>
+							)}
+						</div>
+						{isTextExpanded && showSeeMore && (
 							<div
 								style={{
 									cursor: "pointer",
 									color: "var(--vscode-textLink-foreground)",
-									paddingRight: 0,
-									paddingLeft: 3,
-									backgroundColor: "var(--vscode-badge-background)",
+									marginLeft: "auto",
+									textAlign: "right",
+									paddingRight: 2,
 								}}
-								onClick={() => setIsExpanded(!isExpanded)}>
-								See more
+								onClick={() => setIsTextExpanded(!isTextExpanded)}>
+								See less
 							</div>
-						</div>
-					)}
-				</div>
-				{isExpanded && showSeeMore && (
-					<div
-						style={{
-							cursor: "pointer",
-							color: "var(--vscode-textLink-foreground)",
-							marginLeft: "auto",
-							textAlign: "right",
-							paddingRight: 2,
-						}}
-						onClick={() => setIsExpanded(!isExpanded)}>
-						See less
-					</div>
-				)}
-				{task.images && task.images.length > 0 && <Thumbnails images={task.images} />}
-				<div style={{ display: "flex", flexDirection: "column", gap: "4px" }}>
-					<div
-						style={{
-							display: "flex",
-							justifyContent: "space-between",
-							alignItems: "center",
-						}}>
-						<div style={{ display: "flex", alignItems: "center", gap: "4px", flexWrap: "wrap" }}>
-							<span style={{ fontWeight: "bold" }}>Tokens:</span>
-							<span style={{ display: "flex", alignItems: "center", gap: "3px" }}>
-								<i
-									className="codicon codicon-arrow-up"
-									style={{ fontSize: "12px", fontWeight: "bold", marginBottom: "-2px" }}
-								/>
-								{tokensIn?.toLocaleString()}
-							</span>
-							<span style={{ display: "flex", alignItems: "center", gap: "3px" }}>
-								<i
-									className="codicon codicon-arrow-down"
-									style={{ fontSize: "12px", fontWeight: "bold", marginBottom: "-2px" }}
-								/>
-								{tokensOut?.toLocaleString()}
-							</span>
-						</div>
-						{(apiConfiguration?.apiProvider === "openai" || apiConfiguration?.apiProvider === "ollama") && (
-							<ExportButton />
 						)}
-					</div>
-
-					{(doesModelSupportPromptCache || cacheReads !== undefined || cacheWrites !== undefined) && (
-						<div style={{ display: "flex", alignItems: "center", gap: "4px", flexWrap: "wrap" }}>
-							<span style={{ fontWeight: "bold" }}>Cache:</span>
-							<span style={{ display: "flex", alignItems: "center", gap: "3px" }}>
-								<i
-									className="codicon codicon-database"
-									style={{ fontSize: "12px", fontWeight: "bold", marginBottom: "-1px" }}
-								/>
-								+{(cacheWrites || 0)?.toLocaleString()}
-							</span>
-							<span style={{ display: "flex", alignItems: "center", gap: "3px" }}>
-								<i
-									className="codicon codicon-arrow-right"
-									style={{ fontSize: "12px", fontWeight: "bold", marginBottom: 0 }}
-								/>
-								{(cacheReads || 0)?.toLocaleString()}
-							</span>
-						</div>
-					)}
-					{apiConfiguration?.apiProvider !== "openai" && apiConfiguration?.apiProvider !== "ollama" && (
-						<div
-							style={{
-								display: "flex",
-								justifyContent: "space-between",
-								alignItems: "center",
-							}}>
-							<div style={{ display: "flex", alignItems: "center", gap: "4px" }}>
-								<span style={{ fontWeight: "bold" }}>API Cost:</span>
-								<span>${totalCost?.toFixed(4)}</span>
+						{task.images && task.images.length > 0 && <Thumbnails images={task.images} />}
+						<div style={{ display: "flex", flexDirection: "column", gap: "4px" }}>
+							<div
+								style={{
+									display: "flex",
+									justifyContent: "space-between",
+									alignItems: "center",
+								}}>
+								<div style={{ display: "flex", alignItems: "center", gap: "4px", flexWrap: "wrap" }}>
+									<span style={{ fontWeight: "bold" }}>Tokens:</span>
+									<span style={{ display: "flex", alignItems: "center", gap: "3px" }}>
+										<i
+											className="codicon codicon-arrow-up"
+											style={{ fontSize: "12px", fontWeight: "bold", marginBottom: "-2px" }}
+										/>
+										{tokensIn?.toLocaleString()}
+									</span>
+									<span style={{ display: "flex", alignItems: "center", gap: "3px" }}>
+										<i
+											className="codicon codicon-arrow-down"
+											style={{ fontSize: "12px", fontWeight: "bold", marginBottom: "-2px" }}
+										/>
+										{tokensOut?.toLocaleString()}
+									</span>
+								</div>
+								{(apiConfiguration?.apiProvider === "openai" ||
+									apiConfiguration?.apiProvider === "ollama") && <ExportButton />}
 							</div>
-							<ExportButton />
+
+							{(doesModelSupportPromptCache || cacheReads !== undefined || cacheWrites !== undefined) && (
+								<div style={{ display: "flex", alignItems: "center", gap: "4px", flexWrap: "wrap" }}>
+									<span style={{ fontWeight: "bold" }}>Cache:</span>
+									<span style={{ display: "flex", alignItems: "center", gap: "3px" }}>
+										<i
+											className="codicon codicon-database"
+											style={{ fontSize: "12px", fontWeight: "bold", marginBottom: "-1px" }}
+										/>
+										+{(cacheWrites || 0)?.toLocaleString()}
+									</span>
+									<span style={{ display: "flex", alignItems: "center", gap: "3px" }}>
+										<i
+											className="codicon codicon-arrow-right"
+											style={{ fontSize: "12px", fontWeight: "bold", marginBottom: 0 }}
+										/>
+										{(cacheReads || 0)?.toLocaleString()}
+									</span>
+								</div>
+							)}
+							{apiConfiguration?.apiProvider !== "openai" &&
+								apiConfiguration?.apiProvider !== "ollama" && (
+									<div
+										style={{
+											display: "flex",
+											justifyContent: "space-between",
+											alignItems: "center",
+										}}>
+										<div style={{ display: "flex", alignItems: "center", gap: "4px" }}>
+											<span style={{ fontWeight: "bold" }}>API Cost:</span>
+											<span>${totalCost?.toFixed(4)}</span>
+										</div>
+										<ExportButton />
+									</div>
+								)}
 						</div>
-					)}
-				</div>
+					</>
+				)}
 			</div>
 			{/* {apiProvider === "kodu" && (
 				<div
@@ -284,10 +332,12 @@ const ExportButton = () => (
 	<VSCodeButton
 		appearance="icon"
 		onClick={() => vscode.postMessage({ type: "exportCurrentTask" })}
-		style={{
-			marginBottom: "-2px",
-			marginRight: "-2.5px",
-		}}>
+		style={
+			{
+				// marginBottom: "-2px",
+				// marginRight: "-2.5px",
+			}
+		}>
 		<div style={{ fontSize: "10.5px", fontWeight: "bold", opacity: 0.6 }}>EXPORT</div>
 	</VSCodeButton>
 )