build / image (push) Failing after 15s
- Linktree-style homepage with photo, social links, and self-hosted services (container registry, Git server) - Registry pages rendered on demand from Harbor via a live content collection, cached in memory (60s fresh, 10min stale-while-revalidate) - Pinned registry images (PINNED_IMAGES) shown first with a star - Node standalone server with security headers; self-contained server build so the Docker image ships no node_modules - Gitea workflow building multi-arch images for Flux Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
51 lines
1.1 KiB
Plaintext
51 lines
1.1 KiB
Plaintext
---
|
|
interface Props {
|
|
text: string;
|
|
label?: string;
|
|
}
|
|
|
|
const { text, label = 'Copy' } = Astro.props;
|
|
---
|
|
|
|
<copy-button data-text={text}>
|
|
<button type="button" aria-label={`${label}: ${text}`} hidden>{label}</button>
|
|
</copy-button>
|
|
|
|
<script>
|
|
// Progressive enhancement: the button only appears when the Clipboard API is available.
|
|
class CopyButton extends HTMLElement {
|
|
connectedCallback() {
|
|
const button = this.querySelector('button');
|
|
const text = this.dataset.text;
|
|
if (!button || !text || !navigator.clipboard) return;
|
|
|
|
button.hidden = false;
|
|
const original = button.textContent;
|
|
button.addEventListener('click', async () => {
|
|
await navigator.clipboard.writeText(text);
|
|
button.textContent = 'Copied';
|
|
setTimeout(() => (button.textContent = original), 1500);
|
|
});
|
|
}
|
|
}
|
|
|
|
customElements.define('copy-button', CopyButton);
|
|
</script>
|
|
|
|
<style>
|
|
button {
|
|
font: inherit;
|
|
font-size: 0.8rem;
|
|
padding: 0.2rem 0.6rem;
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
background: var(--surface);
|
|
color: var(--text);
|
|
cursor: pointer;
|
|
}
|
|
|
|
button:hover {
|
|
border-color: var(--accent);
|
|
}
|
|
</style>
|