-- DreamStack Tetris — Signal Composition Showcase -- -- This game demonstrates how DreamStack composes independent signal layers: -- -- Layer 1: DATA SIGNALS — grid cells, piece encoding, bag randomizer -- Layer 2: PHYSICS SIGNALS — gravity timer, drop speed, lock delay -- Layer 3: INPUT SIGNALS — keyboard events mapped to commands -- Layer 4: DERIVED SIGNALS — ghost position, line detection, game over -- Layer 5: UI SIGNALS — score, level, next piece preview -- Layer 6: STREAM SIGNALS — real-time spectator broadcast -- -- Each layer is self-contained; the reactive runtime composites them. -- -- Grid: 10 wide x 20 tall. Rows g0 (top) to g19 (bottom). -- Cell values: 0=empty, 1=cyan(I), 2=blue(J), 3=orange(L), -- 4=yellow(O), 5=green(S), 6=purple(T), 7=red(Z) -- -- Run: -- Tab 1: cargo run -p ds-stream (relay) -- Tab 2: dreamstack dev examples/game-tetris.ds (player) import { Badge } from "../registry/components/badge" -- ================================================================ -- LAYER 1: DATA SIGNALS — Frozen grid + active piece state -- ================================================================ -- Frozen grid: 20 rows x 10 columns (row 0 = top, row 19 = bottom) let g0 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] let g1 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] let g2 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] let g3 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] let g4 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] let g5 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] let g6 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] let g7 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] let g8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] let g9 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] let g10 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] let g11 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] let g12 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] let g13 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] let g14 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] let g15 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] let g16 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] let g17 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] let g18 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] let g19 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] -- Active piece: 1=I, 2=J, 3=L, 4=O, 5=S, 6=T, 7=Z let piece = 6 let rotation = 0 let px = 3 let py = 0 -- Next piece for preview let nextPiece = 1 -- ================================================================ -- LAYER 2: PHYSICS SIGNALS — Gravity, speed, level -- ================================================================ let score = 0 let lines = 0 let level = 1 let gameOver = 0 let paused = 0 let gravityTick = 0 let dropInterval = 24 let lockTick = 0 -- ================================================================ -- LAYER 3: INPUT SIGNALS — Keyboard events -- ================================================================ on keydown(ev) -> px = if gameOver then px else (if paused then px else (if ev.key == "ArrowLeft" then (if px > 0 then px - 1 else px) else px)) on keydown(ev) -> px = if gameOver then px else (if paused then px else (if ev.key == "ArrowRight" then (if px < 7 then px + 1 else px) else px)) on keydown(ev) -> rotation = if gameOver then rotation else (if paused then rotation else (if ev.key == "ArrowUp" then (rotation + 1) % 4 else rotation)) on keydown(ev) -> py = if gameOver then py else (if paused then py else (if ev.key == "ArrowDown" then (if py < 18 then py + 1 else py) else py)) on keydown(ev) -> py = if gameOver then py else (if paused then py else (if ev.key == " " then 18 else py)) on keydown(ev) -> paused = if ev.key == "p" then (if paused then 0 else 1) else paused -- ================================================================ -- LAYER 4: DERIVED SIGNALS — Collision, gravity, lock, line clear -- ================================================================ -- Collision detection: can the piece move down? -- T-piece at py has top cells at row py, bottom cell at py+1. -- To move down: top cells move to py+1, bottom cell to py+2. -- Check grid[py+1] at px, px+1, px+2 (top row would land here). -- Bottom wall: py >= 18 (piece can't go lower). let blocked = 0 -- Check collision against the CORRECT row: grid[py+1] for top cells every 33 -> blocked = if py > 17 then 1 else (if py == 17 then (if g18[px] > 0 then 1 else (if g18[px + 1] > 0 then 1 else (if g18[px + 2] > 0 then 1 else 0))) else (if py == 16 then (if g17[px] > 0 then 1 else (if g17[px + 1] > 0 then 1 else (if g17[px + 2] > 0 then 1 else 0))) else (if py == 15 then (if g16[px] > 0 then 1 else (if g16[px + 1] > 0 then 1 else (if g16[px + 2] > 0 then 1 else 0))) else (if py == 14 then (if g15[px] > 0 then 1 else (if g15[px + 1] > 0 then 1 else (if g15[px + 2] > 0 then 1 else 0))) else (if py == 13 then (if g14[px] > 0 then 1 else (if g14[px + 1] > 0 then 1 else (if g14[px + 2] > 0 then 1 else 0))) else (if py == 12 then (if g13[px] > 0 then 1 else (if g13[px + 1] > 0 then 1 else (if g13[px + 2] > 0 then 1 else 0))) else 0)))))) -- Gravity tick every 33 -> gravityTick = if paused then gravityTick else (if gameOver then gravityTick else gravityTick + 1) -- Auto-drop: only if NOT blocked every 33 -> py = if paused then py else (if gameOver then py else (if blocked then py else (if gravityTick > dropInterval then (if py < 18 then py + 1 else py) else py))) every 33 -> gravityTick = if gravityTick > dropInterval then 0 else gravityTick -- Lock detection: start counting when blocked every 33 -> lockTick = if blocked then lockTick + 1 else 0 -- ── Freeze piece into grid at py position on lock ── -- The piece top-row cells go into row py, bottom-row cell into py+1 -- For T-piece: (px,py), (px+1,py), (px+2,py) and (px+1,py+1) -- We write the top 3 cells into grid[py] and bottom cell into grid[py+1] -- Freeze top cells into grid row py (for each possible py value) -- Row 19 every 33 -> g19[px] = if lockTick == 3 then (if py == 19 then piece else g19[px]) else g19[px] every 33 -> g19[px + 1] = if lockTick == 3 then (if py == 19 then piece else g19[px + 1]) else g19[px + 1] every 33 -> g19[px + 2] = if lockTick == 3 then (if py == 19 then piece else g19[px + 2]) else g19[px + 2] -- Row 18 every 33 -> g18[px] = if lockTick == 3 then (if py == 18 then piece else g18[px]) else g18[px] every 33 -> g18[px + 1] = if lockTick == 3 then (if py == 18 then piece else g18[px + 1]) else g18[px + 1] every 33 -> g18[px + 2] = if lockTick == 3 then (if py == 18 then piece else g18[px + 2]) else g18[px + 2] -- Row 17 every 33 -> g17[px] = if lockTick == 3 then (if py == 17 then piece else g17[px]) else g17[px] every 33 -> g17[px + 1] = if lockTick == 3 then (if py == 17 then piece else g17[px + 1]) else g17[px + 1] every 33 -> g17[px + 2] = if lockTick == 3 then (if py == 17 then piece else g17[px + 2]) else g17[px + 2] -- Row 16 every 33 -> g16[px] = if lockTick == 3 then (if py == 16 then piece else g16[px]) else g16[px] every 33 -> g16[px + 1] = if lockTick == 3 then (if py == 16 then piece else g16[px + 1]) else g16[px + 1] every 33 -> g16[px + 2] = if lockTick == 3 then (if py == 16 then piece else g16[px + 2]) else g16[px + 2] -- Row 15 every 33 -> g15[px] = if lockTick == 3 then (if py == 15 then piece else g15[px]) else g15[px] every 33 -> g15[px + 1] = if lockTick == 3 then (if py == 15 then piece else g15[px + 1]) else g15[px + 1] every 33 -> g15[px + 2] = if lockTick == 3 then (if py == 15 then piece else g15[px + 2]) else g15[px + 2] -- Row 14 every 33 -> g14[px] = if lockTick == 3 then (if py == 14 then piece else g14[px]) else g14[px] every 33 -> g14[px + 1] = if lockTick == 3 then (if py == 14 then piece else g14[px + 1]) else g14[px + 1] every 33 -> g14[px + 2] = if lockTick == 3 then (if py == 14 then piece else g14[px + 2]) else g14[px + 2] -- Row 13 every 33 -> g13[px] = if lockTick == 3 then (if py == 13 then piece else g13[px]) else g13[px] every 33 -> g13[px + 1] = if lockTick == 3 then (if py == 13 then piece else g13[px + 1]) else g13[px + 1] every 33 -> g13[px + 2] = if lockTick == 3 then (if py == 13 then piece else g13[px + 2]) else g13[px + 2] -- Freeze T-piece bottom center cell into grid[py+1] -- (T has a cell at px+1, py+1; also applicable to O, S, Z shapes) every 33 -> g19[px + 1] = if lockTick == 3 then (if py == 18 then (if piece == 6 then 6 else g19[px + 1]) else g19[px + 1]) else g19[px + 1] every 33 -> g18[px + 1] = if lockTick == 3 then (if py == 17 then (if piece == 6 then 6 else g18[px + 1]) else g18[px + 1]) else g18[px + 1] every 33 -> g17[px + 1] = if lockTick == 3 then (if py == 16 then (if piece == 6 then 6 else g17[px + 1]) else g17[px + 1]) else g17[px + 1] every 33 -> g16[px + 1] = if lockTick == 3 then (if py == 15 then (if piece == 6 then 6 else g16[px + 1]) else g16[px + 1]) else g16[px + 1] every 33 -> g15[px + 1] = if lockTick == 3 then (if py == 14 then (if piece == 6 then 6 else g15[px + 1]) else g15[px + 1]) else g15[px + 1] every 33 -> g14[px + 1] = if lockTick == 3 then (if py == 13 then (if piece == 6 then 6 else g14[px + 1]) else g14[px + 1]) else g14[px + 1] -- Spawn new piece after lock every 33 -> piece = if lockTick == 3 then nextPiece else piece every 33 -> nextPiece = if lockTick == 3 then (gravityTick % 7 + 1) else nextPiece every 33 -> py = if lockTick == 3 then 0 else py every 33 -> px = if lockTick == 3 then 3 else px every 33 -> rotation = if lockTick == 3 then 0 else rotation every 33 -> score = if lockTick == 3 then score + 10 else score -- Line clear: row 19 full (check 5 sample cells) every 33 -> score = if g19[0] > 0 then (if g19[2] > 0 then (if g19[4] > 0 then (if g19[6] > 0 then (if g19[8] > 0 then score + 100 else score) else score) else score) else score) else score every 33 -> lines = if g19[0] > 0 then (if g19[2] > 0 then (if g19[4] > 0 then (if g19[6] > 0 then (if g19[8] > 0 then lines + 1 else lines) else lines) else lines) else lines) else lines -- Shift rows down on clear: copy row 18 into row 19 every 33 -> g19[0] = if g19[0] > 0 then (if g19[4] > 0 then (if g19[8] > 0 then g18[0] else g19[0]) else g19[0]) else g19[0] every 33 -> g19[1] = if g19[0] > 0 then (if g19[4] > 0 then (if g19[8] > 0 then g18[1] else g19[1]) else g19[1]) else g19[1] every 33 -> g19[2] = if g19[0] > 0 then (if g19[4] > 0 then (if g19[8] > 0 then g18[2] else g19[2]) else g19[2]) else g19[2] every 33 -> g19[3] = if g19[0] > 0 then (if g19[4] > 0 then (if g19[8] > 0 then g18[3] else g19[3]) else g19[3]) else g19[3] every 33 -> g19[4] = if g19[0] > 0 then (if g19[4] > 0 then (if g19[8] > 0 then g18[4] else g19[4]) else g19[4]) else g19[4] every 33 -> g19[5] = if g19[0] > 0 then (if g19[4] > 0 then (if g19[8] > 0 then g18[5] else g19[5]) else g19[5]) else g19[5] every 33 -> g19[6] = if g19[0] > 0 then (if g19[4] > 0 then (if g19[8] > 0 then g18[6] else g19[6]) else g19[6]) else g19[6] every 33 -> g19[7] = if g19[0] > 0 then (if g19[4] > 0 then (if g19[8] > 0 then g18[7] else g19[7]) else g19[7]) else g19[7] every 33 -> g19[8] = if g19[0] > 0 then (if g19[4] > 0 then (if g19[8] > 0 then g18[8] else g19[8]) else g19[8]) else g19[8] every 33 -> g19[9] = if g19[0] > 0 then (if g19[4] > 0 then (if g19[8] > 0 then g18[9] else g19[9]) else g19[9]) else g19[9] -- Level up every 33 -> level = if lines > 0 then (lines / 10 + 1) else 1 every 33 -> dropInterval = if level > 9 then 3 else (27 - level * 3) -- Game over: any cell in row 0 filled every 33 -> gameOver = if g0[3] > 0 then 1 else (if g0[4] > 0 then 1 else (if g0[5] > 0 then 1 else gameOver)) -- ================================================================ -- LAYER 5: SOUND SIGNALS -- ================================================================ every 33 -> play_tone(if lockTick == 3 then 220 else 0, 80) every 33 -> play_tone(if gameOver then 110 else 0, 500, "sawtooth") -- ================================================================ -- LAYER 6: STREAM SIGNALS — Spectator broadcast -- ================================================================ stream tetris on "ws://localhost:9100/peer/tetris" { mode: signal, output: piece, rotation, px, py, nextPiece, score, lines, level, gameOver, paused, g0, g1, g2, g3, g4, g5, g6, g7, g8, g9, g10, g11, g12, g13, g14, g15, g16, g17, g18, g19 } -- ================================================================ -- VIEW: All 6 signal layers composited into one display -- ================================================================ view tetris_game = column [ text "DreamStack Tetris" { variant: "title" } text "Signal Composition Demo" { variant: "subtitle" } -- Status bar (Layer 5 UI signals <- Layer 2 physics signals) row [ Badge { label: "Score: {score}", variant: "success" } Badge { label: "Lines: {lines}", variant: "info" } Badge { label: "Level: {level}", variant: "warning" } Badge { label: "Next: {nextPiece}", variant: "default" } ] row [ -- Main Board: 300x600 (10x20 cells at 30px each) stack { style: "position:relative; width:300px; height:600px; background:linear-gradient(180deg,#0a0a1a,#111133); border:2px solid #334155; border-radius:12px; overflow:hidden; box-shadow:0 0 60px rgba(99,102,241,0.15)" } [ -- Grid lines (subtle, UI-only signals) for r in [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19] -> column { style: "position:absolute; width:300px; height:1px; top:{r * 30}px; background:rgba(99,102,241,0.08)" } [] for k in [0,1,2,3,4,5,6,7,8,9] -> column { style: "position:absolute; width:1px; height:600px; left:{k * 30}px; background:rgba(99,102,241,0.08)" } [] -- Frozen grid: row 13 (Layer 1 data -> Layer 5 UI) for c in [0,1,2,3,4,5,6,7,8,9] -> column { style: "position:absolute; width:28px; height:28px; top:391px; left:{c * 30 + 1}px; border-radius:4px; background:linear-gradient(180deg,#a855f7,#7c3aed); opacity:{if g13[c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(168,85,247,0.4)" } [] -- Frozen grid: row 14 for c in [0,1,2,3,4,5,6,7,8,9] -> column { style: "position:absolute; width:28px; height:28px; top:421px; left:{c * 30 + 1}px; border-radius:4px; background:linear-gradient(180deg,#a855f7,#7c3aed); opacity:{if g14[c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(168,85,247,0.4)" } [] -- Frozen grid: row 15 for c in [0,1,2,3,4,5,6,7,8,9] -> column { style: "position:absolute; width:28px; height:28px; top:451px; left:{c * 30 + 1}px; border-radius:4px; background:linear-gradient(180deg,#a855f7,#7c3aed); opacity:{if g15[c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(168,85,247,0.4)" } [] -- Frozen grid: row 16 for c in [0,1,2,3,4,5,6,7,8,9] -> column { style: "position:absolute; width:28px; height:28px; top:481px; left:{c * 30 + 1}px; border-radius:4px; background:linear-gradient(180deg,#a855f7,#7c3aed); opacity:{if g16[c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(168,85,247,0.4)" } [] -- Frozen grid: row 17 for c in [0,1,2,3,4,5,6,7,8,9] -> column { style: "position:absolute; width:28px; height:28px; top:511px; left:{c * 30 + 1}px; border-radius:4px; background:linear-gradient(180deg,#a855f7,#7c3aed); opacity:{if g17[c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(168,85,247,0.4)" } [] -- Frozen grid: row 18 for c in [0,1,2,3,4,5,6,7,8,9] -> column { style: "position:absolute; width:28px; height:28px; top:541px; left:{c * 30 + 1}px; border-radius:4px; background:linear-gradient(180deg,#a855f7,#7c3aed); opacity:{if g18[c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(168,85,247,0.4)" } [] -- Frozen grid: row 19 (bottom) for c in [0,1,2,3,4,5,6,7,8,9] -> column { style: "position:absolute; width:28px; height:28px; top:571px; left:{c * 30 + 1}px; border-radius:4px; background:linear-gradient(180deg,#a855f7,#7c3aed); opacity:{if g19[c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(168,85,247,0.4)" } [] -- Active piece: 4 cells (Layer 1 data + Layer 3 input -> Layer 5 UI) -- Cell 0: top-left of piece column { style: "position:absolute; width:28px; height:28px; border-radius:4px; background:linear-gradient(180deg,#c084fc,#a855f7); box-shadow:0 0 12px rgba(168,85,247,0.5); transition:left 0.05s,top 0.05s; top:{(if piece == 1 then py + 1 else py) * 30 + 1}px; left:{(if piece == 5 then px + 1 else px) * 30 + 1}px" } [] -- Cell 1 column { style: "position:absolute; width:28px; height:28px; border-radius:4px; background:linear-gradient(180deg,#c084fc,#a855f7); box-shadow:0 0 12px rgba(168,85,247,0.5); transition:left 0.05s,top 0.05s; top:{(if piece == 1 then py + 1 else py) * 30 + 1}px; left:{(if piece == 2 then px else px + 1) * 30 + 1}px" } [] -- Cell 2 column { style: "position:absolute; width:28px; height:28px; border-radius:4px; background:linear-gradient(180deg,#c084fc,#a855f7); box-shadow:0 0 12px rgba(168,85,247,0.5); transition:left 0.05s,top 0.05s; top:{(if piece == 1 then py + 1 else py) * 30 + 1}px; left:{(if piece == 1 then px + 2 else (if piece == 3 then px + 2 else px + 2)) * 30 + 1}px" } [] -- Cell 3 (second row of piece, e.g. T-piece center bottom) column { style: "position:absolute; width:28px; height:28px; border-radius:4px; background:linear-gradient(180deg,#c084fc,#a855f7); box-shadow:0 0 12px rgba(168,85,247,0.5); transition:left 0.05s,top 0.05s; top:{(if piece == 1 then py + 1 else py + 1) * 30 + 1}px; left:{(if piece == 1 then px + 3 else (if piece == 6 then px + 1 else (if piece == 4 then px + 2 else px + 2))) * 30 + 1}px" } [] -- Ghost piece (Layer 4 derived -> Layer 5 UI) -- Shows where piece will land (row 18) column { style: "position:absolute; width:28px; height:28px; border-radius:4px; border:2px dashed rgba(168,85,247,0.3); top:{18 * 30 + 1}px; left:{px * 30 + 1}px" } [] column { style: "position:absolute; width:28px; height:28px; border-radius:4px; border:2px dashed rgba(168,85,247,0.3); top:{18 * 30 + 1}px; left:{(px + 1) * 30 + 1}px" } [] column { style: "position:absolute; width:28px; height:28px; border-radius:4px; border:2px dashed rgba(168,85,247,0.3); top:{18 * 30 + 1}px; left:{(px + 2) * 30 + 1}px" } [] ] -- Side panel column [ -- Next piece preview (Layer 1 data -> Layer 5 UI) text "NEXT" { variant: "subtitle" } stack { style: "position:relative; width:120px; height:90px; background:#0f0f23; border-radius:8px; border:1px solid #334155" } [ column { style: "position:absolute; width:24px; height:24px; border-radius:3px; top:20px; left:15px; background:linear-gradient(180deg,#c084fc,#a855f7); box-shadow:0 0 6px rgba(168,85,247,0.4)" } [] column { style: "position:absolute; width:24px; height:24px; border-radius:3px; top:20px; left:41px; background:linear-gradient(180deg,#c084fc,#a855f7); box-shadow:0 0 6px rgba(168,85,247,0.4)" } [] column { style: "position:absolute; width:24px; height:24px; border-radius:3px; top:20px; left:67px; background:linear-gradient(180deg,#c084fc,#a855f7); box-shadow:0 0 6px rgba(168,85,247,0.4)" } [] column { style: "position:absolute; width:24px; height:24px; border-radius:3px; top:46px; left:41px; background:linear-gradient(180deg,#c084fc,#a855f7); opacity:{if nextPiece == 6 then 1 else (if nextPiece == 4 then 1 else 0)}" } [] ] -- Stats (Layer 2 physics -> Layer 5 UI) text "STATS" { variant: "subtitle" } text "Score: {score}" text "Lines: {lines}" text "Level: {level}" text "Speed: {dropInterval}" { variant: "muted" } text "Ticks: {gravityTick}" { variant: "muted" } -- Signal debug panel (live values) text "SIGNALS" { variant: "subtitle" } text "piece:{piece} px:{px} py:{py}" { variant: "muted" } text "rot:{rotation} blk:{blocked}" { variant: "muted" } text "lock:{lockTick} grav:{gravityTick}" { variant: "muted" } text "score:{score} lines:{lines}" { variant: "muted" } text "level:{level} speed:{dropInterval}" { variant: "muted" } text "g13:{g13}" { variant: "muted" } text "g14:{g14}" { variant: "muted" } text "g15:{g15}" { variant: "muted" } text "g16:{g16}" { variant: "muted" } text "g17:{g17}" { variant: "muted" } text "g18:{g18}" { variant: "muted" } text "g19:{g19}" { variant: "muted" } ] ] -- Controls (Layer 3 input -> signal writes) row [ button (if paused then "Resume" else "Pause") { click: paused = if paused then 0 else 1, variant: "primary" } button "Left" { click: px = if px > 0 then px - 1 else px, variant: "secondary" } button "Right" { click: px = if px < 7 then px + 1 else px, variant: "secondary" } button "Drop" { click: py = 18, variant: "secondary" } button "Rotate" { click: rotation = (rotation + 1) % 4, variant: "secondary" } button "Reset" { click: score = 0; lines = 0; level = 1; gameOver = 0; paused = 0; piece = 6; nextPiece = 1; px = 3; py = 0; rotation = 0; g16 = [0,0,0,0,0,0,0,0,0,0]; g17 = [0,0,0,0,0,0,0,0,0,0]; g18 = [0,0,0,0,0,0,0,0,0,0]; g19 = [0,0,0,0,0,0,0,0,0,0], variant: "destructive" } ] when gameOver > 0 -> text "GAME OVER - Score: {score}, Lines: {lines}" { variant: "title" } text "Arrow keys: Move/Rotate | Space: Hard drop | P: Pause" { variant: "muted" } text "Streaming: 6 signal layers composited" { variant: "muted" } ]