@@ -5,6 +5,7 @@ import { SCROLL_TO_TOP_THRESHOLD } from '~/composables/useScrollToTop'
55import { useModal } from ' ~/composables/useModal'
66import { useAtproto } from ' ~/composables/atproto/useAtproto'
77import { togglePackageLike } from ' ~/utils/atproto/likes'
8+ import { isEditableElement } from ' ~/utils/input'
89
910const props = defineProps <{
1011 pkg: Pick <SlimPackument , ' name' | ' versions' | ' dist-tags' > | null
@@ -13,10 +14,7 @@ const props = defineProps<{
1314 latestVersion: SlimVersion | null
1415 provenanceData: ProvenanceDetails | null
1516 provenanceStatus: string
16- docsLink: RouteLocationRaw | null
17- codeLink: RouteLocationRaw | null
1817 page: ' readme' | ' docs' | ' code' | ' diff'
19- versionUrlPattern: string
2018}>()
2119
2220const { requestedVersion, orgName } = usePackageRoute ()
@@ -80,6 +78,113 @@ function hasProvenance(version: PackumentVersion | null): boolean {
8078 return !! (version .dist as { attestations? : unknown }).attestations
8179}
8280
81+ const router = useRouter ()
82+ // Docs URL: use our generated API docs
83+ const docsLink = computed (() => {
84+ if (! props .resolvedVersion ) return null
85+
86+ return {
87+ name: ' docs' as const ,
88+ params: {
89+ path: [props .pkg ?.name ?? ' ' , ' v' , props .resolvedVersion ] satisfies [string , string , string ],
90+ },
91+ }
92+ })
93+
94+ const codeLink = computed ((): RouteLocationRaw | null => {
95+ if (props .pkg == null || props .resolvedVersion == null ) {
96+ return null
97+ }
98+ const split = props .pkg .name .split (' /' )
99+ return {
100+ name: ' code' ,
101+ params: {
102+ org: split .length === 2 ? split [0 ] : undefined ,
103+ packageName: split .length === 2 ? split [1 ]! : split [0 ]! ,
104+ version: props .resolvedVersion ,
105+ filePath: ' ' ,
106+ },
107+ }
108+ })
109+
110+ const readmeLink = computed ((): RouteLocationRaw | null => {
111+ if (props .pkg == null || props .resolvedVersion == null ) {
112+ return null
113+ }
114+ return packageRoute (props .pkg .name , props .resolvedVersion )
115+ })
116+
117+ const diffLink = computed ((): RouteLocationRaw | null => {
118+ if (
119+ props .pkg == null ||
120+ props .resolvedVersion == null ||
121+ props .latestVersion == null ||
122+ props .latestVersion .version === props .resolvedVersion
123+ ) {
124+ return null
125+ }
126+ return diffRoute (props .pkg .name , props .resolvedVersion , props .latestVersion .version )
127+ })
128+
129+ // URL pattern for version selector - includes file path if present
130+ const versionUrlPattern = computed (
131+ () => ` /package/${props .pkg ?.name || packageName .value }/v/{version} ` ,
132+ )
133+
134+ const keyboardShortcuts = useKeyboardShortcuts ()
135+
136+ onKeyStroke (
137+ e => keyboardShortcuts .value && isKeyWithoutModifiers (e , ' .' ) && ! isEditableElement (e .target ),
138+ e => {
139+ if (codeLink .value === null ) return
140+ e .preventDefault ()
141+
142+ navigateTo (codeLink .value )
143+ },
144+ { dedupe: true },
145+ )
146+
147+ onKeyStroke (
148+ e => keyboardShortcuts .value && isKeyWithoutModifiers (e , ' r' ) && ! isEditableElement (e .target ),
149+ e => {
150+ if (readmeLink .value === null ) return
151+ e .preventDefault ()
152+
153+ navigateTo (readmeLink .value )
154+ },
155+ { dedupe: true },
156+ )
157+
158+ onKeyStroke (
159+ e => keyboardShortcuts .value && isKeyWithoutModifiers (e , ' d' ) && ! isEditableElement (e .target ),
160+ e => {
161+ if (! docsLink .value ) return
162+ e .preventDefault ()
163+ navigateTo (docsLink .value )
164+ },
165+ { dedupe: true },
166+ )
167+
168+ onKeyStroke (
169+ e => keyboardShortcuts .value && isKeyWithoutModifiers (e , ' c' ) && ! isEditableElement (e .target ),
170+ e => {
171+ if (! props .pkg ) return
172+ e .preventDefault ()
173+ router .push ({ name: ' compare' , query: { packages: props .pkg .name } })
174+ },
175+ { dedupe: true },
176+ )
177+
178+ onKeyStroke (
179+ e => keyboardShortcuts .value && isKeyWithoutModifiers (e , ' f' ) && ! isEditableElement (e .target ),
180+ e => {
181+ if (diffLink .value === null ) return
182+ e .preventDefault ()
183+ navigateTo (diffLink .value )
184+ },
185+ { dedupe: true },
186+ )
187+
83188// atproto
84189// TODO: Maybe set this where it's not loaded here every load?
85190const { user } = useAtproto ()
@@ -288,8 +393,8 @@ const likeAction = async () => {
288393 :class =" $style.packageNav"
289394 >
290395 <LinkBase
291- v-if =" docsLink "
292- :to =" docsLink "
396+ v-if =" readmeLink "
397+ :to =" readmeLink "
293398 aria-keyshortcuts =" r"
294399 class =" decoration-none border-b-2 p-1 hover:border-accent/50 lowercase"
295400 :class =" page === 'readme' ? 'border-accent text-accent!' : 'border-transparent'"
@@ -315,8 +420,8 @@ const likeAction = async () => {
315420 {{ $t('package.links.code') }}
316421 </LinkBase >
317422 <LinkBase
318- v-if =" displayVersion && latestVersion && displayVersion.version !== latestVersion.version "
319- :to =" diffRoute(packageName, displayVersion.version, latestVersion.version) "
423+ v-if =" diffLink "
424+ :to =" diffLink "
320425 :title =" $t('compare.compare_versions_title')"
321426 aria-keyshortcuts =" f"
322427 class =" decoration-none border-b-2 p-1 hover:border-accent/50"
0 commit comments