Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 46 additions & 9 deletions app/components/BlueskyComment.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,27 @@ function getHostname(uri: string): string {
return uri
}
}

let segmenter: Intl.Segmenter | null = null
function firstChar(str: string): string {
segmenter ??= new Intl.Segmenter(undefined, { granularity: 'grapheme' })
return Array.from(segmenter.segment(str))[0]?.segment ?? ''
}
</script>

<template>
<div :class="depth === 0 ? 'flex gap-3' : 'flex gap-3 mt-3'">
<!-- Avatar -->
<!--
Depth 0: classic avatar-column layout (all screens)
Depth 1+: Medium-style inline avatar on mobile, avatar-column on desktop
-->
<div :class="depth === 0 ? 'flex gap-3' : 'sm:flex sm:gap-3'">
<!-- Column avatar: always shown at depth 0, desktop-only at depth 1+ -->
<a
:href="`https://bsky.app/profile/${comment.author.handle}`"
target="_blank"
rel="noopener noreferrer"
class="shrink-0"
:class="depth > 0 ? 'hidden sm:block' : ''"
>
<img
v-if="comment.author.avatar"
Expand All @@ -65,22 +76,45 @@ function getHostname(uri: string): string {
depth === 0 ? 'w-10 h-10' : 'w-8 h-8 text-sm',
]"
>
{{ (comment.author.displayName || comment.author.handle).charAt(0).toUpperCase() }}
{{ firstChar(comment.author.displayName || comment.author.handle).toUpperCase() }}
</div>
</a>

