add binary view

This commit is contained in:
Salem Yaslem 2024-11-04 09:12:02 +03:00
parent c8209c5799
commit 5a739b543d
4 changed files with 157 additions and 5 deletions

View File

@ -5,6 +5,7 @@ import { useDisplay } from 'vuetify/lib/framework.mjs';
import { HistoryManager } from './composables/history-manager';
import { FilesManager } from './composables/files-manager';
import type { iFile } from "composables/worker/7zip-manager"
import { videoExtensions, binaryExtensions } from '#imports';
let display = useDisplay();
let drawer = ref(!display.mdAndDown.value);
@ -57,6 +58,8 @@ watchEffect(async () => {
const file = filesManager.getFile(selectedPath.value);
filesGridList.value = file?.content;
console.log("Selected file", file);
selectedList.value = [];
for (const selectedElement of document.querySelectorAll(".selectable.selected")) {
selectedElement.classList.remove("selected");
@ -193,9 +196,13 @@ function stepUp(path: string) {
<MediaVideoPlayer :src="mediaBlobUrl"></MediaVideoPlayer>
</template>
<template
v-if="!filesManager.getFile(selectedPath)?.isFolder && files.length && !videoExtensions.includes(filesManager.getFile(selectedPath)?.extension)">
v-if="!filesManager.getFile(selectedPath)?.isFolder && files.length && !videoExtensions.includes(filesManager.getFile(selectedPath)?.extension) && !binaryExtensions.includes(filesManager.getFile(selectedPath)?.extension)">
<TextEditor :file="filesManager.getFile(selectedPath)" :filesManager="filesManager"></TextEditor>
</template>
<template
v-if="!filesManager.getFile(selectedPath)?.isFolder && files.length && binaryExtensions.includes(filesManager.getFile(selectedPath)?.extension)">
<BinaryViewer :file="filesManager.getFile(selectedPath)" :filesManager="filesManager"></BinaryViewer>
</template>
<template v-if="!files.length">
<!-- tutorial drag and drop zipped file here and review it securely -->
<v-row align="center" justify="center">

View File

@ -0,0 +1,144 @@
<script lang="ts" setup>
import { ref, onMounted, computed } from 'vue'
import { FilesManager } from '~/composables/files-manager';
import { iFile } from '~/composables/worker/7zip-manager';
interface Props {
file: iFile,
filesManager: FilesManager
}
const { file, filesManager } = defineProps<Props>()
const buffer = ref<Uint8Array>()
const containerRef = ref<HTMLElement>()
const isLoading = ref(true)
const rowHeight = 24 // pixels per row
const visibleRows = ref(0)
const scrollTop = ref(0)
// Calculate total rows and visible window
const totalRows = computed(() => buffer.value ? Math.ceil(buffer.value.length / 16) : 0)
const startRow = computed(() => Math.floor(scrollTop.value / rowHeight))
const endRow = computed(() => Math.min(startRow.value + visibleRows.value + 5, totalRows.value))
// Get only the visible rows data
const visibleData = computed(() => {
if (!buffer.value) return []
const rows = []
for (let i = startRow.value; i < endRow.value; i++) {
const offset = i * 16
const chunk = buffer.value.slice(offset, offset + 16)
const hex = Array.from(chunk).map(b => b.toString(16).padStart(2, '0')).join(' ')
const ascii = Array.from(chunk).map(b => b >= 32 && b <= 126 ? String.fromCharCode(b) : '.').join('')
rows.push({
address: offset.toString(16).padStart(8, '0'),
hex: hex.padEnd(48, ' '),
ascii
})
}
return rows
})
// Handle scroll events
function onScroll(event: Event) {
const container = event.target as HTMLElement
scrollTop.value = container.scrollTop
}
// Calculate visible rows based on container height
function updateVisibleRows() {
if (containerRef.value) {
visibleRows.value = Math.ceil(containerRef.value.clientHeight / rowHeight)
}
}
onMounted(async () => {
try {
buffer.value = await filesManager.getFileContent(file.path, "binary") as Uint8Array
updateVisibleRows()
window.addEventListener('resize', updateVisibleRows)
} finally {
isLoading.value = false
}
})
</script>
<template>
<div class="binary-viewer">
<div v-if="isLoading" class="loading">
Loading...
</div>
<div
v-else
ref="containerRef"
class="hex-container"
@scroll="onScroll"
>
<div
class="scroll-content"
:style="{
height: `${totalRows * rowHeight}px`,
paddingTop: `${startRow * rowHeight}px`
}"
>
<div
v-for="row in visibleData"
:key="row.address"
class="hex-line"
>
<span class="address">{{ row.address }}</span>
<span class="hex">{{ row.hex }}</span>
<span class="ascii">{{ row.ascii }}</span>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.binary-viewer {
font-family: monospace;
white-space: pre;
padding: 1rem;
background-color: var(--v-theme-surface);
height: calc(100vh - 150px);
}
.hex-container {
height: 100%;
overflow-y: auto;
position: relative;
}
.scroll-content {
position: absolute;
width: 100%;
}
.hex-line {
line-height: 1.5;
height: v-bind(rowHeight + 'px');
}
.address {
color: #666;
margin-right: 1rem;
}
.hex {
margin-right: 1rem;
}
.ascii {
color: #666;
}
.loading {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
</style>

View File

@ -10,7 +10,8 @@ import mime from 'mime';
export const videoExtensions = ['mp4', 'avi', 'mov', 'mkv'];
export const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
export const audioExtensions = ['mp3', 'wav', 'ogg', 'flac'];
export const textExtensions = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'md', 'js', 'ts', 'php', 'c', 'cpp', 'py', 'html', 'css', 'scss', 'sass', 'less', 'json', 'xml', 'sql', 'java', 'go', 'rb', 'sh', 'bat', 'ps1', 'cmd', 'yml', 'yaml', 'ini', 'toml', 'csv', 'tsv', 'gitignore', 'lock', 'htaccess', 'htpasswd', 'env', 'dockerfile', 'gitattributes', 'gitmodules', 'editorconfig', 'babelrc', 'eslintrc', 'eslintignore', 'prettierrc', 'prettierignore', 'stylelintrc', 'stylelintignore', 'postcssrc', 'postcss.config', 'jsx', 'tsx', 'license']
export const textExtensions = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'md', 'js', 'ts', 'php', 'c', 'cpp', 'py', 'html', 'css', 'scss', 'sass', 'less', 'json', 'xml', 'sql', 'java', 'go', 'rb', 'sh', 'bat', 'ps1', 'cmd', 'yml', 'yaml', 'ini', 'toml', 'csv', 'tsv', 'gitignore', 'lock', 'htaccess', 'htpasswd', 'env', 'dockerfile', 'gitattributes', 'gitmodules', 'editorconfig', 'babelrc', 'eslintrc', 'eslintignore', 'prettierrc', 'prettierignore', 'stylelintrc', 'stylelintignore', 'postcssrc', 'postcss.config', 'jsx', 'tsx', 'license'];
export const binaryExtensions = ['exe', 'dll', 'so', 'dylib', 'bin', 'dat', 'db', 'sqlite', 'o', 'class', 'pyc'];
export class FilesManager {
consoleOutputBuffer: string[] = [];

6
package-lock.json generated
View File

@ -2662,9 +2662,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001487",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001487.tgz",
"integrity": "sha512-83564Z3yWGqXsh2vaH/mhXfEM0wX+NlBCm1jYHOb97TrTWJEmPTccZgeLTPBUUb0PNVo+oomb7wkimZBIERClA==",
"version": "1.0.30001677",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz",
"integrity": "sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog==",
"dev": true,
"funding": [
{