Auto-mode commit 7aa33cd accidentally deleted 78 files (14,814 lines) during M005
execution. Subsequent commits rebuilt some frontend files but backend/, alembic/,
tests/, whisper/, docker configs, and prompts were never restored in this repo.
This commit restores the full project tree by syncing from ub01's working directory,
which has all M001-M007 features running in production containers.
Restored: backend/ (config, models, routers, database, redis, search_service, worker),
alembic/ (6 migrations), docker/ (Dockerfiles, nginx, compose), prompts/ (4 stages),
tests/, whisper/, README.md, .env.example, chrysopedia-spec.md
58 lines
1.9 KiB
TypeScript
58 lines
1.9 KiB
TypeScript
/**
|
|
* Inline copy-link button — click to copy the current page URL.
|
|
* Shows a brief "Copied" tooltip on success.
|
|
* Styled as a subtle icon that appears on hover of the parent.
|
|
*/
|
|
|
|
import { useState, useCallback } from "react";
|
|
|
|
interface CopyLinkButtonProps {
|
|
url?: string; // defaults to window.location.href
|
|
className?: string;
|
|
}
|
|
|
|
export default function CopyLinkButton({ url, className = "" }: CopyLinkButtonProps) {
|
|
const [copied, setCopied] = useState(false);
|
|
|
|
const handleCopy = useCallback(async (e: React.MouseEvent) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
const target = url || window.location.href;
|
|
try {
|
|
await navigator.clipboard.writeText(target);
|
|
} catch {
|
|
// Fallback for non-HTTPS
|
|
const ta = document.createElement("textarea");
|
|
ta.value = target;
|
|
ta.style.position = "fixed";
|
|
ta.style.opacity = "0";
|
|
document.body.appendChild(ta);
|
|
ta.select();
|
|
document.execCommand("copy");
|
|
document.body.removeChild(ta);
|
|
}
|
|
setCopied(true);
|
|
setTimeout(() => setCopied(false), 1500);
|
|
}, [url]);
|
|
|
|
return (
|
|
<button
|
|
className={`copy-link-btn ${className}`}
|
|
onClick={handleCopy}
|
|
title="Copy link"
|
|
aria-label="Copy link to clipboard"
|
|
>
|
|
{copied ? (
|
|
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
<polyline points="20 6 9 17 4 12" />
|
|
</svg>
|
|
) : (
|
|
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
|
|
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
|
|
</svg>
|
|
)}
|
|
{copied && <span className="copy-link-btn__tooltip">Copied</span>}
|
|
</button>
|
|
);
|
|
}
|