enzotar
5f09886c3c
fix: bidirectional streaming sync — phone→laptop now works
...
- Fix URL routing: /source/default and /stream/default paths
- _streamDiff sends via both source WS and receiver WS (INPUT flag)
- Source rebroadcasts 0x31 diffs from receivers to all other receivers
- Echo loop guard prevents infinite rebroadcast
2026-02-25 21:11:12 -08:00
enzotar
69c7ff1e22
feat: bidirectional signal streaming sync
...
- _signalRegistry: maps signal names → signal objects
- _registerSignal: called after each DS.signal() declaration
- _applyRemoteDiff: JSON diff → update local signals + flush
- _initStreamReceiver: parallel receiver WS for incoming diffs
- Echo loop guard: _applyingRemoteDiff prevents re-broadcasting
- 0x31 handler in _handleRemoteInput for signal diff frames
Changes sync both directions: laptop → phone, phone → laptop.
2026-02-25 21:00:57 -08:00
enzotar
55ec9353ae
feat: v2 codegen hardening — scoped local variables
...
Replace flat HashSet<String> local_vars with Vec<HashSet<String>> scope
stack. For-in loops push/pop scopes for loop variables. Nested loops
no longer clobber outer loop variable visibility.
Methods: push_scope(), pop_scope(), is_local_var()
110 tests, 0 failures.
2026-02-25 20:39:41 -08:00
enzotar
6368b798cf
feat: v2 module system — import/export with multi-file compilation
...
Syntax:
import { Counter, shared_count } from "./shared"
export let shared_count = 0
export component Counter = ...
Implementation:
- Lexer: Import, Export keywords
- AST: ImportDecl(names, source), Export(name, inner_decl)
- Parser: parse_import_decl, parse_export_decl
- CLI: resolve_imports() — recursive file resolution, dedup, inline
Resolves relative paths, adds .ds extension, handles transitive imports.
110 tests, 0 failures.
2026-02-25 20:36:18 -08:00
enzotar
26d6c4f17a
feat: v2 built-in functions — 90+ native functions
...
Array: len, push, pop, filter, map, concat, contains, reverse, slice,
indexOf, find, some, every, flat, sort (mutating ops re-trigger signal)
Math: abs, min, max, floor, ceil, round, random, sqrt, pow, sin, cos,
tan, atan2, clamp, lerp
String: split, join, trim, upper, lower, replace, starts_with, ends_with,
char_at, substring
Conversion: int, float, string, bool
Console: log, debug, warn
Timer: delay
Also adds ExprStatement support for top-level expressions (log, push, etc).
110 tests, 0 failures.
2026-02-25 20:30:08 -08:00
enzotar
2aa2c7ad8e
feat: step sequencer demo — reactive pads, playhead, BPM
...
Step sequencer: 4 instruments × 8 steps, timer-driven playhead,
toggleable pads, BPM controls with streaming. 75 lines of .ds code.
Parser fixes:
- UI elements checked before LParen (button (if ...) is element, not call)
- Element args support parenthesized expressions: button (if cond ...)
- StringInterp recognized as valid string start in parse_primary/parse_element
Codegen fixes:
- emit_expr checks local_vars: loop var i emits 'i' not 'i.value'
- Array index mutations re-trigger signal: pads.value = [...pads.value]
110 tests, 0 failures.
2026-02-25 19:33:12 -08:00
enzotar
2d07b1652a
feat: v2 phase 1 — array access, timer, string interpolation
...
Array access: Expr::Index in AST, [expr] postfix parsing, codegen for
reads (grid.value[i.value]) and writes (event handler assignments with
root signal extraction for stream diff broadcasting).
Timer: 'every N -> expr' declaration. Every keyword in lexer. EveryDecl
in AST. parse_every_decl in parser. setInterval codegen with DS.flush.
String interpolation: already committed separately.
Type checker: handles Expr::Index (infers array element type).
110 tests, 0 failures.
2026-02-25 19:20:20 -08:00
enzotar
cde84ae270
feat: production hardening — relay v1.0.0, receiver protocol completeness
...
Relay v1.0.0:
- Max receivers per channel (default: 64) with rejection counting
- Max channels limit (default: 256) with over-limit rejection
- Channel GC: periodic scan removes idle channels after grace period (30s)
- Source reconnection: cache preserved on disconnect, new input channels
- Stats: peak_receivers, total_connections, rejected_connections, uptime
- ChannelState::is_idle(), grace_period_expired() for lifecycle mgmt
- StateCache::clear(), has_state() for cache introspection
- Banner shows config: max receivers, max channels, grace period
- 54 tests (+8 new: channel_max_limit, idle detection, grace period, cache ops)
Receiver (_connectStream):
- Complete frame type handling: SignalSync (full replace), SignalDiff (merge),
Pixels/Keyframe (emit stream_frame), DeltaPixels (XOR + RLE decode),
Ping (keepalive), StreamEnd (emit stream_end)
- RLE decoder for delta pixel frames
- Exponential backoff reconnect (1s → capped at 10s)
- Connection stats: frames, bytes, reconnects
- Event emissions: stream_frame, stream_end for app-level handling
105 tests, 0 failures across workspace
2026-02-25 18:30:40 -08:00
enzotar
392e478351
feat: WebRTC transport — peer-to-peer data channels with auto-fallback
...
Relay:
- /signal/{channel} path for SDP/ICE exchange via WebSocket
- handle_signaling broadcasts text messages between signaling peers
- signaling_tx broadcast channel in ChannelState
- 46 ds-stream tests (+2 signaling path tests)
JS Runtime:
- _initWebRTC(signalingUrl, streamUrl, mode) with RTCPeerConnection
- Unordered DataChannel (ordered:false, maxRetransmits:0) for low latency
- Auto-fallback: WebSocket starts immediately, WebRTC upgrades in ≤5s
- Data channel override of _streamSend for transparent binary protocol
Parser/AST:
- StreamTransport enum (WebSocket, WebRTC)
- transport field in StreamDecl
- Parses: stream x on url { mode: signal, transport: webrtc }
Codegen:
- WebRTC: emits DS._initWebRTC(sigUrl, streamUrl, mode)
- WebSocket: emits DS._initStream(url, mode) (unchanged)
97 tests, 0 failures
2026-02-25 15:02:31 -08:00
enzotar
439a775dec
feat(compiler): complete bitstream integration — all 9 changes
...
- AST: StreamDecl, StreamMode, Declaration::Stream, StreamFrom struct variant
- Lexer: Pixel, Delta, Signals keywords
- Parser: parse_stream_decl with mode block, fixed TokenKind::On match
- Signal graph: streamable flag, SignalManifest, Declaration::Stream detection
- Checker: StreamFrom { source, .. } pattern
- Codegen: DS._initStream(), DS._connectStream(), DS._streamDiff() hooks
- Runtime JS: full streaming layer with binary protocol encoding
- Layout: to_bytes/from_bytes on LayoutRect
82 tests pass (5 new: 3 parser stream + 2 analyzer streamable)
2026-02-25 13:26:59 -08:00
enzotar
d86818ca6a
feat(compiler): full bitstream integration across 7 pipeline stages
...
AST: StreamDecl, StreamMode, Expr::StreamFrom { source, mode }
Lexer: Pixel, Delta, Signals keywords
Parser: parse_stream_decl() with mode parsing, StreamFrom expression
Signal Graph: streamable flag on SignalNode, auto-detect stream decls
Type Checker: StreamFrom returns Type::Stream
Codegen: emit_stream_init phase, StreamFrom → DS.streamConnect(),
streaming runtime JS (WebSocket relay, binary protocol, signal frames,
remote input handler, auto-reconnect)
CLI: 'dreamstack stream' command — compile+serve with streaming enabled,
auto-inject stream declaration for the first view
All 77 workspace tests pass, 0 failures.
2026-02-25 13:13:21 -08:00
enzotar
ea64617569
feat: physics language integration — scene container with Rapier2D WASM
...
- 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
2026-02-25 10:58:43 -08:00
enzotar
a35d44bd59
feat: two-way binding, form props, and async resources
...
Phase 8 features:
- bind prop: two-way signal <-> input sync
- placeholder, value, style, disabled props
- DS.resource() for reactive async data fetching
- DS.fetchJSON() convenience wrapper
- Props-only element parsing: input { bind: x }
- examples/form.ds: contact form with validation
Fixed: double .value.value bug in bind codegen.
2026-02-25 08:08:37 -08:00
enzotar
adff40d47f
feat: hash-based router + keyed list reconciliation
...
Phase 7 features:
- route "/path" -> body declarations with hash-based routing
- navigate "/path" built-in function
- DS.route signal tracks current hash path
- DS.matchRoute() with :param pattern matching
- DS.keyedList() for keyed DOM reconciliation
- Route-aware mounting: views + routes compose
- examples/router.ds: 3-page app (home/about/counter)
- State persists across route changes (10818 bytes)
2026-02-25 07:54:00 -08:00
enzotar
ca45c688df
feat: for-in list rendering + component system
...
Phase 6 features:
- ForIn reactive list rendering: for item in items -> body
- Optional index binding: for item, idx in items -> body
- Component declarations: component Name(props) = body
- Component instantiation: ComponentUse { name, props }
Added across 5 crates:
- ds-parser: For/In/Component tokens, ForIn/ComponentDecl AST nodes
- ds-codegen: reactive list effect, component function emission
- ds-types: ForIn/ComponentUse type inference
- local_vars tracking for non-reactive for-in loop vars
Includes examples/list.ds showcasing for-in + when + signals.
2026-02-25 01:33:28 -08:00
enzotar
c9b1913a57
feat: dev server with file watching + poll-based HMR
...
- File watcher thread using notify crate (100ms debounce)
- Auto-recompiles .ds files on change with timed output
- Poll-based HMR: client fetches /__hmr every 500ms for version
- Injects HMR script into compiled HTML before </body>
- Error pages rendered with DreamStack styling on compile failure
- Version counter shared between watcher and HTTP server via AtomicU64
2026-02-25 01:01:59 -08:00
enzotar
462663830e
feat: Phase 3+4 — Cassowary constraint solver + type system
...
Phase 3 — Constraint Layout:
- ds-layout crate: Gaussian elimination constraint solver
- eq, gte, lte, sum_eq, ratio constraints with strength priority
- get_rect() for resolving absolute (x,y,w,h) layouts
- 7 tests: simple eq, two-var eq, sums, rects, gte, ratio, 3-panel
Phase 4 — Type System:
- ds-types crate: Signal<T>, Derived<T>, Stream<T>, Spring<T>, View
- Effect types: Http, Storage, Time, Dom, Custom(name)
- Hindley-Milner type checker with signal-awareness
- Elm-inspired error messages (TYPE MISMATCH, UNHANDLED EFFECT, etc.)
- 11 tests: type display, reactive checks, mismatch errors, etc.
Total: 34 tests passing across 6 crates
2026-02-25 00:22:35 -08:00
enzotar
a634152318
feat: DreamStack compiler foundation — Phase 0/1
...
Complete compiler pipeline from .ds source to reactive browser apps:
- ds-parser: lexer (string interpolation, operators, keywords) + recursive
descent parser with operator precedence + full AST types
- ds-analyzer: signal graph extraction (source/derived classification),
topological sort for glitch-free propagation, DOM binding analysis
- ds-codegen: JavaScript emitter with embedded reactive runtime (~3KB
signal/derived/effect system) and dark-theme CSS design system
- ds-cli: build (compile to HTML+JS), dev (live server), check (analyze)
Verified working: source signals, derived signals, event handlers,
conditional rendering (when), 12 unit tests passing, 6.8KB output.
2026-02-25 00:03:06 -08:00