Something went wrong. Try again.
A modern frontend for Github repositories (ironically hosted on Tangled)
Something went wrong. Try again.
5.2 kB · 282 lines
Vue
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283<script setup lang="ts">defineProps<{ html: string;}>();
const router = useRouter();const { copy } = useClipboard();
// Combined click handler for:// 1. Intercepting GitHub links to route internally (if needed)// 2. Copy button functionality for code blocksfunction handleClick(event: MouseEvent) { const target = event.target as HTMLElement | undefined; if (!target) return;
// Handle copy button clicks const copyTarget = target.closest("[data-copy]"); if (copyTarget) { const wrapper = copyTarget.closest(".readme-code-block"); if (!wrapper) return;
const pre = wrapper.querySelector("pre"); if (!pre?.textContent) return;
copy(pre.textContent);
const icon = copyTarget.querySelector("span"); if (!icon) return;
const originalIcon = "i-heroicons-document-duplicate"; const successIcon = "i-heroicons-check";
icon.classList.remove(originalIcon); icon.classList.add(successIcon);
setTimeout(() => { icon.classList.remove(successIcon); icon.classList.add(originalIcon); }, 2000); }}</script>
<template> <article class="readme prose prose-gray dark:prose-invert max-w-none" v-html="html" @click="handleClick" /></template>
<style scoped>/* README prose styling */.readme { line-height: 1.75; /* Prevent horizontal overflow on mobile */ overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; /* Contain all children */ overflow: hidden; min-width: 0;}
@media screen and (prefers-color-scheme: light) { .readme { color: #000; }
.readme svg { filter: invert(1); }}
@media screen and (prefers-color-scheme: dark) { .readme { color: #fff; }
.readme svg { filter: invert(1); }}
/* README headings */.readme :deep(h1),.readme :deep(h2),.readme :deep(h3),.readme :deep(h4),.readme :deep(h5),.readme :deep(h6) { font-family: "Monaspace Krypton", ui-monospace, SFMono-Regular, monospace; font-weight: 500; margin-top: 2rem; margin-bottom: 1rem; line-height: 1.3;}
.readme :deep(h1) { font-size: 1.875rem; border-bottom: 1px solid rgb(229 231 235); padding-bottom: 0.5rem;}
.readme :deep(h2) { font-size: 1.5rem; border-bottom: 1px solid rgb(229 231 235); padding-bottom: 0.5rem;}
.readme :deep(h3) { font-size: 1.25rem;}
.readme :deep(h4) { font-size: 1.125rem;}
.readme :deep(h5) { font-size: 1rem;}
.readme :deep(h6) { font-size: 0.875rem;}
.readme :deep(p) { margin-bottom: 1rem; color: rgb(55 65 81);}
.readme :deep(a) { color: rgb(37 99 235); text-decoration: underline; text-underline-offset: 2px;}
.readme :deep(a:hover) { color: rgb(30 64 175);}
.readme :deep(code) { font-family: "Monaspace Krypton", ui-monospace, SFMono-Regular, monospace; background: rgb(243 244 246); padding: 0.375rem 0.625rem; border-radius: 0.25rem; font-size: 0.875rem; color: rgb(31 41 55);}
/* Code blocks */.readme :deep(pre) { background: rgb(249 250 251); border: 1px solid rgb(229 231 235); border-radius: 0.5rem; padding: 1rem; overflow-x: auto; margin: 1.5rem 0; position: relative;}
.readme :deep(pre code) { background: transparent; border: none; padding: 0; font-family: "Monaspace Krypton", ui-monospace, SFMono-Regular, monospace; font-size: 0.875rem; color: rgb(31 41 55);}
.readme :deep(ul),.readme :deep(ol) { margin: 1rem 0; padding-left: 1.5rem;}
.readme :deep(li) { margin-bottom: 0.5rem;}
.readme :deep(blockquote) { border-left: 4px solid rgb(209 213 219); padding-left: 1rem; margin: 1.5rem 0; color: rgb(75 85 99); font-style: italic;}
.readme :deep(table) { width: 100%; border-collapse: collapse; margin: 1.5rem 0; overflow-x: auto; display: block; white-space: nowrap;}
.readme :deep(th),.readme :deep(td) { border: 1px solid rgb(209 213 219); padding: 0.75rem; text-align: left;}
.readme :deep(th) { background: rgb(249 250 251); font-weight: 600;}
.readme :deep(img) { max-width: 100%; height: auto; border-radius: 8px; margin: 1rem 0;}
.readme :deep(hr) { border: none; border-top: 1px solid rgb(209 213 219); margin: 2rem 0;}
/* Badge images inline */.readme :deep(p > a > img),.readme :deep(p > img) { display: inline-block; margin: 0 0.25rem 0.25rem 0; border-radius: 4px;}
/* Dark mode adjustments */@media (prefers-color-scheme: dark) { .readme :deep(h1), .readme :deep(h2) { border-bottom-color: rgb(55 65 81); }
.readme :deep(p) { color: rgb(209 213 219); }
.readme :deep(a) { color: rgb(96 165 250); }
.readme :deep(a:hover) { color: rgb(147 197 253); }
.readme :deep(code) { background: rgb(31 41 55); color: rgb(229 231 235); }
.readme :deep(pre) { background: rgb(17 24 39); border-color: rgb(55 65 81); }
.readme :deep(pre code) { color: rgb(229 231 235); }
.readme :deep(blockquote) { border-left-color: rgb(75 85 99); color: rgb(156 163 175); }
.readme :deep(th), .readme :deep(td) { border-color: rgb(75 85 99); }
.readme :deep(th) { background: rgb(31 41 55); }
.readme :deep(hr) { border-top-color: rgb(75 85 99); }}</style>