v0.0.48 Thred형 북마크·회원가입 카드와 X 임베드 보강
북마크·뉴스레터 CTA 마크다운 블록과 컴포넌트를 추가하고, Twitter/X URL은 공식 embed iframe으로 렌더링한다. Callout 강조선과 이미지 캡션 색을 테마 변수에 맞춘다. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
95
components/content/ProseBookmark.vue
Normal file
95
components/content/ProseBookmark.vue
Normal file
@@ -0,0 +1,95 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
url: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
thumbnail: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 북마크 카드에 표시할 호스트명을 반환한다.
|
||||
* @returns {string} www 없는 호스트 또는 빈 문자열
|
||||
*/
|
||||
const displayHost = computed(() => {
|
||||
try {
|
||||
return new URL(props.url).hostname.replace(/^www\./, '')
|
||||
} catch {
|
||||
return ''
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 썸네일이 비었을 때 파비콘 보조 URL을 만든다.
|
||||
* @returns {string} favicon 요청 URL
|
||||
*/
|
||||
const faviconUrl = computed(() => {
|
||||
if (!displayHost.value) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return `https://www.google.com/s2/favicons?domain=${encodeURIComponent(displayHost.value)}&sz=128`
|
||||
})
|
||||
|
||||
/**
|
||||
* 실제로 표시할 이미지 주소
|
||||
* @returns {string}
|
||||
*/
|
||||
const imageSrc = computed(() => props.thumbnail || faviconUrl.value)
|
||||
|
||||
/**
|
||||
* 표시 제목(없으면 호스트·URL)
|
||||
* @returns {string}
|
||||
*/
|
||||
const displayTitle = computed(() => props.title || displayHost.value || props.url)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a
|
||||
class="prose-bookmark group prose-bookmark-card my-8 flex max-w-full flex-col overflow-hidden rounded-[10px] border border-[var(--site-line)] bg-[var(--site-panel)] no-underline transition-[background-color,box-shadow] hover:bg-[color-mix(in_srgb,var(--site-panel)_86%,var(--site-text)_14%)] sm:flex-row"
|
||||
:href="url"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<div class="prose-bookmark__media relative h-36 w-full shrink-0 overflow-hidden bg-[color-mix(in_srgb,var(--site-line)_40%,var(--site-panel))] sm:h-auto sm:w-[min(44%,220px)] sm:min-h-[9rem]">
|
||||
<img
|
||||
v-if="imageSrc"
|
||||
class="prose-bookmark__thumb h-full w-full object-cover transition-transform duration-300 group-hover:scale-[1.03]"
|
||||
:src="imageSrc"
|
||||
alt=""
|
||||
loading="lazy"
|
||||
>
|
||||
</div>
|
||||
<div class="prose-bookmark__body flex min-w-0 flex-1 flex-col justify-center gap-1 px-4 py-4 sm:px-5 sm:py-5">
|
||||
<p v-if="displayHost" class="prose-bookmark__host text-[11px] font-semibold uppercase tracking-[0.06em] text-[var(--site-muted)]">
|
||||
{{ displayHost }}
|
||||
</p>
|
||||
<p class="prose-bookmark__title text-[15px] font-semibold leading-snug text-[var(--site-text)]">
|
||||
{{ displayTitle }}
|
||||
</p>
|
||||
<p v-if="description" class="prose-bookmark__desc line-clamp-2 text-sm leading-relaxed text-[var(--site-muted)]">
|
||||
{{ description }}
|
||||
</p>
|
||||
<p class="prose-bookmark__meta mt-1 flex items-center gap-1.5 text-xs font-medium text-[var(--site-soft)]">
|
||||
<svg class="shrink-0 opacity-80" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M12 6h-6a2 2 0 0 0 -2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-6" />
|
||||
<path d="M11 13l9 -9" />
|
||||
<path d="M15 4h5v5" />
|
||||
</svg>
|
||||
<span class="truncate">{{ url }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
</template>
|
||||
Reference in New Issue
Block a user