xet-object-viewer / src /lib /components /XorbViewer.svelte
assafvayner's picture
assafvayner HF Staff
copy from app in xet-core
e2a9158
raw
history blame
6.2 kB
<script lang="ts">
import type { Chunk } from "../types.js";
import { formatBytes } from "../parsers.js";
export let data: Chunk[];
export let filename: string;
export let fileSize: number;
$: totalChunks = data.length;
$: totalCompressedSize = data.reduce(
(sum, chunk) => sum + chunk.header.compressed_size,
0
);
$: totalUncompressedSize = data.reduce(
(sum, chunk) => sum + chunk.header.uncompressed_size,
0
);
$: averageChunkSize =
totalChunks > 0 ? totalUncompressedSize / totalChunks : 0;
$: compressionRatio =
totalUncompressedSize > 0
? (totalCompressedSize / totalUncompressedSize) * 100
: 0;
function getCompressionTypeName(type: number): string {
switch (type) {
case 0:
return "None";
case 1:
return "LZ4";
case 2:
return "Zstd";
case 3:
return "Gzip";
default:
return `Unknown (${type})`;
}
}
</script>
<div class="xorb-viewer">
<div class="header">
<h2>📦 XORB File Analysis</h2>
<div class="file-info">
<span class="filename">{filename}</span>
<span class="file-size">{formatBytes(fileSize)}</span>
</div>
</div>
<div class="metadata-grid">
<div class="metadata-section">
<h3>File Information</h3>
<div class="metadata-item">
<span class="label">File Size:</span>
<span class="value">{formatBytes(fileSize)}</span>
</div>
<div class="metadata-item">
<span class="label">Total Chunks:</span>
<span class="value">{totalChunks.toLocaleString()}</span>
</div>
</div>
<div class="metadata-section">
<h3>Compression Statistics</h3>
<div class="metadata-item">
<span class="label">Total Compressed Size:</span>
<span class="value">{formatBytes(totalCompressedSize)}</span>
</div>
<div class="metadata-item">
<span class="label">Total Uncompressed Size:</span>
<span class="value">{formatBytes(totalUncompressedSize)}</span>
</div>
<div class="metadata-item">
<span class="label">Overall Compression Ratio:</span>
<span class="value">{compressionRatio.toFixed(1)}%</span>
</div>
<div class="metadata-item">
<span class="label">Average Chunk Size:</span>
<span class="value">{formatBytes(averageChunkSize)}</span>
</div>
</div>
</div>
{#if data.length > 0}
<div class="chunk-details">
<h3>Chunk Headers ({data.length} chunks)</h3>
<div class="chunk-table-container">
<table class="chunk-table">
<thead>
<tr>
<th>Index</th>
<th>Version</th>
<th>Compression Type</th>
<th>Compressed Size</th>
<th>Uncompressed Size</th>
<th>Compression Ratio</th>
</tr>
</thead>
<tbody>
{#each data as chunk, i}
<tr>
<td>{i}</td>
<td>{chunk.header.version}</td>
<td>{getCompressionTypeName(chunk.header.compression_type)}</td>
<td>{formatBytes(chunk.header.compressed_size)}</td>
<td>{formatBytes(chunk.header.uncompressed_size)}</td>
<td>
{chunk.header.uncompressed_size > 0
? (
(chunk.header.compressed_size /
chunk.header.uncompressed_size) *
100
).toFixed(1) + "%"
: "N/A"}
</td>
</tr>
{/each}
</tbody>
</table>
</div>
</div>
{/if}
</div>
<style>
.xorb-viewer {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 2px solid #eee;
}
.header h2 {
margin: 0;
color: #333;
font-size: 24px;
}
.file-info {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 4px;
}
.filename {
font-weight: 600;
color: #555;
}
.file-size {
font-size: 14px;
color: #888;
}
.metadata-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.metadata-section {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
border: 1px solid #e9ecef;
}
.metadata-section h3 {
margin: 0 0 15px 0;
color: #495057;
font-size: 18px;
font-weight: 600;
}
.metadata-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
padding: 8px 0;
border-bottom: 1px solid #e9ecef;
}
.metadata-item:last-child {
border-bottom: none;
margin-bottom: 0;
}
.metadata-item .label {
font-weight: 500;
color: #6c757d;
margin-right: 10px;
}
.metadata-item .value {
font-family: "Monaco", "Consolas", monospace;
font-size: 14px;
color: #212529;
text-align: right;
}
.chunk-details {
margin-top: 30px;
}
.chunk-details h3 {
margin: 0 0 20px 0;
color: #495057;
font-size: 18px;
font-weight: 600;
}
.chunk-table-container {
overflow-x: auto;
border-radius: 8px;
border: 1px solid #e9ecef;
}
.chunk-table {
width: 100%;
border-collapse: collapse;
background: white;
}
.chunk-table th,
.chunk-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #e9ecef;
}
.chunk-table th {
background: #f8f9fa;
font-weight: 600;
color: #495057;
}
.chunk-table tr:hover {
background: #f8f9fa;
}
@media (max-width: 768px) {
.header {
flex-direction: column;
align-items: flex-start;
gap: 10px;
}
.metadata-grid {
grid-template-columns: 1fr;
}
.metadata-item {
flex-direction: column;
align-items: flex-start;
gap: 4px;
}
.metadata-item .value {
text-align: left;
}
}
</style>