Skip to content

Commit d3cfce5

Browse files
authored
feat: add binary file warning (#1959)
1 parent 3de681d commit d3cfce5

File tree

4 files changed

+105
-3
lines changed

4 files changed

+105
-3
lines changed

app/pages/package-code/[[org]]/[packageName]/v/[version]/[...filePath].vue

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type {
44
PackageFileTreeResponse,
55
PackageFileContentResponse,
66
} from '#shared/types'
7+
import { isBinaryFilePath } from '~/utils/file-types'
78
89
definePageMeta({
910
name: 'code',
@@ -105,6 +106,9 @@ const isViewingFile = computed(() => currentNode.value?.type === 'file')
105106
106107
// Maximum file size we'll try to load (500KB) - must match server
107108
const MAX_FILE_SIZE = 500 * 1024
109+
110+
const isBinaryFile = computed(() => !!filePath.value && isBinaryFilePath(filePath.value))
111+
108112
const isFileTooLarge = computed(() => {
109113
const size = currentNode.value?.size
110114
return size !== undefined && size > MAX_FILE_SIZE
@@ -113,7 +117,13 @@ const isFileTooLarge = computed(() => {
113117
// Fetch file content when a file is selected (and not too large)
114118
const fileContentUrl = computed(() => {
115119
// Don't fetch if no file path, file tree not loaded, file is too large, or it's a directory
116-
if (!filePath.value || !fileTree.value || isFileTooLarge.value || !isViewingFile.value) {
120+
if (
121+
!filePath.value ||
122+
!fileTree.value ||
123+
isFileTooLarge.value ||
124+
!isViewingFile.value ||
125+
isBinaryFile.value
126+
) {
117127
return null
118128
}
119129
return `/api/registry/file/${packageName.value}/v/${version.value}/${filePath.value}`
@@ -426,7 +436,7 @@ defineOgImageComponent('Default', {
426436
ref="contentContainer"
427437
>
428438
<!-- File viewer -->
429-
<template v-if="isViewingFile && fileContent">
439+
<template v-if="isViewingFile && !isBinaryFile && fileContent">
430440
<div
431441
class="sticky z-10 top-0 bg-bg border-b border-border px-4 py-2 flex items-center justify-between"
432442
>
@@ -519,6 +529,19 @@ defineOgImageComponent('Default', {
519529
/>
520530
</template>
521531

532+
<!-- Binary file warning -->
533+
<div v-else-if="isViewingFile && isBinaryFile" class="py-20 text-center">
534+
<div class="i-lucide:binary w-12 h-12 mx-auto text-fg-subtle mb-4" />
535+
<p class="text-fg-muted mb-2">{{ $t('code.binary_file') }}</p>
536+
<p class="text-fg-subtle text-sm mb-4">{{ $t('code.binary_rendering_warning') }}</p>
537+
<LinkBase
538+
variant="button-secondary"
539+
:to="`https://cdn.jsdelivr.net/npm/${packageName}@${version}/${filePath}`"
540+
>
541+
{{ $t('code.view_raw') }}
542+
</LinkBase>
543+
</div>
544+
522545
<!-- File too large warning -->
523546
<div v-else-if="isViewingFile && isFileTooLarge" class="py-20 text-center">
524547
<div class="i-lucide:file-text w-12 h-12 mx-auto text-fg-subtle mb-4" />

app/utils/file-types.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Extensions that are binary and cannot be meaningfully displayed as text
2+
const BINARY_EXTENSIONS = new Set([
3+
// Images
4+
'png',
5+
'jpg',
6+
'jpeg',
7+
'gif',
8+
'webp',
9+
'ico',
10+
'bmp',
11+
'tiff',
12+
'tif',
13+
'avif',
14+
'heic',
15+
'heif',
16+
// Fonts
17+
'woff',
18+
'woff2',
19+
'ttf',
20+
'otf',
21+
'eot',
22+
// Archives
23+
'zip',
24+
'tar',
25+
'gz',
26+
'tgz',
27+
'bz2',
28+
'xz',
29+
'7z',
30+
'rar',
31+
// Executables / compiled
32+
'exe',
33+
'dll',
34+
'so',
35+
'dylib',
36+
'node',
37+
'wasm',
38+
'pyc',
39+
'class',
40+
// Media
41+
'mp3',
42+
'mp4',
43+
'ogg',
44+
'wav',
45+
'avi',
46+
'mov',
47+
'webm',
48+
'flac',
49+
'aac',
50+
'mkv',
51+
// Documents
52+
'pdf',
53+
'doc',
54+
'docx',
55+
'xls',
56+
'xlsx',
57+
'ppt',
58+
'pptx',
59+
// Data
60+
'bin',
61+
'dat',
62+
'db',
63+
'sqlite',
64+
'sqlite3',
65+
])
66+
67+
export function isBinaryFilePath(filePath: string): boolean {
68+
const dotIndex = filePath.lastIndexOf('.')
69+
const ext = dotIndex > -1 ? filePath.slice(dotIndex + 1).toLowerCase() : ''
70+
return BINARY_EXTENSIONS.has(ext)
71+
}

i18n/locales/en.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -792,7 +792,9 @@
792792
"code": "code"
793793
},
794794
"file_path": "File path",
795-
"scroll_to_top": "Scroll to top"
795+
"scroll_to_top": "Scroll to top",
796+
"binary_file": "Binary file",
797+
"binary_rendering_warning": "File type not supported for preview."
796798
},
797799
"badges": {
798800
"provenance": {

i18n/schema.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2382,6 +2382,12 @@
23822382
},
23832383
"scroll_to_top": {
23842384
"type": "string"
2385+
},
2386+
"binary_file": {
2387+
"type": "string"
2388+
},
2389+
"binary_rendering_warning": {
2390+
"type": "string"
23852391
}
23862392
},
23872393
"additionalProperties": false

0 commit comments

Comments
 (0)