mirror of
https://github.com/sst/opencode.git
synced 2025-08-31 02:07:24 +00:00
docs: share add copy button to messages in web interface (#902)
Co-authored-by: Jay <air@live.ca>
This commit is contained in:
parent
1af103d29e
commit
7c91f668d1
5 changed files with 89 additions and 0 deletions
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
47
packages/web/src/components/share/copy-button.module.css
Normal file
47
packages/web/src/components/share/copy-button.module.css
Normal 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;
|
||||
}
|
35
packages/web/src/components/share/copy-button.tsx
Normal file
35
packages/web/src/components/share/copy-button.tsx
Normal 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>
|
||||
)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue