**Bug:** Conditional branches (match/case) rendered empty content.
- Tabs would switch but show no content in each tab
- Wrapper div was created but children never appended to it
**Root Cause:**
In `_emit_conditional_branch_function`, statements were rendered but
their return values were discarded:
```python
for stmt in body:
self._emit_statement(stmt) # ← return value ignored!
```
**Fix:**
Append returned elements to wrapper, just like `_emit_with` does:
```python
for stmt in body:
child_var = self._emit_statement(stmt)
if child_var:
self.lines.append(f"call void @js_append_child(i32 %{wrapper_var}, i32 %{child_var})")
```
**LLVM Before:**
```llvm
%var87 = call i32 @js_create_element(...) ; wrapper
%var89 = call i32 @js_create_element(...) ; content
; ... build var89's children ...
ret i32 %var87 ; ← var89 never appended!
```
**LLVM After:**
```llvm
%var87 = call i32 @js_create_element(...) ; wrapper
%var89 = call i32 @js_create_element(...) ; content
; ... build var89's children ...
call void @js_append_child(i32 %var87, i32 %var89) ; ← FIX!
ret i32 %var87
```
**Result:**
✅ Tabs example now shows content in each tab
✅ Vertical tabs (nested conditionals) also work
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Reactive Text Nodes:**
- F-strings with signal dependencies now update automatically
- Added js_create_text_with_int() and js_update_text_with_int() for efficient "prefix{int}" pattern
- Text nodes stored in @reactive_text_N_node globals for updates
- Scan and track f-string dependencies during collection phase
**Binary Operations:**
- Implemented BinOp support in parser and codegen
- Maps Add → add i32, Sub → sub i32, Mult → mul i32, Div → sdiv i32, Mod → srem i32
- count = count + 1 now generates correct arithmetic instead of store i32 0
**LLVM Syntax Fixes (clang 20 compatibility):**
- Fixed getelementptr syntax: removed parentheses around type
- Before: getelementptr inbounds ([N x i8], [N x i8]* @str, ...)
- After: getelementptr inbounds [N x i8], [N x i8]* @str, ...
- Created _emit_string_ptr() helper for consistent string pointer generation
- Fixed in: element creation, attribute setting, event binding, select instructions
**Runtime (ritz-runtime.js):**
- Added js_create_text_with_int(prefix, intValue) - creates text node with "prefix" + intValue
- Added js_update_text_with_int(node, prefix, intValue) - updates text content
**Result:**
✅ TextInput counter increments/decrements correctly
✅ Tabs example works (reactive attributes, conditionals)
✅ Both examples compile with clang 20
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problem:**
- HTab/VTab components received onclick as a parameter
- When binding events, codegen used parameter name "onclick" instead of actual function
- Runtime couldn't find handler function in WASM exports
**Root Cause:**
We don't support passing functions as component parameters yet. When a child
component uses onclick=onclick where onclick is a parameter, we bind the
string "onclick" instead of tracking which function was actually passed.
**Fix:**
Inlined HTab and VTab components back into parent components. Now lambdas
are directly on Button elements, so they generate proper synthetic handlers
(_lambda_0_Tabs, _lambda_1_Tabs, etc.) that get exported.
**Result:**
✅ All tab click handlers now work
✅ Handlers exported: _lambda_0_Tabs, _lambda_1_Tabs, _lambda_2_Tabs,
_lambda_0_VerticalTabs, _lambda_1_VerticalTabs, _lambda_2_VerticalTabs
**TODO:**
Implement function pointers/delegates as component parameters to restore
component reusability (tracked in TYPE_SYSTEM_ROADMAP.md)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Bug Fixes:**
- Fixed duplicate js_append_child calls causing DOM element re-insertion
- Elements were being appended twice: once by self-append logic, once by parent loop
- This was breaking CSS transitions on vertical tabs (elements moved in DOM)
- Removed self-append logic from _emit_with() and _emit_yield_component()
**Refactoring:**
- Created reusable HTab and VTab components for horizontal/vertical tabs
- Reduced repetitive tab code from 9 lines per tab to 1 line
- Improved code maintainability and reusability
- WASM bundle size reduced from 5987 bytes to 4690 bytes (22% reduction!)
**Result:**
- CSS transitions now work smoothly on vertical tabs ✓
- All 55 tests passing ✓
- Cleaner, more composable tab components ✓
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Features:
- Track attributes that depend on signals (ternary expressions)
- Store element references in globals for reactive attributes
- Re-evaluate and update only changed attributes when signals change
- Similar to reactive conditionals but for attributes
Performance:
- Minimal DOM updates - only changed attributes are updated
- No full re-renders, no virtual DOM diffing
- Direct attribute updates via js_set_attribute
Example:
class_='tab active' if tab_index == 0 else 'tab'
When tab_index changes, only the class attribute updates.
Implementation:
- Collection phase: scan for attributes with signal dependencies
- Render phase: store element references to globals
- Signal update: re-evaluate expression and update attribute
All tests passing (55 passed, 2 skipped)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changes:
- Component render functions return i32 (root element) instead of void
- Nested components (yield SomeComponent()) now work correctly
- Added main() entry point that calls top-level component and sets root
- Updated runtime to call main() instead of render_ComponentName()
Note: Active state in ternary expressions still needs reactive updates
This commit fixes the nested component rendering issue
All tests passing (51 passed, 2 skipped)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes:
- Lambda functions now correctly pass arguments to handlers
- Function parameters properly emitted in LLVM IR
- Event handlers defined with parameter lists
New feature - Ternary expressions:
- Support Python ternary syntax (x if cond else y) in props
- Eliminates repetitive if/else blocks for active states
- Before: 6 lines per tab button
- After: 1 line with ternary expression
Example:
class_='tab active' if active else 'tab'
All tests passing (51 passed, 2 skipped)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Critical change that makes conditionals reactive:
- Added signal_conditional_deps map to track dependencies
- Added _extract_signals_from_expr() to find signals in expressions
- Populate dependency map during _collect_conditionals()
- Replace TODO comments in signal setters with reevaluate calls
How it works:
1. During collection, analyze if/match test/subject expressions
2. Extract all signal names referenced in conditions
3. Build map: signal_name -> [conditional_ids that use it]
4. When signal is updated (assign/augassign), call reevaluate for each dependent conditional
Example: set_tab_0() now:
store i32 0, i32* @signal_Tabs_tab_index
call void @reevaluate_conditional_4()
call void @reevaluate_conditional_5()
call void @reevaluate_conditional_6()
call void @reevaluate_conditional_7()
This makes tabs fully interactive!
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Implemented _emit_match_reactive() to generate init/reevaluate functions
- Modified _emit_match() to create markers and call init functions
- Init function pre-renders all cases and uses switch to insert active one
- Reevaluate function checks if subject changed and swaps DOM
Match init function:
- Calls _render_cond_X_case_Y() for each case
- Stores all case roots in globals
- Evaluates subject with switch instruction
- Inserts matching case at marker position
Fallback to _emit_match_static() if not collected.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Modified _emit_if() to:
- Create marker node via js_create_marker()
- Append marker to parent element
- Store marker ID in global
- Call _init_conditional_X() to render and insert active branch
Branch render functions work correctly:
- Build element tree locally
- Return root element ID
- Called by init function to pre-render all branches
Fallback to _emit_if_static() if conditional not collected.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add parent_element_stack to track nesting context
- Elements inside if/match branches append to parent
- Fixes: Li elements inside if statements now append to Ul parent
- Tabs now render visibly!
- TODO: Fix duplicate appends
- All tests passing (30/30)
- Add _emit_match() using LLVM switch instruction
- Handle value patterns and wildcard (case _:) patterns
- Each case gets its own basic block with branch to end
- Wildcard case becomes default label in switch
- All tests passing (30/30)
- Tabs.wasm size: 1908 → 3185 bytes
- Handle 'assign' type statements in _emit_handler_statement()
- Evaluate value expression and store to signal global
- Enables patterns like: setting_tab = 0
- All tests passing (30/30)