fix: copy raw markdown from chat messages

This commit is contained in:
Federico Jaramillo Martinez
2026-05-13 14:22:25 +02:00
parent e12382cdd2
commit 7a9e7db743
2 changed files with 27 additions and 1 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@jmfederico/pi-web": patch
---
Copying selected rendered chat markdown now places the raw markdown source on the clipboard.
+22 -1
View File
@@ -9,7 +9,7 @@ export class FormattedText extends LitElement {
@property() text = "";
override render() {
return html`<div class="formatted" @click=${this.onFormattedClick}>${unsafeHTML(toSafeMarkdownHtml(this.text))}</div>`;
return html`<div class="formatted" @click=${this.onFormattedClick} @copy=${this.onFormattedCopy}>${unsafeHTML(toSafeMarkdownHtml(this.text))}</div>`;
}
override updated(): void {
@@ -48,6 +48,13 @@ export class FormattedText extends LitElement {
void this.copyCode(code.textContent, button);
};
private readonly onFormattedCopy = (event: ClipboardEvent): void => {
if (event.clipboardData === null || !hasSelectedText(currentSelection(this))) return;
event.clipboardData.setData("text/plain", this.text);
event.preventDefault();
event.stopPropagation();
};
private async copyCode(text: string, button: HTMLButtonElement): Promise<void> {
const ok = await writeClipboard(text);
this.setCopyButtonState(button, ok ? "copied" : "failed");
@@ -67,6 +74,20 @@ export class FormattedText extends LitElement {
static override styles = formattedTextStyles;
}
function currentSelection(element: Element): Selection | null {
const root = element.getRootNode();
if (typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot && hasRootSelection(root)) return root.getSelection();
return element.ownerDocument.getSelection();
}
function hasRootSelection(root: Node): root is Node & { getSelection: () => Selection | null } {
return "getSelection" in root && typeof root.getSelection === "function";
}
function hasSelectedText(selection: Selection | null): boolean {
return selection !== null && !selection.isCollapsed && selection.toString() !== "";
}
async function writeClipboard(text: string): Promise<boolean> {
try {
await navigator.clipboard.writeText(text);