From fcaab090e54a6668b180d8f71279eb974a41de53 Mon Sep 17 00:00:00 2001 From: Jorkey Liu Date: Sat, 12 Apr 2025 21:25:23 +0800 Subject: [PATCH 01/11] fix: Markdown Tables and Styling Inconsistencies in Chat Messages --- webview-ui/jest.config.cjs | 5 +- webview-ui/src/components/chat/ChatRow.tsx | 143 +++++++++--------- .../src/components/ui/markdown/Markdown.tsx | 56 ++++++- 3 files changed, 130 insertions(+), 74 deletions(-) diff --git a/webview-ui/jest.config.cjs b/webview-ui/jest.config.cjs index be5b6aa1be3..4f48a3271fb 100644 --- a/webview-ui/jest.config.cjs +++ b/webview-ui/jest.config.cjs @@ -17,7 +17,10 @@ module.exports = { "^\\./setup$": "/src/__mocks__/i18n/setup.ts", "^src/i18n/TranslationContext$": "/src/__mocks__/i18n/TranslationContext.tsx", "^\\.\\./TranslationContext$": "/src/__mocks__/i18n/TranslationContext.tsx", - "^\\./TranslationContext$": "/src/__mocks__/i18n/TranslationContext.tsx" + "^\\./TranslationContext$": "/src/__mocks__/i18n/TranslationContext.tsx", + '^react-markdown$': 'identity-obj-proxy', + '^remark-gfm$': 'identity-obj-proxy', + '^shiki$': 'identity-obj-proxy' }, reporters: [["jest-simple-dot-reporter", {}]], transformIgnorePatterns: [ diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 05005e46a1a..8031ebc4b8f 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -17,7 +17,6 @@ import { vscode } from "../../utils/vscode" import CodeAccordian, { removeLeadingNonAlphanumeric } from "../common/CodeAccordian" import CodeBlock, { CODE_BLOCK_BG_COLOR } from "../common/CodeBlock" import CommandOutputViewer from "../common/CommandOutputViewer" -import MarkdownBlock from "../common/MarkdownBlock" import { ReasoningBlock } from "./ReasoningBlock" import Thumbnails from "../common/Thumbnails" import McpResourceRow from "../mcp/McpResourceRow" @@ -25,6 +24,7 @@ import McpToolRow from "../mcp/McpToolRow" import { highlightMentions } from "./TaskHeader" import { CheckpointSaved } from "./checkpoints/CheckpointSaved" import FollowUpSuggest from "./FollowUpSuggest" +import { Markdown } from "@/components/ui/markdown/Markdown" interface ChatRowProps { message: ClineMessage @@ -74,6 +74,71 @@ const ChatRow = memo( export default ChatRow +// Define the new wrapper component with copy functionality +const MarkdownWithCopy = memo(({ content, partial }: { content: string; partial?: boolean }) => { + const [isHovering, setIsHovering] = useState(false) + // Assuming useCopyToClipboard is imported correctly (it is, line 5) + const { copyWithFeedback } = useCopyToClipboard(200) // Use shorter feedback duration like original + + return ( +
setIsHovering(true)} + onMouseLeave={() => setIsHovering(false)} + style={{ position: "relative" }}> + {/* Apply negative margins and text wrap styles */} +
+ {/* Use the imported shared Markdown component */} + +
+ {/* Conditional Copy Button */} + {content && !partial && isHovering && ( +
+ + { + const success = await copyWithFeedback(content) // Use content prop + if (success) { + const button = document.activeElement as HTMLElement + if (button) { + button.style.background = "var(--vscode-button-background)" + setTimeout(() => { + button.style.background = "" + }, 200) + } + } + }} + title="Copy as markdown"> + + +
+ )} +
+ ) +}) + export const ChatRowContent = ({ message, lastModifiedMessage, @@ -555,7 +620,7 @@ export const ChatRowContent = ({ {t("chat:subtasks.newTaskContent")}
- +
@@ -592,7 +657,7 @@ export const ChatRowContent = ({ {t("chat:subtasks.completionContent")}
- +
@@ -729,7 +794,7 @@ export const ChatRowContent = ({ padding: "12px 16px", backgroundColor: "var(--vscode-editor-background)", }}> - + @@ -844,7 +909,7 @@ export const ChatRowContent = ({ case "text": return (
- +
) case "user_feedback": @@ -932,7 +997,7 @@ export const ChatRowContent = ({ {title}
- +
) @@ -1023,7 +1088,7 @@ export const ChatRowContent = ({ )}
- +
) @@ -1200,7 +1265,7 @@ export const ChatRowContent = ({ {title}
- +
) @@ -1218,7 +1283,7 @@ export const ChatRowContent = ({ )}
( ) - -const Markdown = memo(({ markdown, partial }: { markdown?: string; partial?: boolean }) => { - const [isHovering, setIsHovering] = useState(false) - const { copyWithFeedback } = useCopyToClipboard(200) // shorter feedback duration for copy button flash - - return ( -
setIsHovering(true)} - onMouseLeave={() => setIsHovering(false)} - style={{ position: "relative" }}> -
- -
- {markdown && !partial && isHovering && ( -
- - { - const success = await copyWithFeedback(markdown) - if (success) { - const button = document.activeElement as HTMLElement - if (button) { - button.style.background = "var(--vscode-button-background)" - setTimeout(() => { - button.style.background = "" - }, 200) - } - } - }} - title="Copy as markdown"> - - -
- )} -
- ) -}) diff --git a/webview-ui/src/components/ui/markdown/Markdown.tsx b/webview-ui/src/components/ui/markdown/Markdown.tsx index e6188ffd1a2..c265dd4137c 100644 --- a/webview-ui/src/components/ui/markdown/Markdown.tsx +++ b/webview-ui/src/components/ui/markdown/Markdown.tsx @@ -1,6 +1,7 @@ import { FC, memo } from "react" import ReactMarkdown, { Options } from "react-markdown" import remarkGfm from "remark-gfm" +import { cn } from "@/lib/utils" import { Separator } from "@/components/ui" @@ -16,7 +17,7 @@ export function Markdown({ content }: { content: string }) { return ( {children} @@ -26,14 +27,14 @@ export function Markdown({ content }: { content: string }) { }, ol({ children }) { return ( -
    +
      {children}
    ) }, ul({ children }) { return ( -