ruchy 3.71.0

A systems scripting language that transpiles to idiomatic Rust with extreme quality engineering
Documentation
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Ruchy WASM REPL - E2E Test Harness</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
            background: #1e1e1e;
            color: #d4d4d4;
            padding: 20px;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
        }

        h1 {
            color: #569cd6;
            margin-bottom: 10px;
        }

        #status {
            padding: 10px;
            margin-bottom: 20px;
            border-radius: 4px;
            font-weight: bold;
        }

        #status.status-loading {
            background: #856404;
            color: #ffc107;
        }

        #status.status-ready {
            background: #155724;
            color: #28a745;
        }

        #status.status-error {
            background: #721c24;
            color: #f8d7da;
        }

        #output {
            background: #252526;
            border: 1px solid #3c3c3c;
            border-radius: 4px;
            padding: 15px;
            min-height: 400px;
            margin-bottom: 20px;
            overflow-y: auto;
            font-size: 14px;
            line-height: 1.6;
            white-space: pre-wrap;
        }

        .input-container {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
        }

        #repl-input {
            flex: 1;
            background: #3c3c3c;
            border: 1px solid #555;
            color: #d4d4d4;
            padding: 10px;
            font-family: inherit;
            font-size: 14px;
            border-radius: 4px;
        }

        #repl-input:focus {
            outline: none;
            border-color: #569cd6;
        }

        #repl-input:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }

        button {
            background: #0e639c;
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 4px;
            cursor: pointer;
            font-family: inherit;
            font-size: 14px;
        }

        button:hover {
            background: #1177bb;
        }

        button:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }

        .controls {
            display: flex;
            gap: 10px;
        }

        .error {
            color: #f48771;
        }

        .success {
            color: #4ec9b0;
        }

        .prompt {
            color: #608b4e;
        }

        .result {
            color: #ce9178;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>🦀 Ruchy WASM REPL</h1>
        <div id="status" class="status-loading">Loading WASM...</div>

        <div id="output">Welcome to Ruchy REPL v3.67.0 WASM Edition
Type expressions to evaluate, or commands:
  :help    - Show help
  :clear   - Clear output
  :quit    - Not applicable in browser

</div>

        <div class="input-container">
            <input
                type="text"
                id="repl-input"
                placeholder="Enter Ruchy expression..."
                disabled
                autocomplete="off"
            />
        </div>

        <div class="controls">
            <button id="clear-history">Clear History</button>
            <button id="reset-env">Reset Environment</button>
        </div>
    </div>

    <script type="module">
        import initWasm, { WasmRepl } from './pkg/ruchy.js';

        let wasmRepl = null;
        let history = [];
        let historyIndex = -1;

        const status = document.getElementById('status');
        const output = document.getElementById('output');
        const input = document.getElementById('repl-input');
        const clearHistoryBtn = document.getElementById('clear-history');
        const resetEnvBtn = document.getElementById('reset-env');

        // Load history from localStorage
        function loadHistory() {
            const saved = localStorage.getItem('ruchy-repl-history');
            if (saved) {
                try {
                    history = JSON.parse(saved);
                    // Restore history to output
                    history.forEach(entry => {
                        appendOutput(`<span class="prompt">ruchy&gt;</span> ${entry.input}`, false);
                        if (entry.output) {
                            appendOutput(`<span class="result">${entry.output}</span>`, false);
                        }
                        if (entry.error) {
                            appendOutput(`<span class="error">Error: ${entry.error}</span>`, false);
                        }
                    });
                    if (history.length > 0) {
                        appendOutput('\n(from history)\n', false);
                    }
                } catch (e) {
                    console.error('Failed to load history:', e);
                }
            }
        }

        // Save history to localStorage
        function saveHistory() {
            try {
                localStorage.setItem('ruchy-repl-history', JSON.stringify(history));
            } catch (e) {
                console.error('Failed to save history:', e);
            }
        }

        // Append output to terminal
        function appendOutput(text, saveToHistory = true) {
            output.innerHTML += text + '\n';
            output.scrollTop = output.scrollHeight;
        }

        // Set status
        function setStatus(state, message) {
            status.className = `status-${state}`;
            status.textContent = message;
        }

        // Execute REPL command
        async function executeCommand(code) {
            if (!code.trim()) return;

            appendOutput(`<span class="prompt">ruchy&gt;</span> ${code}`);

            // Handle special commands
            if (code === ':help') {
                appendOutput('<span class="success">Available commands:</span>');
                appendOutput('  :help    - Show this help');
                appendOutput('  :clear   - Clear output');
                appendOutput('  :quit    - Not applicable in browser');
                history.push({ input: code, output: 'Help displayed' });
                saveHistory();
                return;
            }

            if (code === ':clear') {
                output.innerHTML = 'Welcome to Ruchy REPL v3.67.0 WASM Edition\n\n';
                appendOutput('<span class="success">Output cleared</span>');
                history.push({ input: code, output: 'Output cleared' });
                saveHistory();
                return;
            }

            // Execute via WASM
            try {
                const resultJson = wasmRepl.eval(code);
                const result = JSON.parse(resultJson);

                if (result.success) {
                    appendOutput(`<span class="result">${result.display || 'OK'}</span>`);
                    history.push({ input: code, output: result.display });
                } else {
                    appendOutput(`<span class="error">Error: ${result.error}</span>`);
                    history.push({ input: code, error: result.error });
                }
                saveHistory();
            } catch (err) {
                const errorMsg = err.message || String(err);
                appendOutput(`<span class="error">Error: ${errorMsg}</span>`);
                history.push({ input: code, error: errorMsg });
                saveHistory();
            }
        }

        // Handle input
        input.addEventListener('keydown', async (e) => {
            if (e.key === 'Enter') {
                const code = input.value;
                input.value = '';
                historyIndex = -1;
                await executeCommand(code);
            } else if (e.key === 'ArrowUp') {
                e.preventDefault();
                if (history.length > 0) {
                    if (historyIndex === -1) {
                        historyIndex = history.length - 1;
                    } else if (historyIndex > 0) {
                        historyIndex--;
                    }
                    input.value = history[historyIndex].input;
                }
            } else if (e.key === 'ArrowDown') {
                e.preventDefault();
                if (historyIndex !== -1) {
                    if (historyIndex < history.length - 1) {
                        historyIndex++;
                        input.value = history[historyIndex].input;
                    } else {
                        historyIndex = -1;
                        input.value = '';
                    }
                }
            }
        });

        // Clear history button
        clearHistoryBtn.addEventListener('click', () => {
            history = [];
            localStorage.removeItem('ruchy-repl-history');
            output.innerHTML = 'Welcome to Ruchy REPL v3.67.0 WASM Edition\n\n';
            appendOutput('<span class="success">History cleared</span>');
        });

        // Reset environment button
        resetEnvBtn.addEventListener('click', () => {
            // Reset WASM instance (JavaScript constructor)
            wasmRepl = new WasmRepl();
            output.innerHTML = 'Welcome to Ruchy REPL v3.67.0 WASM Edition\n\n';
            appendOutput('<span class="success">Environment reset</span>');
        });

        // Initialize
        async function init() {
            try {
                setStatus('loading', 'Loading WASM module...');

                // Load WASM module
                await initWasm();

                // Create REPL instance (JavaScript constructor, not Rust .new())
                wasmRepl = new WasmRepl();

                // Load history
                loadHistory();

                setStatus('ready', 'Ready');
                input.disabled = false;
                input.focus();

                appendOutput('<span class="success">✅ WASM loaded successfully</span>\n');
            } catch (err) {
                setStatus('error', 'Failed to load WASM');
                appendOutput(`<span class="error"> Failed to load WASM: ${err.message}</span>`);
            }
        }

        // Start initialization
        init();
    </script>
</body>
</html>