11<script setup lang="ts">
22import { SEVERITY_LEVELS } from ' #shared/types'
33import { SEVERITY_COLORS } from ' #shared/utils/severity'
4- import { useVulnerabilityTree } from ' ~/composables/useVulnerabilityTree'
54
65const props = defineProps <{
76 packageName: string
87 version: string
98}>()
109
10+ const { t } = useI18n ()
11+
1112const {
1213 data : vulnTree,
1314 status,
14- error,
1515 fetch : fetchVulnTree,
1616} = useVulnerabilityTree (
1717 () => props .packageName ,
@@ -27,33 +27,30 @@ const hasVulnerabilities = computed(
2727 () => vulnTree .value && vulnTree .value .vulnerablePackages .length > 0 ,
2828)
2929
30- // Banner
31- const bannerColor = ' border-yellow-500/30 bg-yellow -500/10 text-yellow -400'
30+ // Banner - amber for better light mode contrast
31+ const bannerColor = ' border-amber-600/40 bg-amber -500/10 text-amber-700 dark:text-amber -400'
3232
3333const summaryText = computed (() => {
3434 if (! vulnTree .value ) return ' '
3535 const { totalCounts } = vulnTree .value
3636 return SEVERITY_LEVELS .filter (s => totalCounts [s ] > 0 )
37- .map (s => ` ${totalCounts [s ]} ${s } ` )
37+ .map (s => ` ${totalCounts [s ]} ${t ( ` package.vulnerabilities.severity.${ s } ` ) } ` )
3838 .join (' , ' )
3939})
4040
41- // Styling for each depth level - using design tokens for accessible contrast
41+ // Styling for each depth level - using accessible colors for both themes
4242const depthStyles = {
4343 root: {
44- bg: ' bg-yellow -500/5 border-l-2 border-l-yellow-500 ' ,
44+ bg: ' bg-amber -500/5 border-l-2 border-l-amber-600 ' ,
4545 text: ' text-fg' ,
46- tooltip: ' This package' ,
4746 },
4847 direct: {
49- bg: ' bg-orange -500/5 border-l-2 border-l-orange -500' ,
48+ bg: ' bg-amber -500/5 border-l-2 border-l-amber -500' ,
5049 text: ' text-fg-muted' ,
51- tooltip: ' Direct dependency' ,
5250 },
5351 transitive: {
54- bg: ' bg-orange -500/5 border-l-2 border-l-orange-700 ' ,
52+ bg: ' bg-amber -500/5 border-l-2 border-l-amber-400 ' ,
5553 text: ' text-fg-muted' ,
56- tooltip: ' Transitive dependency (indirect)' ,
5754 },
5855} as const
5956
@@ -67,7 +64,11 @@ function getDepthStyle(depth: string | undefined) {
6764 </script >
6865
6966<template >
70- <section v-if =" status === 'success' && hasVulnerabilities" aria-labelledby =" vuln-tree-heading" >
67+ <section
68+ v-if =" status === 'success' && hasVulnerabilities"
69+ aria-labelledby =" vuln-tree-heading"
70+ class =" relative"
71+ >
7172 <!-- Collapsible vulnerability banner -->
7273 <div role =" alert" class =" rounded-lg border overflow-hidden" :class =" bannerColor" >
7374 <!-- Header -->
@@ -81,9 +82,17 @@ function getDepthStyle(depth: string | undefined) {
8182 <div class =" flex items-center gap-2 min-w-0" >
8283 <span class =" i-carbon-warning-alt w-4 h-4 shrink-0" aria-hidden =" true" />
8384 <span class =" font-mono text-sm font-medium truncate" >
84- {{ vulnTree!.totalCounts.total }}
85- {{ vulnTree!.totalCounts.total === 1 ? 'vulnerability' : 'vulnerabilities' }}
86- in {{ vulnTree!.vulnerablePackages.length }}/{{ vulnTree!.totalPackages }} packages
85+ {{
86+ t(
87+ 'package.vulnerabilities.tree_found',
88+ {
89+ vulns: vulnTree!.totalCounts.total,
90+ packages: vulnTree!.vulnerablePackages.length,
91+ total: vulnTree!.totalPackages,
92+ },
93+ vulnTree!.totalCounts.total,
94+ )
95+ }}
8796 </span >
8897 </div >
8998 <div class =" flex items-center gap-2 shrink-0" >
@@ -106,26 +115,16 @@ function getDepthStyle(depth: string | undefined) {
106115 :class =" getDepthStyle(pkg.depth).bg"
107116 >
108117 <div class =" flex items-center justify-between gap-2 mb-2" >
109- <div class =" flex items-center gap-2 min-w-0" >
110- <!-- Depth indicator icon with tooltip -->
111- <span
112- v-if =" pkg.depth === 'transitive'"
113- class =" i-carbon-tree-view w-3.5 h-3.5 shrink-0 cursor-help"
114- :class =" getDepthStyle(pkg.depth).text"
115- :title =" getDepthStyle(pkg.depth).tooltip"
116- />
117- <span
118- v-else-if =" pkg.depth === 'direct'"
119- class =" i-carbon-arrow-right w-3.5 h-3.5 shrink-0 cursor-help"
120- :class =" getDepthStyle(pkg.depth).text"
121- :title =" getDepthStyle(pkg.depth).tooltip"
122- />
118+ <div class =" flex items-center gap-2 min-w-0 relative" >
119+ <!-- Path badge - click to show tree popup -->
120+ <DependencyPathPopup v-if =" pkg.path && pkg.path.length > 1" :path =" pkg.path" />
121+
123122 <NuxtLink
124123 :to =" {
125124 name: 'package',
126125 params: { package: [...pkg.name.split('/'), 'v', pkg.version] },
127126 }"
128- class =" font-mono text-sm font-medium hover:underline truncate"
127+ class =" font-mono text-sm font-medium hover:underline truncate shrink min-w-0 "
129128 :class =" getDepthStyle(pkg.depth).text"
130129 >
131130 {{ pkg.name }}@{{ pkg.version }}
@@ -138,7 +137,7 @@ function getDepthStyle(depth: string | undefined) {
138137 class =" px-1.5 py-0.5 text-[10px] font-mono rounded border"
139138 :class =" SEVERITY_COLORS[s]"
140139 >
141- {{ pkg.counts[s] }} {{ s }}
140+ {{ pkg.counts[s] }} {{ t(`package.vulnerabilities.severity.${s}`) }}
142141 </span >
143142 </div >
144143 </div >
@@ -160,7 +159,7 @@ function getDepthStyle(depth: string | undefined) {
160159 <span class =" truncate" >{{ vuln.summary }}</span >
161160 </li >
162161 <li v-if =" pkg.vulnerabilities.length > 2" class =" text-xs text-fg-subtle" >
163- + {{ pkg.vulnerabilities.length - 2 }} more
162+ {{ t('package.vulnerabilities.more', { count: pkg.vulnerabilities.length - 2 }) }}
164163 </li >
165164 </ul >
166165 </li >
@@ -172,13 +171,26 @@ function getDepthStyle(depth: string | undefined) {
172171 class =" w-full px-4 py-2 text-xs font-mono text-fg-muted hover:text-fg border-t border-border transition-colors duration-200"
173172 @click =" showAllPackages = true"
174173 >
175- show all {{ vulnTree!.vulnerablePackages.length }} affected packages
174+ {{
175+ t('package.vulnerabilities.show_all_packages', {
176+ count: vulnTree!.vulnerablePackages.length,
177+ })
178+ }}
176179 </button >
180+
181+ <!-- Warning if some queries failed -->
182+ <div
183+ v-if =" vulnTree!.failedQueries"
184+ class =" px-4 py-2 text-xs text-fg-subtle border-t border-border flex items-center gap-2"
185+ >
186+ <span class =" i-carbon-warning w-3 h-3" aria-hidden =" true" />
187+ <span >{{ t('package.vulnerabilities.packages_failed', vulnTree!.failedQueries) }}</span >
188+ </div >
177189 </div >
178190 </div >
179191 </section >
180192
181- <!-- Loading state -->
193+ <!-- Loading state - muted - ->
182194 <section
183195 v-else-if =" status === 'pending' || status === 'idle'"
184196 aria-labelledby =" vuln-tree-loading"
@@ -189,34 +201,41 @@ function getDepthStyle(depth: string | undefined) {
189201 class =" i-carbon-circle-dash w-4 h-4 animate-spin motion-reduce:animate-none text-fg-subtle"
190202 aria-hidden =" true"
191203 />
192- <span class =" text-sm text-fg-subtle " >Scanning dependency tree... </span >
204+ <span class =" text-sm text-fg-muted " >{{ t('package.vulnerabilities.scanning_tree') }} </span >
193205 </div >
194206 </div >
195207 </section >
196208
197- <!-- No vulnerabilities found -->
209+ <!-- No vulnerabilities found - muted, not attention-grabbing - ->
198210 <section
199211 v-else-if =" status === 'success' && !hasVulnerabilities"
200212 aria-labelledby =" vuln-tree-success"
201213 >
202- <div class =" rounded-lg border border-green-500/30 bg-green-500/10 px-4 py-3" >
214+ <div class =" rounded-lg border border-border bg-bg-subtle px-4 py-3" >
203215 <div class =" flex items-center gap-2" >
204- <span class =" i-carbon-checkmark-filled w-4 h-4 text-green-400 " aria-hidden =" true" />
205- <span class =" text-sm text-green-400 " >
206- No known vulnerabilities in {{ vulnTree?.totalPackages ?? 0 }} packages
216+ <span class =" i-carbon-checkmark w-4 h-4 text-fg-subtle " aria-hidden =" true" />
217+ <span class =" text-sm text-fg-muted " >
218+ {{ t('package. vulnerabilities.no_known', { count: vulnTree?.totalPackages ?? 0 }) }}
207219 </span >
208220 </div >
221+ <!-- Warning if some queries failed -->
222+ <div
223+ v-if =" vulnTree?.failedQueries"
224+ class =" flex items-center gap-2 mt-2 text-xs text-fg-subtle"
225+ >
226+ <span class =" i-carbon-warning w-3 h-3" aria-hidden =" true" />
227+ <span >{{ t('package.vulnerabilities.packages_failed', vulnTree.failedQueries) }}</span >
228+ </div >
209229 </div >
210230 </section >
211231
212- <!-- Error state -->
232+ <!-- Error state - subtle, not alarming - ->
213233 <section v-else-if =" status === 'error'" aria-labelledby =" vuln-tree-error" >
214- <div class =" rounded-lg border border-red-500/30 bg-red-500/10 px-4 py-3" >
234+ <div class =" rounded-lg border border-border bg-bg-subtle px-4 py-3" >
215235 <div class =" flex items-center gap-2" >
216- <span class =" i-carbon-warning-alt w-4 h-4 text-red-400" aria-hidden =" true" />
217- <span class =" text-sm text-red-400" >
218- Failed to scan vulnerabilities
219- <template v-if =" error ?.message " >: {{ error.message }}</template >
236+ <span class =" i-carbon-warning w-4 h-4 text-fg-subtle" aria-hidden =" true" />
237+ <span class =" text-sm text-fg-muted" >
238+ {{ t('package.vulnerabilities.scan_failed') }}
220239 </span >
221240 </div >
222241 </div >
0 commit comments