Skip to content

Commit bb8b09f

Browse files
authored
Merge branch 'main' into feat/changelog-1
2 parents ddd4121 + f9c8e9c commit bb8b09f

22 files changed

Lines changed: 587 additions & 83 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ test-results/
4040
# generated files
4141
shared/types/lexicons
4242
file-tree-sprite.svg
43+
public/blog/avatar
4344

4445
**/__screenshots__/**
4546

app/components/AuthorList.vue

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
<script setup lang="ts">
2-
import type { Author } from '#shared/schemas/blog'
2+
import type { ResolvedAuthor } from '#shared/schemas/blog'
33
4-
const props = defineProps<{
5-
authors: Author[]
4+
defineProps<{
5+
authors: ResolvedAuthor[]
66
variant?: 'compact' | 'expanded'
77
}>()
8-
9-
const { resolvedAuthors } = useBlueskyAuthorProfiles(props.authors)
108
</script>
119

1210
<template>
1311
<!-- Expanded variant: vertical list with larger avatars -->
1412
<div v-if="variant === 'expanded'" class="flex flex-wrap items-center gap-4">
15-
<div v-for="author in resolvedAuthors" :key="author.name" class="flex items-center gap-2">
13+
<div v-for="author in authors" :key="author.name" class="flex items-center gap-2">
1614
<AuthorAvatar :author="author" size="md" disable-link />
1715
<div class="flex flex-col">
1816
<span class="text-sm font-medium text-fg">{{ author.name }}</span>
@@ -34,7 +32,7 @@ const { resolvedAuthors } = useBlueskyAuthorProfiles(props.authors)
3432
<div v-else class="flex items-center gap-2 min-w-0">
3533
<div class="flex items-center">
3634
<AuthorAvatar
37-
v-for="(author, index) in resolvedAuthors"
35+
v-for="(author, index) in authors"
3836
:key="author.name"
3937
:author="author"
4038
size="md"
@@ -43,7 +41,7 @@ const { resolvedAuthors } = useBlueskyAuthorProfiles(props.authors)
4341
/>
4442
</div>
4543
<span class="text-xs text-fg-muted font-mono truncate">
46-
{{ resolvedAuthors.map(a => a.name).join(', ') }}
44+
{{ authors.map(a => a.name).join(', ') }}
4745
</span>
4846
</div>
4947
</template>

app/components/BlogPostListCard.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<script setup lang="ts">
2-
import type { Author } from '#shared/schemas/blog'
2+
import type { ResolvedAuthor } from '#shared/schemas/blog'
33
44
defineProps<{
55
/** Authors of the blog post */
6-
authors: Author[]
6+
authors: ResolvedAuthor[]
77
/** Blog Title */
88
title: string
99
/** Tags such as OpenSource, Architecture, Community, etc. */

