docs: share add copy button to messages in web interface (#902)

Co-authored-by: Jay <air@live.ca>
This commit is contained in:
John Henry Rudden 2025-07-15 16:56:33 -05:00 committed by GitHub
parent 1af103d29e
commit 7c91f668d1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 89 additions and 0 deletions

View file

@ -2,6 +2,7 @@ import { marked } from "marked"
import { codeToHtml } from "shiki"
import markedShiki from "marked-shiki"
import { createOverflow } from "./common"
import { CopyButton } from "./copy-button"
import { createResource, createSignal } from "solid-js"
import { transformerNotationDiff } from "@shikijs/transformers"
import style from "./content-markdown.module.css"
@ -41,6 +42,7 @@ export function ContentMarkdown(props: Props) {
class={style.root}
data-highlight={props.highlight === true ? true : undefined}
data-expanded={expanded() || props.expand === true ? true : undefined}
style={{ position: "relative" }}
>
<div data-slot="markdown" ref={overflow.ref} innerHTML={html()} />
@ -54,6 +56,7 @@ export function ContentMarkdown(props: Props) {
{expanded() ? "Show less" : "Show more"}
</button>
)}
<CopyButton text={props.text} />
</div>
)
}

View file

@ -2,6 +2,7 @@
color: var(--sl-color-text);
background-color: var(--sl-color-bg-surface);
padding: 0.5rem calc(0.5rem + 3px);
padding-right: calc(1rem + 18px);
border-radius: 0.25rem;
display: flex;
flex-direction: column;

View file

@ -1,6 +1,7 @@
import style from "./content-text.module.css"
import { createSignal } from "solid-js"
import { createOverflow } from "./common"
import { CopyButton } from "./copy-button"
interface Props {
text: string
@ -16,6 +17,7 @@ export function ContentText(props: Props) {
class={style.root}
data-expanded={expanded() || props.expand === true ? true : undefined}
data-compact={props.compact === true ? true : undefined}
style={{ position: "relative" }}
>
<pre data-slot="text" ref={overflow.ref}>
{props.text}
@ -30,6 +32,7 @@ export function ContentText(props: Props) {
{expanded() ? "Show less" : "Show more"}
</button>
)}
<CopyButton text={props.text} />
</div>
)
}

View file

@ -0,0 +1,47 @@
.copyButtonWrapper {
position: absolute;
top: 0.5rem;
right: 0.5rem;
opacity: 0;
visibility: hidden;
transition: opacity 0.15s ease;
}
.copyButton {
width: 18px;
cursor: pointer;
background: none;
border: none;
padding: 0;
color: var(--sl-color-text-secondary);
svg {
display: block;
width: 16px;
height: 16px;
}
&[data-copied="true"] {
color: var(--sl-color-green-high);
}
}
/* Show copy button when parent is hovered */
*:hover > .copyButtonWrapper {
opacity: 0.65;
visibility: visible;
}
.copyTooltip {
position: absolute;
top: 50%;
left: calc(100% + 12px);
transform: translate(0, -50%);
padding: 0.375em 0.5em;
background: var(--sl-color-white);
color: var(--sl-color-text-invert);
font-size: 0.6875rem;
border-radius: 7px;
white-space: nowrap;
z-index: 11;
}

View file

@ -0,0 +1,35 @@
import { createSignal } from "solid-js"
import { IconClipboard, IconCheckCircle } from "../icons"
import styles from "./copy-button.module.css"
interface CopyButtonProps {
text: string
}
export function CopyButton(props: CopyButtonProps) {
const [copied, setCopied] = createSignal(false)
function handleCopyClick() {
if (props.text) {
navigator.clipboard.writeText(props.text).catch((err) => console.error("Copy failed", err))
setCopied(true)
setTimeout(() => setCopied(false), 2000)
}
}
return (
<div class={styles.copyButtonWrapper}>
<button
type="button"
class={styles.copyButton}
onClick={handleCopyClick}
data-copied={copied() ? true : undefined}
title="Copy content"
>
{copied() ? <IconCheckCircle width={16} height={16} /> : <IconClipboard width={16} height={16} />}
</button>
{copied() && <span class={styles.copyTooltip}>Copied!</span>}
</div>
)
}