Skip to content

Commit 4e69d98

Browse files
committed
feat: render markdown in code view
1 parent cbfc01e commit 4e69d98

11 files changed

Lines changed: 99 additions & 8 deletions

File tree

app/pages/code/[...path].vue

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,20 @@ const canonicalUrl = computed(() => {
243243
return url
244244
})
245245
246+
// Toggle markdown view mode
247+
const markdownViewModes = [
248+
{
249+
label: $t('code.markdown_view_mode.preview'),
250+
icon: 'i-carbon-view',
251+
},
252+
{
253+
label: $t('code.markdown_view_mode.code'),
254+
icon: 'i-carbon-code',
255+
},
256+
] as const
257+
258+
const markdownViewMode = ref<(typeof markdownViewModes)[number]['label']>('preview')
259+
246260
useHead({
247261
link: [{ rel: 'canonical', href: canonicalUrl }],
248262
})
@@ -359,15 +373,38 @@ useSeoMeta({
359373
<!-- File viewer -->
360374
<template v-if="isViewingFile && fileContent">
361375
<div
362-
class="sticky top-0 bg-bg border-b border-border px-4 py-2 flex items-center justify-between"
376+
class="sticky z-10 top-0 bg-bg border-b border-border px-4 py-2 flex items-center justify-between"
363377
>
364-
<div class="flex items-center gap-3 text-sm">
365-
<span class="text-fg-muted">{{
366-
$t('code.lines', { count: fileContent.lines })
367-
}}</span>
368-
<span v-if="currentNode?.size" class="text-fg-subtle">{{
369-
formatBytes(currentNode.size)
370-
}}</span>
378+
<div class="flex items-center gap-2">
379+
<div
380+
v-if="fileContent.markdownHtml"
381+
class="flex items-center gap-1 p-0.5 bg-bg-subtle border border-border-subtle rounded-md overflow-x-auto"
382+
role="tablist"
383+
aria-label="Markdown view mode selector"
384+
>
385+
<button
386+
v-for="mode in markdownViewModes"
387+
:key="mode.label"
388+
class="px-2 py-1.5 font-mono text-xs rounded transition-colors duration-150 border border-solid focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 inline-flex items-center gap-1.5"
389+
:class="
390+
markdownViewMode === mode.label
391+
? 'bg-bg shadow text-fg border-border'
392+
: 'text-fg-subtle hover:text-fg border-transparent'
393+
"
394+
@click="markdownViewMode = mode.label"
395+
>
396+
<span class="inline-block h-3 w-3" :class="mode.icon" aria-hidden="true" />
397+
{{ mode.label }}
398+
</button>
399+
</div>
400+
<div class="flex items-center gap-3 text-sm">
401+
<span class="text-fg-muted">{{
402+
$t('code.lines', { count: fileContent.lines })
403+
}}</span>
404+
<span v-if="currentNode?.size" class="text-fg-subtle">{{
405+
formatBytes(currentNode.size)
406+
}}</span>
407+
</div>
371408
</div>
372409
<div class="flex items-center gap-2">
373410
<button
@@ -389,7 +426,19 @@ useSeoMeta({
389426
</a>
390427
</div>
391428
</div>
429+
<div
430+
v-if="fileContent.markdownHtml"
431+
v-show="markdownViewMode === 'preview'"
432+
class="flex justify-center p-4"
433+
>
434+
<div
435+
class="readme-content prose prose-invert max-w-[70ch]"
436+
v-html="fileContent.markdownHtml.html"
437+
></div>
438+
</div>
439+
392440
<CodeViewer
441+
v-show="!fileContent.markdownHtml || markdownViewMode === 'code'"
393442
:html="fileContent.html"
394443
:lines="fileContent.lines"
395444
:selected-lines="selectedLines"

i18n/locales/ar.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,10 @@
532532
"table": {
533533
"name": "الاسم",
534534
"size": "الحجم"
535+
},
536+
"markdown_view_mode": {
537+
"preview": "معاينة",
538+
"code": "الكود"
535539
}
536540
},
537541
"badges": {

i18n/locales/de-DE.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,10 @@
532532
"table": {
533533
"name": "Name",
534534
"size": "Größe"
535+
},
536+
"markdown_view_mode": {
537+
"preview": "Vorschau",
538+
"code": "Code"
535539
}
536540
},
537541
"badges": {

i18n/locales/en.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,10 @@
546546
"table": {
547547
"name": "Name",
548548
"size": "Size"
549+
},
550+
"markdown_view_mode": {
551+
"preview": "preview",
552+
"code": "code"
549553
}
550554
},
551555
"badges": {

i18n/locales/es.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,10 @@
537537
"table": {
538538
"name": "Nombre",
539539
"size": "Tamaño"
540+
},
541+
"markdown_view_mode": {
542+
"preview": "vista previa",
543+
"code": "código"
540544
}
541545
},
542546
"badges": {

i18n/locales/fr-FR.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,10 @@
537537
"table": {
538538
"name": "Nom",
539539
"size": "Taille"
540+
},
541+
"markdown_view_mode": {
542+
"preview": "aperçu",
543+
"code": "code"
540544
}
541545
},
542546
"badges": {

i18n/locales/it-IT.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,10 @@
537537
"table": {
538538
"name": "Nome",
539539
"size": "Dimensione"
540+
},
541+
"markdown_view_mode": {
542+
"preview": "anteprima",
543+
"code": "codice"
540544
}
541545
},
542546
"filters": {

i18n/locales/ja-JP.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,10 @@
537537
"table": {
538538
"name": "名前",
539539
"size": "サイズ"
540+
},
541+
"markdown_view_mode": {
542+
"preview": "プレビュー",
543+
"code": "コード"
540544
}
541545
},
542546
"badges": {

i18n/locales/zh-CN.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,10 @@
502502
"table": {
503503
"name": "名称",
504504
"size": "大小"
505+
},
506+
"markdown_view_mode": {
507+
"preview": "预览",
508+
"code": "代码"
505509
}
506510
},
507511
"badges": {

server/api/registry/file/[...pkg].get.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,13 @@ export default defineCachedEventHandler(
166166
resolveRelative,
167167
})
168168

169+
let markdownHtml: ReadmeResponse | undefined
170+
if (language === 'markdown') {
171+
const packageData = await fetchNpmPackage(rawPackageName)
172+
const repoInfo = parseRepositoryInfo(packageData.repository)
173+
markdownHtml = await renderReadmeHtml(content, rawPackageName, repoInfo)
174+
}
175+
169176
return {
170177
package: packageName,
171178
version,
@@ -174,6 +181,7 @@ export default defineCachedEventHandler(
174181
content,
175182
html,
176183
lines: content.split('\n').length,
184+
markdownHtml,
177185
}
178186
} catch (error: unknown) {
179187
handleApiError(error, {

0 commit comments

Comments
 (0)