From 055223bfdf6ab3955655c09407069df2726a1349 Mon Sep 17 00:00:00 2001 From: klarkxy <278370456@qq.com> Date: Fri, 13 Mar 2026 19:32:38 +0800 Subject: [PATCH] feat(problem): add copy button to markdown code blocks Add a copy button to markdown code blocks in the problem view for easier code snippet copying. The button appears in the top-right corner of code blocks and provides visual feedback on copy success or failure. This improves user experience by eliminating the need to manually select and copy code. --- web/template/syzoj/problem.php | 75 ++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/web/template/syzoj/problem.php b/web/template/syzoj/problem.php index 335f4a3..606aebe 100644 --- a/web/template/syzoj/problem.php +++ b/web/template/syzoj/problem.php @@ -33,6 +33,29 @@ if ($pr_flag) { cursor: pointer; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05), 0 2px 4px rgba(0, 0, 0, 0.05); } + + .md pre { + position: relative; + } + + .md-code-copy { + position: absolute; + top: 8px; + right: 8px; + z-index: 1; + font-size: 12px; + color: #4d4d4d; + background-color: #fff; + border: 1px solid #e0e0e0; + padding: 2px 8px; + border-radius: 4px; + cursor: pointer; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); + } + + .md-code-copy:hover { + background-color: #f5f5f5; + } @@ -501,6 +524,56 @@ if ($pr_flag) { $(document).ready(function () { $("#creator").load("problem-ajax.php?pid="); + + function copyText(content, onSuccess, onFail) { + if (navigator.clipboard && window.isSecureContext) { + navigator.clipboard.writeText(content).then(onSuccess).catch(onFail); + return; + } + const textarea = document.createElement('textarea'); + textarea.value = content; + textarea.style.position = 'fixed'; + textarea.style.left = '-9999px'; + document.body.appendChild(textarea); + textarea.focus(); + textarea.select(); + try { + const copied = document.execCommand('copy'); + copied ? onSuccess() : onFail(); + } catch (err) { + onFail(); + } + document.body.removeChild(textarea); + } + + function initMarkdownCodeCopy() { + $('.md pre').each(function () { + const pre = $(this); + if (pre.find('.md-code-copy').length > 0) return; + + const btn = $(''); + btn.on('click', function () { + const code = pre.find('code').first(); + const text = code.length > 0 ? code.text() : pre.text(); + const currentBtn = $(this); + + copyText( + text, + function () { + currentBtn.text('!'); + setTimeout(function () { currentBtn.text(''); }, 1500); + }, + function () { + currentBtn.text('!'); + setTimeout(function () { currentBtn.text(''); }, 1500); + } + ); + }); + + pre.prepend(btn); + }); + } + marked.use({ // 开启异步渲染 @@ -557,6 +630,8 @@ if ($pr_flag) { "text-align": "center" }); + initMarkdownCodeCopy(); + //单纯文本1. A. B. C. D. 自动变控件 $('span[class=auto_select]').each(function () {