Spaces:
Running
Running
<script lang="ts"> | |
import FileUpload from "$lib/components/FileUpload.svelte"; | |
import XorbViewer from "$lib/components/XorbViewer.svelte"; | |
import ShardViewer from "$lib/components/ShardViewer.svelte"; | |
import ErrorDisplay from "$lib/components/ErrorDisplay.svelte"; | |
import { parseFile } from "$lib/parsers.js"; | |
import type { ParsedFileMetadata, Chunk, ShardData } from "$lib/types.js"; | |
let parsedData: ParsedFileMetadata | null = null; | |
let loading = false; | |
async function handleFileSelected(event: { | |
file: File; | |
type: "xorb" | "shard"; | |
}) { | |
loading = true; | |
parsedData = null; | |
try { | |
const { file, type } = event; | |
parsedData = await parseFile(file, type); | |
} catch (error) { | |
parsedData = { | |
type: event.type, | |
filename: event.file.name, | |
fileSize: event.file.size, | |
data: {} as any, | |
error: | |
error instanceof Error ? error.message : "Unknown error occurred", | |
}; | |
} finally { | |
loading = false; | |
} | |
} | |
function resetView() { | |
parsedData = null; | |
loading = false; | |
} | |
</script> | |
<svelte:head> | |
<title>XORB & Shard File Viewer</title> | |
<meta | |
name="description" | |
content="Parse and view metadata from XORB and Shard object files" | |
/> | |
</svelte:head> | |
<main> | |
<div class="container"> | |
<header> | |
<h1>π XORB & Shard File Viewer</h1> | |
<p> | |
Upload and analyze XORB or Shard object files to view their metadata | |
structure | |
</p> | |
{#if parsedData} | |
<button class="reset-btn" onclick={resetView}> | |
β Upload New File | |
</button> | |
{/if} | |
</header> | |
{#if loading} | |
<div class="loading"> | |
<div class="spinner"></div> | |
<p>Parsing file...</p> | |
</div> | |
{:else if parsedData} | |
{#if parsedData.error} | |
<ErrorDisplay error={parsedData.error} filename={parsedData.filename} /> | |
{:else if parsedData.type === "xorb"} | |
<XorbViewer | |
data={parsedData.data as Chunk[]} | |
filename={parsedData.filename} | |
fileSize={parsedData.fileSize} | |
/> | |
{:else if parsedData.type === "shard"} | |
<ShardViewer | |
data={parsedData.data as ShardData} | |
filename={parsedData.filename} | |
fileSize={parsedData.fileSize} | |
/> | |
{/if} | |
{:else} | |
<FileUpload fileSelected={handleFileSelected} /> | |
<div class="info-section"> | |
<h2>Supported File Types</h2> | |
<div class="file-types"> | |
<div class="file-type"> | |
<h3>π¦ XORB Files</h3> | |
<p> | |
XORB (Xet Orb) files contain collections of compressed chunks with | |
metadata. The viewer will display chunk information, compression | |
statistics, and structural details. | |
</p> | |
<ul> | |
<li>Chunk count and sizes</li> | |
<li>Compression ratios</li> | |
<li>Hash information</li> | |
<li>Boundary offsets</li> | |
</ul> | |
</div> | |
<div class="file-type"> | |
<h3>ποΈ Shard Files</h3> | |
<p> | |
MDB Shard files store file metadata and content-addressable | |
storage information for efficient deduplication. The viewer shows | |
file and CAS details. | |
</p> | |
<ul> | |
<li>File information entries</li> | |
<li>CAS (Content Addressable Storage) data</li> | |
<li>Lookup tables</li> | |
<li>Timestamps and security keys</li> | |
</ul> | |
</div> | |
</div> | |
</div> | |
{/if} | |
</div> | |
</main> | |
<style> | |
:global(body) { | |
margin: 0; | |
padding: 0; | |
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, | |
sans-serif; | |
background: #f5f7fa; | |
color: #333; | |
line-height: 1.6; | |
} | |
:global(*) { | |
box-sizing: border-box; | |
} | |
main { | |
min-height: 100vh; | |
padding: 20px; | |
} | |
.container { | |
max-width: 1200px; | |
margin: 0 auto; | |
} | |
header { | |
text-align: center; | |
margin-bottom: 40px; | |
position: relative; | |
} | |
header h1 { | |
font-size: 36px; | |
margin: 0 0 16px 0; | |
color: #2c3e50; | |
font-weight: 700; | |
} | |
header p { | |
font-size: 18px; | |
color: #6c757d; | |
margin: 0; | |
} | |
.reset-btn { | |
position: absolute; | |
left: 0; | |
top: 50%; | |
transform: translateY(-50%); | |
background: #007bff; | |
color: white; | |
border: none; | |
padding: 10px 20px; | |
border-radius: 6px; | |
cursor: pointer; | |
font-size: 14px; | |
font-weight: 500; | |
transition: background-color 0.2s; | |
} | |
.reset-btn:hover { | |
background: #0056b3; | |
} | |
.loading { | |
text-align: center; | |
padding: 60px 20px; | |
} | |
.spinner { | |
width: 40px; | |
height: 40px; | |
border: 4px solid #f3f3f3; | |
border-top: 4px solid #007bff; | |
border-radius: 50%; | |
animation: spin 1s linear infinite; | |
margin: 0 auto 20px; | |
} | |
@keyframes spin { | |
0% { | |
transform: rotate(0deg); | |
} | |
100% { | |
transform: rotate(360deg); | |
} | |
} | |
.loading p { | |
color: #6c757d; | |
font-size: 16px; | |
margin: 0; | |
} | |
.info-section { | |
margin-top: 60px; | |
text-align: center; | |
} | |
.info-section h2 { | |
font-size: 24px; | |
color: #2c3e50; | |
margin: 0 0 30px 0; | |
} | |
.file-types { | |
display: grid; | |
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); | |
gap: 30px; | |
margin-top: 30px; | |
} | |
.file-type { | |
background: white; | |
padding: 30px; | |
border-radius: 12px; | |
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); | |
text-align: left; | |
} | |
.file-type h3 { | |
font-size: 20px; | |
margin: 0 0 16px 0; | |
color: #2c3e50; | |
} | |
.file-type p { | |
color: #6c757d; | |
margin: 0 0 20px 0; | |
line-height: 1.6; | |
} | |
.file-type ul { | |
list-style: none; | |
padding: 0; | |
margin: 0; | |
} | |
.file-type li { | |
padding: 8px 0; | |
border-bottom: 1px solid #eee; | |
color: #495057; | |
} | |
.file-type li:before { | |
content: "β"; | |
color: #28a745; | |
font-weight: bold; | |
margin-right: 8px; | |
} | |
.file-type li:last-child { | |
border-bottom: none; | |
} | |
@media (max-width: 768px) { | |
header h1 { | |
font-size: 28px; | |
} | |
header p { | |
font-size: 16px; | |
} | |
.reset-btn { | |
position: static; | |
transform: none; | |
margin-top: 20px; | |
} | |
.file-types { | |
grid-template-columns: 1fr; | |
} | |
.file-type { | |
padding: 20px; | |
} | |
} | |
</style> | |