- Add cliff.toml for git-cliff changelog generation (one-line entries,
no commit body dumps, improve/refine prefixes mapped)
- Add @changesets/cli config and README in .changeset/
- Add release.sh script with per-package version bumps from changesets,
changeset-driven per-crate changelog updates, and --all/--dry-run flags
- Switch all crates from workspace version to independent version = "0.1.0"
- Generate clean root CHANGELOG.md and per-crate CHANGELOGs with [0.1.0]
- Retag v1.0.0 → v0.1.0 to match actual crate versions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rewrote game-tetris.ds from scratch using a single flat 200-element
grid array instead of 20 separate row signals, eliminating all row
dispatch chains. Added SRS-standard rotations for all 7 pieces via
array lookups, full collision detection (down/left/right/rotation),
line clear with slice/concat cascade, computed ghost piece with
togglable visibility (G key or button), hard drop, per-piece colors,
and next piece preview.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaced single monolithic blocked expression with 4 composable signals:
- blockedWall: piece-type aware bottom wall (T=18, others=19)
- blockedTop: grid[py+1] at px,px+1,px+2 (all pieces)
- blockedFoot: grid[py+2] at px+1 (T-piece foot only)
- blockedI4: grid[py+1] at px+3 (I-piece 4th cell only)
- blocked: OR combination of all 4
Fixed auto-drop cap from py<18 to py<19 so flat pieces
reach the bottom row. Fixed hard drop and keyboard per piece type.
Added px+3 freeze writes for I-piece (piece==1) at all 20 rows.
Fixed I-piece rendering from py+1 to py for consistent positioning.
Extended collision to check px+3 for I-piece at every py level.
I-piece now correctly shows and persists as 4 cells in a row.
Cell 3 (foot/bottom cell) was always visible for ALL pieces,
creating a phantom T-shape foot that overlapped frozen blocks.
Now hidden via opacity:0 for non-T/non-I pieces.
Also added frozen grid rendering for rows 0-12 (was only 13-19).
All 20 rows now fully rendered, frozen, and collision-checked.
Extended collision detection from py 12-17 to py 0-17 (full grid).
Extended freeze writes from rows 13-19 to all 20 rows (0-19).
Extended T-piece bottom cell freeze from 6 rows to all 19 rows.
Pieces now correctly collide at ANY height in the grid.
Previously pieces fell through tall stacks because collision
only checked rows 12-17.
grid[py+2] at px+1 now only checked when piece==6 (T-piece).
Other pieces (I, J, L, O, S, Z) only check grid[py+1] for
top row collision. Flat pieces stack flush; T-pieces correctly
account for their protruding foot.
Added grid[py+2] at px+1 check for the T-piece's protruding
bottom cell. Collision now checks 4 cells total per piece:
- grid[py+1] at px, px+1, px+2 (top row destination)
- grid[py+2] at px+1 (bottom cell destination)
This prevents pieces from passing through the angled/protruding
parts of frozen T-shaped blocks.
ArrowDown soft drop, Space hard drop, and Drop button all bypassed
the blocked signal, letting pieces pass through frozen blocks.
Now all three gate on blocked before modifying py.
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
Refinement pass:
- DotAccess: unwraps Signal/Derived before field lookup
- UnaryOp: uses unification instead of manual matching
- Call: unifies each arg with param type, applies subst to return
- List: unifies all element types (not just first)
- If/else: unifies both branches, checks condition is Bool
- When/else: unifies body with else body, checks condition
- Match: unifies all arm types for consistency
- Assign: checks assigned value compatible with variable type
- ForIn: binds iteration variable from Array element type
Tests: 39 ds-types (up from 34), 164 workspace total, 0 failures
Type System Completion:
- Add unify() with occurs check for proper type variable binding
- Add apply_subst() to chase type variables through substitution map
- Add SignalInfo/SignalClass for graph-based signal classification
- Add check_program_with_signals() accepting optional signal graph data
- Push Dom effect handler scope automatically when checking view blocks
- Wire unification into BinOp, comparison, and logical operator inference
- Include List/Record literals in source signal heuristic
Tests: 34 ds-types tests (up from 11), 159 workspace total, 0 failures
- DREAMSTACK.md: rewritten with accurate counts (48 examples, 14
components, 136 tests), full CLI reference, architecture diagrams,
quick start guide, comparison table, and phased roadmap
- IMPLEMENTATION_PLAN.md: rewritten with all 10 phases showing
accurate completion status, current capabilities, and next steps
- BITSTREAM_INTEGRATION.md: updated test count (82 → 136)
- USE_CASES.md and STREAM_COMPOSITION.md: already current, unchanged
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
Relay HTTP /meta endpoint:
- GET /meta → JSON with all channel stats
- GET /meta/{name} → JSON with specific channel stats, schema, current state
- Uses TCP peek to intercept raw HTTP before WS handshake
- CORS headers for browser access
Signal deduplication:
- _lastSentValues tracks last-sent value per signal
- JSON.stringify comparison skips unchanged values
- Prevents redundant WS frames from derived signals
Periodic auto-sync:
- Every 50 diff batches, source sends full SignalSync (0x30)
- Relay can compact its cache instead of accumulating infinite diffs
- Resets pending_signal_diffs in relay cache
All 57 relay tests pass. All 9 examples pass.
- 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
- Imports Card, Badge, Button from components/
- Counter with callback props, Greeting with when/else
- Mood match expression with Badge, Todo dynamic lists
- Full init → build flow verified (51,401 bytes, 0 errors)