|
1 | 1 | import { VSCodeBadge, VSCodeButton, VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react" |
2 | 2 | import deepEqual from "fast-deep-equal" |
3 | | -import React, { memo, useEffect, useMemo, useRef } from "react" |
| 3 | +import React, { memo, useEffect, useMemo, useRef, useState } from "react" |
4 | 4 | import { useSize } from "react-use" |
5 | 5 | import { |
6 | 6 | ClineApiReqInfo, |
@@ -551,7 +551,7 @@ export const ChatRowContent = ({ |
551 | 551 | case "text": |
552 | 552 | return ( |
553 | 553 | <div> |
554 | | - <Markdown markdown={message.text} /> |
| 554 | + <Markdown markdown={message.text} partial={message.partial} /> |
555 | 555 | </div> |
556 | 556 | ) |
557 | 557 | case "user_feedback": |
@@ -703,7 +703,7 @@ export const ChatRowContent = ({ |
703 | 703 | </div> |
704 | 704 | )} |
705 | 705 | <div style={{ paddingTop: 10 }}> |
706 | | - <Markdown markdown={message.text} /> |
| 706 | + <Markdown markdown={message.text} partial={message.partial} /> |
707 | 707 | </div> |
708 | 708 | </> |
709 | 709 | ) |
@@ -876,7 +876,7 @@ export const ChatRowContent = ({ |
876 | 876 | {title} |
877 | 877 | </div> |
878 | 878 | <div style={{ color: "var(--vscode-charts-green)", paddingTop: 10 }}> |
879 | | - <Markdown markdown={message.text} /> |
| 879 | + <Markdown markdown={message.text} partial={message.partial} /> |
880 | 880 | </div> |
881 | 881 | </div> |
882 | 882 | ) |
@@ -918,10 +918,63 @@ export const ProgressIndicator = () => ( |
918 | 918 | </div> |
919 | 919 | ) |
920 | 920 |
|
921 | | -const Markdown = memo(({ markdown }: { markdown?: string }) => { |
| 921 | +const Markdown = memo(({ markdown, partial }: { markdown?: string; partial?: boolean }) => { |
| 922 | + const [isHovering, setIsHovering] = useState(false); |
| 923 | + |
922 | 924 | return ( |
923 | | - <div style={{ wordBreak: "break-word", overflowWrap: "anywhere", marginBottom: -15, marginTop: -15 }}> |
924 | | - <MarkdownBlock markdown={markdown} /> |
| 925 | + <div |
| 926 | + onMouseEnter={() => setIsHovering(true)} |
| 927 | + onMouseLeave={() => setIsHovering(false)} |
| 928 | + style={{ position: "relative" }} |
| 929 | + > |
| 930 | + <div style={{ wordBreak: "break-word", overflowWrap: "anywhere", marginBottom: -15, marginTop: -15 }}> |
| 931 | + <MarkdownBlock markdown={markdown} /> |
| 932 | + </div> |
| 933 | + {markdown && !partial && isHovering && ( |
| 934 | + <div |
| 935 | + style={{ |
| 936 | + position: "absolute", |
| 937 | + bottom: "-4px", |
| 938 | + right: "8px", |
| 939 | + opacity: 0, |
| 940 | + animation: "fadeIn 0.2s ease-in-out forwards", |
| 941 | + borderRadius: "4px" |
| 942 | + }} |
| 943 | + > |
| 944 | + <style> |
| 945 | + {` |
| 946 | + @keyframes fadeIn { |
| 947 | + from { opacity: 0; } |
| 948 | + to { opacity: 1.0; } |
| 949 | + } |
| 950 | + `} |
| 951 | + </style> |
| 952 | + <VSCodeButton |
| 953 | + className="copy-button" |
| 954 | + appearance="icon" |
| 955 | + style={{ |
| 956 | + height: "24px", |
| 957 | + border: "none", |
| 958 | + background: "var(--vscode-editor-background)", |
| 959 | + transition: "background 0.2s ease-in-out" |
| 960 | + }} |
| 961 | + onClick={() => { |
| 962 | + navigator.clipboard.writeText(markdown); |
| 963 | + // Flash the button background briefly to indicate success |
| 964 | + const button = document.activeElement as HTMLElement; |
| 965 | + if (button) { |
| 966 | + button.style.background = "var(--vscode-button-background)"; |
| 967 | + setTimeout(() => { |
| 968 | + button.style.background = ""; |
| 969 | + }, 200); |
| 970 | + } |
| 971 | + }} |
| 972 | + title="Copy as markdown" |
| 973 | + > |
| 974 | + <span className="codicon codicon-copy"></span> |
| 975 | + </VSCodeButton> |
| 976 | + </div> |
| 977 | + )} |
925 | 978 | </div> |
926 | 979 | ) |
927 | 980 | }) |
0 commit comments