<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<script type="module">
import init, { OkId } from "./okid/okid.js";
async function run() {
await init();
window.OkId = OkId;
}
window.copyToClipboard = function (text, buttonId) {
navigator.clipboard.writeText(text).then(() => {
const button = document.getElementById(buttonId);
const icon = button.querySelector("i");
const originalClass = icon.getAttribute("data-lucide");
icon.setAttribute("data-lucide", "check");
button.classList.remove("text-gray-600");
button.classList.add("text-green-600");
lucide.createIcons();
setTimeout(() => {
icon.setAttribute("data-lucide", originalClass);
button.classList.remove("text-green-600");
button.classList.add("text-gray-600");
lucide.createIcons();
}, 2000);
});
};
window.pasteFromClipboard = async function (inputId) {
try {
const text = await navigator.clipboard.readText();
document.getElementById(inputId).value = text;
} catch (err) {
console.error("Failed to read clipboard:", err);
}
};
window.createHashes = async function () {
const textInput = document.getElementById("hashInput").value;
const fileInput = document.getElementById("fileInput");
const file = fileInput.files[0];
if (file) {
const arrayBuffer = await file.arrayBuffer();
const data = new Uint8Array(arrayBuffer);
displayHashes(data, file.name);
} else if (textInput) {
const encoder = new TextEncoder();
const data = encoder.encode(textInput);
displayHashes(data, textInput);
} else {
alert("Please enter text or select a file");
}
};
window.displayHashes = function (data, source) {
const results = document.getElementById("hashResults");
const sha256Id = OkId.fromSha256(data);
const blake3Id = OkId.fromBlake3(data);
const fingerprintId = OkId.fingerprint(data);
results.innerHTML = `
<div class="overflow-x-auto">
<table class="w-full">
<thead>
<tr class="border-b border-gray-200">
<th class="text-left py-3 px-4 font-semibold text-gray-700">Content</th>
<th class="text-left py-3 px-4 font-semibold text-gray-700">Type</th>
<th class="text-left py-3 px-4 font-semibold text-gray-700">ID</th>
<th class="text-center py-3 px-4 font-semibold text-gray-700">Action</th>
</tr>
</thead>
<tbody>
<tr class="border-b border-gray-100 hover:bg-gray-50">
<td class="py-3 px-4 text-sm text-gray-600" rowspan="3">${source}</td>
<td class="py-3 px-4 font-medium text-gray-600">SHA256</td>
<td class="py-3 px-4 font-mono text-sm break-all">${sha256Id.toString()}</td>
<td class="py-3 px-4 text-center">
<button id="copy-sha256" onclick="copyToClipboard('${sha256Id.toString()}', 'copy-sha256')"
class="p-1 text-gray-600 hover:text-gray-900 transition-colors" title="Copy">
<i data-lucide="copy" class="w-4 h-4"></i>
</button>
</td>
</tr>
<tr class="border-b border-gray-100 hover:bg-gray-50">
<td class="py-3 px-4 font-medium text-gray-600">Blake3</td>
<td class="py-3 px-4 font-mono text-sm break-all">${blake3Id.toString()}</td>
<td class="py-3 px-4 text-center">
<button id="copy-blake3" onclick="copyToClipboard('${blake3Id.toString()}', 'copy-blake3')"
class="p-1 text-gray-600 hover:text-gray-900 transition-colors" title="Copy">
<i data-lucide="copy" class="w-4 h-4"></i>
</button>
</td>
</tr>
<tr class="hover:bg-gray-50">
<td class="py-3 px-4 font-medium text-gray-600">Fingerprint</td>
<td class="py-3 px-4 font-mono text-sm break-all">${fingerprintId.toString()}</td>
<td class="py-3 px-4 text-center">
<button id="copy-fingerprint" onclick="copyToClipboard('${fingerprintId.toString()}', 'copy-fingerprint')"
class="p-1 text-gray-600 hover:text-gray-900 transition-colors" title="Copy">
<i data-lucide="copy" class="w-4 h-4"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
`;
lucide.createIcons();
};
window.generateUuid = function () {
const uuidId = OkId.newUuid();
const resultDiv = document.getElementById("uuidResult");
resultDiv.innerHTML = `
<div class="flex items-center justify-between p-2 bg-gray-50 rounded">
<span class="font-mono text-sm">${uuidId.toString()}</span>
<button id="copy-uuid" onclick="copyToClipboard('${uuidId.toString()}', 'copy-uuid')"
class="p-1 text-gray-600 hover:text-gray-900 transition-colors" title="Copy">
<i data-lucide="copy" class="w-4 h-4"></i>
</button>
</div>`;
lucide.createIcons();
};
window.generateUlid = function () {
const ulidId = OkId.newUlid();
const resultDiv = document.getElementById("ulidResult");
resultDiv.innerHTML = `
<div class="flex items-center justify-between p-2 bg-gray-50 rounded">
<span class="font-mono text-sm">${ulidId.toString()}</span>
<button id="copy-ulid" onclick="copyToClipboard('${ulidId.toString()}', 'copy-ulid')"
class="p-1 text-gray-600 hover:text-gray-900 transition-colors" title="Copy">
<i data-lucide="copy" class="w-4 h-4"></i>
</button>
</div>`;
lucide.createIcons();
};
window.convertToPathSafe = function () {
const okidInput = document.getElementById("pathSafeInput").value;
const resultDiv = document.getElementById("pathSafeResult");
try {
const okidInstance = OkId.fromString(okidInput);
const pathSafe = okidInstance.toAscii();
resultDiv.innerHTML = `
<div class="flex items-center justify-between p-2 bg-green-50 text-green-700 rounded">
<div>
<span class="font-medium">Path-safe:</span>
<span class="font-mono text-sm">${pathSafe}</span>
</div>
<button id="copy-pathsafe" onclick="copyToClipboard('${pathSafe}', 'copy-pathsafe')"
class="p-1 text-green-600 hover:text-green-900 transition-colors" title="Copy">
<i data-lucide="copy" class="w-4 h-4"></i>
</button>
</div>`;
lucide.createIcons();
} catch (error) {
resultDiv.innerHTML = `<div class="p-2 bg-red-50 text-red-700 rounded"><span class="font-medium">Error:</span> ${error.message}</div>`;
}
};
window.displaySafe = function () {
const input = document.getElementById("secretInput").value;
const resultDiv = document.getElementById("fromDisplaySafeResult");
try {
const okid = OkId.fromDisplaySafe(input);
const okidString = okid.toString();
resultDiv.innerHTML = `
<div class="flex items-center justify-between p-2 bg-green-50 text-green-700 rounded">
<div>
<span class="font-medium">OkId:</span>
<span class="font-mono text-sm">${okidString}</span>
</div>
<button id="copy-displaysafe" onclick="copyToClipboard('${okidString}', 'copy-displaysafe')"
class="p-1 text-green-600 hover:text-green-900 transition-colors" title="Copy">
<i data-lucide="copy" class="w-4 h-4"></i>
</button>
</div>`;
lucide.createIcons();
} catch (error) {
resultDiv.innerHTML = `<div class="p-2 bg-red-50 text-red-700 rounded"><span class="font-medium">Invalid OkId format:</span> ${error.message}</div>`;
}
};
window.toDisplaySafe = function () {
const okidInput = document.getElementById("okidInput").value;
const resultDiv = document.getElementById("toDisplaySafeResult");
try {
const okidInstance = OkId.fromString(okidInput);
const displaySafe = okidInstance.toDisplaySafe();
resultDiv.innerHTML = `
<div class="flex items-center justify-between p-2 bg-green-50 text-green-700 rounded">
<div>
<span class="font-medium">Display-safe:</span>
<span class="font-mono text-sm">${displaySafe}</span>
</div>
<button id="copy-todisplaysafe" onclick="copyToClipboard('${displaySafe}', 'copy-todisplaysafe')"
class="p-1 text-green-600 hover:text-green-900 transition-colors" title="Copy">
<i data-lucide="copy" class="w-4 h-4"></i>
</button>
</div>`;
lucide.createIcons();
} catch (error) {
resultDiv.innerHTML = `<div class="p-2 bg-red-50 text-red-700 rounded"><span class="font-medium">Error:</span> ${error.message}</div>`;
}
};
window.convertToBubblebabble = function () {
const okidInput = document.getElementById("bubblebabbleInput").value;
const resultDiv = document.getElementById("bubblebabbleResult");
try {
const okidInstance = OkId.fromString(okidInput);
const bubblebabble = okidInstance.toBubblebabble();
resultDiv.innerHTML = `
<div class="space-y-3">
<div class="flex items-center justify-between p-3 bg-green-50 text-green-700 rounded">
<div>
<span class="font-medium">Bubblebabble:</span>
<div class="font-mono text-sm mt-1 break-all">${bubblebabble}</div>
</div>
<button id="copy-bubblebabble" onclick="copyToClipboard('${bubblebabble}', 'copy-bubblebabble')"
class="p-1 text-green-600 hover:text-green-900 transition-colors flex-shrink-0" title="Copy">
<i data-lucide="copy" class="w-4 h-4"></i>
</button>
</div>
<div class="text-sm text-gray-600 p-3 bg-blue-50 rounded">
<strong>About Bubblebabble:</strong> This is a human-readable encoding format that makes binary data easier to verify and communicate verbally. It uses pronounceable syllables and is commonly used for SSH key fingerprints.
</div>
</div>`;
lucide.createIcons();
} catch (error) {
resultDiv.innerHTML = `<div class="p-2 bg-red-50 text-red-700 rounded"><span class="font-medium">Error:</span> ${error.message}</div>`;
}
};
window.generateBubblebabbleFromText = function () {
const textInput = document.getElementById(
"bubblebabbleTextInput",
).value;
const hashType = document.getElementById("bubblebabbleHashType").value;
const resultDiv = document.getElementById("bubblebabbleFromTextResult");
if (!textInput.trim()) {
resultDiv.innerHTML = `<div class="p-2 bg-red-50 text-red-700 rounded">Please enter some text</div>`;
return;
}
try {
const encoder = new TextEncoder();
const data = encoder.encode(textInput);
let okidInstance;
switch (hashType) {
case "sha256":
okidInstance = OkId.fromSha256(data);
break;
case "blake3":
okidInstance = OkId.fromBlake3(data);
break;
case "fingerprint":
okidInstance = OkId.fingerprint(data);
break;
default:
okidInstance = OkId.fromSha256(data);
}
const bubblebabble = okidInstance.toBubblebabble();
const okidString = okidInstance.toString();
resultDiv.innerHTML = `
<div class="space-y-3">
<div class="p-3 bg-gray-50 rounded">
<div class="text-sm font-medium text-gray-700 mb-1">OkId (${hashType.toUpperCase()}):</div>
<div class="font-mono text-sm break-all text-gray-600">${okidString}</div>
</div>
<div class="flex items-center justify-between p-3 bg-green-50 text-green-700 rounded">
<div class="flex-1">
<span class="font-medium">Bubblebabble:</span>
<div class="font-mono text-sm mt-1 break-all">${bubblebabble}</div>
</div>
<button id="copy-generated-bubblebabble" onclick="copyToClipboard('${bubblebabble}', 'copy-generated-bubblebabble')"
class="p-1 text-green-600 hover:text-green-900 transition-colors flex-shrink-0 ml-2" title="Copy">
<i data-lucide="copy" class="w-4 h-4"></i>
</button>
</div>
</div>`;
lucide.createIcons();
} catch (error) {
resultDiv.innerHTML = `<div class="p-2 bg-red-50 text-red-700 rounded"><span class="font-medium">Error:</span> ${error.message}</div>`;
}
};
run();
document.addEventListener("DOMContentLoaded", () => {
lucide.createIcons();
});
</script>
<script type="module">
import initChunks, { FileChunker, chunk_text } from "./chunks/chunks.js";
async function initializeChunks() {
try {
await initChunks();
window.FileChunker = FileChunker;
window.chunk_text = chunk_text;
console.log("Chunks module initialized");
} catch (error) {
console.error("Failed to initialize chunks module:", error);
}
}
initializeChunks();
</script>
<div class="w-full space-y-12">
<div>
<h2 class="text-2xl font-bold text-gray-800 mb-6">Hash Generators</h2>
<div class="space-y-4">
<div class="relative">
<input
type="text"
id="hashInput"
value="Hello, World!"
class="w-full px-4 py-2 pr-10 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono"
/>
<button
onclick="pasteFromClipboard('hashInput')"
class="absolute right-2 top-1/2 transform -translate-y-1/2 p-1 text-gray-600 hover:text-gray-900 transition-colors"
title="Paste"
>
<i data-lucide="clipboard" class="w-4 h-4"></i>
</button>
</div>
<div class="flex items-center justify-center my-4">
<div class="flex-1 border-t border-gray-300"></div>
<span class="px-4 text-gray-500 text-sm">or</span>
<div class="flex-1 border-t border-gray-300"></div>
</div>
<div>
<input
type="file"
id="fileInput"
class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100"
/>
</div>
<button
onclick="createHashes()"
class="px-6 py-2 bg-blue-500 text-white font-medium rounded-md hover:bg-blue-600 transition-colors w-full sm:w-auto"
>
Generate Hashes
</button>
<div id="hashResults" class="mt-4"></div>
</div>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800 mb-6">ID Generators</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<h3 class="text-lg font-semibold text-gray-700 mb-4">UUID</h3>
<button
onclick="generateUuid()"
class="px-6 py-2 bg-blue-500 text-white font-medium rounded-md hover:bg-blue-600 transition-colors mb-4"
>
Generate UUID
</button>
<div id="uuidResult"></div>
</div>
<div>
<h3 class="text-lg font-semibold text-gray-700 mb-4">ULID</h3>
<button
onclick="generateUlid()"
class="px-6 py-2 bg-blue-500 text-white font-medium rounded-md hover:bg-blue-600 transition-colors mb-4"
>
Generate ULID
</button>
<div id="ulidResult"></div>
</div>
</div>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800 mb-6">
Path-safe Converter
</h2>
<div class="space-y-4">
<div class="relative">
<input
type="text"
id="pathSafeInput"
value=""
placeholder="Enter an OkId string"
class="w-full px-4 py-2 pr-10 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono"
/>
<button
onclick="pasteFromClipboard('pathSafeInput')"
class="absolute right-2 top-1/2 transform -translate-y-1/2 p-1 text-gray-600 hover:text-gray-900 transition-colors"
title="Paste"
>
<i data-lucide="clipboard" class="w-4 h-4"></i>
</button>
</div>
<button
onclick="convertToPathSafe()"
class="px-6 py-2 bg-blue-500 text-white font-medium rounded-md hover:bg-blue-600 transition-colors"
>
Convert to Path-safe
</button>
<div id="pathSafeResult" class="mt-2"></div>
</div>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800 mb-6">OkSecret</h2>
<div class="space-y-8">
<h3 class="text-lg font-semibold text-gray-700 mb-4">
To Display Safe
</h3>
<div class="space-y-4">
<div class="relative">
<input
type="text"
id="okidInput"
value=""
placeholder="Enter an OkId string"
class="w-full px-4 py-2 pr-10 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono"
/>
<button
onclick="pasteFromClipboard('okidInput')"
class="absolute right-2 top-1/2 transform -translate-y-1/2 p-1 text-gray-600 hover:text-gray-900 transition-colors"
title="Paste"
>
<i data-lucide="clipboard" class="w-4 h-4"></i>
</button>
</div>
<button
onclick="toDisplaySafe()"
class="px-6 py-2 bg-blue-500 text-white font-medium rounded-md hover:bg-blue-600 transition-colors"
>
Convert to Display Safe
</button>
<div id="toDisplaySafeResult" class="mt-2"></div>
</div>
<div>
<h3 class="text-lg font-semibold text-gray-700 mb-4">
From Display Safe
</h3>
<div class="space-y-4">
<div class="relative">
<input
type="text"
id="secretInput"
value=""
placeholder="Enter an OkId string"
class="w-full px-4 py-2 pr-10 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono"
/>
<button
onclick="pasteFromClipboard('secretInput')"
class="absolute right-2 top-1/2 transform -translate-y-1/2 p-1 text-gray-600 hover:text-gray-900 transition-colors"
title="Paste"
>
<i data-lucide="clipboard" class="w-4 h-4"></i>
</button>
</div>
<button
onclick="displaySafe()"
class="px-6 py-2 bg-blue-500 text-white font-medium rounded-md hover:bg-blue-600 transition-colors"
>
Parse OkId
</button>
<div id="fromDisplaySafeResult" class="mt-2"></div>
</div>
</div>
</div>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800 mb-6">
Bubblebabble Converter
</h2>
<div class="space-y-8">
<div>
<h3 class="text-lg font-semibold text-gray-700 mb-4">
Convert OkId to Bubblebabble
</h3>
<div class="space-y-4">
<div class="relative">
<input
type="text"
id="bubblebabbleInput"
value=""
placeholder="Enter an OkId string (e.g., 2ːb94d27b...)"
class="w-full px-4 py-2 pr-10 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono"
/>
<button
onclick="pasteFromClipboard('bubblebabbleInput')"
class="absolute right-2 top-1/2 transform -translate-y-1/2 p-1 text-gray-600 hover:text-gray-900 transition-colors"
title="Paste"
>
<i data-lucide="clipboard" class="w-4 h-4"></i>
</button>
</div>
<button
onclick="convertToBubblebabble()"
class="px-6 py-2 bg-blue-500 text-white font-medium rounded-md hover:bg-blue-600 transition-colors"
>
Convert to Bubblebabble
</button>
<div id="bubblebabbleResult" class="mt-2"></div>
</div>
</div>
<div>
<h3 class="text-lg font-semibold text-gray-700 mb-4">
Generate Bubblebabble from Text
</h3>
<div class="space-y-4">
<div class="relative">
<input
type="text"
id="bubblebabbleTextInput"
value="Hello, World!"
placeholder="Enter text to hash"
class="w-full px-4 py-2 pr-10 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono"
/>
<button
onclick="
pasteFromClipboard('bubblebabbleTextInput')
"
class="absolute right-2 top-1/2 transform -translate-y-1/2 p-1 text-gray-600 hover:text-gray-900 transition-colors"
title="Paste"
>
<i data-lucide="clipboard" class="w-4 h-4"></i>
</button>
</div>
<div>
<label
for="bubblebabbleHashType"
class="block text-sm font-medium text-gray-700 mb-2"
>
Hash Algorithm:
</label>
<select
id="bubblebabbleHashType"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<option value="sha256">SHA256</option>
<option value="blake3">Blake3</option>
<option value="fingerprint">Fingerprint</option>
</select>
</div>
<button
onclick="generateBubblebabbleFromText()"
class="px-6 py-2 bg-blue-500 text-white font-medium rounded-md hover:bg-blue-600 transition-colors"
>
Generate Bubblebabble
</button>
<div id="bubblebabbleFromTextResult" class="mt-2"></div>
</div>
</div>
</div>
</div>
</div>