Implement self-hosted debug console and @external decorator

This commit completes the self-hosting of Ritz's debug console and
introduces the @external decorator for elegant JavaScript bridge functions.

## Major Features

### 1. Self-Hosted Debug Console (DebugConsole.py)
- Entire debug console UI written in Ritz itself
- Signal-based state management for toggle functionality
- Displays runtime stats (memory, signals, DOM nodes, logs)
- Live reactive updates when signals change in main app
- Keyboard shortcut support (Ctrl+`)

### 2. @external Decorator for JavaScript Bridges
- Pythonic decorator syntax: @external or @bridge
- Parser automatically recognizes and tracks external functions
- Codegen emits proper LLVM declarations
- No more hacky string checks (startswith("js_"))
- Clean, declarative bridge function definitions

Example:
```python
@external
def js_toggle_debug_console():
    pass

def toggle():
    js_toggle_debug_console()  # Clean call to JS bridge
```

### 3. Enhanced RitzLoader Debug Mode
- `new RitzLoader({ debug: true })` auto-loads DebugConsole.wasm
- Keyboard shortcut registration (Ctrl+` toggle)
- Runtime stats injection (memory, signals, DOM nodes)
- Automatic status element hiding when debug enabled
- Debug console isolated in separate WASM runtime

## Bug Fixes

- Fixed reactive text node scoping (prevent global name collisions)
- Fixed CSS specificity for debug console status (use #ritz-debug)
- Fixed signal cross-contamination between runtimes (scope to rootElement)
- Fixed parser to handle expression statements in function bodies
- Fixed conditional branch rendering (append children to wrapper divs)

## Infrastructure Improvements

- Added H4 and Pre elements to syntax.py
- Parser handles @external decorator on top-level functions
- Codegen pre-collects external functions before emitting declarations
- Runtime scopes querySelector to prevent cross-runtime interference
- Debug stats update reactively on every signal change

## Files Added/Modified

New files:
- examples/runtime/DebugConsole.py - Self-hosted debug UI
- examples/runtime/DebugConsole.wasm - Compiled debug console (1.8KB)
- PHASE4_CLEANUP.md - Next phase planning document

Modified:
- ritz/parser.py - @external decorator support
- ritz/llvm_codegen_signals.py - External function handling
- ritz/syntax.py - Added external/bridge decorators, H4, Pre
- examples/runtime/ritz-runtime.js - Debug mode, stats updates
- examples/runtime/styles.css - Merged debug styles
- examples/template.html - Debug mode enabled by default
- examples/highfive/HighFive.html - Simplified using template

## Known Issues (Addressed in PHASE4_CLEANUP.md)

1. Debug console shifts page RIGHT (should shift LEFT)
2. Toggle implementation un-Ritzy (calls JS to manipulate DOM)
   - Should use reactive class binding + @event decorator
   - Needs event object bridge for keyboard events
3. Missing test infrastructure

## Next Steps (Phase 4)

See PHASE4_CLEANUP.md for detailed plan:
- Implement reactive class binding (signal-driven classes)
- Add @event decorator for declarative event registration
- Build event object bridge (access event.key, event.ctrlKey)
- Create Python test mode (execute without browser)
- Add comprehensive test suite
- Update all examples to use new patterns

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Aaron SinclairandClaude committed 2025-11-15 18:28:24 -08:00
1 parent ddd2c03a9d
commit ea7dadc5ae
20 files changed
+1324 -70

No files matched your search

+7 -61
View File
@@ -4,27 +4,15 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HighFive Counter - Ritz Framework</title>
<link rel="stylesheet" href="../runtime/ritz-debug.css">
<link rel="stylesheet" href="../runtime/styles.css">
<style>
body {
font-family: system-ui, sans-serif;
background: #f5f5f5;
}
#app {
max-width: 800px;
margin: 50px auto;
padding: 40px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
/* Component-specific styles */
.high-five-app {
text-align: center;
}
.high-five-app p {
font-size: 18px;
margin: 15px 0;
color: #333;
}
.high-five-app .count {
font-size: 48px;
@@ -32,64 +20,22 @@
color: #0066cc;
margin: 30px 0;
}
.high-five-app button {
font-size: 18px;
padding: 15px 40px;
background: #0066cc;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s;
}
.high-five-app button:hover {
background: #0052a3;
transform: scale(1.05);
}
.high-five-app button:active {
transform: scale(0.98);
}
</style>
</head>
<body>
<div id="app"></div>
<div id="ritz-debug" class="ritz-debug hidden">
<div class="debug-header">
<h3>🎭 Ritz Debug</h3>
<button onclick="ritzDebug.toggle()">×</button>
</div>
<div id="status" class="status">Loading...</div>
<div class="debug-content">
<div class="debug-section">
<h4>Component</h4>
<pre>HighFive.py (Reactive Signals)</pre>
</div>
<div class="debug-section">
<h4>Features</h4>
<pre>✓ Signal in WASM memory
✓ Event handlers
✓ Reactive updates</pre>
</div>
<div class="debug-section">
<h4>Console Commands</h4>
<pre>ritzRuntime.getLog()
ritzInstance.exports.get_high_fives()
ritzInstance.exports.increment()</pre>
</div>
</div>
</div>
<div id="status"></div>
<script src="../runtime/ritz-runtime.js"></script>
<script src="../runtime/ritz-debug.js"></script>
<script>
// Wait for DOM to be ready
// Load Main.wasm with debug console enabled
// The debug console is itself written in Ritz!
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
new RitzLoader().load('Main.wasm');
new RitzLoader({ debug: true }).load('Main.wasm');
});
} else {
new RitzLoader().load('Main.wasm');
new RitzLoader({ debug: true }).load('Main.wasm');
}
</script>
</body>