<div class="flex-1 min-w-0">
<!-- Author info + timestamp -->
<div class="flex flex-wrap items-baseline gap-x-2 gap-y-0">
<div class="flex flex-wrap items-center gap-x-2 gap-y-0">
<!-- Inline avatar: mobile-only for nested comments -->
<a
v-if="depth > 0"
:href="`https://bsky.app/profile/${comment.author.handle}`"
target="_blank"
rel="noopener noreferrer"
class="shrink-0 sm:hidden"
>
<img
v-if="comment.author.avatar"
:src="comment.author.avatar"
:alt="comment.author.displayName || comment.author.handle"
class="w-6 h-6 rounded-full"
width="24"
height="24"
loading="lazy"
/>
<div
v-else
class="w-6 h-6 rounded-full bg-border flex items-center justify-center text-fg-muted text-xs"
>
{{ firstChar(comment.author.displayName || comment.author.handle).toUpperCase() }}
</div>
</a>
<a
:href="`https://bsky.app/profile/${comment.author.handle}`"
target="_blank"
rel="noopener noreferrer"
class="font-medium text-fg hover:underline"
:class="['font-medium text-fg hover:underline', depth > 0 ? 'text-sm' : '']"
>
{{ comment.author.displayName || comment.author.handle }}
</a>
<span class="text-fg-subtle text-sm">@{{ comment.author.handle }}</span>
<span class="text-fg-subtle text-sm">·</span>
<a
:href="getCommentUrl(props.comment)"
Expand All @@ -93,7 +127,7 @@ function getHostname(uri: string): string {
</div>

<!-- Comment text with rich segments -->
<p class="text-fg-muted whitespace-pre-wrap">
<p class="text-fg-muted whitespace-pre-wrap mt-2 mb-3">
<template v-for="(segment, i) in processedSegments" :key="i">
<a
v-if="segment.url"
Expand Down Expand Up @@ -162,7 +196,7 @@ function getHostname(uri: string): string {
<!-- Like/repost counts -->
<div
v-if="comment.likeCount > 0 || comment.repostCount > 0"
class="mt-2 flex gap-4 text-sm text-fg-subtle"
class="mt-1 flex gap-4 text-sm text-fg-subtle"
>
<span v-if="comment.likeCount > 0">
{{ $t('blog.atproto.like_count', { count: comment.likeCount }, comment.likeCount) }}
Expand All @@ -174,7 +208,10 @@ function getHostname(uri: string): string {

<!-- Nested replies -->
<template v-if="comment.replies.length > 0">
<div v-if="depth < MaxDepth" class="mt-2 ps-2 border-is-2 border-border flex flex-col">
<div
v-if="depth < MaxDepth"
class="mt-3 ps-3 border-is-2 border-border flex flex-col gap-3"
>
<BlueskyComment
v-for="reply in comment.replies"
:key="reply.uri"
Expand Down
4 changes: 2 additions & 2 deletions app/components/global/BlogPostFederatedArticles.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const federatedArticles = computed(() => {
</script>

<template>
<aside class="px-4 sm:-mx-6 sm:px-6 sm:-my-3 sm:py-3 sm:rounded-md">
<aside class="sm:-mx-6 sm:px-6 sm:-my-3 sm:py-3 sm:rounded-md">
<h2 class="font-mono text-xl font-medium text-fg mt-0">
{{ headline }}
</h2>
Expand All @@ -55,7 +55,7 @@ const federatedArticles = computed(() => {
rel="noopener noreferrer"
v-for="article in federatedArticles"
:key="article.url"
class="grid grid-cols-[auto_1fr] gap-x-5 no-underline hover:no-underline rounded-lg border border-border p-4 transition-shadow hover:shadow-lg hover:shadow-gray-500/50"
class="grid grid-cols-[auto_1fr] gap-x-5 no-underline hover:no-underline rounded-lg border border-border p-4 transition-all hover:shadow-md hover:shadow-fg/5 hover:border-border-hover"
>
<AuthorAvatar v-if="article.author" :author="article.author" size="md" class="row-span-2" />
<div class="flex flex-col">
Expand Down
17 changes: 16 additions & 1 deletion app/components/global/BlogPostWrapper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const blueskyPostUri = computed(() => blueskyLink.value?.postUri ?? null)
<AuthorList :authors="post.authors" variant="expanded" />
</div>
</div>
<article class="max-w-prose mx-auto p-2 prose dark:prose-invert">
<article class="max-w-prose mx-auto prose dark:prose-invert">
<div class="text-sm text-fg-muted font-mono mb-4">
<DateTime :datetime="frontmatter.date" year="numeric" month="short" day="numeric" />
</div>
Expand All @@ -77,4 +77,19 @@ const blueskyPostUri = computed(() => blueskyLink.value?.postUri ?? null)
:deep(.markdown-body) {
@apply prose dark:prose-invert;
}

:deep(.prose a:not(.not-prose a):not([class*='no-underline'])) {
text-decoration: underline;
text-underline-offset: 0.2rem;
text-decoration-thickness: 1px;
text-decoration-color: var(--fg-subtle);
transition:
text-decoration-color 0.2s,
color 0.2s;
}

:deep(.prose a:not(.not-prose a):not([class*='no-underline']):hover) {
text-decoration-color: var(--fg);
color: var(--fg);
}
</style>
14 changes: 8 additions & 6 deletions app/components/global/BlueskyPostEmbed.client.vue
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,16 @@ const postUrl = computed(() => {
:href="postUrl ?? '#'"
target="_blank"
rel="noopener noreferrer"
class="not-prose block rounded-lg border border-border bg-bg-subtle p-4 sm:p-5 no-underline hover:border-border-hover transition-colors duration-200"
class="not-prose block rounded-lg border border-border bg-bg-subtle p-4 sm:p-5 no-underline hover:border-border-hover transition-colors duration-200 relative group"
>
<!-- Bluesky icon -->
<span
class="i-simple-icons:bluesky w-5 h-5 text-fg-subtle group-hover:text-blue-500 absolute top-4 end-4 sm:top-5 sm:end-5"
aria-hidden="true"
/>

<!-- Author row -->
<div class="flex items-center gap-3 mb-3">
<div class="flex items-center gap-3 mb-3 pe-7">
<img
v-if="post.author.avatar"
:src="`${post.author.avatar}?size=48`"
Expand All @@ -126,10 +132,6 @@ const postUrl = computed(() => {
</div>
<div class="text-sm text-fg-subtle truncate">@{{ post.author.handle }}</div>
</div>
<span
class="i-simple-icons:bluesky w-5 h-5 text-fg-subtle ms-auto shrink-0"
aria-hidden="true"
/>
</div>

<!-- Post text -->
Expand Down
2 changes: 1 addition & 1 deletion i18n/locales/az-AZ.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
"reply_count": "{count} cavab",
"like_count": "{count} bəyənmə",
"repost_count": "{count} yenidən paylaşım",
"more_replies": "{count} cavab daha..."
"more_replies": "daha {count} cavab... | daha {count} cavab..."
}
},
"settings": {
Expand Down
2 changes: 1 addition & 1 deletion i18n/locales/cs-CZ.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
"reply_count": "{count} odpověď | {count} odpovědi | {count} odpovědí",
"like_count": "{count} lajk | {count} lajky | {count} lajků",
"repost_count": "{count} repost | {count} reposty | {count} repostů",
"more_replies": "{count} další odpověď ... | {count} další odpovědi ... | {count} dalších odpovědí ..."
"more_replies": "ještě {count} odpověď… | ještě {count} odpovědi… | ještě {count} odpovědí"
}
},
"settings": {
Expand Down
2 changes: 1 addition & 1 deletion i18n/locales/de-DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
"reply_count": "{count} Antwort | {count} Antworten",
"like_count": "{count} Like | {count} Likes",
"repost_count": "{count} Repost | {count} Reposts",
"more_replies": "{count} weitere Antwort anzeigen | {count} weitere Antworten anzeigen"
"more_replies": "noch {count} Antwort… | noch {count} Antworten"
}
},
"settings": {
Expand Down
2 changes: 1 addition & 1 deletion i18n/locales/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
"reply_count": "{count} réponse | {count} réponses",
"like_count": "{count} j'aime | {count} j'aime",
"repost_count": "{count} repartage | {count} repartages",
"more_replies": "{count} réponse de plus... | {count} réponses de plus..."
"more_replies": "{count} réponse supplémentaire... | {count} réponses supplémentaires..."
}
},
"settings": {
Expand Down
2 changes: 1 addition & 1 deletion i18n/locales/id-ID.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
"reply_count": "{count} balasan | {count} balasan",
"like_count": "{count} suka | {count} suka",
"repost_count": "{count} repost | {count} repost",
"more_replies": "{count} balasan lainnya... | {count} balasan lainnya..."
"more_replies": "{count} balasan lagi... | {count} balasan lagi..."
}
},
"settings": {
Expand Down
2 changes: 1 addition & 1 deletion i18n/locales/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
"reply_count": "{count} 件の返信",
"like_count": "{count} 件のいいね",
"repost_count": "{count} 件のリポスト",
"more_replies": "さらに {count} 件のリプライを表示..."
"more_replies": "さらに{count}件の返信…"
}
},
"settings": {
Expand Down
2 changes: 1 addition & 1 deletion i18n/locales/pl-PL.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
"reply_count": "{count} odpowiedzi | {count} odpowiedź | {count} odpowiedzi | {count} odpowiedzi | {count} odpowiedzi",
"like_count": "{count} polubień | {count} polubienie | {count} polubienia | {count} polubień | {count} polubień",
"repost_count": "{count} udostępnień | {count} udostępnienie | {count} udostępnienia | {count} udostępnień | {count} udostępnień",
"more_replies": "{count} odpowiedzi więcej... | {count} odpowiedź więcej... | {count} odpowiedzi więcej... | {count} odpowiedzi więcej... | {count} odpowiedzi więcej..."
"more_replies": "jeszcze {count} odpowiedź… | jeszcze {count} odpowiedzi… | jeszcze {count} odpowiedzi"
}
},
"settings": {
Expand Down
2 changes: 1 addition & 1 deletion i18n/locales/ru-RU.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
"reply_count": "{count} ответ | {count} ответа | {count} ответов",
"like_count": "{count} лайк | {count} лайка | {count} лайков",
"repost_count": "{count} репост | {count} репоста | {count} репостов",
"more_replies": "Ещё {count} ответ | Ещё {count} ответа | Ещё {count} ответов"
"more_replies": "ещё {count} ответ... | ещё {count} ответа... | ещё {count} ответов..."
}
},
"settings": {
Expand Down
2 changes: 1 addition & 1 deletion i18n/locales/tr-TR.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
"reply_count": "{count} yanıt",
"like_count": "{count} beğeni",
"repost_count": "{count} yeniden paylaşım",
"more_replies": "{count} yanıt daha..."
"more_replies": "{count} yanıt daha... | {count} yanıt daha..."
}
},
"settings": {
Expand Down
2 changes: 1 addition & 1 deletion i18n/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
"reply_count": "{count} 条回复 | {count} 条回复",
"like_count": "{count} 个赞 | {count} 个赞",
"repost_count": "{count} 次转发 | {count} 次转发",
"more_replies": "还有 {count} 条回复... | 还有 {count} 条回复..."
"more_replies": "还有 {count} 条回复..."
}
},
"settings": {
Expand Down
Loading