$(document).ready(function() {
const $input = $('#binary-input');
const $output = $('#binary-output');
const $error = $('#binary-error');
let mode = 'protobuf';
let lastDecoded = null;
const wireTypeName = (t) => ({ 0: 'varint', 1: 'fixed64', 2: 'len-delim', 5: 'fixed32' }[t] || `type ${t}`);
const renderProtoField = (f) => {
const v = f.value;
if (v.kind === 'bytes' && v.nested && v.nested.length) {
return `<details class="ml-1" open>
<summary class="cursor-pointer text-blue-300 hover:text-blue-200">Field ${f.field_number} <span class="text-gray-600">(${wireTypeName(f.wire_type)}, ${v.nested.length} sub-field${v.nested.length !== 1 ? 's' : ''})</span></summary>
<div class="ml-3 border-l border-gray-800 pl-3 mt-0.5">${v.nested.map(renderProtoField).join('')}</div>
</details>`;
}
let valueHtml;
if (v.kind === 'varint') {
valueHtml = `<span class="text-emerald-400">${v.value}</span>`;
} else if (v.kind === 'fixed64' || v.kind === 'fixed32') {
valueHtml = `<span class="text-emerald-400">${v.value}</span>`;
} else if (v.kind === 'bytes') {
valueHtml = v.text
? `<span class="text-amber-300">"${window.escHtml(v.text)}"</span>`
: `<span class="text-gray-400">0x${v.hex}</span>`;
} else {
valueHtml = '<span class="text-gray-600">?</span>';
}
return `<div class="py-0.5"><span class="text-blue-400 font-bold">Field ${f.field_number}</span> <span class="text-gray-600">(${wireTypeName(f.wire_type)})</span> → ${valueHtml}</div>`;
};
const decode = async () => {
const hex = $input.val().trim();
$error.addClass('hidden');
if (!hex) {
$output.html('');
lastDecoded = null;
return;
}
try {
if (mode === 'protobuf') {
const fields = await window.tauriApi.decodeProtobuf(hex);
lastDecoded = fields;
$output.html(fields.length
? fields.map(renderProtoField).join('')
: '<div class="text-gray-600 italic">No fields decoded.</div>');
} else {
const value = await window.tauriApi.decodeMsgpack(hex);
lastDecoded = value;
$output.html(`<pre class="whitespace-pre-wrap break-all">${window.escHtml(JSON.stringify(value, null, 2))}</pre>`);
}
} catch (e) {
lastDecoded = null;
$output.html('');
$error.removeClass('hidden').text('Decode error: ' + e);
}
};
$input.on('input', decode);
$('.binary-mode-btn').on('click', function() {
mode = $(this).data('mode');
$('.binary-mode-btn').removeClass('active text-blue-400 border-blue-500').addClass('text-gray-500 border-transparent hover:text-gray-300');
$(this).addClass('active text-blue-400 border-blue-500').removeClass('text-gray-500 border-transparent hover:text-gray-300');
decode();
});
$('#binary-clear-btn').on('click', () => {
$input.val('').focus();
$output.html('');
$error.addClass('hidden');
lastDecoded = null;
});
$('#binary-copy-btn').on('click', function() {
if (lastDecoded !== null) window.copyToClipboard(JSON.stringify(lastDecoded, null, 2), $(this));
});
$('#binary-file-btn').on('click', async () => {
try {
const filePath = await window.tauriApi.openFileDialog();
if (filePath) {
const hex = await window.tauriApi.readFileHex(filePath);
$input.val(hex.match(/.{1,2}/g).join(' '));
decode();
}
} catch (e) {
console.error('Binary file open error', e);
$error.removeClass('hidden').text('File error: ' + e);
}
});
});