fix(comment-cards): 복사 버튼 제거, 카드 클릭으로 복사

복사 버튼이 캡처 이미지에 함께 찍히던 문제 해결. 버튼을 없애고 카드 자체 클릭으로
복사하도록 변경, 완료 피드백은 카드 밖 토스트로 표시(캡처에 미포함). JS 캐시버스터 갱신.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
hehihoho3@gmail.com 2026-06-29 18:31:25 +09:00
parent ee5a13163b
commit 7de8f954ad
2 changed files with 34 additions and 16 deletions

View File

@ -63,13 +63,11 @@
head.querySelector('.cc-time').textContent = timeAgo(c.publishedAt); head.querySelector('.cc-time').textContent = timeAgo(c.publishedAt);
head.querySelector('.cc-text').textContent = toPlainText(c.text); head.querySelector('.cc-text').textContent = toPlainText(c.text);
const copyBtn = document.createElement('button'); // 카드 자체를 클릭하면 복사 (버튼이 캡처 이미지에 찍히지 않도록 버튼 제거)
copyBtn.className = 'cc-btn secondary cc-copy'; card.title = '클릭하면 이미지 복사';
copyBtn.textContent = '복사'; card.addEventListener('click', () => window.copyCard(card));
copyBtn.addEventListener('click', () => window.copyCard(card, copyBtn)); // Task 5에서 정의
card.appendChild(head); card.appendChild(head);
card.appendChild(copyBtn);
grid.appendChild(card); grid.appendChild(card);
}); });
if (window.lucide) window.lucide.createIcons(); if (window.lucide) window.lucide.createIcons();
@ -91,29 +89,44 @@
URL.revokeObjectURL(a.href); URL.revokeObjectURL(a.href);
} }
window.copyCard = async function (cardEl, btnEl) { let toastTimer = null;
const original = btnEl.textContent; function showToast(msg) {
btnEl.disabled = true; btnEl.textContent = '복사 중…'; let t = document.getElementById('ccToast');
if (!t) {
t = document.createElement('div');
t.id = 'ccToast';
t.className = 'cc-toast';
document.body.appendChild(t);
}
t.textContent = msg;
t.classList.add('show');
if (toastTimer) clearTimeout(toastTimer);
toastTimer = setTimeout(() => t.classList.remove('show'), 1500);
}
window.copyCard = async function (cardEl) {
if (cardEl.dataset.copying === '1') return; // 중복 클릭 방지
cardEl.dataset.copying = '1';
try { try {
const blob = await captureBlob(cardEl); const blob = await captureBlob(cardEl);
if (navigator.clipboard && window.ClipboardItem) { if (navigator.clipboard && window.ClipboardItem) {
await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })]); await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })]);
btnEl.textContent = '복사됨 ✓'; showToast('복사됨 ✓');
} else { } else {
downloadBlob(blob); downloadBlob(blob);
btnEl.textContent = '다운로드됨'; showToast('다운로드됨');
} }
} catch (e) { } catch (e) {
// 클립보드 권한 거부 등 → 다운로드 폴백 // 클립보드 권한 거부 등 → 다운로드 폴백
try { try {
const blob = await captureBlob(cardEl); const blob = await captureBlob(cardEl);
downloadBlob(blob); downloadBlob(blob);
btnEl.textContent = '다운로드됨'; showToast('다운로드됨');
} catch (e2) { } catch (e2) {
btnEl.textContent = '실패'; showToast('복사 실패');
} }
} finally { } finally {
setTimeout(() => { btnEl.disabled = false; btnEl.textContent = original; }, 1500); setTimeout(() => { delete cardEl.dataset.copying; }, 600);
} }
}; };

View File

@ -26,7 +26,12 @@
.cc-time { font-size:11.5px; color:var(--text-3); margin-left:.4rem; } .cc-time { font-size:11.5px; color:var(--text-3); margin-left:.4rem; }
.cc-text { font-size:14px; color:var(--text); margin-top:.35rem; white-space:pre-wrap; word-break:break-word; line-height:1.45; } .cc-text { font-size:14px; color:var(--text); margin-top:.35rem; white-space:pre-wrap; word-break:break-word; line-height:1.45; }
.cc-stats { font-size:12px; color:var(--text-3); margin-top:.5rem; display:flex; gap:1rem; } .cc-stats { font-size:12px; color:var(--text-3); margin-top:.5rem; display:flex; gap:1rem; }
.cc-copy { position:absolute; top:.6rem; right:.6rem; } .comment-card { cursor:pointer; }
/* 복사 완료 토스트 — 카드 밖(body)에 떠서 캡처 이미지에 안 찍힘 */
.cc-toast { position:fixed; left:50%; bottom:2rem; transform:translateX(-50%) translateY(1rem);
background:var(--text); color:var(--surface); padding:.6rem 1.1rem; border-radius:999px;
font-size:14px; font-weight:600; opacity:0; pointer-events:none; transition:opacity .15s, transform .15s; z-index:9999; }
.cc-toast.show { opacity:1; transform:translateX(-50%) translateY(0); }
/* 모자이크 */ /* 모자이크 */
.comment-card.mosaic .cc-avatar { filter: blur(6px); } .comment-card.mosaic .cc-avatar { filter: blur(6px); }
.comment-card.mosaic .cc-author { filter: blur(5px); } .comment-card.mosaic .cc-author { filter: blur(5px); }
@ -37,7 +42,7 @@
<div layout:fragment="content"> <div layout:fragment="content">
<h1 style="font-size:1.5rem;font-weight:700;margin-bottom:.3rem;">댓글 카드</h1> <h1 style="font-size:1.5rem;font-weight:700;margin-bottom:.3rem;">댓글 카드</h1>
<p style="color:var(--text-3);margin-bottom:1.2rem;font-size:14px;"> <p style="color:var(--text-3);margin-bottom:1.2rem;font-size:14px;">
유튜브 링크의 댓글을 가져와 카드로 만들고, 프로필·아이디를 모자이크해 영상 소스로 복사하세요. 유튜브 링크의 댓글을 가져와 카드로 만들고, 프로필·아이디를 모자이크하세요. <b>카드를 클릭하면 이미지가 복사</b>됩니다(캡컷 등에 Ctrl+V).
</p> </p>
<div class="cc-toolbar"> <div class="cc-toolbar">
@ -66,7 +71,7 @@
<th:block layout:fragment="script"> <th:block layout:fragment="script">
<!-- DOM→이미지 캡처 (Task 5에서 사용). blur 필터 지원 위해 modern-screenshot 사용. --> <!-- DOM→이미지 캡처 (Task 5에서 사용). blur 필터 지원 위해 modern-screenshot 사용. -->
<script src="https://cdn.jsdelivr.net/npm/modern-screenshot@4/dist/index.js"></script> <script src="https://cdn.jsdelivr.net/npm/modern-screenshot@4/dist/index.js"></script>
<script th:src="@{/js/comment-cards.js(v=20260629b)}"></script> <script th:src="@{/js/comment-cards.js(v=20260629c)}"></script>
</th:block> </th:block>
</body> </body>
</html> </html>