Collision was checking grid[py+2] instead of grid[py+1].
Pieces stopped one row too early, creating gaps.
Now checks grid[py+1] (where top cells would land) with full
3-column width (px, px+1, px+2).
Pieces now stack directly on top of each other.
Added 'blocked' signal that checks frozen grid cells below active piece.
Collision check branches on py to inspect the correct grid row (13-19).
Gravity only drops when not blocked. Lock triggers on blocked state.
Freeze writes piece into correct row based on py (7 rows supported).
Added grid rendering for rows 13-15 (total 7 visible frozen rows).
Build output: 84KB
Compiler fixes:
- Component props with signal-dependent expressions now wrapped as
reactive getters (() => expr) at call site
- Component declarations handle fn-typed props via
{ get value() { return props.x(); } } for live reactivity
- Container style: prop wrapped in DS.effect when expr contains .value
- Timer merging: all same-interval 'every' statements grouped into
single setInterval with one DS.flush()
Breakout game improvements:
- Classic row order: blue top (far), red bottom (near paddle)
- All 5 rows have full collision detection (was only 2)
- Faster ball (4px/frame) and paddle (40px/keypress)
- Score/Lives badges now update in real-time
All 48 examples compile, 136 tests pass
Performance:
- All every N statements with same interval now merge into one setInterval
- Pong: 24 timers → 1, Breakout: 38 timers → 1 (single DS.flush per frame)
New examples:
- game-breakout.ds: brick-breaker with 5×10 colored bricks, keyboard, audio
- beats-viewer.ds: step sequencer spectator via relay
Fixes:
- _playTone/_playNoise early-exit when freq/duration <= 0
- Breakout score race: score+bounce checks before brick destruction
- Score sound effects in pong (220Hz/440Hz sawtooth)
All 48 examples compile, 136 tests pass
- New pong-viewer.ds: full visual court rendered from streamed state
- Extended container prop signal detection: js_val.contains('.value')
fixes DotAccess on stream proxies (game.value.p1y) not being reactive
- Verified two-tab relay demo: player → relay → viewer syncs in real-time
- All 46 examples compile, 136 tests pass
- Native DreamStack pong game with visual court (stack container),
CSS-positioned paddles/ball, 30fps game loop, AI tracking, auto-serve
- Parser: containers (column/row/stack) now support leading props
e.g. column { style: '...' } [children]
- Codegen: fixed signal detection for container layout props
(strip .value suffix for signal graph lookup)
- All 136 tests pass, 45 examples compile
- New streaming-dashboard.ds: first example combining components + streaming
- 4 Card grid with Badge, Button, match, stream receivers
- Receives live data from counter, clock, stats streams
- Button callbacks (Refresh/Reset) work within stream context
- All 8 examples pass regression (47,799 bytes)
- Duplicate prop keys now merge into Block expressions
click: a then click: b → Block([a, b])
- All actions fire in one click (was silently overwriting)
- streaming-mood.ds upgraded to semicolons
- streaming-stats.ds upgraded to semicolons
- Browser-verified: Happy button fires 3 actions (mood+color+energy)
- All 7 examples pass regression
- Added can_be_pattern() with look-ahead disambiguation
- Ident only treated as pattern if followed by -> or (
- Keywords (row/column/when/each) correctly terminate match
- Match arms now support: "active" -> row [ Badge + text ]
- Siblings after match (text, button, row) no longer consumed
- Project Manager updated with rich row/Badge match arms
- All 6 existing examples pass regression
- Dashboard with metrics, match status, progress bars
- Task Manager with dynamic add/remove via push/remove
- Team directory with member cards and status badges
- Settings with toggle and about section
- 64,622 bytes, zero console errors, all 11 components used
- Home/Counter/Todos/About with hash navigation
- State persists across route changes
- Uses existing router infrastructure (no compiler changes)
- navigate keyword, matchRoute, _route signal
- When/else inside slots: anchor parentNode is null during initial effect
Fixed with named effect function + requestAnimationFrame retry
- Match parser now terminates on ] } else tokens (works inside containers)
- Updated Progress/Badge components
- Added examples/showcase.ds: 5-section demo exercising all features
- AST: When(cond, body) -> When(cond, body, Option<else_body>)
- Parser: optional 'else -> expr' after when body
- Codegen: reactive DOM swap with anchor comments
- Signal graph + type checker updated for 3-arg When
- Component prop signal wrapping: .value compatible accessors
- Added examples/when-else-demo.ds
Codegen: BinOp::Div now emits Math.trunc(l / r)
- Clock displays 0:1:30 instead of 0.016:1.5:30
- Affects both emit_expr and predicate_to_js
Lexer: removed duplicate 'in' keyword mapping
- InKw at line 312 is canonical, removed old In at line 338
Examples: added project-tracker.ds (each loops + cards)
Add compose-metrics.ds (Layer 1): receives counter+clock+stats,
derives uptime/events/status, re-streams on /peer/metrics. This app
is BOTH a receiver and a source.
Add compose-master.ds (Layer 2): receives chained metrics from
Layer 1 + mood direct from Layer 0. Demonstrates multi-layer
signal composition with independent stream mixing.
Verified: Uptime: 51s, Total Events: 9 flowing through the full
three-layer chain to the master dashboard.
The 'default' relay channel accumulated stale Source connections from
previous sessions, causing frame delivery issues to Receivers on
/stream/default. Moving counter to an explicit /peer/counter channel
(matching clock, stats, mood pattern) fixes the composition dashboard.
All 4 streams now show live data: Count: 3, Doubled: 6.
Add 3 new streaming apps with explicit output declarations:
- streaming-clock.ds: output hours, minutes, seconds (tick private)
- streaming-stats.ds: output total, average, max (sum private)
- streaming-mood.ds: output mood, energy, color (clicks private)
Add compose-dashboard.ds that receives all 4 streams via unique
relay channels (/stream/default, /stream/clock, /stream/stats,
/stream/mood) into a single dashboard view.
Each app demonstrates selective signal registration — only declared
outputs are streamed, internal state remains private.
Add 'output: field1, field2' syntax to stream declarations to control
which signals are exposed over the relay. Only listed signals are
registered in _signalRegistry (and thus streamed). Omitting output
streams all signals (backwards-compatible).
Also strips internal sync metadata (_pid, _v) from receiver state
so composition consumers only see clean signal values.
Parser: parse comma-separated idents after 'output:' key
AST: Vec<String> output field on StreamDecl
Codegen: conditional _registerSignal, delete _pid/_v on receive
Example: stream counter on 'ws://...' { mode: signal, output: count, doubled }
- Register derived signals in _signalRegistry so _streamSync includes them
- Auto-sync all signals (source + derived) after flush() recomputes effects
- Fix Object.assign identity check: create new object so signal setter detects changes
- Change _connectStream receiver path from /signal/main to /stream/default
- Initialize stream state with {} instead of null to prevent crashes
- Emit StreamFrom bindings directly without double-wrapping in signal()
Verified: static build shows Count: 9, Doubled: 18 on composition page.
HMR interference with WebSocket connections is a separate issue.
- Added /peer/{name} route to relay: all clients are equal peers
- handle_peer: binary broadcast to all other peers, catchup for late joiners
- Simplified runtime: single /peer/ WS replaces dual source+receiver
- _peerId: random 8-char ID prevents self-echo from broadcast
- _pid in each diff JSON, filtered in _applyRemoteDiff
- streaming-counter.ds: reactive counter with 'stream counter on ws://...'
- streaming-receiver.ds: remote receiver with 'stream from ws://...'
- E2E verified: compiles to streaming JS, connects to relay, sends signal diffs
- Add scene container to AST, lexer, parser, analyzer, and codegen
- Add circle/rect/line as UI elements for physics body declaration
- Compile scene {} to canvas + async WASM init + Rapier2D PhysicsWorld
- Reactive gravity via DS.effect() — bodies wake on gravity change
- Mouse drag interaction with impulse-based body movement
- Compile-time hex color parsing for body colors
- Fix is_signal_ref matching numeric literals (700.value bug)
- Fix body variable uniqueness (next_node_id per body)
- Fix gravity signal detection (check AST Ident before emit_expr)
- Add physics.ds example with 5 bodies + 4 gravity control buttons
- Update DREAMSTACK.md and IMPLEMENTATION_PLAN.md with Phase 10-11
- 39 tests pass across all crates, 22KB output