213 lines
7.5 KiB
Text
213 lines
7.5 KiB
Text
-- ═══════════════════════════════════════════════════════════
|
||
-- ⚡ DreamStack Playground
|
||
-- ═══════════════════════════════════════════════════════════
|
||
-- One page. Everything visible. Click and watch data flow.
|
||
--
|
||
-- dreamstack build examples/mission-control.ds -o /tmp/mission-control
|
||
|
||
import { Card } from "../registry/components/card"
|
||
import { Badge } from "../registry/components/badge"
|
||
|
||
-- ════════════════════════════════
|
||
-- SIGNALS — the core of DreamStack
|
||
-- ════════════════════════════════
|
||
|
||
let score = 0
|
||
let player = "Player 1"
|
||
let status = "idle"
|
||
let hp = 100
|
||
let xp = 0
|
||
let level = 1
|
||
let inventory = ["Sword", "Shield", "Potion"]
|
||
let log = ["Game started"]
|
||
|
||
-- Derived: auto-computed from source signals
|
||
let damage = score * 3
|
||
let shield = hp > 50
|
||
let rank = level * 10 + xp
|
||
|
||
-- Spring: physics-animated value
|
||
let energy = spring(100)
|
||
|
||
view main = column [
|
||
text "⚡ DreamStack Playground" { variant: "title" }
|
||
|
||
-- ════════════════════════════════
|
||
-- SOURCE: You control signals here
|
||
-- ════════════════════════════════
|
||
|
||
row [
|
||
-- SIGNAL CONTROL PANEL
|
||
Card { title: "🎮 Source Signals", subtitle: "change these → everything reacts" } [
|
||
text "score" { variant: "subtitle" }
|
||
text "{score}" { variant: "title" }
|
||
row [
|
||
button "+1" { click: score += 1, variant: "primary" }
|
||
button "+10" { click: score += 10, variant: "primary" }
|
||
button "0" { click: score = 0, variant: "ghost" }
|
||
]
|
||
|
||
text "hp" { variant: "subtitle" }
|
||
text "{hp}" { variant: "title" }
|
||
row [
|
||
button "Hit -20" { click: hp -= 20, variant: "destructive" }
|
||
button "Heal +30" { click: hp += 30, variant: "primary" }
|
||
button "Full" { click: hp = 100, variant: "ghost" }
|
||
]
|
||
|
||
text "status" { variant: "subtitle" }
|
||
row [
|
||
button "idle" { click: status = "idle", variant: "ghost" }
|
||
button "fighting" { click: status = "fighting", variant: "primary" }
|
||
button "dead" { click: status = "dead", variant: "destructive" }
|
||
]
|
||
|
||
text "level / xp" { variant: "subtitle" }
|
||
row [
|
||
button "XP +5" { click: xp += 5, variant: "primary" }
|
||
button "Level Up" { click: level += 1, variant: "primary" }
|
||
]
|
||
]
|
||
|
||
-- DERIVED SIGNALS — auto-updated
|
||
Card { title: "⚙️ Derived Signals", subtitle: "auto-computed, zero code" } [
|
||
text "damage = score × 3" { variant: "subtitle" }
|
||
text "{damage}" { variant: "title" }
|
||
|
||
text "shield = hp > 50" { variant: "subtitle" }
|
||
when shield ->
|
||
Badge { label: "SHIELD UP ✓", variant: "success" }
|
||
else ->
|
||
Badge { label: "SHIELD DOWN ✗", variant: "error" }
|
||
|
||
text "rank = level × 10 + xp" { variant: "subtitle" }
|
||
text "{rank}" { variant: "title" }
|
||
|
||
text "energy (spring)" { variant: "subtitle" }
|
||
text "{energy}" { variant: "title" }
|
||
row [
|
||
button "Drain" { click: energy = 10, variant: "destructive" }
|
||
button "Charge" { click: energy = 100, variant: "primary" }
|
||
]
|
||
]
|
||
]
|
||
|
||
-- ════════════════════════════════
|
||
-- REACTIVE UI — responds to signals
|
||
-- ════════════════════════════════
|
||
|
||
row [
|
||
-- MATCH: status drives which badge shows
|
||
Card { title: "📊 Status Display", subtitle: "match status" } [
|
||
match status
|
||
"idle" -> Badge { label: "💤 IDLE", variant: "info" }
|
||
"fighting" -> Badge { label: "⚔️ FIGHTING", variant: "warning" }
|
||
"dead" -> Badge { label: "💀 DEAD", variant: "error" }
|
||
_ -> Badge { label: "???", variant: "info" }
|
||
|
||
-- WHEN/ELSE: conditional rendering
|
||
when hp > 80 ->
|
||
text "💚 Healthy"
|
||
when hp > 30 ->
|
||
text "💛 Wounded"
|
||
else ->
|
||
text "❤️ Critical!"
|
||
|
||
when score > 20 ->
|
||
Badge { label: "🔥 ON FIRE", variant: "warning" }
|
||
]
|
||
|
||
-- EACH: dynamic list
|
||
Card { title: "🎒 Inventory", subtitle: "each + array methods" } [
|
||
each item in inventory ->
|
||
row [
|
||
Badge { label: item, variant: "info" }
|
||
button "Drop" { click: inventory.remove(_idx), variant: "ghost" }
|
||
]
|
||
row [
|
||
button "+Bow" { click: inventory.push("Bow"), variant: "primary" }
|
||
button "+Ring" { click: inventory.push("Ring"), variant: "primary" }
|
||
button "Sort" { click: inventory.sort(), variant: "ghost" }
|
||
button "Clear" { click: inventory.clear(), variant: "destructive" }
|
||
]
|
||
]
|
||
|
||
-- INPUT BINDING + LOG
|
||
Card { title: "📝 Event Log", subtitle: "input bind + push" } [
|
||
input { bind: player, placeholder: "Player name..." }
|
||
text "Playing as: {player}" { variant: "title" }
|
||
button "Log Score" { click: log.push(player), variant: "primary" }
|
||
each entry in log ->
|
||
text "→ {entry}"
|
||
]
|
||
]
|
||
|
||
-- ════════════════════════════════
|
||
-- EMBED: what a page needs to receive this
|
||
-- ════════════════════════════════
|
||
|
||
Card { title: "📡 Receiver — Live Signal Values", subtitle: "these signals stream to any receiver" } [
|
||
row [
|
||
column [
|
||
text "score = {score}"
|
||
text "damage = {damage}"
|
||
text "hp = {hp}"
|
||
]
|
||
column [
|
||
text "shield = {shield}"
|
||
text "level = {level}"
|
||
text "rank = {rank}"
|
||
]
|
||
column [
|
||
text "status = {status}"
|
||
text "player = {player}"
|
||
text "xp = {xp}"
|
||
]
|
||
]
|
||
]
|
||
|
||
Card { title: "🔌 Embed Option 1 — iframe", subtitle: "one line, any website" } [
|
||
text "Paste into any HTML page:" { variant: "subtitle" }
|
||
text ""
|
||
text "[iframe src=your-relay/view/game /]"
|
||
text ""
|
||
text "The compiled .ds app runs inside the iframe."
|
||
text "Self-contained, no dependencies."
|
||
Badge { label: "ZERO CONFIG", variant: "success" }
|
||
]
|
||
|
||
Card { title: "🔌 Embed Option 2 — JS API", subtitle: "vanilla JavaScript, full control" } [
|
||
text "Add to any web page:" { variant: "subtitle" }
|
||
text ""
|
||
text "[script src=dreamstack-runtime.js]"
|
||
text ""
|
||
text " const game = DS._connectStream(RELAY_URL)"
|
||
text ""
|
||
text " game.on(score, (v) => scoreEl.textContent = v)"
|
||
text ""
|
||
text " game.on(hp, (v) => healthBar.style.width = v)"
|
||
text ""
|
||
text "[/script]"
|
||
text ""
|
||
Badge { label: "3 LINES OF JS", variant: "success" }
|
||
text "Works with React, Vue, Svelte, vanilla — anything."
|
||
]
|
||
|
||
Card { title: "🔌 Embed Option 3 — DreamStack .ds", subtitle: "full reactive receiver" } [
|
||
text "Write a .ds file — compile to self-contained HTML:" { variant: "subtitle" }
|
||
text ""
|
||
text "let game = stream from RELAY_URL"
|
||
text ""
|
||
text "view main = column ["
|
||
text " text Score: (game.score)"
|
||
text " when game.hp is low ->"
|
||
text " text DANGER!"
|
||
text "]"
|
||
text ""
|
||
Badge { label: "FULL REACTIVITY", variant: "info" }
|
||
text "Remote signals become local reactive proxies."
|
||
text "Match, when/else, each — everything works on remote data."
|
||
]
|
||
]
|
||
|
||
|