Use toast feedback for copy actions
This commit is contained in:
+32
-12
@@ -115,6 +115,9 @@ h1 { margin: 0; font-size: clamp(28px, 5vw, 44px); line-height: 1.12; letter-spa
|
||||
.shot-menu { position: absolute; left: 0; bottom: calc(100% + 8px); z-index: 20; min-width: 150px; padding: 6px; border: 1px solid var(--border); border-radius: 8px; background: var(--panel); box-shadow: 0 12px 30px rgba(0,0,0,.12); }
|
||||
.shot-menu button { width: 100%; border: 0; border-radius: 6px; background: transparent; color: var(--text); padding: 8px 10px; font: inherit; font-size: 13px; text-align: left; cursor: pointer; }
|
||||
.shot-menu button:hover, .shot-menu button[data-current="true"] { background: var(--soft); }
|
||||
.toast-region { position: fixed; left: 50%; bottom: 22px; z-index: 40; display: grid; justify-items: center; pointer-events: none; transform: translateX(-50%); }
|
||||
.toast { max-width: min(calc(100vw - 32px), 320px); border: 1px solid var(--border); border-radius: 8px; background: var(--panel); color: var(--text); padding: 9px 12px; font-size: 13px; line-height: 1.35; box-shadow: 0 12px 30px rgba(0,0,0,.14); opacity: 0; transform: translateY(8px); transition: opacity .16s ease, transform .16s ease; }
|
||||
.toast[data-visible="true"] { opacity: 1; transform: translateY(0); }
|
||||
.source { max-width: 880px; margin: 0 auto; padding: 18px 20px 34px; color: var(--muted); font-size: 12px; }
|
||||
.source a { color: var(--muted); }
|
||||
.failure { max-width: 720px; margin: 0 auto; padding: 80px 20px; }
|
||||
@@ -145,29 +148,45 @@ function pageScript(slug: string): string {
|
||||
function downloadShot(variant) {
|
||||
location.href = "/aishare/${slug}/screenshots/" + variant + ".png";
|
||||
}
|
||||
var toastTimer;
|
||||
function showToast(message) {
|
||||
var region = document.querySelector("[data-toast-region]");
|
||||
if (!region) return;
|
||||
var toast = region.querySelector("[data-toast]");
|
||||
if (!toast) {
|
||||
toast = document.createElement("div");
|
||||
toast.className = "toast";
|
||||
toast.dataset.toast = "true";
|
||||
region.appendChild(toast);
|
||||
}
|
||||
toast.textContent = message;
|
||||
toast.dataset.visible = "true";
|
||||
clearTimeout(toastTimer);
|
||||
toastTimer = setTimeout(function () {
|
||||
delete toast.dataset.visible;
|
||||
}, 1600);
|
||||
}
|
||||
async function copyText(text, successMessage) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
showToast(successMessage);
|
||||
} catch (error) {
|
||||
showToast("Copy failed");
|
||||
}
|
||||
}
|
||||
var params = new URLSearchParams(location.search);
|
||||
var theme = params.get("theme");
|
||||
if (theme === "light" || theme === "dark") document.documentElement.dataset.theme = theme;
|
||||
var copy = document.querySelector("[data-copy]");
|
||||
if (copy) copy.addEventListener("click", async function () {
|
||||
await navigator.clipboard.writeText(location.href.replace(/[?#].*$/, ""));
|
||||
copy.dataset.copied = "true";
|
||||
copy.setAttribute("aria-label", "Copied");
|
||||
copy.setAttribute("title", "Copied");
|
||||
setTimeout(function () {
|
||||
delete copy.dataset.copied;
|
||||
copy.setAttribute("aria-label", "Copy link");
|
||||
copy.setAttribute("title", "Copy link");
|
||||
}, 1400);
|
||||
await copyText(location.href.replace(/[?#].*$/, ""), "Link copied");
|
||||
});
|
||||
document.querySelectorAll("[data-copy-code]").forEach(function (button) {
|
||||
button.addEventListener("click", async function () {
|
||||
var block = button.closest(".code-block");
|
||||
var code = block && block.querySelector("code");
|
||||
if (!code) return;
|
||||
await navigator.clipboard.writeText(code.textContent || "");
|
||||
button.textContent = "Copied";
|
||||
setTimeout(function () { button.textContent = "Copy"; }, 1400);
|
||||
await copyText(code.textContent || "", "Code copied");
|
||||
});
|
||||
});
|
||||
var defaultShot = document.querySelector("[data-shot-current]");
|
||||
@@ -241,6 +260,7 @@ export function renderConversationPage(slug: string, conversation: NormalizedCon
|
||||
Captured from ChatGPT on ${escapeHtml(new Date(conversation.capturedAt).toLocaleString())}.
|
||||
Source: <a href="${escapeHtml(conversation.sourceUrl)}" rel="nofollow noreferrer">${escapeHtml(conversation.sourceUrl)}</a>
|
||||
</footer>
|
||||
<div class="toast-region" data-toast-region aria-live="polite" aria-atomic="true"></div>
|
||||
</div>
|
||||
<script>${pageScript(slug)}</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user