bit-twiddler 0.2.1

Cross-platform developer toolbox: bit manipulation, hashing, YAML/JSON/SQL, QR, Markdown, cron, and 40+ more tools — Tauri v2, no Node.js
$(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> &rarr; ${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);
    }
  });
});