11import { memo , type ReactNode , useEffectEvent , useLayoutEffect , useRef , useState } from "react" ;
2- import { Link , Surface , Tooltip , toast } from "@heroui/react" ;
2+ import { Link , Surface , Tooltip } from "@heroui/react" ;
33import { useLingui } from "@lingui/react/macro" ;
4- import { ChevronDown , ChevronUp , Copy , Sparkles } from "lucide-react" ;
4+ import { ChevronDown , ChevronUp , Sparkles } from "lucide-react" ;
55import type { CanonicalContentBlock , MessageItemPayload } from "@/shared/contracts" ;
6- import { friendlyError } from "@/shared/messages" ;
76import { AttachmentBar } from "@/renderer/components/composer/AttachmentBar" ;
87import { openAttachmentLightbox } from "@/renderer/components/composer/ImageLightbox" ;
98import type { Attachment } from "@/renderer/components/composer/useAttachments" ;
@@ -20,6 +19,7 @@ import { normalizeChatProjectPath } from "../../chatPathUtils";
2019import { openUserMessageActions } from "../../userMessageActions" ;
2120import { CheckpointRevertButton , type CheckpointRevertRequest } from "../CheckpointRevertControls" ;
2221import { chatPromptSurfaceClass } from "./chatMessageSurface" ;
22+ import { CopyTextButton } from "./CopyTextButton" ;
2323import { InlineFilePathChip } from "./InlineFilePathChip" ;
2424import { ItemMarkdown } from "./ItemMarkdown" ;
2525import { extractSelectorPayloads } from "./SelectorBadge" ;
@@ -270,72 +270,13 @@ export const UserMessage = memo(function UserMessage({ item, checkpointRevert }:
270270 onRequestRevert = { checkpointRevert . onRequestRevert }
271271 />
272272 ) : null }
273- < CopyUserMessageButton text = { rawText } />
273+ < CopyTextButton text = { rawText } label = { t `Copy message` } />
274274 </ div >
275275 ) : null }
276276 </ Surface >
277277 ) ;
278278} ) ;
279279
280- function CopyUserMessageButton ( { text } : { text : string } ) {
281- const { t } = useLingui ( ) ;
282- const [ copyState , setCopyState ] = useState < "idle" | "copied" > ( "idle" ) ;
283- const [ isTooltipOpen , setIsTooltipOpen ] = useState ( false ) ;
284- const resetTimerRef = useRef < number | null > ( null ) ;
285- const labelResetTimerRef = useRef < number | null > ( null ) ;
286-
287- useLayoutEffect (
288- ( ) => ( ) => {
289- if ( resetTimerRef . current !== null ) window . clearTimeout ( resetTimerRef . current ) ;
290- if ( labelResetTimerRef . current !== null ) window . clearTimeout ( labelResetTimerRef . current ) ;
291- } ,
292- [ ] ,
293- ) ;
294-
295- return (
296- < Tooltip delay = { 300 } isOpen = { isTooltipOpen } onOpenChange = { setIsTooltipOpen } >
297- < Tooltip . Trigger >
298- < button
299- type = "button"
300- aria-label = { copyState === "copied" ? t `Copied` : t `Copy message` }
301- className = "flex size-5 items-center justify-center rounded text-muted/70 transition-colors hover:bg-foreground/5 hover:text-foreground"
302- onClick = { ( event ) => {
303- event . stopPropagation ( ) ;
304- navigator . clipboard
305- . writeText ( text )
306- . then ( ( ) => {
307- setCopyState ( "copied" ) ;
308- setIsTooltipOpen ( true ) ;
309- if ( resetTimerRef . current !== null ) {
310- window . clearTimeout ( resetTimerRef . current ) ;
311- }
312- if ( labelResetTimerRef . current !== null ) {
313- window . clearTimeout ( labelResetTimerRef . current ) ;
314- }
315- resetTimerRef . current = window . setTimeout ( ( ) => {
316- setIsTooltipOpen ( false ) ;
317- resetTimerRef . current = null ;
318- labelResetTimerRef . current = window . setTimeout ( ( ) => {
319- setCopyState ( "idle" ) ;
320- labelResetTimerRef . current = null ;
321- } , 200 ) ;
322- } , 1200 ) ;
323- } )
324- . catch ( ( error : unknown ) => {
325- toast . danger ( friendlyError ( error ) ) ;
326- } ) ;
327- } }
328- >
329- < Copy className = "size-3" />
330- </ button >
331- </ Tooltip . Trigger >
332- < Tooltip . Content placement = "top" >
333- { copyState === "copied" ? t `Copied` : t `Copy message` }
334- </ Tooltip . Content >
335- </ Tooltip >
336- ) ;
337- }
338-
339280const LEADING_SLASH_COMMAND_RE = / ^ \/ ( [ A - Z a - z ] [ A - Z a - z 0 - 9 _ - ] * ) ( \s + | $ ) / ;
340281
341282function extractLeadingSlashCommand ( text : string ) : { slashCommand : string | null ; body : string } {
0 commit comments