Skip to content

Commit 083cd63

Browse files
authored
Merge branch 'main' into main
2 parents fc61e2d + 79eeee3 commit 083cd63

File tree

12 files changed

+370
-105
lines changed

12 files changed

+370
-105
lines changed

app/app.vue

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script setup lang="ts">
22
import type { Directions } from '@nuxtjs/i18n'
3-
import { useEventListener } from '@vueuse/core'
3+
import { useEventListener, onKeyDown, onKeyUp } from '@vueuse/core'
44
import { isEditableElement } from '~/utils/input'
55
66
const route = useRoute()
@@ -47,16 +47,12 @@ if (import.meta.server) {
4747
setJsonLd(createWebSiteSchema())
4848
}
4949
50-
// Global keyboard shortcut:
51-
// "/" focuses search or navigates to search page
52-
// "?" highlights all keyboard shortcut elements
53-
function handleGlobalKeydown(e: KeyboardEvent) {
54-
if (isEditableElement(e.target)) return
55-
56-
if (isKeyWithoutModifiers(e, '/')) {
50+
onKeyDown(
51+
'/',
52+
e => {
53+
if (isEditableElement(e.target)) return
5754
e.preventDefault()
5855
59-
// Try to find and focus search input on current page
6056
const searchInput = document.querySelector<HTMLInputElement>(
6157
'input[type="search"], input[name="q"]',
6258
)
@@ -67,18 +63,29 @@ function handleGlobalKeydown(e: KeyboardEvent) {
6763
}
6864
6965
router.push('/search')
70-
}
66+
},
67+
{ dedupe: true },
68+
)
7169
72-
// For "?" we check the key property directly since it's usually combined with shift
73-
if (e.key === '?') {
70+
onKeyDown(
71+
'?',
72+
e => {
73+
if (isEditableElement(e.target)) return
7474
e.preventDefault()
7575
showKbdHints.value = true
76-
}
77-
}
76+
},
77+
{ dedupe: true },
78+
)
7879
79-
function handleGlobalKeyup() {
80-
showKbdHints.value = false
81-
}
80+
onKeyUp(
81+
'?',
82+
e => {
83+
if (isEditableElement(e.target)) return
84+
e.preventDefault()
85+
showKbdHints.value = false
86+
},
87+
{ dedupe: true },
88+
)
8289
8390
// Light dismiss fallback for browsers that don't support closedby="any" (Safari + old Chrome/Firefox)
8491
// https://codepen.io/paramagicdev/pen/gbYompq
@@ -99,9 +106,6 @@ function handleModalLightDismiss(e: MouseEvent) {
99106
}
100107
101108
if (import.meta.client) {
102-
useEventListener(document, 'keydown', handleGlobalKeydown)
103-
useEventListener(document, 'keyup', handleGlobalKeyup)
104-
105109
// Feature check for native light dismiss support via closedby="any"
106110
// https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dialog#closedby
107111
const supportsClosedBy =

app/assets/main.css

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ html {
155155
-moz-osx-font-smoothing: grayscale;
156156
text-rendering: optimizeLegibility;
157157
scroll-padding-top: 5rem; /* Offset for fixed header - otherwise anchor headers are cutted */
158+
scrollbar-gutter: stable;
158159
}
159160

160161
/*
@@ -229,6 +230,10 @@ select:focus-visible {
229230
}
230231

231232
/* Scrollbar styling */
233+
* {
234+
scrollbar-color: var(--border) var(--bg);
235+
}
236+
232237
::-webkit-scrollbar {
233238
width: 8px;
234239
height: 8px;
@@ -244,14 +249,13 @@ select:focus-visible {
244249
}
245250

246251
::-webkit-scrollbar-thumb:hover {
247-
background: #404040;
252+
background: var(--border-hover);
248253
}
249254

250255
/* Scrollbar styling for Firefox */
251256
@supports not selector(::-webkit-scrollbar) {
252257
* {
253258
scrollbar-width: thin;
254-
scrollbar-color: var(--border) var(--bg);
255259
}
256260
}
257261

app/components/ColumnPicker.vue

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script setup lang="ts">
22
import type { ColumnConfig, ColumnId } from '#shared/types/preferences'
3+
import { onKeyDown } from '@vueuse/core'
34
45
const props = defineProps<{
56
columns: ColumnConfig[]
@@ -26,13 +27,16 @@ onClickOutside(
2627
},
2728
)
2829
29-
// Close on Escape key
30-
useEventListener('keydown', event => {
31-
if (event.key === 'Escape' && isOpen.value) {
30+
onKeyDown(
31+
'Escape',
32+
e => {
33+
if (!isOpen.value) return
3234
isOpen.value = false
3335
buttonRef.value?.focus()
34-
}
35-
})
36+
},
37+
{ dedupe: true },
38+
)
39+
3640
// Columns that can be toggled (name is always visible)
3741
const toggleableColumns = computed(() => props.columns.filter(col => col.id !== 'name'))
3842

app/components/DependencyPathPopup.vue

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
<script setup lang="ts">
2+
import { onKeyDown } from '@vueuse/core'
3+
import { UseFocusTrap } from '@vueuse/integrations/useFocusTrap/component'
4+
25
defineProps<{
36
/** Dependency path from root to vulnerable package (readonly from VulnerabilityTreeResult) */
47
path: readonly string[]
@@ -17,13 +20,16 @@ onClickOutside(popupEl, () => {
1720
if (isOpen.value) closePopup()
1821
})
1922
20-
// Close popup on ESC or scroll
21-
function handleKeydown(e: KeyboardEvent) {
22-
if (e.key === 'Escape') closePopup()
23-
}
23+
onKeyDown(
24+
'Escape',
25+
e => {
26+
e.preventDefault()
27+
closePopup()
28+
},
29+
{ dedupe: true, target: popupEl },
30+
)
2431
25-
useEventListener(document, 'keydown', handleKeydown)
26-
useEventListener('scroll', closePopup, true)
32+
useEventListener('scroll', closePopup, { passive: true })
2733
2834
function togglePopup(event: MouseEvent) {
2935
if (isOpen.value) {
@@ -77,34 +83,36 @@ function parsePackageString(pkg: string): { name: string; version: string } {
7783
class="fixed z-[100] bg-bg-elevated border border-border rounded-lg shadow-xl p-3 min-w-64 max-w-sm"
7884
:style="getPopupStyle()"
7985
>
80-
<ul class="list-none m-0 p-0 space-y-0.5">
81-
<li
82-
v-for="(pathItem, idx) in path"
83-
:key="idx"
84-
class="font-mono text-xs"
85-
:style="{ paddingLeft: `${idx * 12}px` }"
86-
>
87-
<span v-if="idx > 0" class="text-fg-subtle me-1">└─</span>
88-
<NuxtLink
89-
:to="{
90-
name: 'package',
91-
params: {
92-
package: [
93-
...parsePackageString(pathItem).name.split('/'),
94-
'v',
95-
parsePackageString(pathItem).version,
96-
],
97-
},
98-
}"
99-
class="hover:underline"
100-
:class="idx === path.length - 1 ? 'text-fg font-medium' : 'text-fg-muted'"
101-
@click="closePopup"
86+
<UseFocusTrap :options="{ immediate: true }">
87+
<ul class="list-none m-0 p-0 space-y-0.5">
88+
<li
89+
v-for="(pathItem, idx) in path"
90+
:key="idx"
91+
class="font-mono text-xs"
92+
:style="{ paddingLeft: `${idx * 12}px` }"
10293
>
103-
{{ pathItem }}
104-
</NuxtLink>
105-
<span v-if="idx === path.length - 1" class="ms-1 text-amber-500">⚠</span>
106-
</li>
107-
</ul>
94+
<span v-if="idx > 0" class="text-fg-subtle me-1">└─</span>
95+
<NuxtLink
96+
:to="{
97+
name: 'package',
98+
params: {
99+
package: [
100+
...parsePackageString(pathItem).name.split('/'),
101+
'v',
102+
parsePackageString(pathItem).version,
103+
],
104+
},
105+
}"
106+
class="hover:underline"
107+
:class="idx === path.length - 1 ? 'text-fg font-medium' : 'text-fg-muted'"
108+
@click="closePopup"
109+
>
110+
{{ pathItem }}
111+
</NuxtLink>
112+
<span v-if="idx === path.length - 1" class="ms-1 text-amber-500">⚠</span>
113+
</li>
114+
</ul>
115+
</UseFocusTrap>
108116
</div>
109117
</div>
110118
</template>

app/components/Readme.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ function handleClick(event: MouseEvent) {
8585
/* Contain all children */
8686
overflow: hidden;
8787
min-width: 0;
88+
/* Contain all children z-index values inside this container */
89+
isolation: isolate;
8890
}
8991
9092
/* README headings - styled by visual level (data-level), not semantic level */
@@ -402,6 +404,8 @@ function handleClick(event: MouseEvent) {
402404
display: revert-layer;
403405
border-radius: 8px;
404406
margin: 1rem 0;
407+
position: relative;
408+
z-index: 1;
405409
}
406410
407411
.readme :deep(video) {

app/components/ReadmeTocDropdown.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ function handleScroll(event: Event) {
6969
}
7070
close()
7171
}
72-
useEventListener('scroll', handleScroll, true)
72+
useEventListener('scroll', handleScroll, { passive: true })
7373
7474
// Generate unique ID for accessibility
7575
const inputId = useId()

app/composables/useRepoMeta.ts

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -566,43 +566,18 @@ const tangledAdapter: ProviderAdapter = {
566566
},
567567

568568
async fetchMeta(cachedFetch, ref, links, options = {}) {
569-
// Tangled doesn't have a public JSON API, but we can scrape the star count
570-
// from the HTML page (it's in the hx-post URL as countHint=N)
571569
try {
572-
const { data: html } = await cachedFetch<string>(
573-
`https://tangled.org/${ref.owner}/${ref.repo}`,
574-
{
575-
headers: { 'User-Agent': 'npmx', 'Accept': 'text/html', ...options.headers },
576-
...options,
577-
},
570+
const { data } = await cachedFetch<{ stars: number; forks: number }>(
571+
`/api/atproto/tangled-stats/${ref.owner}/${ref.repo}`,
572+
options,
578573
REPO_META_TTL,
579574
)
580-
// Extracts the at-uri used in atproto
581-
const atUriMatch = html.match(/data-star-subject-at="([^"]+)"/)
582-
// Extract star count from: hx-post="/star?subject=...&countHint=23"
583-
const starMatch = html.match(/countHint=(\d+)/)
584-
//We'll set the stars from tangled's repo page and may override it with constellation if successful
585-
let stars = starMatch?.[1] ? parseInt(starMatch[1], 10) : 0
586-
let forks = 0
587-
const atUri = atUriMatch?.[1]
588-
589-
if (atUri) {
590-
try {
591-
const constellation = new Constellation(cachedFetch)
592-
//Get counts of records that reference this repo in the atmosphere using constellation
593-
const { data: allLinks } = await constellation.getAllLinks(atUri)
594-
stars = allLinks.links['sh.tangled.feed.star']?.['.subject']?.distinct_dids ?? stars
595-
forks = allLinks.links['sh.tangled.repo']?.['.source']?.distinct_dids ?? stars
596-
} catch {
597-
//failing silently since this is just an enhancement to the information already showing
598-
}
599-
}
600575

601576
return {
602577
provider: 'tangled',
603578
url: links.repo,
604-
stars,
605-
forks,
579+
stars: data.stars,
580+
forks: data.forks,
606581
links,
607582
}
608583
} catch {

app/pages/package/[...package].vue

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,23 +1344,23 @@ onKeyStroke(
13441344
@media (min-width: 1024px) {
13451345
.sidebar-scroll {
13461346
scrollbar-gutter: stable;
1347-
scrollbar-width: none;
1347+
scrollbar-width: 8px;
1348+
scrollbar-color: transparent transparent;
13481349
}
13491350
13501351
.sidebar-scroll::-webkit-scrollbar {
1351-
width: 0;
1352-
height: 0;
1352+
width: 8px;
1353+
height: 8px;
13531354
}
13541355
1355-
.sidebar-scroll:hover,
1356-
.sidebar-scroll:focus-within {
1357-
scrollbar-width: auto;
1356+
.sidebar-scroll::-webkit-scrollbar-track,
1357+
.sidebar-scroll::-webkit-scrollbar-thumb {
1358+
background: transparent;
13581359
}
13591360
1360-
.sidebar-scroll:hover::-webkit-scrollbar,
1361-
.sidebar-scroll:focus-within::-webkit-scrollbar {
1362-
width: 8px;
1363-
height: 8px;
1361+
.sidebar-scroll:hover,
1362+
.sidebar-scroll:focus-within {
1363+
scrollbar-color: var(--border) transparent;
13641364
}
13651365
13661366
.sidebar-scroll:hover::-webkit-scrollbar-thumb,

0 commit comments

Comments
 (0)