Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
import { CopilotTooltip } from "../../directives/tooltip";
import { cn } from "../../utils";
import { injectChatLabels } from "../../chat-config";
import { copyToClipboard } from "@copilotkit/shared";

// Base toolbar button component
@Component({
Expand Down Expand Up @@ -121,18 +122,13 @@ export class CopilotChatAssistantMessageCopyButton {
event?.stopPropagation();
if (!this.content()) return;

// Set copied immediately for instant feedback
this.copied.set(true);
setTimeout(() => this.copied.set(false), 2000);

// Copy to clipboard (fire and forget)
navigator.clipboard.writeText(this.content()!).then(
() => this.clicked.emit(),
(err) => {
console.error("Failed to copy message:", err);
this.copied.set(false);
},
);
copyToClipboard(this.content()!).then((success) => {
if (success) {
this.copied.set(true);
this.clicked.emit();
setTimeout(() => this.copied.set(false), 2000);
}
});
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import hljs from "highlight.js";
import * as katex from "katex";
import { completePartialMarkdown } from "@copilotkit/core";
import { LucideAngularModule } from "lucide-angular";
import { copyToClipboard } from "@copilotkit/shared";
import { injectChatLabels } from "../../chat-config";

@Component({
Expand Down Expand Up @@ -387,37 +388,34 @@ export class CopilotChatAssistantMessageRenderer implements AfterViewInit {
}

private copyCodeBlock(blockId: string, code: string): void {
navigator.clipboard.writeText(code).then(
() => {
// Update the button in the DOM
const button = this.elementRef.nativeElement.querySelector(
`[data-code-block-id="${blockId}"]`,
);
if (button) {
const originalHTML = button.innerHTML;
button.innerHTML = `
copyToClipboard(code).then((success) => {
if (!success) return;

// Update the button in the DOM
const button = this.elementRef.nativeElement.querySelector(
`[data-code-block-id="${blockId}"]`,
);
if (button) {
const originalHTML = button.innerHTML;
button.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>
<span>${this.labels.assistantMessageToolbarCopyCodeCopiedLabel}</span>
`;
button.setAttribute(
"aria-label",
`${this.labels.assistantMessageToolbarCopyCodeCopiedLabel} code`,
);

// Reset after 2 seconds
setTimeout(() => {
button.innerHTML = originalHTML;
button.setAttribute(
"aria-label",
`${this.labels.assistantMessageToolbarCopyCodeCopiedLabel} code`,
`${this.labels.assistantMessageToolbarCopyCodeLabel} code`,
);

// Reset after 2 seconds
setTimeout(() => {
button.innerHTML = originalHTML;
button.setAttribute(
"aria-label",
`${this.labels.assistantMessageToolbarCopyCodeLabel} code`,
);
}, 2000);
}
},
(err) => {
console.error("Failed to copy code:", err);
},
);
}, 2000);
}
});
}

private generateBlockId(code: string): string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { LucideAngularModule, Copy, Check, Edit } from "lucide-angular";
import { CopilotTooltip } from "../../directives/tooltip";
import { cn } from "../../utils";
import { injectChatLabels } from "../../chat-config";
import { copyToClipboard } from "@copilotkit/shared";

// Base toolbar button component
@Component({
Expand Down Expand Up @@ -108,18 +109,13 @@ export class CopilotChatUserMessageCopyButton {
handleCopy(): void {
if (!this.content()) return;

// Set copied immediately for instant feedback
this.copied.set(true);
setTimeout(() => this.copied.set(false), 2000);

// Copy to clipboard (fire and forget)
navigator.clipboard.writeText(this.content()!).then(
() => this.clicked.emit(),
(err) => {
console.error("Failed to copy message:", err);
this.copied.set(false);
},
);
copyToClipboard(this.content()!).then((success) => {
if (success) {
this.copied.set(true);
this.clicked.emit();
setTimeout(() => this.copied.set(false), 2000);
}
});
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
import { useKatexStyles } from "../../hooks/useKatexStyles";
import { WithSlots, renderSlot } from "../../lib/slots";
import { Streamdown } from "streamdown";
import { copyToClipboard } from "@copilotkit/shared";
import CopilotChatToolCallsView from "./CopilotChatToolCallsView";

export type CopilotChatAssistantMessageProps = WithSlots<
Expand Down Expand Up @@ -86,12 +87,9 @@ export function CopilotChatAssistantMessage({
{
onClick: async () => {
if (message.content) {
try {
await navigator.clipboard.writeText(message.content);
} catch (err) {
console.error("Failed to copy message:", err);
}
return await copyToClipboard(message.content);
}
return false;
},
},
);
Expand Down Expand Up @@ -275,18 +273,23 @@ export namespace CopilotChatAssistantMessage {
};
}, []);

const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setCopied(true);
if (timerRef.current !== null) {
clearTimeout(timerRef.current);
const handleClick = async (event: React.MouseEvent<HTMLButtonElement>) => {
let success = false;
if (onClick) {
// onClick may return a boolean indicating copy success
const result = await Promise.resolve(onClick(event));
success = result === true;
}
timerRef.current = setTimeout(() => {
timerRef.current = null;
setCopied(false);
}, 2000);

if (onClick) {
onClick(event);
if (success) {
setCopied(true);
if (timerRef.current !== null) {
clearTimeout(timerRef.current);
}
timerRef.current = setTimeout(() => {
timerRef.current = null;
setCopied(false);
}, 2000);
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
type AudioInputPart,
type VideoInputPart,
type DocumentInputPart,
copyToClipboard,
} from "@copilotkit/shared";
import { CopilotChatAttachmentRenderer } from "./CopilotChatAttachmentRenderer";

Expand Down Expand Up @@ -147,12 +148,9 @@ export function CopilotChatUserMessage({
{
onClick: async () => {
if (flattenedContent) {
try {
await navigator.clipboard.writeText(flattenedContent);
} catch (err) {
console.error("Failed to copy message:", err);
}
return await copyToClipboard(flattenedContent);
}
return false;
},
},
);
Expand Down Expand Up @@ -314,12 +312,17 @@ export namespace CopilotChatUserMessage {
const labels = config?.labels ?? CopilotChatDefaultLabels;
const [copied, setCopied] = useState(false);

const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setCopied(true);
setTimeout(() => setCopied(false), 2000);

const handleClick = async (event: React.MouseEvent<HTMLButtonElement>) => {
let success = false;
if (onClick) {
onClick(event);
// onClick may return a boolean indicating copy success
const result = await Promise.resolve(onClick(event));
success = result === true;
}

if (success) {
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ describe("CopilotChatAssistantMessage", () => {

await waitFor(() => {
expect(consoleSpy).toHaveBeenCalledWith(
"Failed to copy message:",
"Failed to copy to clipboard:",
expect.any(Error),
);
});
Expand Down
Loading
Loading