Skip to content

Commit a739616

Browse files
authored
Merge branch 'main' into light-theme
2 parents 7204a44 + 21ed847 commit a739616

5 files changed

Lines changed: 571 additions & 69 deletions

File tree

app/components/PackageCard.vue

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
defineProps<{
2+
const props = defineProps<{
33
/** The search result object containing package data */
44
result: NpmSearchResult
55
/** Heading level for the package name (h2 for search, h3 for lists) */
@@ -9,8 +9,18 @@ defineProps<{
99
prefetch?: boolean
1010
selected?: boolean
1111
index?: number
12+
/** Search query for highlighting exact matches */
13+
searchQuery?: string
1214
}>()
1315
16+
/** Check if this package is an exact match for the search query */
17+
const isExactMatch = computed(() => {
18+
if (!props.searchQuery) return false
19+
const query = props.searchQuery.trim().toLowerCase()
20+
const name = props.result.package.name.toLowerCase()
21+
return query === name
22+
})
23+
1424
const emit = defineEmits<{
1525
focus: [index: number]
1626
}>()
@@ -19,8 +29,17 @@ const emit = defineEmits<{
1929
<template>
2030
<article
2131
class="group card-interactive scroll-mt-48 scroll-mb-6 relative focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-bg focus-within:ring-offset-2 focus-within:ring-fg/50"
22-
:class="{ 'bg-bg-muted border-border-hover': selected }"
32+
:class="{
33+
'bg-bg-muted border-border-hover': selected,
34+
'border-accent/30 bg-accent/5': isExactMatch,
35+
}"
2336
>
37+
<!-- Glow effect for exact matches -->
38+
<div
39+
v-if="isExactMatch"
40+
class="absolute -inset-px rounded-lg bg-gradient-to-r from-accent/0 via-accent/20 to-accent/0 opacity-100 blur-sm -z-1 pointer-events-none motion-reduce:opacity-50"
41+
aria-hidden="true"
42+
/>
2443
<div class="mb-2 flex items-baseline justify-between gap-2">
2544
<component
2645
:is="headingLevel ?? 'h3'"
@@ -37,6 +56,13 @@ const emit = defineEmits<{
3756
{{ result.package.name }}
3857
</NuxtLink>
3958
</component>
59+
<!-- Exact match badge -->
60+
<span
61+
v-if="isExactMatch"
62+
class="shrink-0 text-xs px-1.5 py-0.5 rounded bg-accent/20 border border-accent/30 text-accent font-mono"
63+
>
64+
{{ $t('search.exact_match') }}
65+
</span>
4066
<!-- Mobile: version next to package name -->
4167
<div class="sm:hidden text-fg-subtle flex items-center gap-1.5 shrink-0">
4268
<span

app/components/PackageList.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ const props = defineProps<{
2020
initialPage?: number
2121
/** Selected result index (for keyboard navigation) */
2222
selectedIndex?: number
23+
/** Search query for highlighting exact matches */
24+
searchQuery?: string
2325
}>()
2426
2527
const emit = defineEmits<{
@@ -111,6 +113,7 @@ defineExpose({
111113
:show-publisher="showPublisher"
112114
:selected="index === (selectedIndex ?? -1)"
113115
:index="index"
116+
:search-query="searchQuery"
114117
class="motion-safe:animate-fade-in motion-safe:animate-fill-both"
115118
:style="{ animationDelay: `${Math.min(index * 0.02, 0.3)}s` }"
116119
@focus="emit('select', $event)"
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<script setup lang="ts">
2+
defineProps<{
3+
/** Type of suggestion: 'user' or 'org' */
4+
type: 'user' | 'org'
5+
/** The name (username or org name) */
6+
name: string
7+
/** Whether this suggestion is currently selected (keyboard nav) */
8+
selected?: boolean
9+
/** Whether this is an exact match for the query */
10+
isExactMatch?: boolean
11+
/** Index for keyboard navigation */
12+
index?: number
13+
}>()
14+
15+
const emit = defineEmits<{
16+
focus: [index: number]
17+
}>()
18+
</script>
19+
20+
<template>
21+
<article
22+
class="group card-interactive relative focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-bg focus-within:ring-offset-2 focus-within:ring-fg/50"
23+
:class="{
24+
'bg-bg-muted border-border-hover': selected,
25+
'border-accent/30 bg-accent/5': isExactMatch,
26+
}"
27+
>
28+
<!-- Glow effect for exact matches -->
29+
<div
30+
v-if="isExactMatch"
31+
class="absolute -inset-px rounded-lg bg-gradient-to-r from-accent/0 via-accent/20 to-accent/0 opacity-100 blur-sm -z-1 pointer-events-none motion-reduce:opacity-50"
32+
aria-hidden="true"
33+
/>
34+
<NuxtLink
35+
:to="type === 'user' ? `/~${name}` : `/@${name}`"
36+
:data-suggestion-index="index"
37+
class="flex items-center gap-4 focus-visible:outline-none after:content-[''] after:absolute after:inset-0"
38+
@focus="index != null && emit('focus', index)"
39+
@mouseenter="index != null && emit('focus', index)"
40+
>
41+
<!-- Avatar placeholder -->
42+
<div
43+
class="w-10 h-10 shrink-0 flex items-center justify-center border border-border"
44+
:class="type === 'org' ? 'rounded-lg bg-bg-muted' : 'rounded-full bg-bg-muted'"
45+
aria-hidden="true"
46+
>
47+
<span class="text-lg text-fg-subtle font-mono">{{ name.charAt(0).toUpperCase() }}</span>
48+
</div>
49+
50+
<div class="min-w-0 flex-1">
51+
<div class="flex items-center gap-2">
52+
<span
53+
class="font-mono text-sm sm:text-base font-medium text-fg group-hover:text-fg transition-colors"
54+
>
55+
{{ type === 'user' ? '~' : '@' }}{{ name }}
56+
</span>
57+
<span
58+
class="text-xs px-1.5 py-0.5 rounded bg-bg-muted border border-border text-fg-muted font-mono"
59+
>
60+
{{ type === 'user' ? $t('search.suggestion.user') : $t('search.suggestion.org') }}
61+
</span>
62+
<!-- Exact match badge -->
63+
<span
64+
v-if="isExactMatch"
65+
class="text-xs px-1.5 py-0.5 rounded bg-accent/20 border border-accent/30 text-accent font-mono"
66+
>
67+
{{ $t('search.exact_match') }}
68+
</span>
69+
</div>
70+
<p class="text-xs sm:text-sm text-fg-muted mt-0.5">
71+
{{
72+
type === 'user'
73+
? $t('search.suggestion.view_user_packages')
74+
: $t('search.suggestion.view_org_packages')
75+
}}
76+
</p>
77+
</div>
78+
79+
<span
80+
class="i-carbon-arrow-right w-4 h-4 text-fg-subtle group-hover:text-fg transition-colors shrink-0"
81+
aria-hidden="true"
82+
/>
83+
</NuxtLink>
84+
</article>
85+
</template>

0 commit comments

Comments
 (0)