diff --git a/CHANGELOG.md b/CHANGELOG.md index 36806311..f03f19df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file. ### Changed - Migrated AI logic from numbered demo folders into `src/`; shared LLM client at `src/core/llm-client.ts`. - Moved marketplace HTML to `docs/` with `css/` and `js/` assets; updated all script paths and README links. +- Code snippet copy buttons now show a failure state if browser clipboard access is denied. ### Removed - Deleted legacy folders `01-Self-Healing-Tests`, `02-Smart-Data-Gen`, `03-Automated-Bug-Report`, `04-AI-Agents-QA`, `05-Visual-Regression`, and `lib/`. diff --git a/README.md b/README.md index 656889c2..f424a8e2 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ Fill in a form at `/submit` — name, system prompt, category, run command — a Cheatsheet -One-click copy reference with **10 collapsible sections** — all your go-to patterns in one place: +One-click copy reference with **10 collapsible sections** — all your go-to patterns in one place. Copy buttons show a failure state if browser clipboard access is denied. | Section | Contents | |---------|----------| diff --git a/ui/src/components/CodeSnippet.tsx b/ui/src/components/CodeSnippet.tsx index 1b33ecea..ee413ae5 100644 --- a/ui/src/components/CodeSnippet.tsx +++ b/ui/src/components/CodeSnippet.tsx @@ -1,22 +1,35 @@ -import { useState } from 'react' -import { useTranslation } from 'react-i18next' -import ContentCopyIcon from '@mui/icons-material/ContentCopy' -import CheckIcon from '@mui/icons-material/Check' +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import ContentCopyIcon from '@mui/icons-material/ContentCopy'; +import CheckIcon from '@mui/icons-material/Check'; +import ErrorOutlinedIcon from '@mui/icons-material/ErrorOutlined'; interface Props { - code: string - language?: string + code: string; + language?: string; } export default function CodeSnippet({ code, language }: Props) { - const { t } = useTranslation() - const [copied, setCopied] = useState(false) + const { t } = useTranslation(); + const [copyStatus, setCopyStatus] = useState<'idle' | 'copied' | 'failed'>('idle'); const handleCopy = async () => { - await navigator.clipboard.writeText(code) - setCopied(true) - setTimeout(() => setCopied(false), 2000) - } + try { + await navigator.clipboard.writeText(code); + setCopyStatus('copied'); + } catch { + setCopyStatus('failed'); + } + + setTimeout(() => setCopyStatus('idle'), 2000); + }; + + const copyLabel = + copyStatus === 'copied' + ? t('common.copied') + : copyStatus === 'failed' + ? t('common.copy_failed') + : t('common.copy'); return ( // dir="ltr" — code is always left-to-right regardless of document language @@ -37,15 +50,26 @@ export default function CodeSnippet({ code, language }: Props) { -
+      
         {code}
       
- ) + ); } diff --git a/ui/src/locales/en.json b/ui/src/locales/en.json index 0ee05cdb..9754a8ce 100644 --- a/ui/src/locales/en.json +++ b/ui/src/locales/en.json @@ -16,6 +16,7 @@ "common": { "copy": "Copy", "copied": "Copied!", + "copy_failed": "Failed to copy", "add_to_claude": "Add to Claude", "active": "Active", "planned": "Planned", diff --git a/ui/src/locales/he.json b/ui/src/locales/he.json index b5ff85b6..89d312ff 100644 --- a/ui/src/locales/he.json +++ b/ui/src/locales/he.json @@ -16,6 +16,7 @@ "common": { "copy": "העתק", "copied": "הועתק!", + "copy_failed": "ההעתקה נכשלה", "add_to_claude": "הוסף לקלוד", "active": "פעיל", "planned": "מתוכנן",