mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-09 07:10:34 +01:00
feat(ui): copy own task messages as Markdown
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
832ede93ed
commit
a24fdf83c4
2 files changed
+45
No files matched your search
@@ -9,6 +9,7 @@ import { ThemeProvider } from "@/context/ThemeContext";
|
||||
import { IssueGalleryContext } from "@/context/IssueGalleryContext";
|
||||
import { MemoryRouter, useLocation } from "@/lib/router";
|
||||
import { TaskChatAgentIdentity, TaskChatBubble } from "./TaskChatBubble";
|
||||
import { copyTextToClipboard } from "@/lib/clipboard";
|
||||
import { paperclipHumanAvatarUrl } from "@/lib/issue-chat-human-author";
|
||||
import type { TaskChatMessageItem } from "./task-chat-model";
|
||||
|
||||
@@ -17,6 +18,8 @@ vi.mock("@/context/CompanyContext", async (importOriginal) => ({
|
||||
useCompany: () => ({ selectedCompany: { issuePrefix: "TES" } }),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/clipboard", () => ({ copyTextToClipboard: vi.fn().mockResolvedValue(undefined) }));
|
||||
|
||||
function CurrentPath() {
|
||||
return <output>{useLocation().pathname}</output>;
|
||||
}
|
||||
@@ -144,6 +147,36 @@ describe("TaskChatBubble attachment chips", () => {
|
||||
expect(container.textContent).not.toContain("Sent from iMessage");
|
||||
});
|
||||
|
||||
it("copies the viewer's original Markdown beside the timestamp, including attachment links", async () => {
|
||||
const text = "**My message**\n\n[report.txt](/api/attachments/report/content)";
|
||||
flushSync(() => root!.render(
|
||||
<ThemeProvider>
|
||||
<TaskChatBubble item={{ id: "own", kind: "message", author: "human", isCurrentUser: true, text, timestamp: "15m ago" }} />
|
||||
</ThemeProvider>,
|
||||
));
|
||||
const toolbar = container.querySelector('[data-testid="task-chat-bubble-actions"]')!;
|
||||
expect(toolbar.parentElement?.parentElement?.textContent).toContain("15m ago");
|
||||
const button = toolbar.querySelector('button[aria-label="Copy message"]') as HTMLButtonElement;
|
||||
await act(async () => button.click());
|
||||
expect(copyTextToClipboard).toHaveBeenLastCalledWith(text);
|
||||
expect(button.querySelector(".lucide-check")).not.toBeNull();
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ author: "human" as const, isCurrentUser: false },
|
||||
{ author: "human" as const, isCurrentUser: undefined },
|
||||
{ author: "agent" as const, isCurrentUser: true },
|
||||
{ author: "human" as const, isCurrentUser: true, optimistic: "pending" as const },
|
||||
{ author: "human" as const, isCurrentUser: true, optimistic: "queued" as const },
|
||||
])("omits the own-message toolbar for %j", (state) => {
|
||||
flushSync(() => root!.render(
|
||||
<ThemeProvider>
|
||||
<TaskChatBubble item={{ id: "other", kind: "message", text: "Message", timestamp: "15m ago", ...state }} />
|
||||
</ThemeProvider>,
|
||||
));
|
||||
expect(container.querySelector('[data-testid="task-chat-bubble-actions"]')).toBeNull();
|
||||
});
|
||||
|
||||
it("opens attachment images in the shared task gallery", () => {
|
||||
const openGallery = vi.fn(() => true);
|
||||
const contentPath = "/api/attachments/shared-image/content";
|
||||
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
stripStandaloneImageEmbeds,
|
||||
type AttachmentRef,
|
||||
} from "./task-chat-attachments";
|
||||
import { TaskChatBubbleActions } from "./TaskChatBubbleActions";
|
||||
import { TaskChatSystemNotice } from "./TaskChatSystemNotice";
|
||||
import type { TaskChatMessageItem } from "./task-chat-model";
|
||||
|
||||
@@ -431,6 +432,17 @@ function TaskChatBubbleContent({
|
||||
>
|
||||
{attachedTurn}
|
||||
</div>
|
||||
) : isOwnMessage ? (
|
||||
<div className="group/message-toolbar flex items-center gap-1">
|
||||
<span className="px-1 text-(length:--text-micro) text-muted-foreground">
|
||||
{sentFromIMessage ? "Sent from iMessage" : null}
|
||||
{sentFromIMessage && timestamp ? " · " : null}
|
||||
{timestamp}
|
||||
</span>
|
||||
<div className="transition-opacity motion-reduce:transition-none sm:opacity-0 sm:group-hover/message-toolbar:opacity-100 sm:group-focus-within/message-toolbar:opacity-100 pointer-coarse:opacity-100">
|
||||
<TaskChatBubbleActions copyText={item.text} />
|
||||
</div>
|
||||
</div>
|
||||
) : actions ? (
|
||||
streamlined ? (
|
||||
<div className="flex w-full items-center justify-between gap-2 px-1">
|
||||
|
||||
Reference in new issue
Block a user