Commit Graph
16 Commits
Author SHA1 Message Date
Aaron SinclairandClaude d8a2e28fd5 Fix tabs onclick handlers by inlining components
**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>
2025-11-15 14:41:53 -08:00
Aaron SinclairandClaude e1219ea0c1 Implement binary operations (BinOp) support
**Parser changes (parser.py):**
- Added ast.BinOp handling in _transform_expr()
- Emits: {type: "binop", left: ..., op: "Add|Sub|Mult|Div|Mod", right: ...}

**Codegen changes (llvm_codegen_signals.py):**
- Added BinOp handling in _emit_expr()
- Maps Python ops to LLVM instructions:
  - Add → add i32
  - Sub → sub i32
  - Mult → mul i32
  - Div → sdiv i32 (signed division)
  - Mod → srem i32 (signed remainder)

**Result:**
Before: count = count + 1 → store i32 0 (WRONG!)
After:  count = count + 1 → load, add i32 1, store ✓

**Still TODO:**
The counter buttons now UPDATE the signal correctly, but the display
doesn't update because f-string text nodes aren't reactive yet.
We need reactive text nodes (similar to reactive attributes).

All 55 tests passing ✓

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-15 12:18:16 -08:00
Aaron SinclairandClaude 134ddc4c42 Fix duplicate append bug and componentize tabs
**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>
2025-11-15 11:39:53 -08:00
Aaron SinclairandClaude 1575571f3e Implement reactive attributes for minimal DOM updates
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>
2025-11-15 11:23:09 -08:00
Aaron SinclairandClaude b0e38db22c Fix nested components - render functions now return i32
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>
2025-11-15 11:16:23 -08:00
Aaron SinclairandClaude adf95f424d Fix lambda event handlers & add ternary expression support
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>
2025-11-15 11:07:58 -08:00
Aaron SinclairandClaude c947bcf6e0 Update Tabs example to use lambda event handlers
Replace verbose boilerplate (set_tab_0, set_tab_1, etc.) with clean
lambda syntax using the new feature.

Before (18 lines of boilerplate):
  def set_tab_0(): tab_index = 0
  def set_tab_1(): tab_index = 1
  def set_tab_2(): tab_index = 2
  def set_setting_0(): setting_tab = 0
  def set_setting_1(): setting_tab = 1
  def set_setting_2(): setting_tab = 2

After (2 lines):
  def set_tab(value): tab_index = value
  def set_setting(value): setting_tab = value

Usage:
  onclick=lambda: set_tab(0)
  onclick=lambda: set_setting(1)

Generated WASM: 8810 bytes (smaller than before!)
Lambda functions: 36 total (12 per component × 2 components)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-15 10:48:40 -08:00
Aaron SinclairandClaude ac7726df46 Wire up reactive signal updates to conditionals
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>
2025-11-15 03:57:15 -08:00
Aaron SinclairandClaude 3540c44abb Implement reactive match/case rendering
- 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>
2025-11-15 03:55:26 -08:00
Aaron SinclairandClaude 44db8ff022 Integrate reactive rendering into _emit_if()
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>
2025-11-15 03:53:38 -08:00
Aaron Sinclair 9f601b1787 Fix element appending in conditional branches
- 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)
2025-11-15 03:43:06 -08:00
Aaron Sinclair cdddbf675a Add component parameter support and fix JS runtime
- Emit function parameters in component signature
- JS runtime passes props as positional arguments
- Remove incorrect return value handling (components call js_set_root internally)
- Enables: def Tabs(orientation=0)
- All tests passing (30/30)
2025-11-15 03:39:43 -08:00
Aaron Sinclair 41474aceda Add basic component composition support
- Handle yield_component statement type
- Call child component render function
- Enables: yield VerticalTabs()
- All tests passing (30/30)
- Tabs.wasm size: 3185 → 3205 bytes
2025-11-15 03:36:38 -08:00
Aaron Sinclair cd89f75c1c Implement match/case statement code generation
- 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
2025-11-15 03:35:31 -08:00
Aaron Sinclair e18baf3106 Add direct signal assignment in event handlers
- 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)
2025-11-15 03:33:45 -08:00
Aaron Sinclair 81d51d5617 Parser: Support multiple components in one file, change args to params with defaults 2025-11-15 03:25:13 -08:00