<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>rustixml - WASMZ Demo (True wasm:// Routing)</title>
<script>
window.htmzUpdate = function(iframe) {
try {
const targetId = iframe.src.split('#')[1];
console.log('🔄 htmzUpdate called, targetId:', targetId, 'src:', iframe.src.substring(0, 100));
if (targetId && iframe.contentDocument) {
const content = iframe.contentDocument.body.innerHTML;
console.log('📝 Content to inject:', content.substring(0, 100) + '...');
const target = document.getElementById(targetId);
if (target) {
target.innerHTML = content;
console.log('✅ Content injected into #' + targetId);
} else {
console.error('❌ Target element not found:', targetId);
}
} else {
console.log('⏭️ Skipping update (no targetId or contentDocument)');
}
} catch (e) {
console.error('htmz update failed:', e);
}
};
function copyTextareas() {
document.getElementById('form_grammar').value = document.getElementById('grammar').value;
document.getElementById('form_input').value = document.getElementById('input').value;
}
window.copyTextareas = copyTextareas;
</script>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
background: white;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
overflow: hidden;
}
header {
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
color: white;
padding: 30px;
text-align: center;
}
h1 {
font-size: 2.5em;
margin-bottom: 10px;
font-weight: 700;
}
.subtitle {
font-size: 1.1em;
opacity: 0.9;
}
.badge {
display: inline-block;
background: rgba(255, 255, 255, 0.2);
padding: 6px 12px;
border-radius: 12px;
font-size: 0.85em;
margin: 5px;
}
.wasmz-badge {
background: rgba(255, 107, 107, 0.4);
border: 2px solid #ff6b6b;
font-weight: bold;
}
main {
padding: 30px;
}
.info {
background: #ffe6e6;
border-left: 4px solid #ff6b6b;
padding: 15px;
margin-bottom: 20px;
border-radius: 4px;
}
.info h3 {
margin-bottom: 10px;
color: #c92a2a;
}
.info code {
background: rgba(0, 0, 0, 0.05);
padding: 2px 6px;
border-radius: 3px;
font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
}
.editor-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-bottom: 20px;
}
.editor-section {
display: flex;
flex-direction: column;
}
.editor-section label {
font-weight: 600;
margin-bottom: 8px;
color: #333;
}
textarea {
font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
font-size: 14px;
padding: 15px;
border: 2px solid #e0e0e0;
border-radius: 8px;
resize: vertical;
min-height: 250px;
transition: border-color 0.3s;
}
textarea:focus {
outline: none;
border-color: #ff6b6b;
}
.controls {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
button {
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
color: white;
border: none;
padding: 12px 24px;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(255, 107, 107, 0.3);
}
button:active {
transform: translateY(0);
}
.examples {
display: flex;
gap: 10px;
flex-wrap: wrap;
margin-bottom: 20px;
}
.example-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 8px 16px;
font-size: 14px;
}
.result {
margin-top: 20px;
padding: 20px;
border-radius: 8px;
animation: fadeIn 0.3s ease-in;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.result.success {
background: #e8f5e9;
border-left: 4px solid #4caf50;
}
.result.error {
background: #ffebee;
border-left: 4px solid #f44336;
}
.result h3 {
margin-bottom: 15px;
color: #333;
}
.output-section, .error-section {
margin-top: 10px;
}
.output-section h4 {
margin-bottom: 8px;
color: #555;
}
pre {
background: #f5f5f5;
padding: 15px;
border-radius: 6px;
overflow-x: auto;
font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
font-size: 13px;
line-height: 1.5;
}
.xml-output {
background: #263238;
color: #aed581;
}
.error-message {
background: #fff5f5;
color: #c92a2a;
border: 1px solid #ffc9c9;
}
.stats {
margin-top: 15px;
display: flex;
gap: 10px;
}
.stats .badge {
background: #4caf50;
color: white;
border: none;
}
.example-loaded {
background: #e3f2fd;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
}
.example-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
margin-top: 10px;
}
.grammar-preview, .input-preview {
background: white;
padding: 10px;
border-radius: 4px;
}
.wasmz-demo {
background: #fff3e0;
border: 2px solid #ff9800;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
}
.wasmz-demo h3 {
color: #e65100;
margin-bottom: 10px;
}
.wasmz-demo code {
background: rgba(0, 0, 0, 0.05);
padding: 2px 6px;
border-radius: 3px;
}
#htmz {
display: none;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>rustixml WASMZ</h1>
<p class="subtitle">WebAssembly + htmz Binary Server Pattern</p>
<div>
<span class="badge">⚡ Native Performance</span>
<span class="badge">🦀 Rust to WASM</span>
<span class="badge wasmz-badge">🔧 wasm:// Routing</span>
<span class="badge">📦 50KB gzipped</span>
</div>
</header>
<main>
<div class="wasmz-demo">
<h3>🚀 WASMZ Pattern Active</h3>
<p>
This demo uses <strong>true wasm:// routing</strong> where HTML forms directly call compiled
Rust functions without JavaScript glue code. Forms like
<code><form action="wasm://parse_ixml_template"></code> trigger native WASM execution
that returns HTML templates for instant DOM updates.
</p>
<p style="margin-top: 10px;">
<strong>Key Innovation:</strong> The router intercepts <code>wasm://</code> URLs and routes
them to WebAssembly functions compiled from Rust. Each function returns an HTML template string
that the router directly injects into the target DOM element. Zero network latency, native speed,
zero backend code.
</p>
</div>
<div class="info">
<h3>How WASMZ Works Here</h3>
<p>1. <strong>Form Submission</strong>: Click "Parse with WASM" submits form with <code>action="wasm://parse_ixml_template"</code></p>
<p>2. <strong>Router Intercepts</strong>: JavaScript router catches the wasm:// URL before browser handles it</p>
<p>3. <strong>Native Execution</strong>: Rust code runs at ~5-10x JavaScript speed in browser sandbox</p>
<p>4. <strong>Template Response</strong>: WASM function returns HTML template string (not just data!)</p>
<p>5. <strong>DOM Update</strong>: Router directly injects template into target div instantly (no iframe needed!)</p>
</div>
<div class="examples">
<h4 style="width: 100%; margin-bottom: 10px;">📚 Load Examples:</h4>
<form action="wasm://load_example_template" target="htmz" method="post" data-target="example-display">
<input type="hidden" name="example_name" value="simple">
<button type="submit" class="example-btn">Simple Words</button>
</form>
<form action="wasm://load_example_template" target="htmz" method="post" data-target="example-display">
<input type="hidden" name="example_name" value="numbers">
<button type="submit" class="example-btn">Numbers</button>
</form>
<form action="wasm://load_example_template" target="htmz" method="post" data-target="example-display">
<input type="hidden" name="example_name" value="date">
<button type="submit" class="example-btn">Date Parser</button>
</form>
<form action="wasm://load_example_template" target="htmz" method="post" data-target="example-display">
<input type="hidden" name="example_name" value="greeting">
<button type="submit" class="example-btn">Greeting</button>
</form>
</div>
<div id="example-display">
</div>
<div class="editor-grid">
<div class="editor-section">
<label for="grammar">iXML Grammar:</label>
<textarea id="grammar" placeholder="Enter your iXML grammar here...
Example:
greeting: "Hello, ", name, "!".
name: letter+.
letter: ["A"-"Z"; "a"-"z"].">greeting: "Hello, ", name, "!".
name: letter+.
letter: ["A"-"Z"; "a"-"z"].</textarea>
</div>
<div class="editor-section">
<label for="input">Input Text:</label>
<textarea id="input" placeholder="Enter text to parse...">Hello, World!</textarea>
</div>
</div>
<div class="controls">
<form action="wasm://parse_ixml_template" target="htmz" method="post" data-target="result" style="display: inline;">
<input type="hidden" name="grammar" id="form_grammar">
<input type="hidden" name="input" id="form_input">
<button type="submit" onclick="copyTextareas()">⚡ Parse with WASM</button>
</form>
</div>
<div id="result">
</div>
<iframe name="htmz" id="htmz" onload="window.htmzUpdate(this)"></iframe>
</main>
</div>
<script type="module">
import init, {
parse_ixml_template,
load_example_template,
version,
conformance_info
} from './rustixml.js';
let wasmReady = false;
let wasmFunctions = {};
async function initWasm() {
try {
await init();
console.log('🦀 WASM initialized:', version());
console.log('📊', conformance_info());
wasmFunctions = {
parse_ixml_template,
load_example_template
};
wasmReady = true;
console.log('🚀 WASMZ Router active - wasm:// URLs ready');
installWasmzRouter();
} catch (err) {
console.error('WASM initialization failed:', err);
document.getElementById('result').innerHTML = `
<div class="result error">
<h3>❌ WASM Loading Failed</h3>
<pre class="error-message">${err}</pre>
<p>Make sure you're running from an HTTP server (not file://).</p>
</div>
`;
}
}
function installWasmzRouter() {
document.addEventListener('submit', async function(e) {
const form = e.target;
const action = form.getAttribute('action');
if (!action || !action.startsWith('wasm://')) {
return;
}
e.preventDefault();
if (!wasmReady) {
console.error('WASM not ready yet');
return;
}
const functionName = action.substring(7); const wasmFunction = wasmFunctions[functionName];
if (!wasmFunction) {
console.error('Unknown WASM function:', functionName);
return;
}
const formData = new FormData(form);
const params = {};
for (let [key, value] of formData.entries()) {
params[key] = value;
}
console.log('🔧 WASMZ routing:', functionName, params);
try {
let html;
if (functionName === 'parse_ixml_template') {
html = wasmFunction(params.grammar, params.input);
} else if (functionName === 'load_example_template') {
html = wasmFunction(params.example_name);
} else {
html = wasmFunction(...Object.values(params));
}
console.log('✨ WASM returned HTML:', html.substring(0, 100) + '...');
const target = form.getAttribute('target');
const targetId = form.dataset.target || 'result';
const targetElement = document.getElementById(targetId);
if (targetElement) {
targetElement.innerHTML = html;
console.log('✅ Content injected into #' + targetId);
const scripts = targetElement.querySelectorAll('script');
scripts.forEach(script => {
const newScript = document.createElement('script');
newScript.textContent = script.textContent;
script.parentNode.replaceChild(newScript, script);
});
} else {
console.error('❌ Target element not found:', targetId);
}
} catch (err) {
console.error('WASMZ execution failed:', err);
document.getElementById('result').innerHTML = `
<div class="result error">
<h3>❌ WASM Execution Error</h3>
<pre class="error-message">${err}</pre>
</div>
`;
}
});
}
function findTargetId(form) {
if (form.dataset.target) {
return form.dataset.target;
}
let parent = form.closest('[id]');
if (parent) {
return parent.id;
}
if (form.closest('.examples')) {
return 'example-display';
}
return 'result';
}
initWasm();
</script>
</body>
</html>