Spaces:
Running
Running
| <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> | |