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>
240 lines
4.3 KiB
CSS
240 lines
4.3 KiB
CSS
/* Ritz Framework - Shared Example Styles */
|
|
|
|
* {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
margin: 0;
|
|
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
background: #f5f5f5;
|
|
color: #333;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
#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);
|
|
}
|
|
|
|
#status {
|
|
position: fixed;
|
|
top: 20px;
|
|
right: 20px;
|
|
padding: 12px 20px;
|
|
background: #fff;
|
|
border: 1px solid #ddd;
|
|
border-radius: 6px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
font-size: 14px;
|
|
}
|
|
|
|
#status.success {
|
|
background: #d4edda;
|
|
border-color: #c3e6cb;
|
|
color: #155724;
|
|
}
|
|
|
|
#status.error {
|
|
background: #f8d7da;
|
|
border-color: #f5c6cb;
|
|
color: #721c24;
|
|
}
|
|
|
|
h1, h2, h3 {
|
|
margin-top: 0;
|
|
color: #222;
|
|
}
|
|
|
|
p {
|
|
margin: 15px 0;
|
|
}
|
|
|
|
button {
|
|
padding: 12px 24px;
|
|
font-size: 16px;
|
|
font-weight: 500;
|
|
background: #0066cc;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
button:hover {
|
|
background: #0052a3;
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
|
}
|
|
|
|
button:active {
|
|
transform: translateY(0);
|
|
box-shadow: none;
|
|
}
|
|
|
|
input, select, textarea {
|
|
padding: 10px;
|
|
font-size: 16px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
font-family: inherit;
|
|
}
|
|
|
|
input:focus, select:focus, textarea:focus {
|
|
outline: none;
|
|
border-color: #0066cc;
|
|
box-shadow: 0 0 0 3px rgba(0,102,204,0.1);
|
|
}
|
|
|
|
ul {
|
|
padding-left: 20px;
|
|
}
|
|
|
|
li {
|
|
margin: 8px 0;
|
|
}
|
|
|
|
/* ========================================
|
|
Ritz Debug Console Styles
|
|
======================================== */
|
|
|
|
.ritz-debug {
|
|
position: fixed;
|
|
top: 0;
|
|
right: 0;
|
|
width: 400px;
|
|
height: 100vh;
|
|
background: #1e1e1e;
|
|
color: #d4d4d4;
|
|
font-family: 'Consolas', 'Monaco', monospace;
|
|
font-size: 13px;
|
|
box-shadow: -2px 0 10px rgba(0,0,0,0.3);
|
|
transition: transform 0.3s ease;
|
|
z-index: 9999;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.ritz-debug.hidden {
|
|
transform: translateX(100%);
|
|
}
|
|
|
|
.ritz-debug .debug-header {
|
|
background: #252525;
|
|
padding: 15px;
|
|
border-bottom: 1px solid #3e3e3e;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.ritz-debug .debug-header h3 {
|
|
margin: 0;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: #d4d4d4;
|
|
}
|
|
|
|
.ritz-debug .debug-header button {
|
|
background: none;
|
|
border: none;
|
|
color: #d4d4d4;
|
|
font-size: 24px;
|
|
cursor: pointer;
|
|
padding: 0;
|
|
width: 30px;
|
|
height: 30px;
|
|
line-height: 1;
|
|
transform: none;
|
|
box-shadow: none;
|
|
}
|
|
|
|
.ritz-debug .debug-header button:hover {
|
|
color: #fff;
|
|
background: none;
|
|
transform: none;
|
|
box-shadow: none;
|
|
}
|
|
|
|
#ritz-debug .status {
|
|
position: static !important;
|
|
margin: 15px;
|
|
padding: 10px;
|
|
background: #2d2d30;
|
|
border: none !important;
|
|
border-left: 3px solid #007acc !important;
|
|
border-radius: 3px;
|
|
font-size: 12px;
|
|
box-shadow: none !important;
|
|
}
|
|
|
|
#ritz-debug .status.success {
|
|
background: #2d2d30 !important;
|
|
border-left-color: #4ec9b0 !important;
|
|
color: #4ec9b0 !important;
|
|
}
|
|
|
|
#ritz-debug .status.error {
|
|
background: #2d2d30 !important;
|
|
border-left-color: #f48771 !important;
|
|
color: #f48771 !important;
|
|
}
|
|
|
|
.debug-content {
|
|
padding: 15px;
|
|
}
|
|
|
|
.debug-section {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.debug-section h4 {
|
|
margin: 0 0 10px 0;
|
|
font-size: 12px;
|
|
text-transform: uppercase;
|
|
color: #858585;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.debug-section pre {
|
|
background: #252525;
|
|
padding: 10px;
|
|
border-radius: 3px;
|
|
overflow-x: auto;
|
|
margin: 0;
|
|
font-size: 11px;
|
|
line-height: 1.6;
|
|
color: #d4d4d4;
|
|
}
|
|
|
|
.debug-toggle-hint {
|
|
position: fixed;
|
|
bottom: 20px;
|
|
right: 20px;
|
|
background: #252525;
|
|
color: #d4d4d4;
|
|
padding: 8px 12px;
|
|
border-radius: 4px;
|
|
font-family: 'Consolas', 'Monaco', monospace;
|
|
font-size: 11px;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
|
opacity: 0;
|
|
animation: fadeInOut 3s ease-in-out;
|
|
pointer-events: none;
|
|
z-index: 10000;
|
|
}
|
|
|
|
@keyframes fadeInOut {
|
|
0%, 100% { opacity: 0; }
|
|
10%, 90% { opacity: 1; }
|
|
}
|
|
|
|
/* Adjust app container when debug is open */
|
|
body.debug-open #app {
|
|
margin-right: 420px;
|
|
}
|