app/components/Code/Viewer.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ watch(
115115
<!-- Code content -->
116116
<div class="code-content flex-1 overflow-x-auto min-w-0">
117117
<!-- eslint-disable vue/no-v-html -- HTML is generated server-side by Shiki -->
118-
<div ref="codeRef" class="code-lines w-fit" v-html="html" />
118+
<div ref="codeRef" class="code-lines min-w-full w-fit" v-html="html" />
119119
<!-- eslint-enable vue/no-v-html -->
120120
</div>
121121
</div>
@@ -157,7 +157,7 @@ watch(
157157
158158
/* Highlighted lines in code content - extend full width with negative margin */
159159
.code-content :deep(.line.highlighted) {
160-
background: rgb(234 179 8 / 0.2); /* yellow-500/20 */
160+
@apply bg-yellow-500/20;
161161
margin: 0 -1rem;
162162
padding: 0 1rem;
163163
}

app/components/Header/SearchBox.vue

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,17 @@ const showSearchBar = computed(() => {
1717
})
1818
1919
const { model: searchQuery, startSearch } = useGlobalSearch('header')
20+
const hasSearchQuery = computed(() => searchQuery.value.trim().length > 0)
2021
2122
function handleSubmit() {
2223
startSearch()
2324
}
2425
26+
function clearSearch() {
27+
searchQuery.value = ''
28+
inputRef.value?.focus()
29+
}
30+
2531
// Expose focus method for parent components
2632
const inputRef = useTemplateRef('inputRef')
2733
function focus() {
@@ -52,11 +58,21 @@ defineExpose({ focus })
5258
name="q"
5359
:placeholder="$t('search.placeholder')"
5460
no-correct
55-
class="w-full min-w-25 ps-7"
61+
class="w-full min-w-25 ps-7 pe-8"
5662
@focus="isSearchFocused = true"
5763
@blur="isSearchFocused = false"
5864
size="small"
5965
/>
66+
<button
67+
v-if="hasSearchQuery"
68+
type="button"
69+
class="absolute inset-ie-2 h-6 w-6 items-center justify-center rounded text-fg-muted hover:text-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent group-focus-within:flex group-hover:inline-flex hidden"
70+
@click="clearSearch"
71+
aria-hidden="true"
72+
tabindex="-1"
73+
>
74+
<span class="i-lucide:circle-x h-4 w-4" />
75+
</button>
6076
<button type="submit" class="sr-only">{{ $t('search.button') }}</button>
6177
</div>
6278
</div>

app/components/OgImage/BlogPost.vue

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
<script setup lang="ts">
2-
import type { Author } from '#shared/schemas/blog'
2+
import type { ResolvedAuthor } from '#shared/schemas/blog'
33
44
const props = withDefaults(
55
defineProps<{
66
title: string
7-
authors?: Author[]
7+
authors?: ResolvedAuthor[]
88
date?: string
99
primaryColor?: string
1010
}>(),
@@ -15,8 +15,6 @@ const props = withDefaults(
1515
},
1616
)
1717
18-
const { resolvedAuthors } = useBlueskyAuthorProfiles(props.authors)
19-
2018
const formattedDate = computed(() => {
2119
if (!props.date) return ''
2220
try {
@@ -41,17 +39,17 @@ const getInitials = (name: string) =>
4139
.slice(0, 2)
4240
4341
const visibleAuthors = computed(() => {
44-
if (resolvedAuthors.value.length <= 3) return resolvedAuthors.value
45-
return resolvedAuthors.value.slice(0, MAX_VISIBLE_AUTHORS)
42+
if (props.authors.length <= 3) return props.authors
43+
return props.authors.slice(0, MAX_VISIBLE_AUTHORS)
4644
})
4745
4846
const extraCount = computed(() => {
49-
if (resolvedAuthors.value.length <= 3) return 0
50-
return resolvedAuthors.value.length - MAX_VISIBLE_AUTHORS
47+
if (props.authors.length <= 3) return 0
48+
return props.authors.length - MAX_VISIBLE_AUTHORS
5149
})
5250
5351
const formattedAuthorNames = computed(() => {
54-
const allNames = resolvedAuthors.value.map(a => a.name)
52+
const allNames = props.authors.map(a => a.name)
5553
if (allNames.length === 0) return ''
5654
if (allNames.length === 1) return allNames[0]
5755
if (allNames.length === 2) return `${allNames[0]} and ${allNames[1]}`
@@ -96,7 +94,7 @@ const formattedAuthorNames = computed(() => {
9694

9795
<!-- Authors -->
9896
<div
99-
v-if="resolvedAuthors.length"
97+
v-if="authors.length"
10098
class="flex items-center gap-4 self-start justify-start flex-nowrap"
10199
style="font-family: 'Geist', sans-serif"
102100
>

app/components/Package/Dependencies.vue

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,8 @@ const numberFormatter = useNumberFormatter()
218218
:key="peer.name"
219219
class="flex items-center justify-between py-1 text-sm gap-1 min-w-0"
220220
>
221-
<div class="flex items-center gap-1 min-w-0 flex-1">
222-
<LinkBase :to="packageRoute(peer.name)" class="block truncate" dir="ltr">
221+
<div class="flex items-center gap-2 min-w-0 flex-1">
222+
<LinkBase :to="packageRoute(peer.name)" class="block max-w-[70%] break-words" dir="ltr">
223223
{{ peer.name }}
224224
</LinkBase>
225225
<TagStatic v-if="peer.optional" :title="$t('package.dependencies.optional')">
@@ -228,7 +228,7 @@ const numberFormatter = useNumberFormatter()
228228
</div>
229229
<LinkBase
230230
:to="packageRoute(peer.name, peer.version)"
231-
class="block truncate max-w-[40%]"
231+
class="block truncate max-w-[30%]"
232232
:title="peer.version"
233233
dir="ltr"
234234
>
@@ -278,9 +278,9 @@ const numberFormatter = useNumberFormatter()
278278
optionalDepsExpanded ? undefined : 10,
279279
)"
280280
:key="dep"
281-
class="flex items-center justify-between py-1 text-sm gap-2"
281+
class="flex items-baseline justify-between py-1 text-sm gap-2"
282282
>
283-
<LinkBase :to="packageRoute(dep)" class="block truncate" dir="ltr">
283+
<LinkBase :to="packageRoute(dep)" class="block max-w-[80%] break-words" dir="ltr">
284284
{{ dep }}
285285
</LinkBase>
286286
<LinkBase

app/components/Package/Playgrounds.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const providerIcons: Record<string, string> = {
2121
'svelte-playground': 'i-simple-icons:svelte',
2222
'tailwind-playground': 'i-simple-icons:tailwindcss',
2323
'storybook': 'i-simple-icons:storybook',
24+
'marko-playground': 'i-simple-icons:marko',
2425
}
2526
2627
// Map provider id to color class
@@ -39,6 +40,7 @@ const providerColors: Record<string, string> = {
3940
'svelte-playground': 'text-provider-svelte',
4041
'tailwind-playground': 'text-provider-tailwind',
4142
'storybook': 'text-provider-storybook',
43+
'marko-playground': 'text-provider-marko',
4244
}
4345
4446
function getIcon(provider: string): string {

app/components/Package/WeeklyDownloadStats.vue

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -413,10 +413,7 @@ const config = computed<VueUiSparklineConfig>(() => {
413413
<span v-else-if="isLoadingWeeklyDownloads" class="min-w-6 min-h-6 -m-1 p-1" />
414414
</template>
415415

416-
<div
417-
class="w-full overflow-hidden h-[76px] egg-pulse-target"
418-
:class="{ 'egg-pulse': eggPulse }"
419-
>
416+
<div class="w-full h-[76px] egg-pulse-target" :class="{ 'egg-pulse': eggPulse }">
420417
<template v-if="isLoadingWeeklyDownloads || hasWeeklyDownloads">
421418
<ClientOnly>
422419
<VueUiSparkline class="w-full max-w-xs" :dataset :config>

app/components/global/BlogPostWrapper.vue

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
<script setup lang="ts">
2-
import type { BlogPostFrontmatter } from '#shared/schemas/blog'
2+
import type { RawBlogPostFrontmatter } from '#shared/schemas/blog'
33
import { generateBlogTID } from '#shared/utils/atproto'
4+
import { posts } from '#blog/posts'
45
56
const props = defineProps<{
6-
frontmatter: BlogPostFrontmatter
7+
frontmatter: RawBlogPostFrontmatter
78
}>()
89
10+
const post = computed(() => posts.find(p => p.slug === props.frontmatter.slug))
11+
912
useSeoMeta({
1013
title: props.frontmatter.title,
1114
description: props.frontmatter.description || props.frontmatter.excerpt,
@@ -26,7 +29,7 @@ useHead({
2629
2730
defineOgImageComponent('BlogPost', {
2831
title: props.frontmatter.title,
29-
authors: props.frontmatter.authors,
32+
authors: post.value?.authors ?? [],
3033
date: props.frontmatter.date,
3134
})
3235
@@ -50,9 +53,9 @@ const blueskyPostUri = computed(() => blueskyLink.value?.postUri ?? null)
5053
</span>
5154
</div>
5255
</div>
53-
<div v-if="frontmatter.authors" class="mb-12 max-w-prose mx-auto">
56+
<div v-if="post?.authors" class="mb-12 max-w-prose mx-auto">
5457
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
55-
<AuthorList :authors="frontmatter.authors" variant="expanded" />
58+
<AuthorList :authors="post.authors" variant="expanded" />
5659
</div>
5760
</div>
5861
<article class="max-w-prose mx-auto p-2 prose dark:prose-invert">

0 commit comments

Comments
 (0)