forked from BRETT/BRETT
55 lines
1.3 KiB
Vue
55 lines
1.3 KiB
Vue
<script setup lang="ts">
|
|
|
|
const props = defineProps<{
|
|
url: string
|
|
}>()
|
|
|
|
const image = ref<string | null>(null)
|
|
const title = ref<string | null>(null)
|
|
|
|
const hostname = computed(() => {
|
|
try {
|
|
return new URL(props.url).hostname.replace(/^www\./, '')
|
|
}
|
|
catch {
|
|
return props.url
|
|
}
|
|
})
|
|
|
|
if (import.meta.client) {
|
|
$fetch<{ image: string | null, title: string | null }>('/api/link-preview', {
|
|
query: { url: props.url },
|
|
})
|
|
.then((res) => {
|
|
image.value = res.image
|
|
title.value = res.title
|
|
})
|
|
.catch(() => {})
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<a
|
|
:href="url"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
class="mt-2 flex overflow-hidden rounded-md border transition-colors hover:border-accent"
|
|
@click.stop
|
|
>
|
|
<div class="flex size-24 shrink-0 items-center justify-center overflow-hidden bg-muted">
|
|
<img
|
|
v-if="image"
|
|
:src="image"
|
|
:alt="title ?? hostname"
|
|
loading="lazy"
|
|
class="h-full w-full object-cover"
|
|
>
|
|
<Icon name="ph:link-fill" v-else class="size-6 text-muted-foreground" />
|
|
</div>
|
|
<div class="flex min-w-0 flex-col justify-center gap-0.5 px-3 py-2">
|
|
<p class="truncate font-medium">{{ title ?? hostname }}</p>
|
|
<p class="truncate text-xs text-muted-foreground">{{ url }}</p>
|
|
</div>
|
|
</a>
|
|
</template>
|