Something went wrong. Try again.
A modern frontend for Github repositories (ironically hosted on Tangled)
Something went wrong. Try again.
Readme.vue
<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 blocks
function 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>