Skip to main content

3.9-open-ui-lang-사례

갤러리 섹션 1: 컴포지션 사례 (Composition Programs)​

Case 2, Web analytics, PostHog style (web-analytics)​

설명: A date range drives every number. KPI cards with change badges, an area trend with a metric switch, source bars and a funnel from Box widths, a top-pages Table and a retention grid from @MixColor.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Col([
head,
kpis,
trend,
Grid([sources, pages], 2, 300, 4),
Grid([funnel, retention], 2, 300, 4)
], 4)

// 상태 변수 ($range): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$range = "30d"

// 상태 변수 ($metric): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$metric = "Visitors"

// days 정의
days = $range == "7d" ? 7 : $range == "30d" ? 30 : 90

// traffic 정의
traffic = @Each(@Range(0, days), "i", {day: @FormatDate(@AddDays(@Today(), i - days + 1), "short"), Visitors: 1700 + 380 * ((i * 7) % 5) + i * 9 + (((i + 2) % 7) < 2 ? -520 : 0), Pageviews: 4200 + 820 * ((i * 3) % 4) + i * 21 + (((i + 2) % 7) < 2 ? -1300 : 0)})

// visitors 정의
visitors = @Sum(traffic.Visitors)

// views 정의
views = @Sum(traffic.Pageviews)

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
head = Row([
Col([Title("Web analytics", "xl"), Caption("yoursite.com · sample data")], 1),
Spacer(),
SegmentedControl($range, [{value: "7d", label: "7 days"}, {value: "30d", label: "30 days"}, {value: "90d", label: "90 days"}])
], 3, null, null, true)

// 핵심 지표(KPI) 및 통계 카드
kpiRows = [
{label: "Visitors", value: @Format(visitors, "compact", 1), change: 12.4, good: true},
{label: "Pageviews", value: @Format(views, "compact", 1), change: 8.1, good: true},
{label: "Session length", value: "3m 12s", change: -4.2, good: true},
{label: "Bounce rate", value: "41%", change: -2.6, good: false}
]

// 핵심 지표(KPI) 및 통계 카드
kpis = Grid(@Each(kpiRows, "k", Card([Caption(k.label), Title(k.value, "2xl"), Badge((k.change > 0 ? "+" : "") + k.change + "%", (k.change > 0) == k.good ? "success" : "danger")], null, 4, 1)), 4, 150, 3)

// 시각화 차트
trend = Card([
Row([
Title("Traffic", "sm"),
Spacer(),
SegmentedControl($metric, ["Visitors", "Pageviews"])
], 2),
Chart("area", traffic, "day", [$metric], "number", null, false, 220)
], null, 5, 3)

// 원본/파생 데이터 목록
shares = [
{name: "Google", icon: "search", share: 41},
{name: "Direct", icon: "mouse-pointer-click", share: 24},
{name: "Hacker News", icon: "newspaper", share: 15},
{name: "Twitter / X", icon: "at-sign", share: 11},
{name: "Newsletter", icon: "mail", share: 9}
]

// 데이터 테이블 및 리스트 카드
sources = Card([
Title("Top sources", "sm"),
Col(@Each(shares, "s", Col([Row([Icon(s.icon, "sm", "muted"), Text(s.name, "sm"), Spacer(), Text(@Format(visitors * s.share / 100, "compact", 1), "sm", null, "medium", false, false, true)], 2), Box([Box([], null, null, null, "chart1", "full", null, (s.share / 41 * 100) + "%", 6)], null, null, null, "surface", "full", null, "100%", 6)], 1)), 3)
], null, 5, 3)

// pageRows 정의
pageRows = [
{path: "/", share: 31, time: "0:48"},
{path: "/pricing", share: 18, time: "1:52"},
{path: "/blog/launch-week", share: 14, time: "4:05"},
{path: "/docs/quickstart", share: 11, time: "3:31"},
{path: "/signup", share: 6, time: "1:10"}
]

// 데이터 테이블 및 리스트 카드
pages = Card([
Title("Top pages", "sm"),
Table([
"Page",
"Views",
"Avg. time"], @Each(pageRows,
"p",
[p.path, @Format(views * p.share / 100, "compact", 1), p.time
]))
], null, 5, 3)

// 원본/파생 데이터 목록
steps = [
{step: "Visited site", rate: 100},
{step: "Viewed pricing", rate: 38},
{step: "Started signup", rate: 12.5},
{step: "Activated", rate: 7.4}
]

// funnel 정의
funnel = Card([
Title("Signup funnel", "sm"),
Col(@Each(steps, "f", Col([Row([Text(f.step, "sm"), Spacer(), Text(@Format(visitors * f.rate / 100, "compact", 1) + " · " + f.rate + "%", "sm", "muted", null, false, false, true)], 2), Box([], null, null, null, "chart3", "sm", null, f.rate + "%", 22)], 1)), 3)
], null, 5, 3)

// 원본/파생 데이터 목록
cohorts = [
{week: "Sep 7", users: 1840, r: [100, 41, 30, 26, 23]},
{week: "Sep 14", users: 2015, r: [100, 44, 33, 27]},
{week: "Sep 21", users: 1922, r: [100, 39, 29]},
{week: "Sep 28", users: 2210, r: [100, 46]},
{week: "Oct 5", users: 2390, r: [100]}
]

// retention 정의
retention = Card([
Title("Weekly retention", "sm"),
Row([
Box([Caption("Cohort")], null, null, null, null, null, null, 64),
Box([Row(@Each(["W0", "W1", "W2", "W3", "W4"], "w", Box([Caption(w)], null, null, null, null, null, null, null, null, 1, "center")), 1)], null, null, null, null, null, null, null, null, 1)
], 2),
Col(@Each(cohorts, "c", Row([Box([Text(c.week, "xs", null, "medium"), Caption(@Format(c.users))], null, null, null, null, null, null, 64), Box([Row(@Each(@Range(0, 5), "i", i < @Count(c.r) ? Box([Text(c.r[i] + "%", "xs", null, "medium", false, false, true)], null, null, 1, @MixColor("#EDF5FF", "#0068BC", c.r[i] / 100), "sm", null, null, 32, 1, "center", "center") : Box([], null, null, null, "surface", "sm", null, null, 32, 1)), 1)], null, null, null, null, null, null, null, null, 1)], 2)), 1)
], null, 5, 3)

Case 3, Compare two phones (compare-phones)​

설명: Images found by query, a spec Table with component cells, RadioGroup cards that change the pick below.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Col([
title,
intro,
Grid([iphone, pixel], 2, 240, 4),
Title("Side by side", "md"),
specs,
Title("Which suits you?", "md"),
picker,
verdict
])

// 상태 변수 ($care): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$care = "Camera"

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
title = Title("iPhone 17 vs Pixel 11", "xl")

// intro 정의
intro = Text("Two flagship phones at the same price. The iPhone is the safer all-rounder; the Pixel wins on photos and AI features.")

// iphone 정의
iphone = Col([
Box([SearchImage("iPhone 17", 1, "xl", "contain")], null, null, 4, "surface", "xl"),
Row([Title("iPhone 17", "md"), Spacer(), Badge("From $799")], 2),
Text([Icon("cpu", "sm"), " A19 · 6.3-inch 120 Hz"], "sm", "muted")
], 2)

// pixel 정의
pixel = Col([
Box([SearchImage("Pixel 11", 1, "xl", "contain")], null, null, 4, "surface", "xl"),
Row([Title("Pixel 11", "md"), Spacer(), Badge("From $799")], 2),
Text([Icon("cpu", "sm"), " Tensor G6 · 6.3-inch 120 Hz"], "sm", "muted")
], 2)

// 원본/파생 데이터 목록
rows = [
{f: "Main camera", a: "48 MP", b: "50 MP", win: "b"},
{f: "Battery life", a: "Up to 30 h video", b: "Up to 31 h video", win: "b"},
{f: "Updates", a: "About 6 years", b: "7 years", win: "b"},
{f: "Charging", a: "40 W wired · MagSafe", b: "30 W wired · Qi2", win: "a"},
{f: "Ecosystem", a: "iMessage, Watch, Mac", b: "Gemini, Android", win: ""}
]

// 데이터 테이블 및 리스트 카드
specs = Table([
"",
"iPhone 17",
"Pixel 11"], @Each(rows,
"r",
[r.f, Text([r.win == "a" ? Icon("check-circle", "sm", "success") : "", " " + r.a], "sm"), Text([r.win == "b" ? Icon("check-circle", "sm", "success") : "", " " + r.b], "sm")
]))

// 사용자 입력 및 컨트롤 폼
picker = RadioGroup($care, [{value: "Camera", label: "Photos", description: "Low light, zoom, editing"}, {value: "Apps", label: "My other devices", description: "Mac, Watch, iMessage"}, {value: "Battery", label: "Battery", description: "A long day of use"}], null, "cards")

// pick 정의
pick = $care == "Apps" ? "iPhone 17" : "Pixel 11"

// 요약 및 결과 표시 카드
verdict = Card([
Row([
Box([SearchImage(pick, 1, "lg", "contain")], null, null, 2, "canvas", "lg", null, 64, 64),
Col([Caption("Our pick for you"), Title(pick, "lg"), Text($care == "Camera" ? "Its 50 MP sensor and editing tools take the better low-light shots." : $care == "Apps" ? "It works best with the Apple devices you already own." : "A little more screen-on time and slower battery wear.", "sm", "muted")], 1)
], 4)
], "soft")

Case 4, Spanish flashcards and a quiz (flashcards)​

설명: Flashcards in a Carousel, then a quiz that keeps score with @Set and bursts a Celebration on 4 of 4.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Col([title, intro, cards, Title("Quick quiz", "md"), quiz, Celebration($score == 4)])

// 상태 변수 ($score): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$score = 0

// 상태 변수 ($q): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$q = 0

// 상태 변수 ($picked): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$picked = ""

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
title = Title("8 Spanish words for the kitchen", "xl")

// intro 정의
intro = Text([
Icon("hand", "sm"),
" Tap a card to see the English, then try the quiz."
], "md", "muted")

// 원본/파생 데이터 목록
words = [
{es: "la cuchara", en: "the spoon", emoji: "🥄"},
{es: "el cuchillo", en: "the knife", emoji: "🔪"},
{es: "el tenedor", en: "the fork", emoji: "🍴"},
{es: "la sartén", en: "the frying pan", emoji: "🍳"},
{es: "la olla", en: "the pot", emoji: "🍲"},
{es: "el horno", en: "the oven", emoji: "🔥"},
{es: "la nevera", en: "the fridge", emoji: "🧊"},
{es: "el vaso", en: "the glass", emoji: "🥛"}
]

// 인터랙티브 카드 그리드
cards = Carousel(@Each(words, "w", Flashcard([Title(w.emoji, "2xl"), Title(w.es, "lg")], [Title(w.en, "md"), Caption(w.es)])), 180)

// questions 정의
questions = [
{es: "la sartén", options: ["the pot", "the frying pan", "the oven"], answer: "the frying pan"},
{es: "el tenedor", options: ["the fork", "the knife", "the spoon"], answer: "the fork"},
{es: "la nevera", options: ["the glass", "the oven", "the fridge"], answer: "the fridge"},
{es: "la olla", options: ["the pot", "the spoon", "the fork"], answer: "the pot"}
]

// current 정의
current = questions[$q]

// 인터랙티브 퀴즈 카드
quiz = $q < 4 ? Card([Row([Caption("Question " + ($q + 1) + " of 4"), Spacer(), Badge("Score " + $score, "info")], 2), Title("What is " + current.es + "?", "lg"), Row(@Each(current.options, "o", Button(o, Action([@Set($picked, o), @Set($score, o == current.answer ? $score + 1 : $score), @Set($q, $q + 1)]), "outline")), 2, null, null, true), $picked == "" ? null : Caption("Last answer: " + $picked)], null, 5, 3) : Card([Title($score == 4 ? "Perfect, 4 of 4!" : "You got " + $score + " of 4", "lg"), Text($score == 4 ? "¡Muy bien! You know your way around a Spanish kitchen." : "Flip the cards again and have another go."), Button("Try again", Action([@Reset($score, $q, $picked)]), "solid", null, null, "rotate-ccw")], "soft", 5, 3)

Case 5, New primitives (primitives)​

설명: Text with inline components, Carousel, Popover, Quote, Math, Code, RadioGroup pills, a scatter Chart, Flashcard and YouTube.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Col([
Title("New primitives", "lg"),
Text([
"Text can mix ",
Badge("inline", "purple"),
" components, ",
Icon("sparkles", "sm"),
" icons and ",
Math("\\sqrt{2}", true),
" math."
]),
Carousel([
SearchImage("Golden Gate Bridge", 1.4),
SearchImage("Alcatraz Island", 1.4),
SearchImage("Painted Ladies", 1.4),
SearchImage("Lombard Street", 1.4)
], 220),
Row([
Text("Inflation adjusts nominal GDP"),
Popover(Icon("info", "sm", "muted"), [Text("**Real GDP** removes price changes, so it measures output.", "sm"), Link("Read more", "https://www.bea.gov")])
], 2),
Quote("Simplicity is the ultimate sophistication.", "Leonardo da Vinci"),
Math("\\bar{x} \\sim \\mathcal{N}\\left(\\mu, \\frac{\\sigma^2}{n}\\right)"),
Code("const total = items.reduce((sum, i) => sum + i.price, 0)", "javascript"),
RadioGroup($size, ["S", "M", "L", "XL"], "Size", "pills"),
Chart("scatter", [{car: "Civic", hp: 158, mpg: 36}, {car: "Model 3", hp: 271, mpg: 132}, {car: "F-150", hp: 400, mpg: 20}, {car: "Prius", hp: 194, mpg: 57}, {car: "Mustang", hp: 480, mpg: 19}], "hp", ["mpg"]),
Grid([
Flashcard("Bonjour", "Hello"),
Flashcard("Merci", "Thank you")
], 2, 160, 3),
YouTube("dQw4w9WgXcQ", "A music video")
])

// 상태 변수 ($size): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$size = "M"

갤러리 섹션 2: 독립 툴 애플리케이션 사례 (Working Tools & Intelligent UI)​

Case 6, Split the bill (bill)​

설명: Add and remove people and items (@Append, @Remove); who shared what with @Update and @Toggle; tax and tip; @Allocate keeps the cents adding up; copy the split. 사용자 프롬프트: "Make a bill splitter for dinner with friends"

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Card([
head,
people,
addPerson,
Divider(),
itemsHead,
items,
addItem,
Divider(),
rates,
summary
], null, 5, 4)

// 상태 변수 ($mode): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$mode = "item"

// 상태 변수 ($people): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$people = ["You", "Alex", "Sam", "Jordan"]

// 상태 변수 ($items): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$items = [
{name: "Burrata & sourdough", amount: 18, people: ["You", "Alex", "Sam", "Jordan"]},
{name: "Mains", amount: 96, people: ["You", "Alex", "Sam", "Jordan"]},
{name: "A bottle of red", amount: 42, people: ["You", "Alex", "Jordan"]}
]

// 상태 변수 ($name): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$name = ""

// 상태 변수 ($itemName): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$itemName = ""

// 상태 변수 ($itemAmount): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$itemAmount = null

// 상태 변수 ($tax): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$tax = 8.5

// 상태 변수 ($tip): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$tip = 20

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
head = Row([
Col([
Title("The check, please", "lg"),
Text($mode == "item" ? "Tap names on an item to say who shared it." : "Everyone pays the same share.", "sm", "muted")
], 1),
Spacer(),
SegmentedControl($mode, [{value: "item", label: "By item"}, {value: "equal", label: "Equally"}])
], 3, null, null, true)

// people 정의
people = Row(@Each($people, "p", Button(p, Action([@Set($people, @Remove($people, "", p)), @Set($items, @Each($items, "it", {name: it.name, amount: it.amount, people: @Remove(it.people, "", p)}))]), "soft", null, "sm", "x")), 2, null, null, true)

// addPerson 정의
addPerson = Row([
Input($name, null, "text", "Add a person"),
Button("Add", Action([@Set($people, @Append($people, $name)), @Set($items, @Each($items, "it", {name: it.name, amount: it.amount, people: @Append(it.people, $name)})), @Reset($name)]), "outline", null, null, "user-plus", $name == "" || @Includes($people, $name))
], 2)

// itemsHead 정의
itemsHead = Row([
Title("Items", "md"),
Spacer(),
Text(@Format(subtotal, "currency"), "sm", "muted", "medium", false, false, true)
])

// 원본/파생 데이터 목록
items = Col(@Each($items, "it", Card([Row([Text(it.name, "sm", null, "semibold"), Spacer(), Text(@Format(it.amount, "currency"), "sm", null, "medium", false, false, true), IconButton("trash-2", @Set($items, @Remove($items, "name", it.name)), "Remove " + it.name, "ghost")], 2), $mode == "item" ? Row(@Each($people, "p", Button(p, @Set($items, @Update($items, "name", it.name, "people", @Toggle(it.people, p))), @Includes(it.people, p) ? "solid" : "outline", null, "sm", @Includes(it.people, p) ? "check" : null)), 1, null, null, true) : null], null, 3, 2)), 2)

// addItem 정의
addItem = Row([
Input($itemName, null, "text", "Item"),
Input($itemAmount, null, "number", "Price"),
Button("Add item", Action([@Set($items, @Append($items, {name: $itemName, amount: $itemAmount, people: $people})), @Reset($itemName, $itemAmount)]), "outline", null, null, "plus", $itemName == "" || $itemAmount <= 0)
], 2)

// rates 정의
rates = Grid([
Slider($tax, 0, 15, 0.5, "Tax", "%"),
Slider($tip, 0, 30, 1, "Tip", "%")
], 2, 220, 6)

// subtotal 정의
subtotal = @Sum($items.amount)

// factor 정의
factor = 1 + ($tax + $tip) / 100

// unassigned 정의
unassigned = $mode == "equal" ? 0 : @Sum(@Each($items, "it", @Count(it.people) == 0 ? it.amount : 0))

// raw 정의
raw = @Each($people, "p", factor * ($mode == "equal" ? subtotal / @Max([@Count($people), 1]) : @Sum(@Each($items, "it", @Includes(it.people, p) ? it.amount / @Count(it.people) : 0))))

// cents 정의
cents = @Sum(raw) > 0 ? @Allocate(@Round(@Sum(raw), 2), raw) : raw

// 원본/파생 데이터 목록
shares = @Each(@Range(0, @Count($people)), "i", {name: $people[i], amount: cents[i]})

// 요약 및 결과 표시 카드
summary = Card([
Col(@Each(shares, "s", Row([Text(s.name), Spacer(), Text(@Format(s.amount, "currency"), null, null, "semibold", false, false, true)])), 2),
Divider(),
Row([
Text("Total with tax and tip", "sm", "muted"),
Spacer(),
Title(@Format(subtotal * factor, "currency"), "lg")
]),
unassigned > 0 ? Text(@Format(unassigned, "currency") + " is not assigned to anyone yet.", "xs", "warning") : null,
Button("Copy the split", @CopyToClipboard(@Join(@Each(shares, "s", s.name + ": " + @Format(s.amount, "currency")), "\n")), "solid", null, "sm", "copy")
], "soft", 4, 3)

Case 7, The Monty Hall problem (monty)​

설명: Pick a door: @Random inside @Set hides the car and opens a goat door. Switch or stay, and simulate 1,000 games with @Range and @Each. 사용자 프롬프트: "Explain the Monty Hall problem with an interactive game"

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Card([head, doors, status, actions, record, sim], null, 5, 4)

// 상태 변수 ($choice): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$choice = -1

// 상태 변수 ($opened): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$opened = -1

// 상태 변수 ($final): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$final = -1

// 상태 변수 ($prize): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$prize = 0

// 상태 변수 ($switched): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$switched = 0

// 상태 변수 ($switchWins): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$switchWins = 0

// 상태 변수 ($stayed): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$stayed = 0

// 상태 변수 ($stayWins): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$stayWins = 0

// 상태 변수 ($simStay): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$simStay = 0

// 상태 변수 ($simGames): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$simGames = 0

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
head = Col([
Title("Should you switch doors?", "lg"),
Text("One car. Two goats. A surprisingly good reason to switch.", "sm", "muted")
], 1)

// 인터랙티브 카드 그리드
doors = Grid(@Each(@Range(0, 3), "d", Pressable([Col([Text("Door " + (d + 1), "sm", "muted", "medium"), Title(d == $opened || $final >= 0 ? (d == $prize ? "🚗" : "🐐") : "🚪", "3xl"), Text(d == $choice ? "Your pick" : d == $opened ? "Opened by the host" : "Closed", "xs", d == $choice ? "info" : "muted", "medium")], 1, "center")], $choice < 0 ? Action([@Set($choice, d), @Set($prize, @Random(0, 2, true)), @Set($opened, $prize == d ? (d + 1 + @Random(0, 1, true)) % 3 : 3 - d - $prize)]) : null, d == $choice, 5, d == $opened || $final >= 0 ? "surface" : "warning", "lg")), 3, null, 3)

// 요약 및 결과 표시 카드
status = Box([
Title($final >= 0 ? ($final == $prize ? "You found the car!" : "A goat this time.") : $choice >= 0 ? "Door " + ($opened + 1) + " has a goat." : "Start by choosing a door.", "md"),
Text($final >= 0 ? "Switching wins whenever your first pick was a goat: 2 out of 3 times." : $choice >= 0 ? "Your door still has a 1 in 3 chance. The other closed door has 2 in 3. Switch or stay?" : "The host knows where the car is and will open another door with a goat behind it.", "sm", "muted")
], null, 1, 4, "info", "lg")

// actions 정의
actions = Row([
$choice >= 0 && $final < 0 ? Button("Switch doors", Action([@Set($final, 3 - $choice - $opened), @Set($switched, $switched + 1), @Set($switchWins, $switchWins + ($final == $prize ? 1 : 0))]), "solid", null, null, "shuffle") : null,
$choice >= 0 && $final < 0 ? Button("Keep my door", Action([@Set($final, $choice), @Set($stayed, $stayed + 1), @Set($stayWins, $stayWins + ($final == $prize ? 1 : 0))]), "outline") : null,
$final >= 0 ? Button("Play again", @Reset($choice, $opened, $final), "solid", null, null, "rotate-ccw") : null,
Button("Simulate 1,000 games", Action([@Set($simStay, $simStay + @Count(@Filter(@Each(@Range(0, 1000), "g", @Random(0, 2, true) == @Random(0, 2, true)), "", "==", true))), @Set($simGames, $simGames + 1000)]), "ghost", null, null, "dices")
], 2, null, null, true)

// record 정의
record = $switched + $stayed > 0 ? Text("Your games: switched " + @Plural($switched, "time") + " and won " + $switchWins + "; stayed " + @Plural($stayed, "time") + " and won " + $stayWins + ".", "sm", "muted") : null

// sim 정의
sim = $simGames > 0 ? Card([Row([Title("Simulated games", "sm"), Spacer(), Caption(@Format($simGames) + " games")]), Chart("bar", [{strategy: "Stay", wins: @Percent($simStay, $simGames)}, {strategy: "Switch", wins: 100 - @Percent($simStay, $simGames)}], "strategy", ["wins"], "percent", null, false, 160)], "soft", 4, 2) : null

Case 8, Retirement savings (savings)​

설명: @Each over @Range builds the yearly balance with @Pow; four sliders drive the chart, the totals and the $1 million milestone. 사용자 프롬프트: "Help me explore how my retirement savings could grow"

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Card([
head,
kpis,
chart,
milestone,
controls,
Text("Assumes monthly compounding and contributions at the end of each month. Returns are not guaranteed; this is an illustration, not advice.", "xs", "muted")
], null, 5, 4)

// 상태 변수 ($initial): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$initial = 10000

// 상태 변수 ($monthly): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$monthly = 500

// 상태 변수 ($years): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$years = 30

// 상태 변수 ($rate): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$rate = 5

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
head = Row([
Col([
Title("Retirement savings", "lg"),
Text("Turn today's contributions into a picture of tomorrow.", "sm", "muted")
], 1),
Spacer(),
Button("Reset", @Reset($initial, $monthly, $years, $rate), "ghost", null, "sm", "rotate-ccw")
], 3, "start")

// 원본/파생 데이터 목록
rows = @Each(@Range(0, $years + 1), "y", {year: "Year " + y, Contributions: $initial + $monthly * 12 * y, Balance: $rate == 0 ? $initial + $monthly * 12 * y : $initial * @Pow(1 + $rate / 1200, 12 * y) + $monthly * (@Pow(1 + $rate / 1200, 12 * y) - 1) / ($rate / 1200)})

// final 정의
final = @Last(rows)

// million 정의
million = @First(@Filter(rows, "Balance", ">=", 1000000))

// 핵심 지표(KPI) 및 통계 카드
kpis = Grid([k1, k2, k3], 3, 150, 3)

// k1 정의
k1 = Card([
Caption("Balance in " + @Plural($years, "year")),
Title(@Format(final.Balance, "currency", 0), "xl")
], "soft", 3, 1)

// k2 정의
k2 = Card([
Caption("You put in"),
Title(@Format(final.Contributions, "currency", 0), "xl")
], "soft", 3, 1)

// k3 정의
k3 = Card([
Caption("Growth"),
Title(@Format(final.Balance - final.Contributions, "currency", 0), "xl", "success")
], "soft", 3, 1)

// 시각화 차트
chart = Chart("area", rows, "year", ["Contributions", "Balance"], "currency")

// milestone 정의
milestone = Row([
Icon(million == null ? "hourglass" : "party-popper", "sm", million == null ? "muted" : "success"),
Text(million == null ? "At this pace the balance stays under $1 million." : "You pass **$1 million** in " + million.year + ", " + @Format(@Percent(million.Balance - million.Contributions, million.Balance), "percent", 0) + " of it from growth.", "sm")
], 2)

// controls 정의
controls = Grid([
Slider($initial, 0, 200000, 1000, "Starting balance", "$"),
Slider($monthly, 0, 5000, 50, "Monthly contribution", "$"),
Slider($years, 1, 50, 1, "Years", " yrs"),
Slider($rate, 0, 12, 0.5, "Annual return", "%")
], 2, 240, 6)

Case 9, GDP, explained (gdp-explained)​

설명: Four sliders; the bar and legend come from one parts list with @Each; negative net exports explain themselves; a knowledge check. 사용자 프롬프트: "How does GDP work? Let me explore an economy"

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Card([
head,
Caption("Total output"),
total,
bar,
legend,
note,
sliders,
Row([
Button("Reset economy", @Reset($c, $i, $g, $x), "ghost", null, "sm", "rotate-ccw"),
Spacer(),
Badge("GDP = C + I + G + (X − M)", "purple")
], 2, null, null, true),
Text("GDP counts the value of final goods and services produced within a country. Imports are subtracted because they were produced elsewhere.", "sm", "muted"),
quiz
], null, 5, 4)

// 상태 변수 ($c): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$c = 600

// 상태 변수 ($i): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$i = 200

// 상태 변수 ($g): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$g = 170

// 상태 변수 ($x): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$x = 30

// 상태 변수 ($answer): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$answer = ""

// gdp 정의
gdp = $c + $i + $g + $x

// parts 정의
parts = [
{label: "Consumer spending", value: $c, color: "chart1"},
{label: "Business investment", value: $i, color: "chart2"},
{label: "Government spending", value: $g, color: "chart4"},
{label: "Net exports", value: $x, color: "chart3"}
]

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
head = Row([
Col([
Title("What makes up GDP?", "lg"),
Text("An economy, in four parts.", "sm", "muted")
], 1),
Spacer(),
Badge("Illustrative economy")
], 3, "start")

// total 정의
total = Title("$" + @Format(gdp / 1000, "number", 2) + " trillion", "2xl")

// bar 정의
bar = Box(@Each(parts, "p", Box([p.value >= 80 ? Text("$" + p.value + "B", "xs", "#ffffff", "medium") : null], null, null, null, p.color, 0, null, null, "100%", @Max([p.value, 0]), "center", "center")), "row", 0.5, null, null, "md", null, "100%", 38)

// legend 정의
legend = Grid(@Each(parts, "p", Row([Box([], null, null, null, p.color, "full", null, 10, 10), Text(p.label, "sm"), Spacer(), Text((p.value < 0 ? "-$" : "$") + @Format(@Abs(p.value)) + "B", "sm", "muted", "medium", false, false, true)], 2)), 2, 220, 2)

// note 정의
note = $x < 0 ? Text("Positive contributions add up to $" + ($c + $i + $g) + "B. Net imports subtract $" + @Abs($x) + "B, leaving $" + gdp + "B of output.", "sm", "warning") : null

// 사용자 입력 및 컨트롤 폼
sliders = Grid([
Slider($c, 0, 1200, 10, "Consumer spending, $ billions"),
Slider($i, 0, 600, 10, "Business investment, $ billions"),
Slider($g, 0, 500, 10, "Government spending, $ billions"),
Slider($x, -200, 300, 10, "Net exports, $ billions")
], 2, 240, 6)

// 인터랙티브 퀴즈 카드
quiz = Card([
RadioGroup($answer, [{value: "a", label: "Real GDP increases by 10%"}, {value: "b", label: "Nominal GDP rises; real GDP is unchanged"}, {value: "c", label: "Both measures stay the same"}], "The same goods cost 10% more this year. What changes?"),
$answer == "" ? null : Text(($answer == "b" ? "**Correct.** " : "**Not quite.** ") + "Real GDP adjusts for price changes, so higher prices alone raise nominal GDP but not real output.", "sm")
], "soft", 4, 2)

Case 10, Central limit theorem (clt)​

설명: Draw samples with @Random inside @Set; @Histogram and @Stats show the means narrowing like 1/√n for three populations. 사용자 프롬프트: "Help me understand the central limit theorem intuitively"

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Card([
head,
SegmentedControl($dist, ["Skewed", "Uniform", "Bimodal"]),
Grid([popCard, meanCard], 2, 260, 4),
Slider($n, 1, 50, 1, "Values per sample"),
buttons,
stats,
Text("As the sample size grows, the averages of samples pile up in a bell shape around the population mean, even when the population itself is skewed.", "sm", "muted"),
quiz
], null, 5, 4)

// 상태 변수 ($dist): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$dist = "Skewed"

// 상태 변수 ($n): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$n = 5

// 상태 변수 ($means): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$means = []

// 상태 변수 ($answer): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$answer = ""

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
head = Col([
Title("The central limit theorem", "lg"),
Text("Different populations. The same bell-shaped pattern.", "sm", "muted")
], 1)

// pop 정의
pop = @Each(@Range(0.0025, 1, 0.005), "u", $dist == "Skewed" ? @Pow(u, 3) : $dist == "Uniform" ? u : (u < 0.5 ? 0.05 + 0.4 * u : 0.55 + 0.4 * u))

// current 정의
current = @Filter(@Filter($means, "dist", "==", $dist), "n", "==", $n).mean

// draw 정의
draw = @Each(@Range(0, 100), "s", {dist: $dist, n: $n, mean: @Avg(@Each(@Each(@Range(0, $n), "k", @Random(0, 1)), "u", $dist == "Skewed" ? @Pow(u, 3) : $dist == "Uniform" ? u : (u < 0.5 ? 0.05 + 0.4 * u : 0.55 + 0.4 * u)))})

// popCard 정의
popCard = Col([
Title("Population", "sm"),
Caption("The values we draw from"),
Chart("bar", @Histogram(pop, 20, 0, 1), "key", ["value"], null, null, false, 150)
], 1)

// meanCard 정의
meanCard = Col([
Title("Sample means", "sm"),
Caption("The average of each sample of " + $n),
@Count(current) == 0 ? Box([Text("Draw samples to see the pattern", "sm", "muted")], null, null, 6, "surface", "lg", true, "100%", 150, null, "center", "center") : Chart("bar", @Histogram(current, 20, 0, 1), "key", ["value"], null, null, false, 150)
], 1)

// buttons 정의
buttons = Row([
Button("Draw 100 samples", @Set($means, @Take(@Concat($means, draw), -6000)), "solid", null, null, "dices"),
Button("Draw 500", Action([@Set($means, @Take(@Concat($means, draw), -6000)), @Set($means, @Take(@Concat($means, draw), -6000)), @Set($means, @Take(@Concat($means, draw), -6000)), @Set($means, @Take(@Concat($means, draw), -6000)), @Set($means, @Take(@Concat($means, draw), -6000))]), "outline"),
Button("Reset", @Reset($means), "ghost", null, null, "rotate-ccw")
], 2, null, null, true)

// 핵심 지표(KPI) 및 통계 카드
stats = Grid([s1, s2, s3], 3, 140, 3)

// s1 정의
s1 = Card([Caption("Samples"), Title(@Format(@Count(current)), "xl")], "soft", 3, 1)

// s2 정의
s2 = Card([
Caption("Mean of the means"),
Title(@Count(current) == 0 ? "-" : @Format(@Stats(current).mean, "number", 3), "xl"),
Caption("Population: " + @Format(@Stats(pop).mean, "number", 3))
], "soft", 3, 1)

// s3 정의
s3 = Card([
Caption("Spread of the means"),
Title(@Count(current) == 0 ? "-" : @Format(@Stats(current).stdev, "number", 3), "xl"),
Caption("Theory, σ/√n: " + @Format(@Stats(pop).stdev / @Sqrt($n), "number", 3))
], "soft", 3, 1)

// 인터랙티브 퀴즈 카드
quiz = Card([
RadioGroup($answer, [{value: "a", label: "The population becomes normal"}, {value: "b", label: "The sample means form a narrower bell shape"}, {value: "c", label: "Every mean becomes exactly the same"}], "What changes when the sample size grows?"),
$answer == "" ? null : Text(($answer == "b" ? "**Correct.** " : "**Not quite.** ") + "The population stays the same. The means become more normal and their spread shrinks like 1/√n.", "sm")
], "soft", 4, 2)

Case 11, A 7-speed bike (bicycle)​

설명: A gear slider drives an Svg of seven sprockets (@Each over @Range, index access), the ratio and the speed at a cadence. 사용자 프롬프트: "Break down the design of a 7-speed bike"

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Col([
head,
photos,
Title("The main parts", "md"),
parts,
Title("How power reaches the wheel", "md"),
flow,
explorer,
Title("Freewheel or cassette?", "md"),
hubs,
closing
])

// 상태 변수 ($gear): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$gear = 3

// 상태 변수 ($rpm): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$rpm = 80

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
head = Col([
Title("Inside a 7-speed bike", "xl"),
Text("One chainring at the front, seven sprockets at the back and a derailleur that moves the chain between them. Small sprockets make each pedal stroke go further; big ones make hills easier.", "md", "muted")
], 2)

// photos 정의
photos = Grid([
SearchImage("City bicycle", 1.3, "lg"),
SearchImage("Derailleur", 1.3, "lg"),
SearchImage("Bicycle shifter", 1.3, "lg")
], 3, 150, 2)

// parts 정의
parts = Table([
"Part",
"What it does"], [["Frame and fork", "Hold everything together, steer and set the riding position"],
["Crankset and pedals", "Turn leg power into rotation"],
["Chainring", "The single front gear, here 40 teeth"],
["Chain", "Carries the power to the rear wheel"],
["7 rear sprockets", "Seven sizes, so seven gear ratios"],
["Rear derailleur", "Moves the chain from sprocket to sprocket"],
["Shifter and cable", "Pull or release the cable to pick a gear"],
["Brakes, wheels and tires", "Stop, roll and grip the road"]
])

// flow 정의
flow = Card([
Row(@Each(chain, "s", Row([Box([Text(s, "sm", null, "medium")], null, null, 2, "surface", "md"), s == "Rear wheel" ? null : Icon("arrow-right", "sm", "faint")], 1)), 1, "center", "center", true),
Caption("The shifter pulls a cable; the derailleur guides the chain onto the sprocket you choose.")
], "soft", 3, 2)

// chain 정의
chain = ["Pedals", "Chainring", "Chain", "Rear sprocket", "Rear wheel"]

// teeth 정의
teeth = [28, 24, 21, 18, 16, 14, 12]

// tooth 정의
tooth = teeth[$gear - 1]

// ratio 정의
ratio = 40 / tooth

// speed 정의
speed = $rpm * ratio * 2.1 * 60 / 1000

// mode 정의
mode = $gear <= 2 ? "Easy, for climbing" : $gear <= 5 ? "Moderate, for cruising" : "Hard, for speed"

// explorer 정의
explorer = Card([
exHead,
cogs,
Slider($gear, 1, 7, 1, "Gear", null, [{value: 1, label: "1 · Easy"}, {value: 4, label: "4"}, {value: 7, label: "7 · Fast"}]),
stats,
Slider($rpm, 50, 110, 5, "Pedaling cadence", " rpm"),
Text(feel, "sm"),
Caption("Assumes a 40-tooth chainring, a 12-28 freewheel and a 700c wheel (2.1 m around). Real bikes vary.")
], null, 4, 3)

// exHead 정의
exHead = Row([
Col([
Label("Explore the seven gears"),
Title("Gear " + $gear + " of 7", "xl")
], 1),
Spacer(),
Badge(mode, $gear <= 2 ? "success" : $gear <= 5 ? "info" : "warning")
], 3, null, null, true)

// cogs 정의
cogs = Svg(@Each(@Range(0, 7), "i", G([Circle(26 + i * 45, 52, 6 + teeth[i] * 0.55, i == $gear - 1 ? "info" : "surface", i == $gear - 1 ? "info" : "faint", 2, i == $gear - 1 ? 0.85 : 1), Circle(26 + i * 45, 52, 4, "muted"), SvgText(teeth[i] + "T", 26 + i * 45, 104, 11, i == $gear - 1 ? "info" : "muted", "middle", i == $gear - 1 ? "semibold" : "normal")])), "0 0 320 112")

// 핵심 지표(KPI) 및 통계 카드
stats = Grid([s1, s2, s3], 3, 90, 2)

// s1 정의
s1 = Card([Caption("Rear sprocket"), Title(tooth + " teeth", "lg")], "soft", 3, 1)

// s2 정의
s2 = Card([
Caption("Gear ratio"),
Title(@Format(ratio, "number", 2) + " : 1", "lg")
], "soft", 3, 1)

// s3 정의
s3 = Card([
Caption("Speed at " + $rpm + " rpm"),
Title(@Format(speed, "number", 1) + " km/h", "lg")
], "soft", 3, 1)

// feel 정의
feel = "Each turn of the pedals turns the rear wheel " + @Format(ratio, "number", 2) + " times. " + ($gear <= 2 ? "Light to push, good for hills and starting from a stop." : $gear <= 5 ? "Balanced effort for flat roads and everyday riding." : "Heavy to push but fast, for descents and tailwinds.")

// hubs 정의
hubs = Col([SearchImage("Cassette (bicycle)", 2.4, "lg"), Grid([h1, h2], 2, 220, 3)], 3)

// h1 정의
h1 = Card([
Text("Freewheel", "sm", null, "semibold"),
Caption("The ratchet sits inside the sprocket cluster, which screws onto the hub. Common on budget and youth bikes.")
], "soft", 3, 1)

// h2 정의
h2 = Card([
Text("Cassette", "sm", null, "semibold"),
Caption("The sprockets slide onto a freehub that holds the ratchet. Better axle support and easy gear swaps.")
], "soft", 3, 1)

// closing 정의
closing = Text("Seven speeds keep the drivetrain simple, light and cheap to maintain: one shifter, one derailleur and no front gear changes to think about.", "sm", "muted")

Case 12, Assemble a youth bike (bike-repair)​

설명: Tap steps to tick them (@Toggle on indexes) with a progress bar; the brake type rewrites one step; the inseam sets the saddle height. 사용자 프롬프트: "Help me assemble a youth bicycle, step by step"

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Col([
head,
meta,
setup,
tools,
Title("Assembly, step by step", "md"),
progress,
checklist,
Title("Visual references", "md"),
refs,
fit,
safety
])

// 상태 변수 ($done): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$done = []

// 상태 변수 ($brakes): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$brakes = "Rim"

// 상태 변수 ($inseam): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$inseam = 56

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
head = Col([
Title("Assemble a youth bike", "xl"),
Text("Most youth bikes arrive about 85% built. You fit the handlebars, front wheel, pedals and saddle, then set up the brakes. Tap each step as you finish it.", "md", "muted")
], 2)

// meta 정의
meta = Row([
Badge("45 to 90 minutes", "info"),
Badge("Beginner friendly", "success"),
Badge("Adult job, kid helper", "purple")
], 2, null, null, true)

// setup 정의
setup = Row([
Label("Brake type"),
SegmentedControl($brakes, ["Rim", "Disc", "Coaster"])
], 3, null, null, true)

// tools 정의
tools = Card([
Label("You will need"),
List([
"4, 5 and 6 mm Allen keys",
"15 mm pedal wrench",
"Phillips screwdriver",
"Pump with a pressure gauge",
"Scissors for the zip ties"
])
], "soft", 3, 2)

// stepList 정의
stepList = [
{title: "Unbox and check", detail: "Cut the zip ties, remove the packaging and lay the parts out: front wheel, pedals marked L and R, saddle and reflectors."},
{title: "Fit the handlebars", detail: "Seat the stem, line the bars up with the front wheel and tighten the bolts evenly, a little at a time."},
{title: "Install the front wheel", detail: "Seat the axle fully in the fork, then close the quick release or tighten both axle nuts. Spin it: it should run straight."},
{title: "Attach the pedals", detail: "The right pedal threads clockwise, the left anticlockwise. Start both by hand, then snug them with the 15 mm wrench."},
{title: "Set the saddle height", detail: "Insert the seat post at least to its minimum insertion mark, set the height below and tighten the clamp."},
{title: brakeTitle, detail: brakeDetail},
{title: "Inflate the tires", detail: "Pump to the pressure printed on the sidewall and check the bead sits evenly all round."},
{title: "Final safety check", detail: "Check every bolt, fit the reflectors and the bell, and test the brakes at walking pace."}
]

// brakeTitle 정의
brakeTitle = $brakes == "Coaster" ? "Check the coaster brake" : $brakes == "Disc" ? "Center the disc brakes" : "Set up the rim brakes"

// brakeDetail 정의
brakeDetail = $brakes == "Rim" ? "Hook the cable noodle into the V-brake arms and center the pads on the rim, not the tire. The wheel should lock before the lever reaches the grip." : $brakes == "Disc" ? "Spin the wheel: the rotor should pass through the caliper without rubbing. If it rubs, loosen the caliper bolts, squeeze the brake and retighten." : "Pedal backwards: the rear wheel should lock. Make sure the brake arm is bolted to the frame strap."

// progress 정의
progress = Col([
Row([
Text(@Count($done) + " of " + @Count(stepList) + " steps done", "sm", "muted"),
Spacer(),
@Count($done) == @Count(stepList) ? Badge("Ready to ride", "success") : Button("Reset", @Reset($done), "ghost", null, "sm", "rotate-ccw")
], 2),
Box([Box([], null, null, null, "chart2", "full", null, @Percent(@Count($done), @Count(stepList)) + "%", 6)], null, null, null, "border", "full", null, "100%", 6)
], 2)

// checklist 정의
checklist = Col(@Each(@Range(0, @Count(stepList)), "i", Pressable([Row([Icon(@Includes($done, i) ? "check-circle" : "circle", "md", @Includes($done, i) ? "success" : "faint"), Col([Text((i + 1) + ". " + stepList[i].title, "sm", null, "semibold", false, @Includes($done, i)), Caption(stepList[i].detail)], 1)], 3, "start")], @Set($done, @Toggle($done, i)), @Includes($done, i), 3)), 1)

// refs 정의
refs = Grid([r1, r2, r3, r4], 2, 160, 3)

// r1 정의
r1 = Col([
SearchImage("Bicycle wheel quick release", 1.4, "lg"),
Caption("Front wheel seated in the fork")
], 1)

// r2 정의
r2 = Col([
SearchImage("Bicycle pedal", 1.4, "lg"),
Caption("Pedals: right clockwise, left anticlockwise")
], 1)

// r3 정의
r3 = Col([SearchImage("V-brake", 1.4, "lg"), Caption("Brake pads meet the rim squarely")], 1)

// r4 정의
r4 = Col([
SearchImage("Seatpost", 1.4, "lg"),
Caption("Never above the minimum insertion mark")
], 1)

// fit 정의
fit = Card([
Title("Saddle height", "sm"),
Input($inseam, "Your child's inseam, in cm", "number"),
Text($inseam > 0 ? "Start with the top of the saddle about **" + @Round($inseam - 2) + " cm** from the ground so both feet touch flat. Once your child rides confidently, raise it to about **" + @Round($inseam + 3) + " cm**, where only the balls of the feet touch." : "Measure from the floor to the crotch, in socks, with the back against a wall.", "sm")
], "soft", 4, 2)

// safety 정의
safety = Box([
Row([
Icon("shield-check", "md", "success"),
Text("Before the first ride: the brakes stop the bike, both wheels are tight, the bars do not twist against the front wheel and the helmet fits snugly.", "sm")
], 2, "start")
], null, null, 3, "success", "lg")

Case 13, San Francisco in a day (route)​

설명: Map markers from @Each with a street route; the pace rescales the schedule (@Cumulative, @Pad); a pin or a row selects the stop. 사용자 프롬프트: "Plan a sightseeing route in San Francisco"

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Col([
head,
photos,
badges,
map,
selected,
Row([
Title("Stop by stop", "md"),
Spacer(),
SegmentedControl($pace, ["Relaxed", "Balanced", "Packed"])
], 3, null, null, true),
itinerary,
closing
])

// 상태 변수 ($stop): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$stop = "Ferry Building"

// 상태 변수 ($pace): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$pace = "Balanced"

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
head = Col([
Title("San Francisco in one day", "xl"),
Text("A scenic loop from the waterfront to the Golden Gate, with time to eat, walk and take photos. Tap a pin or a stop for details.", "md", "muted")
], 2)

// photos 정의
photos = Grid([
SearchImage("Golden Gate Bridge", 1.4, "lg"),
SearchImage("Painted Ladies Alamo Square", 1.4, "lg"),
SearchImage("San Francisco cable car", 1.4, "lg")
], 3, 140, 2)

// badges 정의
badges = Row([
Badge(@Plural(@Count(places), "stop"), "info"),
Badge("About " + @Format(@Last(ends).mins / 60, "number", 1) + " hours", "neutral"),
Badge("Walk, cable car and bus", "purple")
], 2, null, null, true)

// map 정의
map = Map(@Each(places, "p", MapMarker(p.name, p.lat, p.lng, p.category)), true, $stop, 340)

// factor 정의
factor = $pace == "Relaxed" ? 1.3 : $pace == "Packed" ? 0.75 : 1

// plan 정의
plan = @Each(places, "p", {name: p.name, mins: @Round(p.minutes * factor / 5) * 5 + 20})

// ends 정의
ends = @Cumulative(plan, "mins")

// starts 정의
starts = @Each(@Range(0, @Count(places)), "i", 540 + ends[i].mins - plan[i].mins)

// 원본/파생 데이터 목록
rows = @Each(@Range(0, @Count(places)), "i", {name: places[i].name, desc: places[i].desc, category: places[i].category, stay: @Round(places[i].minutes * factor / 5) * 5, time: (@Floor(starts[i] / 60) > 12 ? @Floor(starts[i] / 60) - 12 : @Floor(starts[i] / 60)) + ":" + @Pad(starts[i] % 60, 2) + (starts[i] >= 720 ? " PM" : " AM")})

// sel 정의
sel = @Find(rows, "name", $stop)

// selected 정의
selected = sel == null ? null : Card([Row([Icon("map-pin", "md", "info"), Col([Text(sel.name, "md", null, "semibold"), Caption(sel.time + " · " + sel.stay + " min · " + sel.category)], 0), Spacer(), Button("Directions", @OpenUrl("https://www.google.com/maps/search/?api=1&query=" + sel.name + " San Francisco"), "outline", null, "sm", "navigation")], 3, null, null, true), Text(sel.desc, "sm")], "soft", 3, 2)

// itinerary 정의
itinerary = Col(@Each(rows, "r", Pressable([Row([Box([SearchImage(r.name + " San Francisco", 1, "md")], null, null, null, null, "md", null, 64, 64), Col([Row([Text(r.time, "sm", "info", "semibold", false, false, true), Caption(r.stay + " min")], 2), Text(r.name, "sm", null, "semibold"), Caption(r.desc)], 0)], 3)], @Set($stop, r.name), r.name == $stop, 2)), 1)

// closing 정의
closing = Col([
Text("Times are estimates and include about 20 minutes to get to each next stop. Cable cars run every 10 minutes or so; a Muni day pass covers them and the buses.", "xs", "muted"),
Row([Button("Plan a second day", @ToAssistant("Plan a second day in San Francisco that avoids these stops"), "outline", null, "sm", "calendar-plus")])
], 2)

// places 정의
places = [
{name: "Ferry Building", lat: 37.7955, lng: -122.3937, category: "Food", minutes: 45, desc: "Coffee and pastries in the 1898 market hall on the bay."},
{name: "Coit Tower", lat: 37.8024, lng: -122.4058, category: "Views", minutes: 45, desc: "Climb Telegraph Hill for Depression-era murals and a 360° view."},
{name: "Lombard Street", lat: 37.8021, lng: -122.4187, category: "Landmarks", minutes: 30, desc: "Walk down the crooked block of brick switchbacks and hydrangeas."},
{name: "Fisherman's Wharf", lat: 37.808, lng: -122.4177, category: "Food", minutes: 60, desc: "Sea lions at Pier 39 and clam chowder in a sourdough bowl."},
{name: "Palace of Fine Arts", lat: 37.8029, lng: -122.4484, category: "Landmarks", minutes: 40, desc: "A Roman-style rotunda reflected in a quiet lagoon."},
{name: "Golden Gate Bridge", lat: 37.8077, lng: -122.4752, category: "Views", minutes: 75, desc: "Walk out to the first tower for the classic view back at the city."},
{name: "Golden Gate Park", lat: 37.7694, lng: -122.4862, category: "Parks", minutes: 60, desc: "The Japanese Tea Garden and the Conservatory of Flowers."},
{name: "Alamo Square", lat: 37.7764, lng: -122.4346, category: "Views", minutes: 30, desc: "End with the Painted Ladies and the skyline at sunset."}
]

Case 14, Pacific Northwest road trip (roadtrip)​

설명: Nine stops on a driving route; tap a day to select its stop; the budget tier and traveler stepper recompute the totals and the chart. 사용자 프롬프트: "Plan a ten-day road trip through the Pacific Northwest"

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Col([head, photos, badges, map, Title("Day by day", "md"), days, budget, before, ask])

// 상태 변수 ($stop): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$stop = "Seattle"

// 상태 변수 ($tier): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$tier = "Mid-range"

// 상태 변수 ($travelers): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$travelers = 2

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
head = Col([
Title("Ten days in the Pacific Northwest", "xl"),
Text("Rain forest, wild beaches, the Columbia River Gorge and a volcano, in one loop from Seattle. Tap a day to find it on the map.", "md", "muted")
], 2)

// photos 정의
photos = Grid([
SearchImage("Mount Rainier Paradise", 1.4, "lg"),
SearchImage("Hoh Rain Forest", 1.4, "lg"),
SearchImage("Cannon Beach Haystack Rock", 1.4, "lg")
], 3, 140, 2)

// badges 정의
badges = Row([
Badge("Best July to September", "info"),
Badge("About 1,150 miles", "neutral"),
Badge("9 nights", "purple")
], 2, null, null, true)

// map 정의
map = Map(@Each(stops, "s", MapMarker(s.name, s.lat, s.lng, s.kind)), true, $stop, 360)

// stops 정의
stops = [
{name: "Seattle", lat: 47.6062, lng: -122.3321, kind: "City"},
{name: "Port Angeles", lat: 48.1181, lng: -123.4307, kind: "Town"},
{name: "Hoh Rain Forest", lat: 47.8601, lng: -123.9348, kind: "Nature"},
{name: "Ruby Beach", lat: 47.7107, lng: -124.4152, kind: "Nature"},
{name: "Astoria", lat: 46.1879, lng: -123.8313, kind: "Town"},
{name: "Cannon Beach", lat: 45.8918, lng: -123.9615, kind: "Nature"},
{name: "Portland", lat: 45.5152, lng: -122.6784, kind: "City"},
{name: "Multnomah Falls", lat: 45.5762, lng: -122.1158, kind: "Nature"},
{name: "Mount Rainier", lat: 46.786, lng: -121.7353, kind: "Nature"}
]

// plan 정의
plan = [
{day: 1, title: "Seattle", place: "Seattle", drive: "Arrive", night: "Seattle", text: "Pike Place Market, the waterfront and the Space Needle at dusk."},
{day: 2, title: "Ferry to the Olympic Peninsula", place: "Port Angeles", drive: "2.5 h with the ferry", night: "Port Angeles", text: "Take the Edmonds ferry, then drive up to Hurricane Ridge for mountain views."},
{day: 3, title: "Hoh Rain Forest", place: "Hoh Rain Forest", drive: "2 h", night: "Forks", text: "Walk the Hall of Mosses under moss-draped maples; stop at Lake Crescent on the way."},
{day: 4, title: "The wild coast", place: "Ruby Beach", drive: "1 h", night: "Kalaloch", text: "Sea stacks, driftwood and tide pools at Ruby Beach and Rialto Beach."},
{day: 5, title: "South to Astoria", place: "Astoria", drive: "3.5 h", night: "Astoria", text: "Cross the Columbia at its mouth and climb the Astoria Column."},
{day: 6, title: "Cannon Beach", place: "Cannon Beach", drive: "40 min", night: "Cannon Beach", text: "Haystack Rock at low tide, then the trail at Ecola State Park."},
{day: 7, title: "Portland", place: "Portland", drive: "1.5 h", night: "Portland", text: "Food carts, Powell's Books and the Japanese Garden."},
{day: 8, title: "Columbia River Gorge", place: "Multnomah Falls", drive: "1 h each way", night: "Portland", text: "Multnomah Falls and the waterfall corridor; check whether a timed permit is needed."},
{day: 9, title: "Mount Rainier", place: "Mount Rainier", drive: "3.5 h", night: "Ashford", text: "Wildflower meadows and glacier views on the Skyline Trail at Paradise."},
{day: 10, title: "Back to Seattle", place: "Seattle", drive: "2.5 h", night: "", text: "A last morning hike, then return the car in Seattle."}
]

// days 정의
days = Col(@Each(plan, "d", Pressable([Row([Box([Title(d.day + "", "md")], null, null, 2, d.place == $stop ? "info" : "surface", "md", null, 44, 44, null, "center", "center"), Col([Text(d.title, "sm", null, "semibold"), Caption(d.night == "" ? d.drive : d.drive + " · overnight in " + d.night), Text(d.text, "sm", "muted")], 0)], 3, "start")], @Set($stop, d.place), d.place == $stop, 2)), 1)

// tiers 정의
tiers = [
{name: "Budget", lodging: 115, car: 45, food: 40},
{name: "Mid-range", lodging: 190, car: 60, food: 65},
{name: "Comfort", lodging: 320, car: 90, food: 110}
]

// t 정의
t = @Find(tiers, "name", $tier)

// rooms 정의
rooms = @Ceil($travelers / 2)

// costs 정의
costs = [
{item: "Lodging", amount: t.lodging * 9 * rooms},
{item: "Rental car", amount: t.car * 10},
{item: "Gas", amount: @Round(1150 / 27 * 4.6)},
{item: "Food", amount: t.food * 10 * $travelers},
{item: "Park pass", amount: 80},
{item: "Ferries and extras", amount: 250}
]

// total 정의
total = @Sum(costs.amount)

// budget 정의
budget = Card([
Row([
Title("Estimated budget", "md"),
Spacer(),
SegmentedControl($tier, ["Budget", "Mid-range", "Comfort"])
], 3, null, null, true),
Row([
Text("Travelers", "sm"),
Spacer(),
IconButton("minus", @Set($travelers, @Clamp($travelers - 1, 1, 6)), "Fewer travelers"),
Text($travelers + "", "md", null, "semibold", false, false, true),
IconButton("plus", @Set($travelers, @Clamp($travelers + 1, 1, 6)), "More travelers")
], 2),
Grid([k1, k2], 2, 150, 3),
Chart("bar", costs, "item", ["amount"], "currency", null, false, 200),
Caption("Summer prices for 9 nights, " + @Plural(rooms, "room") + ", one rental car. Treat it as a starting point.")
], null, 4, 3)

// k1 정의
k1 = Card([Caption("Trip total"), Title(@Format(total, "currency", 0), "xl")], "soft", 3, 1)

// k2 정의
k2 = Card([
Caption("Per person"),
Title(@Format(total / $travelers, "currency", 0), "xl")
], "soft", 3, 1)

// before 정의
before = Card([
Title("Before you go", "sm"),
List([
"Book lodges near Olympic and Rainier months ahead for summer.",
"Multnomah Falls may need a timed-use permit in peak season.",
"Check wildfire closures and air quality the week you leave.",
"An America the Beautiful pass covers both national parks."
])
], "soft", 4, 2)

// ask 정의
ask = Row([
Button("Swap a day for the San Juan Islands", @ToAssistant("Swap one day of this Pacific Northwest road trip for the San Juan Islands"), "outline", null, "sm", "ship")
])

Case 15, Find your paint color (room)​

설명: Three answers score six colors (@Includes, @Sort, @Take); the room Svg takes the picked color, the sofa and a light tint over the top. 사용자 프롬프트: "Help me choose a new paint color for my living room"

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Col([
head,
preview,
Title("Tell me about your room", "md"),
quiz,
Title("Your best matches", "md"),
matches,
Title("All six colors", "md"),
swatches,
tip
])

// 상태 변수 ($mood): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$mood = "calm"

// 상태 변수 ($light): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$light = "north"

// 상태 변수 ($sofa): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$sofa = "grey"

// 상태 변수 ($pick): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$pick = "Warm ivory"

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
head = Col([
Title("Find your living room color", "xl"),
Text("Answer three questions and the matches update. Tap any color to paint the room.", "md", "muted")
], 2)

// colors 정의
colors = [
{name: "Warm ivory", hex: "#EFE6D3", note: "Soft and bright, never stark", moods: ["calm", "bright"], lights: ["north", "low"], sofas: ["grey", "blue", "green", "brown"]},
{name: "Sage green", hex: "#B5BFA1", note: "Grounded and restful", moods: ["calm", "cozy"], lights: ["south", "east"], sofas: ["grey", "brown", "white"]},
{name: "Greige", hex: "#CFC5B8", note: "A warm neutral that goes with everything", moods: ["calm", "bright"], lights: ["north", "south", "east", "low"], sofas: ["grey", "blue", "green", "brown", "white"]},
{name: "Dusty blue", hex: "#9DB0BF", note: "Airy and cool", moods: ["calm", "bright"], lights: ["south", "east"], sofas: ["white", "brown", "grey"]},
{name: "Terracotta", hex: "#C9785A", note: "Warm and full of character", moods: ["cozy", "bold"], lights: ["north", "low", "east"], sofas: ["white", "green", "blue"]},
{name: "Deep navy", hex: "#2F3E55", note: "Dramatic and enveloping", moods: ["bold", "cozy"], lights: ["south", "east"], sofas: ["white", "brown", "green"]}
]

// scored 정의
scored = @Each(colors, "c", {name: c.name, hex: c.hex, note: c.note, score: (@Includes(c.moods, $mood) ? 2 : 0) + (@Includes(c.lights, $light) ? 2 : 0) + (@Includes(c.sofas, $sofa) ? 1 : 0)})

// best 정의
best = @Take(@Sort(scored, "score", "desc"), 3)

// picked 정의
picked = @Find(colors, "name", $pick)

// sofaHex 정의
sofaHex = $sofa == "grey" ? "#8D9197" : $sofa == "blue" ? "#3F5A7A" : $sofa == "green" ? "#4F6B52" : $sofa == "brown" ? "#7A5236" : "#EFEEEA"

// tint 정의
tint = $light == "north" ? "#9EC5FF" : $light == "south" ? "#FFC870" : $light == "east" ? "#FFE6B0" : "#000000"

// tintOpacity 정의
tintOpacity = $light == "north" ? 0.14 : $light == "south" ? 0.16 : $light == "east" ? 0.08 : 0.22

// lightNote 정의
lightNote = $light == "north" ? "North light is cool and flat: warm colors stay warm" : $light == "south" ? "South light is warm: cool colors keep it fresh" : $light == "east" ? "The color shifts from morning to evening" : "Low light deepens every color"

// room 정의
room = Svg([
Rect(0, 0, 320, 132, picked.hex),
Rect(0, 132, 320, 48, "#B08D6A"),
Rect(0, 128, 320, 6, "#E9E4DA"),
Rect(28, 24, 70, 76, "#DCE9F5", "#FFFFFF", 5),
Line(63, 24, 63, 100, "#FFFFFF", 4),
Line(28, 62, 98, 62, "#FFFFFF", 4),
Rect(196, 30, 70, 44, "#F4F1EA", "#8A7A66", 3),
Circle(214, 52, 9, "#C9785A", null, null, 0.7),
Circle(238, 48, 12, "#9DB0BF", null, null, 0.7),
Rect(150, 98, 150, 40, sofaHex, null, null, 1, 10),
Rect(160, 84, 62, 26, sofaHex, null, null, 1, 8),
Rect(228, 84, 62, 26, sofaHex, null, null, 1, 8),
Rect(144, 104, 16, 34, sofaHex, null, null, 1, 6),
Rect(290, 104, 16, 34, sofaHex, null, null, 1, 6),
Rect(0, 0, 320, 180, tint, null, null, tintOpacity)
], "0 0 320 180")

// preview 정의
preview = Card([
room,
Row([
Box([], null, null, null, picked.hex, "full", true, 18, 18),
Text(picked.name, "sm", null, "semibold"),
Spacer(),
Caption(lightNote)
], 2, null, null, true)
], null, 3, 2)

// 인터랙티브 퀴즈 카드
quiz = Grid([
RadioGroup($mood, [{value: "calm", label: "Calm and restful"}, {value: "cozy", label: "Warm and cozy"}, {value: "bright", label: "Light and airy"}, {value: "bold", label: "A bold statement"}], "How should it feel?"),
RadioGroup($light, [{value: "north", label: "North facing, cool light"}, {value: "south", label: "South facing, warm sun"}, {value: "east", label: "East or west, changing"}, {value: "low", label: "Little daylight"}], "What light does it get?"),
RadioGroup($sofa, [{value: "grey", label: "Grey"}, {value: "blue", label: "Blue"}, {value: "green", label: "Green"}, {value: "brown", label: "Brown leather"}, {value: "white", label: "White or cream"}], "Your sofa")
], 3, 200, 4)

// matches 정의
matches = Grid(@Each(best, "c", Pressable([Box([], null, null, null, c.hex, "md", true, "100%", 64), Text(c.name, "sm", null, "semibold"), Caption(c.note), Badge(c.score >= 4 ? "Great match" : c.score >= 2 ? "Good match" : "Worth a look", c.score >= 4 ? "success" : c.score >= 2 ? "info" : "neutral")], @Set($pick, c.name), c.name == $pick, 2)), 3, 150, 3)

// swatches 정의
swatches = Row(@Each(colors, "c", Pressable([Col([Box([], null, null, null, c.hex, "full", true, 40, 40), Caption(c.name)], 1, "center")], @Set($pick, c.name), c.name == $pick, 2)), 2, null, null, true)

// tip 정의
tip = Text("Paint chips look different on the wall. Try a large sample on two walls and look at it in the morning and the evening before you commit.", "sm", "muted")

Case 16, Fold an origami fox (origami)​

설명: Six Svg diagrams in a list; Back and Next step through them with @Clamp, and the thumbnails jump to a step. 사용자 프롬프트: "Show me how to fold an origami baby fox"

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Col([head, viewer, Title("All six steps", "md"), strip, tip])

// 상태 변수 ($step): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$step = 0

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
head = Col([
Title("Fold an origami fox", "xl"),
Text("A simple fox face from one square of paper: no cutting, about five minutes. Orange on one side and white on the other works best.", "md", "muted"),
Row([
Badge("Beginner", "success"),
Badge("5 minutes", "info"),
Badge("One square sheet", "neutral")
], 2, null, null, true)
], 2)

// 원본/파생 데이터 목록
steps = [
{title: "Start with a diamond", text: "Place the square white side up, with a corner pointing at you."},
{title: "Fold it in half", text: "Fold the top corner down to the bottom corner to make a triangle. Crease well."},
{title: "Corners to the point", text: "Fold the left and right corners down to the bottom point, making a small diamond."},
{title: "Make the ears", text: "Fold each flap's bottom tip back up and out past the top edge. These are the ears."},
{title: "Fold the forehead", text: "Fold the top point down a little to give the fox a flat forehead."},
{title: "Turn over and add a face", text: "Turn the paper over. Draw two eyes and a nose near the bottom point."}
]

// o 정의
o = "#F28C38"

// w 정의
w = "#FBE6D2"

// e 정의
e = "#B85E1C"

// d0 정의
d0 = Svg([
Polygon("100,20 180,100 100,180 20,100", w, e, 2),
Line(20, 100, 180, 100, e, 1.5, 0.5),
Line(100, 34, 100, 156, e, 2, 0.8),
Polygon("93,150 107,150 100,164", e)
], "0 0 200 200")

// d1 정의
d1 = Svg([
Polygon("20,100 180,100 100,180", o, e, 2),
Line(100, 100, 100, 180, e, 1.5, 0.5)
], "0 0 200 200")

// d2 정의
d2 = Svg([
Polygon("100,100 60,140 100,180 140,140", w, e, 2),
Line(100, 100, 100, 180, e, 1.5)
], "0 0 200 200")

// d3 정의
d3 = Svg([
Polygon("60,140 100,100 140,140 100,180", o, e, 2),
Polygon("62,138 98,124 70,58", w, e, 2),
Polygon("138,138 102,124 130,58", w, e, 2)
], "0 0 200 200")

// d4 정의
d4 = Svg([
Polygon("60,140 80,118 120,118 140,140 100,180", o, e, 2),
Polygon("62,138 98,124 70,58", w, e, 2),
Polygon("138,138 102,124 130,58", w, e, 2),
Polygon("80,118 120,118 100,136", w, e, 1.5)
], "0 0 200 200")

// d5 정의
d5 = Svg([
Polygon("60,140 80,118 120,118 140,140 100,180", o, e, 2),
Polygon("62,138 98,124 70,58", o, e, 2),
Polygon("138,138 102,124 130,58", o, e, 2),
Polygon("70,124 88,122 74,82", w),
Polygon("130,124 112,122 126,82", w),
Circle(86, 142, 4.5, "#2B2B2B"),
Circle(114, 142, 4.5, "#2B2B2B"),
Circle(100, 168, 5, "#2B2B2B")
], "0 0 200 200")

// drawings 정의
drawings = [d0, d1, d2, d3, d4, d5]

// viewer 정의
viewer = Card([
Row([
Badge("Step " + ($step + 1) + " of 6", "info"),
Spacer(),
Caption(@Percent($step + 1, 6) + "% folded")
], 2),
Box([drawings[$step]], null, null, 3, "surface", "lg", null, "100%", null, null, "center", "center"),
Title(steps[$step].title, "md"),
Text(steps[$step].text, "sm"),
Box([Box([], null, null, null, "chart1", "full", null, @Percent($step + 1, 6) + "%", 4)], null, null, null, "border", "full", null, "100%", 4),
Row([
Button("Back", @Set($step, @Clamp($step - 1, 0, 5)), "outline", null, null, "arrow-left", $step == 0),
Spacer(),
$step < 5 ? Button("Next fold", @Set($step, $step + 1), "solid", null, null, "arrow-right") : Button("Start again", @Set($step, 0), "solid", null, null, "rotate-ccw")
], 2)
], null, 4, 3)

// strip 정의
strip = Grid(@Each(@Range(0, 6), "i", Pressable([drawings[i], Caption((i + 1) + ". " + steps[i].title)], @Set($step, i), i == $step, 2)), 6, 96, 2)

// tip 정의
tip = Row([
Icon("lightbulb", "sm", "warning"),
Text("Sharp creases make the difference: run a fingernail along each fold before the next one.", "sm", "muted")
], 2)

Case 17, Plan a vegetable garden (garden)​

설명: Bed size and sun sliders, crop chips with @Toggle; a square-foot Grid with $w columns, a sun warning, a summary Table and a checklist. 사용자 프롬프트: "Help me plan a small backyard vegetable garden"

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Col([
head,
size,
Title("Pick what to grow", "md"),
picker,
sunNote,
Title("Your bed, square by square", "md"),
bed,
Caption("Each square is one foot across; the number is how many plants fit in it."),
Title("Planting summary", "md"),
summary,
Title("What you will need", "md"),
needs
])

// 상태 변수 ($w): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$w = 4

// 상태 변수 ($l): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$l = 6

// 상태 변수 ($sun): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$sun = 7

// 상태 변수 ($chosen): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$chosen = ["Tomatoes", "Lettuce", "Beans", "Radishes", "Basil", "Carrots"]

// 상태 변수 ($got): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$got = []

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
head = Col([
Title("Plan a small vegetable garden", "xl"),
Text("Set the bed size and how much sun it gets, choose your crops, and the square-foot plan fills itself in.", "md", "muted")
], 2)

// crops 정의
crops = [
{name: "Tomatoes", icon: "🍅", per: 1, weeks: 10, sun: 8, color: "#F6D5CF"},
{name: "Peppers", icon: "🌶️", per: 1, weeks: 11, sun: 8, color: "#F7DCC4"},
{name: "Beans", icon: "🌱", per: 9, weeks: 8, sun: 6, color: "#DCEBD0"},
{name: "Lettuce", icon: "🥬", per: 4, weeks: 6, sun: 4, color: "#E2F0D3"},
{name: "Spinach", icon: "🍃", per: 9, weeks: 6, sun: 4, color: "#D5EAD6"},
{name: "Carrots", icon: "🥕", per: 16, weeks: 10, sun: 6, color: "#FBE2C9"},
{name: "Radishes", icon: "🔴", per: 16, weeks: 4, sun: 4, color: "#F8D9E2"},
{name: "Basil", icon: "🌿", per: 4, weeks: 8, sun: 6, color: "#D8ECCF"}
]

// picked 정의
picked = @Each($chosen, "n", @Find(crops, "name", n))

// cells 정의
cells = @Count(picked) == 0 ? [] : @Each(@Range(0, $w * $l), "i", picked[i % @Count(picked)])

// size 정의
size = Card([
Grid([
Slider($w, 2, 8, 1, "Bed width", " ft"),
Slider($l, 2, 12, 1, "Bed length", " ft"),
Slider($sun, 2, 12, 1, "Direct sun", " h")
], 3, 200, 5),
Row([
Badge(@Plural($w * $l, "square foot", "square feet"), "info"),
Badge("About " + @Plural(@Ceil($w * $l * 0.83 / 1.5), "bag") + " of soil for 10 inches deep", "neutral")
], 2, null, null, true)
], "soft", 4, 3)

// 사용자 입력 및 컨트롤 폼
picker = Row(@Each(crops, "c", Pressable([Row([Text(c.icon, "md"), Text(c.name, "sm", null, "medium")], 1)], @Set($chosen, @Toggle($chosen, c.name)), @Includes($chosen, c.name), 2, null, "full")), 2, null, null, true)

// shady 정의
shady = @Filter(picked, "sun", ">", $sun)

// sunNote 정의
sunNote = @Count(shady) > 0 ? Box([Text("With " + @Plural($sun, "hour") + " of sun, " + @Join(shady, ", ", "name") + " will struggle. Leafy greens and radishes cope with 4 hours.", "sm", "warning")], null, null, 3, "warning", "lg") : Box([Text(@Count(picked) == 0 ? "Choose at least one crop to see the plan." : "Everything you picked gets enough sun.", "sm", @Count(picked) == 0 ? "muted" : "success")], null, null, 3, @Count(picked) == 0 ? "surface" : "success", "lg")

// bed 정의
bed = Box([
Grid(@Each(cells, "c", Box([Text(c.icon, "lg"), Caption(c.per + "")], null, 0, 1, c.color, "sm", null, null, 56, null, "center", "center")), $w, null, 1)
], null, null, 2, "#8B6A4E", "lg")

// 요약 및 결과 표시 카드
summary = Table([
"Crop",
"Squares",
"Plants",
"First harvest",
"Sun"], @Each(picked,
"p",
[p.icon + " " + p.name, @Count(@Filter(cells, "name", "==", p.name)), @Count(@Filter(cells, "name", "==", p.name)) * p.per, p.weeks + " weeks", p.sun + "+ h"
]))

// 원본/파생 데이터 목록
items = ["A raised bed or a dug-over plot", "Compost-rich soil", "Seeds or seedlings", "Mulch", "A watering can or drip hose", "Stakes or a trellis"]

// needs 정의
needs = Card([
Row([
Text(@Count($got) + " of " + @Count(items) + " ready", "sm", "muted"),
Spacer(),
Button("Reset", @Reset($got), "ghost", null, "sm", "rotate-ccw")
], 2),
Col(@Each(items, "it", Pressable([Row([Icon(@Includes($got, it) ? "check-circle" : "circle", "sm", @Includes($got, it) ? "success" : "faint"), Text(it, "sm", null, null, false, @Includes($got, it))], 2)], @Set($got, @Toggle($got, it)), @Includes($got, it), 2)), 1)
], null, 4, 2)

Case 18, Capsule wardrobe (wardrobe)​

설명: Trip length and weather rebuild the packing list (@Concat, @Take) and the day-by-day outfits (modulo indexes); a packing checklist. 사용자 프롬프트: "Build me a versatile capsule wardrobe for a week away"

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Col([
head,
photos,
setup,
Title("The palette", "md"),
palette,
Title("Packing list", "md"),
packing,
Title("Outfits, day by day", "md"),
outfits,
tip
])

// 상태 변수 ($days): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$days = 7

// 상태 변수 ($weather): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$weather = "Mild"

// 상태 변수 ($packed): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$packed = []

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
head = Col([
Title("A capsule wardrobe for a week away", "xl"),
Text("A dozen pieces in one palette that all work together. Change the trip and the list and the outfits follow.", "md", "muted")
], 2)

// photos 정의
photos = Grid([
SearchImage("Folded clothes", 1.2, "lg"),
SearchImage("Capsule wardrobe", 1.2, "lg"),
SearchImage("Suitcase", 1.2, "lg")
], 3, 140, 2)

// setup 정의
setup = Card([
Grid([
Slider($days, 3, 14, 1, "Trip length", " days"),
Col([Label("Weather"), SegmentedControl($weather, ["Warm", "Mild", "Cool"])], 2)
], 2, 220, 5)
], "soft", 4)

// colors 정의
colors = [
{name: "Navy", hex: "#2F3E55"},
{name: "White", hex: "#F7F5F0"},
{name: "Stone", hex: "#CFC5B8"},
{name: "Olive", hex: "#6B7350"},
{name: "Tan", hex: "#B98B5E"}
]

// palette 정의
palette = Row(@Each(colors, "c", Col([Box([], null, null, null, c.hex, "full", true, 40, 40), Caption(c.name)], 1, "center")), 4, null, null, true)

// tops 정의
tops = $weather == "Warm" ? ["White linen shirt", "Navy tee", "Striped tee", "Olive camp shirt"] : $weather == "Cool" ? ["Merino crewneck", "White Oxford shirt", "Navy long-sleeve tee", "Stone knit"] : ["White tee", "Navy Oxford shirt", "Striped long-sleeve tee", "Stone knit polo"]

// bottoms 정의
bottoms = $weather == "Warm" ? ["Stone linen trousers", "Navy shorts"] : $weather == "Cool" ? ["Dark jeans", "Olive chinos"] : ["Dark jeans", "Stone chinos"]

// layers 정의
layers = $weather == "Warm" ? ["Light overshirt"] : $weather == "Cool" ? ["Wool coat", "Navy cardigan"] : ["Unstructured navy jacket"]

// shoes 정의
shoes = $weather == "Cool" ? ["Leather boots", "White sneakers"] : ["White sneakers", "Tan loafers"]

// nTops 정의
nTops = @Clamp(@Ceil($days / 2) + 1, 3, @Count(tops))

// list 정의
list = @Concat(@Concat(@Each(@Take(tops, nTops), "x", {group: "Top", name: x}), @Each(bottoms, "x", {group: "Bottom", name: x})), @Concat(@Concat(@Each(layers, "x", {group: "Layer", name: x}), @Each(shoes, "x", {group: "Shoes", name: x})), [{group: "Basics", name: "Socks and underwear × " + @Min([$days, 7])}, {group: "Basics", name: "Sleepwear"}, {group: "Basics", name: $weather == "Warm" ? "Swimsuit and sunglasses" : "Compact umbrella"}]))

// done 정의
done = @Count(@Filter(@Each(list, "it", @Includes($packed, it.name)), "", "==", true))

// packing 정의
packing = Card([
Row([
Text(done + " of " + @Count(list) + " packed", "sm", "muted", null, false, false, true),
Spacer(),
Button("Reset", @Reset($packed), "ghost", null, "sm", "rotate-ccw")
], 2),
Box([Box([], null, null, null, "ink", "full", null, @Percent(done, @Count(list)) + "%", 4)], null, null, null, "border", "full", null, "100%", 4),
Col(@Each(list, "it", Pressable([Row([Icon(@Includes($packed, it.name) ? "check-circle" : "circle", "sm", @Includes($packed, it.name) ? "success" : "faint"), Text(it.name, "sm", null, null, false, @Includes($packed, it.name)), Spacer(), Caption(it.group)], 2)], @Set($packed, @Toggle($packed, it.name)), @Includes($packed, it.name), 2)), 1),
$days > 7 ? Caption("Plan one laundry stop around day " + @Ceil($days / 2) + ".") : null
], null, 4, 2)

// outfits 정의
outfits = Card([
Col(@Each(@Range(0, $days), "d", Row([Box([Text("Day " + (d + 1), "xs", null, "semibold")], null, null, 1, "surface", "md", null, 56, null, null, "center", "center"), Text(tops[d % nTops] + " with " + bottoms[d % @Count(bottoms)] + ($weather == "Warm" ? "" : ", " + layers[d % @Count(layers)]), "sm"), Spacer(), Caption(shoes[d % @Count(shoes)])], 2)), 2)
], null, 3)

// tip 정의
tip = Text("Wear the bulkiest shoes and layer on the travel day, and roll rather than fold to fit everything in a carry-on.", "sm", "muted")

Case 19, The drone photography market (drones)​

설명: Segments, a market-size Chart and a players Table; four sliders run a business model with profit, margin and break-even. 사용자 프롬프트: "Help me understand the drone photography market"

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Col([
head,
Title("How the market is structured", "md"),
segments,
Title("How big is it?", "md"),
sizeCard,
Title("Who makes money", "md"),
players,
Title("Try the economics of a drone photo business", "md"),
sim,
Title("What is changing", "md"),
changes,
ask
])

// 상태 변수 ($projects): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$projects = 12

// 상태 변수 ($price): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$price = 450

// 상태 변수 ($cost): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$cost = 90

// 상태 변수 ($fixed): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$fixed = 1800

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
head = Col([
Title("The drone photography market", "xl"),
Text("Aerial photos and video for property, events, inspection and media: a young market where cheap hardware meets licensed, skilled work.", "md", "muted")
], 2)

// segs 정의
segs = [
{name: "Real estate and architecture", query: "Suburb aerial view", text: "Listing photos and video, the steadiest source of work."},
{name: "Weddings and events", query: "Wedding photography", text: "Seasonal and priced per event, often sold with a ground crew."},
{name: "Inspection and surveying", query: "Solar farm aerial", text: "Roofs, solar farms and building sites; more training, better pay."},
{name: "Media and tourism", query: "Aerial photography coastline", text: "Stock footage, tourism boards and film: visible but competitive."}
]

// segments 정의
segments = Grid(@Each(segs, "s", Card([SearchImage(s.query, 1.7, "md"), Text(s.name, "sm", null, "semibold"), Caption(s.text)], null, 3, 2)), 2, 220, 3)

// market 정의
market = [
{year: "2024", size: 860000000},
{year: "2026", size: 1110000000},
{year: "2028", size: 1520000000},
{year: "2030", size: 2260000000}
]

// sizeCard 정의
sizeCard = Card([
Row([
Col([Caption("Drone photography services, worldwide, 2026"), Title(@Format(1110000000, "compact", 1, "USD"), "2xl")], 1),
Spacer(),
Badge("About 19% a year", "success")
], 3, null, null, true),
Chart("bar", market, "year", ["size"], "currency", null, false, 200),
Caption("Illustrative figures in the range of public market reports; estimates vary widely with the market definition.")
], null, 4, 2)

// players 정의
players = Table([
"Who",
"How they earn",
"Margin"], [["Drone makers", "Hardware, cameras and accessories", Badge("Medium", "warning")],
["Freelance pilots", "Per shoot or per project", Badge("High", "success")],
["Agencies", "Bundles of photo, video and editing", Badge("Medium", "warning")],
["Software platforms", "Mapping and processing subscriptions", Badge("High", "success")]
])

// revenue 정의
revenue = $projects * $price

// costs 정의
costs = $fixed + $projects * $cost

// profit 정의
profit = revenue - costs

// margin 정의
margin = revenue > 0 ? profit / revenue * 100 : 0

// breakeven 정의
breakeven = $price > $cost ? @Ceil($fixed / ($price - $cost)) : null

// curve 정의
curve = @Each(@Range(0, 41, 5), "n", {projects: n + "", Revenue: n * $price, Costs: $fixed + n * $cost})

// sim 정의
sim = Card([
Grid([
Slider($projects, 0, 40, 1, "Projects per month"),
Slider($price, 100, 1500, 25, "Price per project", "$"),
Slider($cost, 0, 400, 10, "Cost per project", "$"),
Slider($fixed, 0, 6000, 100, "Fixed costs per month", "$")
], 2, 240, 5),
Grid([k1, k2, k3, k4], 4, 130, 3),
Chart("line", curve, "projects", ["Revenue", "Costs"], "currency", null, false, 200),
Text(breakevenText, "sm"),
Caption("Illustrative. Fixed costs cover insurance, gear, software and marketing; project costs cover editing and travel.")
], null, 4, 4)

// k1 정의
k1 = Card([
Caption("Revenue a month"),
Title(@Format(revenue, "currency", 0), "lg")
], "soft", 3, 1)

// k2 정의
k2 = Card([
Caption("Profit a month"),
Title(@Format(profit, "currency", 0), "lg", profit >= 0 ? "success" : "danger")
], "soft", 3, 1)

// k3 정의
k3 = Card([Caption("Margin"), Title(@Format(margin, "percent", 1), "lg")], "soft", 3, 1)

// k4 정의
k4 = Card([
Caption("Break-even"),
Title(breakeven == null ? "Never" : @Plural(breakeven, "project"), "lg")
], "soft", 3, 1)

// breakevenText 정의
breakevenText = breakeven == null ? "Each project costs more than it earns, so more work only loses more money." : breakeven > 40 ? "You would need " + @Plural(breakeven, "project") + " a month just to cover fixed costs: raise the price or cut costs." : "Fixed costs are covered from project " + breakeven + " each month; every project after that adds " + @Format($price - $cost, "currency", 0) + " of profit."

// changes 정의
changes = List([
"Cheaper drones with better cameras push basic photo prices down.",
"Licensing, such as FAA Part 107 in the US, keeps casual competition out of paid work.",
"Inspection, mapping and thermal imaging grow faster than photography alone.",
"Clients increasingly want edited video and 3D models, not just photos."
])

// ask 정의
ask = Row([
Button("Compare with drone inspection services", @ToAssistant("Compare the drone photography market with drone inspection services"), "outline", null, "sm", "arrow-right")
])

Case 20, A retro arcade game (game)​

설명: HtmlView runs a canvas space shooter with keyboard and on-screen controls, in the sandboxed iframe. 사용자 프롬프트: "Make a retro arcade game I can play here"

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Col([
Title("Neon Invaders", "lg"),
Text("A tiny retro shooter. Clear each wave before the invaders land; every wave is faster than the last.", "sm", "muted"),
HtmlView("<style>\nbody{padding:12px;display:flex;flex-direction:column;gap:10px;align-items:center;background:#0b0b1a;color:#e6e6ff;font-family:ui-monospace,Menlo,monospace}\n.top{display:flex;justify-content:space-between;width:100%;max-width:400px;font-size:13px;letter-spacing:.06em}\ncanvas{width:100%;max-width:400px;aspect-ratio:4/5;border-radius:10px;background:#05050f;box-shadow:0 0 0 1px #2a2a55,0 0 24px #3b1d6e66;touch-action:none;outline:none}\n.pad{display:flex;gap:8px;width:100%;max-width:400px}\n.pad button{flex:1;padding:12px 0;font:600 14px ui-monospace,Menlo,monospace;color:#e6e6ff;background:#1a1a3a;border:1px solid #3a3a7a;border-radius:10px;user-select:none;-webkit-user-select:none;touch-action:none;cursor:pointer}\n.pad button:active{background:#2c2c66}\n#start{background:#ff2bd6;border-color:#ff2bd6;color:#0b0b1a}\np{margin:0;font-size:12px;opacity:.7}\n</style>\n<div class=\"top\"><span id=\"score\">SCORE 0</span><span id=\"wave\">WAVE 1</span><span id=\"lives\">♥♥♥</span></div>\n<canvas id=\"c\" width=\"400\" height=\"500\" tabindex=\"0\"></canvas>\n<div class=\"pad\"><button id=\"left\" aria-label=\"Move left\">◀</button><button id=\"fire\">FIRE</button><button id=\"right\" aria-label=\"Move right\">▶</button></div>\n<div class=\"pad\"><button id=\"start\">START GAME</button></div>\n<p>Arrow keys move, space fires. On a phone, use the buttons.</p>\n<script>\nconst c=document.getElementById('c'),g=c.getContext('2d'),W=400,H=500,S=id=>document.getElementById(id),keys={};\nlet ship,shots,bombs,aliens,dir,speed,score=0,lives=3,wave=1,running=false,over=false,last=0,cool=0;\nconst stars=Array.from({length:60},()=>[Math.random()*W,Math.random()*H,Math.random()*1.5+.3]);\nconst colors=['#ff2bd6','#ffb000','#2bf0ff','#7dff5a'];\nfunction newWave(){aliens=[];for(let r=0;r<4;r++)for(let k=0;k<8;k++)aliens.push({x:40+k*42,y:50+r*34,r});dir=1;speed=20+wave*8}\nfunction reset(){ship={x:W/2};shots=[];bombs=[];score=0;lives=3;wave=1;over=false;newWave();hud()}\nfunction hud(){S('score').textContent='SCORE '+score;S('wave').textContent='WAVE '+wave;S('lives').textContent='♥'.repeat(Math.max(lives,0))||'-'}\nfunction fire(){if(running&&cool<=0&&shots.length<3){shots.push({x:ship.x,y:H-48});cool=.28}}\nfunction step(dt){\nconst mv=(keys.ArrowLeft||keys.left?-1:0)+(keys.ArrowRight||keys.right?1:0);\nship.x=Math.max(20,Math.min(W-20,ship.x+mv*240*dt));cool-=dt;if(keys[' ']||keys.fire)fire();\nshots.forEach(s=>s.y-=420*dt);\nlet edge=false;aliens.forEach(a=>{a.x+=dir*speed*dt;if(a.x<16||a.x>W-16)edge=true});\nif(edge){dir*=-1;aliens.forEach(a=>{a.y+=14;a.x+=dir*2})}\nif(aliens.length&&Math.random()<dt*(.8+wave*.3)){const a=aliens[Math.floor(Math.random()*aliens.length)];bombs.push({x:a.x,y:a.y+10})}\nbombs.forEach(b=>b.y+=(150+wave*15)*dt);\nfor(const s of shots)for(const a of aliens)if(!a.dead&&!s.hit&&Math.abs(s.x-a.x)<14&&Math.abs(s.y-a.y)<11){a.dead=s.hit=true;score+=10*(4-a.r)}\naliens=aliens.filter(a=>!a.dead);shots=shots.filter(s=>!s.hit&&s.y>-10);\nfor(const b of bombs)if(Math.abs(b.x-ship.x)<16&&b.y>H-46&&b.y<H-20){b.hit=true;lives--}\nbombs=bombs.filter(b=>!b.hit&&b.y<H);\nif(aliens.some(a=>a.y>H-70))lives=0;\nif(!aliens.length){wave++;newWave()}\nif(lives<=0){running=false;over=true;S('start').textContent='PLAY AGAIN'}\nhud()}\nfunction draw(){\ng.fillStyle='#05050f';g.fillRect(0,0,W,H);g.fillStyle='#fff';\nstars.forEach(s=>{g.globalAlpha=s[2]/2;g.fillRect(s[0],s[1],s[2],s[2])});g.globalAlpha=1;\naliens.forEach(a=>{g.fillStyle=colors[a.r];g.shadowColor=colors[a.r];g.shadowBlur=8;g.fillRect(a.x-12,a.y-7,24,14);g.shadowBlur=0;g.fillStyle='#05050f';g.fillRect(a.x-7,a.y-3,4,4);g.fillRect(a.x+3,a.y-3,4,4)});\ng.shadowBlur=10;g.shadowColor='#2bf0ff';g.fillStyle='#2bf0ff';g.beginPath();g.moveTo(ship.x,H-46);g.lineTo(ship.x+16,H-24);g.lineTo(ship.x-16,H-24);g.closePath();g.fill();\ng.fillStyle='#fff';shots.forEach(s=>g.fillRect(s.x-1.5,s.y-8,3,10));\ng.fillStyle='#ff4d4d';g.shadowColor='#ff4d4d';bombs.forEach(b=>g.fillRect(b.x-2,b.y-6,4,9));g.shadowBlur=0;\nif(!running){g.fillStyle='rgba(5,5,15,.75)';g.fillRect(0,H/2-50,W,100);g.textAlign='center';g.fillStyle='#ff2bd6';g.font='bold 28px ui-monospace,Menlo,monospace';g.fillText(over?'GAME OVER':'NEON INVADERS',W/2,H/2-5);g.fillStyle='#e6e6ff';g.font='13px ui-monospace,Menlo,monospace';g.fillText(over?'Score '+score+'. Press play again':'Press start to play',W/2,H/2+22)}}\nfunction loop(t){const dt=Math.min(.05,(t-last)/1000||0);last=t;if(running)step(dt);draw();requestAnimationFrame(loop)}\naddEventListener('keydown',e=>{if(['ArrowLeft','ArrowRight',' '].includes(e.key)){keys[e.key]=true;e.preventDefault()}});\naddEventListener('keyup',e=>{keys[e.key]=false});\nfor(const id of ['left','right','fire']){const b=S(id);b.addEventListener('pointerdown',e=>{keys[id]=true;b.setPointerCapture(e.pointerId)});b.addEventListener('pointerup',()=>{keys[id]=false});b.addEventListener('pointercancel',()=>{keys[id]=false})}\nS('start').onclick=()=>{reset();running=true;S('start').textContent='RESTART';c.focus()};\nreset();requestAnimationFrame(loop);\n</script>", null, "Neon Invaders")
], 3)

Case 21, Sales dashboard (dash-sales)​

설명: Query(get_orders) with channel and period filters; @Bucket, @Cumulative, @Change, @GroupBy, @Pivot and @Top. 사용자 프롬프트: "Build a live sales dashboard for the last 90 days: revenue, orders, average order value, revenue by week and by region, and the top products."

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Col([head, kpis, Grid([trend, mix], 2, 300, 4), Grid([regions, top], 2, 300, 4)], 4)

// 상태 변수 ($days): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$days = "90"

// 상태 변수 ($channel): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$channel = "All"

// 상태 변수 ($view): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$view = "Weekly"

// orders 정의
orders = Query("get_orders", {days: $days}, {rows: []}, 300)

// 원본/파생 데이터 목록
rows = $channel == "All" ? orders.rows : @Filter(orders.rows, "channel", "==", $channel)

// weekly 정의
weekly = @Bucket(rows, "date", "week", "amount")

// daily 정의
daily = @Bucket(rows, "date", "day", "amount")

// last7 정의
last7 = @Sum(@Take(daily, -7).value)

// prev7 정의
prev7 = @Sum(@Slice(daily, -14, -7).value)

// change 정의
change = @Change(last7, prev7)

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
head = Row([
Col([
Title("Store performance", "lg"),
Caption("Demo data, refreshed every 5 minutes")
], 1),
Spacer(),
Box([Select($channel, [{value: "All", label: "All channels"}, "Web", "Mobile", "Retail"])], null, null, null, null, null, null, 150),
SegmentedControl($days, [{value: "30", label: "30d"}, {value: "90", label: "90d"}, {value: "365", label: "1y"}])
], 3, null, null, true)

// 핵심 지표(KPI) 및 통계 카드
kpis = Grid([k1, k2, k3, k4], 4, 150, 3)

// k1 정의
k1 = Card([
Caption("Revenue"),
Title(@Format(@Sum(rows.amount), "compact", 1, "USD"), "xl")
], null, 3, 1)

// k2 정의
k2 = Card([Caption("Orders"), Title(@Format(@Count(rows)), "xl")], null, 3, 1)

// k3 정의
k3 = Card([
Caption("Average order"),
Title(@Format(@Avg(rows.amount), "currency"), "xl")
], null, 3, 1)

// k4 정의
k4 = Card([
Caption("Last 7 days"),
Row([
Title(@Format(last7, "compact", 1, "USD"), "xl"),
change == null ? null : Badge((change >= 0 ? "+" : "") + @Format(change, "percent", 1) + " vs prior 7", change >= 0 ? "success" : "danger")
], 2, null, null, true)
], null, 3, 1)

// 시각화 차트
trend = Card([
Row([
Title("Revenue", "sm"),
Spacer(),
SegmentedControl($view, ["Weekly", "Cumulative"])
], 2),
Chart("area", $view == "Weekly" ? weekly : @Cumulative(weekly, "value"), "key", ["value"], "currency")
], null, 4, 2)

// mix 정의
mix = Card([
Title("Channel mix", "sm"),
Chart("donut", @GroupBy(orders.rows, "channel", "amount"), "key", ["value"], "currency")
], null, 4, 2)

// regions 정의
regions = Card([
Title("Revenue by region and category", "sm"),
Chart("bar", @Pivot(rows, "region", "category", "amount"), "key", null, "currency", null, true)
], null, 4, 2)

// top 정의
top = Card([
Title("Top products", "sm"),
Table([
"Product",
"Units",
"Revenue"], @Each(@Top(@GroupBy(rows, "product", "amount"), "value", 5),
"p",
[p.key, @Sum(@Filter(rows, "product", "==", p.key).units), @Format(p.value, "currency", 0)
]))
], null, 4, 2)

Case 22, Support dashboard (dash-support)​

설명: Query(get_tickets); weekly opened vs solved with @Bucket and @Between; @Stats, @Histogram, @MovingAvg; the oldest waiting tickets. 사용자 프롬프트: "Make a support dashboard: open tickets, median resolution time, tickets by week, the slowest teams and satisfaction."

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Col([
head,
kpis,
Grid([weekly, teams], 2, 300, 4),
Grid([spread, csat], 2, 300, 4),
queue
], 4)

// 상태 변수 ($days): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$days = "60"

// 상태 변수 ($team): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$team = "All"

// tickets 정의
tickets = Query("get_tickets", {days: $days}, {rows: []})

// 원본/파생 데이터 목록
rows = $team == "All" ? tickets.rows : @Filter(tickets.rows, "team", "==", $team)

// solved 정의
solved = @Filter(rows, "status", "==", "solved")

// waiting 정의
waiting = @Filter(rows, "status", "!=", "solved")

// hours 정의
hours = @Stats(solved.hours)

// weeks 정의
weeks = @Each(@Bucket(rows, "opened", "week"), "w", {week: @FormatDate(w.key, "short"), Opened: w.value, Solved: @Count(@Between(solved, "opened", w.key, @AddDays(w.key, 6)))})

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
head = Row([
Col([
Title("Support desk", "lg"),
Caption("Tickets opened in the selected period")
], 1),
Spacer(),
Box([Select($team, [{value: "All", label: "All teams"}, "Billing", "Technical", "Accounts", "Shipping"])], null, null, null, null, null, null, 150),
SegmentedControl($days, [{value: "30", label: "30d"}, {value: "60", label: "60d"}, {value: "180", label: "6m"}])
], 3, null, null, true)

// 핵심 지표(KPI) 및 통계 카드
kpis = Grid([k1, k2, k3, k4], 4, 150, 3)

// k1 정의
k1 = Card([Caption("Opened"), Title(@Format(@Count(rows)), "xl")], null, 3, 1)

// k2 정의
k2 = Card([
Caption("Waiting"),
Title(@Format(@Count(waiting)), "xl", @Count(waiting) > 40 ? "danger" : null),
Caption(@Count(@Filter(waiting, "priority", "==", "urgent")) + " urgent")
], null, 3, 1)

// k3 정의
k3 = Card([
Caption("Median time to solve"),
Title(@Format(hours.median, "number", 1) + " h", "xl"),
Caption("Slowest " + @Format(hours.max, "number", 0) + " h")
], null, 3, 1)

// k4 정의
k4 = Card([
Caption("Satisfaction"),
Title(@Format(@Avg(@Filter(solved, "csat", ">", 0).csat), "number", 2) + " / 5", "xl")
], null, 3, 1)

// weekly 정의
weekly = Card([
Title("Opened and solved by week", "sm"),
Chart("bar", weeks, "week", ["Opened", "Solved"])
], null, 4, 2)

// teams 정의
teams = Card([
Title("Average hours to solve, by team", "sm"),
Chart("bar", @Sort(@GroupBy(solved, "team", "hours", "avg"), "value", "desc"), "key", ["value"])
], null, 4, 2)

// spread 정의
spread = Card([
Title("How long tickets take (hours)", "sm"),
Chart("bar", @Histogram(solved.hours, 12), "key", ["value"])
], null, 4, 2)

// csat 정의
csat = Card([
Title("Satisfaction, 7-day average", "sm"),
Chart("line", @MovingAvg(@Bucket(@Filter(solved, "csat", ">", 0), "opened", "day", "csat", "avg"), "value", 7), "key", ["value"])
], null, 4, 2)

// queue 정의
queue = Card([
Title("Waiting longest", "sm"),
Table([
"Ticket",
"Team",
"Priority",
"Waiting"], @Each(@Top(@Each(waiting, "t", {id: t.id, team: t.team, priority: t.priority, age: @DaysBetween(t.opened, @Now())}), "age", 6),
"t",
[t.id, t.team, Badge(t.priority, t.priority == "urgent" ? "danger" : t.priority == "high" ? "warning" : "neutral"), @Plural(t.age, "day")
]))
], null, 4, 2)

갤러리 섹션 3: 핵심 컴포넌트 단위 사례 (Core Components)​

Case 23, An answer, composed (answer)​

설명: Root Col without a gap spaces blocks like an article. Title, Text, Markdown, Grid, Card.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Col([title, intro, photos, h2, md, stats, closing])

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
title = Title("A slow Saturday in Lisbon", "lg")

// intro 정의
intro = Text("Start by the river, climb into **Alfama** for lunch, and end the day at a *miradouro* with the city below you.")

// photos 정의
photos = Grid([
SearchImage("Belém Tower Lisbon"),
SearchImage("Alfama Lisbon"),
SearchImage("Miradouro da Senhora do Monte")
], 3, 100, 2)

// h2 정의
h2 = Title("Before you go", "md")

// md 정의
md = Markdown("- Trams 28 and 12 get crowded after 10 AM.\n- Wear shoes with grip: the cobbles are polished smooth.\n- Most museums close on **Mondays**.")

// 핵심 지표(KPI) 및 통계 카드
stats = Grid([s1, s2, s3], 3, 150, 3)

// s1 정의
s1 = Card([Caption("Walking"), Title("6.4 km", "xl")], null, 4, 1)

// s2 정의
s2 = Card([Caption("Day pass"), Title("€6.80", "xl")], null, 4, 1)

// s3 정의
s3 = Card([Caption("Sunset"), Title("7:42 PM", "xl")], null, 4, 1)

// closing 정의
closing = Text("Times are estimates, not live data.", "xs", "muted")

Case 24, Text (text)​

설명: Title sizes, Text with inline marks, Caption, Label, Badge colors, Link, Markdown with a table.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Card([t, sub, body, sizes, badges, link, Divider(), md])

// t 정의
t = Title("A little clarity goes a long way", "lg")

// sub 정의
sub = Text("Typography that keeps the answer easy to read.", "sm", "muted")

// body 정의
body = Text("The best interface brings the useful details forward: **emphasis where it matters**, *a quiet aside*, `inline code` and [a link](https://openui.com).")

// sizes 정의
sizes = Grid([z1, z2, z3], 3, 150, 4)

// z1 정의
z1 = Col([Caption("Big number, 2xl"), Title("$1.00T", "2xl")], 1)

// z2 정의
z2 = Col([Caption("Card title, lg"), Title("What makes up GDP?", "lg")], 1)

// z3 정의
z3 = Col([
Caption("Section, sm"),
Title("Explore a growth scenario", "sm"),
Text("Struck through", "sm", "muted", null, false, true)
], 1)

// badges 정의
badges = Row([
Badge("Illustrative"),
Badge("Available", "success"),
Badge("In progress", "info"),
Badge("Needs review", "warning"),
Badge("Sold out", "danger"),
Badge("New", "purple"),
Badge("Popular", "pink")
], 2, null, null, true)

// link 정의
link = Row([
Label("Source"),
Link("Bureau of Economic Analysis", "https://www.bea.gov"),
Link("Unsafe links render as text", "javascript:alert(1)")
], 3, null, null, true)

// md 정의
md = Markdown("### Markdown prose\nA paragraph with **bold**, *italic* and ~~strike~~.\n\n1. Numbered steps\n2. Read in order\n\n> A quote stays quiet.\n\n| Plan | Seats | Price |\n|---|---|---|\n| Basic | 1 | $9 |\n| Team | 10 | $49 |")

Case 25, Lists and timeline (lists)​

설명: List markers bullet, number and none; ListItem with a text marker; the timeline connector.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Card([
Title("Sunday cooking schedule", "lg"),
Text("Assuming you serve at **2:00 PM**.", "sm", "muted"),
schedule,
Divider(),
Grid([packing, steps], 2, 240, 6)
])

// schedule 정의
schedule = List([a, b, c, d], "none", true)

// a 정의
a = ListItem("Prepare everything", "Season the lamb, peel the potatoes and trim the beans.", "10:45 AM")

// b 정의
b = ListItem("Start roasting", "The lamb goes into a 200°C oven until it reaches 60°C inside.", "11:15 AM")

// c 정의
c = ListItem("Potatoes in", "Parboil for 10 minutes, rough up the edges and roast in hot oil.", "12:00 PM")

// d 정의
d = ListItem("Rest and carve", "Rest the lamb for 20 minutes while the gravy simmers.", "1:30 PM")

// packing 정의
packing = Col([
Title("Packing", "sm"),
List(["Light rain jacket", "Comfortable shoes", "A reusable bottle"])
], 2)

// 원본/파생 데이터 목록
steps = Col([
Title("Steps", "sm"),
List([
"Choose a direction",
"Explore the answer",
"Keep the conversation going"
], "number")
], 2)

Case 26, Buttons (buttons)​

설명: Variants, colors, sizes, icons, disabled, block; IconButton. Buttons without an action send their label.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Card([
main,
sizes,
colors,
icons,
Button("Add to my route", @ToAssistant("Add Lands End to my route"), "solid", null, "lg", "plus", false, true)
])

// main 정의
main = Row([
Button("Customize my capsule", @ToAssistant("Customize my capsule"), "solid", null, null, "arrow-right"),
Button("Copy split", null, "outline", null, null, "copy"),
Button("Reset", null, "ghost", null, null, "rotate-ccw"),
Button("Soft", null, "soft")
], 2, null, null, true)

// sizes 정의
sizes = Row([
Button("Small", null, "outline", null, "sm"),
Button("Medium", null, "outline"),
Button("Large", null, "outline", null, "lg"),
Button("Disabled", null, "solid", null, null, null, true)
], 2, null, null, true)

// colors 정의
colors = Row([
Button("Delete", null, "solid", "danger"),
Button("Confirm", null, "solid", "success"),
Button("Remove", null, "outline", "danger", null, "trash-2"),
Button("Open docs", @OpenUrl("https://openui.com"), "ghost", null, null, "external-link")
], 2, null, null, true)

// icons 정의
icons = Row([
IconButton("heart", null, "Save"),
IconButton("share", null, "Share", "ghost"),
IconButton("plus", null, "Add", "solid"),
IconButton("bookmark", null, "Bookmark")
], 2)

Case 27, Layout (layout)​

설명: Box backgrounds, radius, border, sizes and flex; Grid with minChildWidth; Card outline and soft; Spacer.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Col([stats, swatches, tints, summary], 4)

// 핵심 지표(KPI) 및 통계 카드
stats = Grid([st1, st2], 2, 220, 4)

// st1 정의
st1 = Card([
Caption("Photography services · demo estimate"),
Title("$617M", "xl"),
Caption("2025, U.S. · narrow market definition")
], null, 4, 2)

// st2 정의
st2 = Card([
Caption("Commercial drones · demo estimate"),
Title("$4.1B", "xl"),
Caption("2024, U.S. · broader market definition")
], null, 4, 2)

// swatches 정의
swatches = Row([
Box([], null, null, null, "#d9c9b0", "lg", null, 44, 44),
Box([], null, null, null, "#9fb4a4", "lg", null, 44, 44),
Box([], null, null, null, "#4f5d75", "lg", null, 44, 44),
Box([], null, null, null, "#c96f53", "full", null, 44, 44),
Spacer(),
Badge("Warm neutrals")
], 2)

// tints 정의
tints = Grid([
Box([Text("Info tint", "sm", "info")], null, null, 3, "info", "lg"),
Box([Text("Success tint", "sm", "success")], null, null, 3, "success", "lg"),
Box([Text("Warning tint", "sm", "warning")], null, null, 3, "warning", "lg"),
Box([
Text("On a hex color", "sm"),
Caption("Text stays readable")
], null, 1, 3, "#f5e6c8", "lg")
], 4, 130, 2)

// 요약 및 결과 표시 카드
summary = Card([
Row([
Text("Subtotal"),
Spacer(),
Text("$156.00", null, null, null, null, null, true)
]),
Row([
Text("Tax · 8.5%", "sm", "muted"),
Spacer(),
Text("$13.26", "sm", "muted", null, null, null, true)
]),
Divider(),
Row([Text("Total"), Spacer(), Title("$169.26", "xl")])
], "soft", null, 2)

Case 28, Sliders bound to state (gdp)​

설명: Four Slider($x) bindings; a computed total, a split bar sized by flex, and @Reset.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Card([
head,
Caption("Total output"),
total,
bar,
sliders,
reset,
Text("GDP = C + I + G + (X − M). Move any slider: the total and the bar follow.", "sm", "muted")
])

// 상태 변수 ($c): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$c = 600

// 상태 변수 ($i): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$i = 200

// 상태 변수 ($g): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$g = 170

// 상태 변수 ($x): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$x = 30

// gdp 정의
gdp = $c + $i + $g + $x

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
head = Row([
Col([
Title("What makes up GDP?", "lg"),
Text("An economy, in four parts.", "sm", "muted")
], 1),
Spacer(),
Badge("Illustrative economy")
], 3, "start")

// total 정의
total = Title("$" + @Round(gdp / 1000, 2) + " trillion", "2xl")

// bar 정의
bar = Box([seg1, seg2, seg3, seg4], "row", 0.5, null, null, "md", null, "100%", 38)

// seg1 정의
seg1 = Box([Text("$" + $c + "B", "xs", "#ffffff", "medium")], null, null, null, "chart1", 0, null, null, "100%", $c, "center", "center")

// seg2 정의
seg2 = Box([Text("$" + $i + "B", "xs", "#ffffff", "medium")], null, null, null, "chart2", 0, null, null, "100%", $i, "center", "center")

// seg3 정의
seg3 = Box([Text("$" + $g + "B", "xs", "#ffffff", "medium")], null, null, null, "chart4", 0, null, null, "100%", $g, "center", "center")

// seg4 정의
seg4 = Box([], null, null, null, "chart3", 0, null, null, "100%", $x > 0 ? $x : 0)

// 사용자 입력 및 컨트롤 폼
sliders = Grid([
Slider($c, 0, 1200, 10, "Consumer spending", "B"),
Slider($i, 0, 600, 10, "Business investment", "B"),
Slider($g, 0, 500, 10, "Government spending", "B"),
Slider($x, -200, 300, 10, "Net exports", "B")
], 2, 240, 6)

// reset 정의
reset = Button("Reset economy", @Reset($c, $i, $g, $x), "ghost", null, "sm", "rotate-ccw")

Case 29, Segmented control (segmented)​

설명: SegmentedControl($mode) switches what renders; number Input and a Slider feed the per-person total.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Card([head, body, summary])

// 상태 변수 ($mode): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$mode = "item"

// 상태 변수 ($people): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$people = 4

// 상태 변수 ($tip): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$tip = 18

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
head = Row([
Title("What are you splitting?", "md"),
Spacer(),
SegmentedControl($mode, [{value: "item", label: "By item"}, {value: "equal", label: "Equally"}])
], 3, null, null, true)

// body 정의
body = $mode == "item" ? items : equal

// 원본/파생 데이터 목록
items = Col([
Row([
Text("Burrata & sourdough"),
Spacer(),
Text("$18", null, null, "medium", null, null, true)
]),
Row([
Text("Mains"),
Spacer(),
Text("$96", null, null, "medium", null, null, true)
]),
Row([
Text("A bottle of red"),
Spacer(),
Text("$42", null, null, "medium", null, null, true)
])
], 3)

// equal 정의
equal = Grid([
Input($people, "People at the table", "number"),
Slider($tip, 0, 30, 1, "Tip", "%", [{value: 0, label: "None"}, {value: 15, label: "15%"}, {value: 30, label: "30%"}])
], 2, 220, 6)

// 요약 및 결과 표시 카드
summary = Card([
Row([
Text("Total with tip"),
Spacer(),
Title("$" + @Round(156 * (1 + $tip / 100), 2), "lg")
]),
Row([
Text("Each of " + $people, "sm", "muted"),
Spacer(),
Text("$" + @Round(156 * (1 + $tip / 100) / ($people > 0 ? $people : 1), 2), "sm", null, "medium", null, null, true)
])
], "soft", null, 2)

Case 30, Checkbox list (checklist)​

설명: Six Checkbox($pN) bindings with lineThrough; @Count(@Filter(...)) drives the progress text and bar.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Card([
Row([
Title("Your suitcase", "md"),
Spacer(),
Text(done + "/6 packed", "sm", "muted", null, null, null, true)
]),
bar,
list,
Button("Clear", @Reset($p1, $p2, $p3, $p4, $p5, $p6), "outline", null, "sm")
], "soft")

// 상태 변수 ($p1): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$p1 = true

// 상태 변수 ($p2): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$p2 = false

// 상태 변수 ($p3): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$p3 = true

// 상태 변수 ($p4): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$p4 = false

// 상태 변수 ($p5): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$p5 = false

// 상태 변수 ($p6): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$p6 = false

// done 정의
done = @Count(@Filter([$p1, $p2, $p3, $p4, $p5, $p6], "", "==", true))

// bar 정의
bar = Box([
Box([], null, null, null, "ink", "full", null, (done / 6 * 100) + "%", 4)
], null, null, null, "border", "full", null, "100%", 4)

// list 정의
list = Grid([
Checkbox($p1, "The everyday tee", true),
Checkbox($p2, "Relaxed Oxford shirt", true),
Checkbox($p3, "Unstructured jacket", true),
Checkbox($p4, "Straight-leg trousers", true),
Checkbox($p5, "Minimal leather sneakers", true),
Checkbox($p6, "Everywhere tote", true)
], 2, 220, 2)

Case 31, Pressable grid (pressable)​

설명: Pressable tiles set $bed with @Set and show the selected ring; the detail line reads $bed.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Card([
Title("Your 4 × 4 ft garden plan", "lg"),
Text("Tap a planter to see what grows there.", "sm", "muted"),
grid,
detail
])

// 상태 변수 ($bed): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$bed = "Lettuce + radishes"

// 인터랙티브 카드 그리드
grid = Grid([p1, p2, p3, p4], 2, null, 3)

// p1 정의
p1 = Pressable([
Col([
Icon("leafy-green", "lg"),
Text("Lettuce + radishes", "sm", null, "medium"),
Caption("24-inch planter")
], 1, "center")
], @Set($bed, "Lettuce + radishes"), $bed == "Lettuce + radishes", 6, "#d6e8cf", "lg")

// p2 정의
p2 = Pressable([
Col([
Icon("bean", "lg"),
Text("Snap peas", "sm", null, "medium"),
Caption("Pot + trellis")
], 1, "center")
], @Set($bed, "Snap peas"), $bed == "Snap peas", 6, "#d6e8cf", "lg")

// p3 정의
p3 = Pressable([
Col([
Icon("leaf", "lg"),
Text("Kale", "sm", null, "medium"),
Caption("12-inch pot")
], 1, "center")
], @Set($bed, "Kale"), $bed == "Kale", 6, "#ecdfc3", "lg")

// p4 정의
p4 = Pressable([
Col([
Icon("sprout", "lg"),
Text("Herbs", "sm", null, "medium"),
Caption("1 to 2 small pots")
], 1, "center")
], @Set($bed, "Herbs"), $bed == "Herbs", 6, "#ecdfc3", "lg")

// detail 정의
detail = Row([
Icon("sun", "sm", "warning"),
Text("Selected: **" + $bed + "**. Give it 6+ hours of sunlight.", "sm")
], 2)

Case 32, Inputs and form (inputs)​

설명: Input (text and number), DatePicker, Select, RadioGroup, TextArea inside a Form; the button sends the values as context.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Card([Title("Make it yours", "lg"), form])

// 상태 변수 ($name): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$name = ""

// 상태 변수 ($people): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$people = 4

// 상태 변수 ($date): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$date = "2026-10-24"

// 상태 변수 ($pace): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$pace = "balanced"

// 상태 변수 ($weather): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$weather = "mild"

// 상태 변수 ($notes): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$notes = ""

// 사용자 입력 및 컨트롤 폼
form = Form([
Grid([
Input($name, "Name", "text", "Your name"),
Input($people, "Travelers", "number")
], 2, 200, 4),
Grid([
DatePicker($date, "Start date"),
Select($pace, [{value: "relaxed", label: "Relaxed"}, {value: "balanced", label: "Balanced"}, {value: "active", label: "Active"}], "Preferred pace")
], 2, 200, 4),
RadioGroup($weather, [{value: "hot", label: "Hot / tropical"}, {value: "mild", label: "Mild / mixed weather"}, {value: "cold", label: "Cold / rainy"}], "What weather are you packing for?"),
TextArea($notes, "Anything else?", "A place you would love to visit…", 3),
submit
])

// submit 정의
submit = Button("Customize my trip", @ToAssistant("Customize my trip", {name: $name, people: $people, date: $date, pace: $pace, weather: $weather, notes: $notes}), "solid", null, null, "arrow-right")

Case 33, Svg driven by a slider (svg)​

설명: G transforms and SvgText read $angle; every shape component appears.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Card([
Title("Turn the crank", "md"),
Text("The rear sprocket turns " + ratio + " times as far as the chainring.", "sm", "muted"),
drawing,
Slider($angle, 0, 360, 5, "Crank angle", "°"),
shapes
])

// 상태 변수 ($angle): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$angle = 40

// ratio 정의
ratio = 2.5

// drawing 정의
drawing = Svg([chain, front, rear, readout], "0 0 320 160")

// chain 정의
chain = Path("M 90 40 L 240 62 A 18 18 0 0 1 240 98 L 90 120 A 40 40 0 0 1 90 40 Z", null, "faint", 2)

// front 정의
front = G([Circle(90, 80, 40, "surface", "ink", 3), Line(90, 80, 90, 44, "ink", 4), Circle(90, 80, 6, "ink")], "rotate(" + $angle + " 90 80)")

// rear 정의
rear = G([Circle(240, 80, 18, "surface", "ink", 3), Line(240, 80, 240, 64, "info", 3), Circle(240, 80, 4, "ink")], "rotate(" + $angle * ratio + " 240 80)")

// readout 정의
readout = SvgText("Rear " + @Round($angle * ratio % 360, 0) + "°", 240, 132, 12, "muted", "middle")

// shapes 정의
shapes = Svg([
Rect(10, 10, 60, 40, "info", null, null, 0.8, 8),
Ellipse(120, 30, 34, 20, "success"),
Polygon("180,50 205,10 230,50", "warning"),
Polyline("250,45 265,15 280,40 295,12", "danger", 3),
SvgText("Rect · Ellipse · Polygon · Polyline", 160, 72, 11, "muted", "middle")
], "0 0 320 80", null, null)

Case 34, Charts (chart)​

설명: SegmentedControl switches Chart($type); currency and percent formats; stacked bars; pie and donut.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Card([head, chart, Grid([pie, donut], 2, 260, 6)])

// 상태 변수 ($type): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$type = "bar"

// 헤더 영역: 제목과 설명 텍스트, 컨트롤 요소
head = Row([
Col([
Title("Drone services revenue", "md"),
Caption("Illustrative revenue path, not a forecast")
], 1),
Spacer(),
SegmentedControl($type, ["bar", "line", "area"])
], 3, null, null, true)

// 시각화 차트
chart = Chart($type, rows, "year", ["Photography", "Inspection"], "currency", null, $type != "line")

// 원본/파생 데이터 목록
rows = [
{year: "2025", Photography: 617000000, Inspection: 410000000},
{year: "2027", Photography: 820000000, Inspection: 690000000},
{year: "2030", Photography: 1240000000, Inspection: 1310000000},
{year: "2036", Photography: 1900000000, Inspection: 2350000000}
]

// pie 정의
pie = Col([
Title("How people get around", "sm"),
Chart("pie", modes, "mode", ["share"], "percent")
], 2)

// donut 정의
donut = Col([
Title("Budget", "sm"),
Chart("donut", budget, "item", ["amount"], "currency", "EUR")
], 2)

// modes 정의
modes = [
{mode: "Walking", share: 52},
{mode: "Transit", share: 31},
{mode: "Cycling", share: 17}
]

// budget 정의
budget = [
{item: "Lodging", amount: 840},
{item: "Food", amount: 420},
{item: "Transport", amount: 260},
{item: "Museums", amount: 90}
]

Case 35, Table (table)​

설명: Rows of cells in column order; number columns align right; a cell can be a component.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Card([
Title("Who makes money in this market?", "md"),
Table([
"Player",
"Business model",
"Typical margin",
"Difficulty"], [["Drone manufacturers", "Sell drones, cameras and accessories", "38%", Badge("Hard", "danger")],
["Freelance pilots", "Charge per shoot or project", "55%", Badge("Easy", "success")],
["Mapping companies", "SaaS and data processing fees", "24%", Badge("Medium", "warning")],
["Enterprises", "Operate their own fleets", "n/a", Badge("Medium", "warning")]
]),
Table([
"Month",
"Shoots",
"Revenue"], [["January", 8, "$2,000"],
["February", 12, "$3,000"],
["March", 15, "$3,750"]
])
])

Case 36, Images (images)​

설명: A working src; no src (search by alt); a failing src that falls back to search; a failing src with nothing found.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Card([Title("Golden hour in San Francisco", "md"), Grid([i1, i2, i3, i4], 4, 150, 3)])

// i1 정의
i1 = Col([
Image("https://upload.wikimedia.org/wikipedia/commons/thumb/b/bf/Golden_Gate_Bridge_as_seen_from_Battery_East.jpg/960px-Golden_Gate_Bridge_as_seen_from_Battery_East.jpg", "Golden Gate Bridge from Battery East"),
Text("Working src", "sm", null, "semibold"),
Caption("Loads the given URL")
], 2)

// i2 정의
i2 = Col([
SearchImage("Alcatraz Island"),
Text("No src", "sm", null, "semibold"),
Caption("Found by searching the query")
], 2)

// i3 정의
i3 = Col([
Image("https://example.invalid/palace.jpg", "Palace of Fine Arts"),
Text("Broken src", "sm", null, "semibold"),
Caption("Falls back to search")
], 2)

// i4 정의
i4 = Col([
Image("https://example.invalid/none.jpg", "qzxv unfindable subject 0000", 1.5, "xl"),
Text("Nothing found", "sm", null, "semibold"),
Caption("Quiet placeholder")
], 2)

Case 37, Map with a route (map)​

설명: Map(markers, route, $stop): street route via /api/street-route, category filter, emoji and numbered pins; clicking a pin sets $stop.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Col([
Title("San Francisco, your way", "md"),
Text("Tap a pin to choose where to start.", "sm", "muted"),
map,
Card([Row([Icon("map-pin", "sm"), Text("Starting at **" + $stop + "**", "sm")], 2)], "soft", 3)
])

// 상태 변수 ($stop): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$stop = "Ferry Building"

// map 정의
map = Map([m1, m2, m3, m4], true, $stop)

// m1 정의
m1 = MapMarker("Ferry Building", 37.7955, -122.3937, "Food", "☕")

// m2 정의
m2 = MapMarker("Coit Tower", 37.8024, -122.4058, "Views")

// m3 정의
m3 = MapMarker("Palace of Fine Arts", 37.8029, -122.4484, "Landmarks", "🏛️")

// m4 정의
m4 = MapMarker("Golden Gate Bridge", 37.8199, -122.4783, "Views", "🌉")

Case 38, HtmlView game (html)​

설명: A sandboxed canvas game (allow-scripts, opaque origin, strict CSP) that sizes itself to its content.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Card([
Title("Take a little break", "md"),
Text("Catch the dot as many times as you can in 15 seconds.", "sm", "muted"),
HtmlView("<style>body{display:flex;flex-direction:column;gap:10px;padding:14px}canvas{width:100%;height:220px;border-radius:12px;background:light-dark(#f7f7f8,#2a2a2a);touch-action:none;cursor:crosshair}.bar{display:flex;justify-content:space-between;font-size:13px}button{font:inherit;font-weight:500;padding:7px 14px;border-radius:999px;border:0;background:light-dark(#0d0d0d,#ececec);color:light-dark(#fff,#171717);cursor:pointer}</style>\n<div class=\"bar\"><span id=\"score\">Score 0</span><span id=\"time\">Press start</span></div>\n<canvas id=\"c\"></canvas>\n<div><button id=\"go\">Start</button></div>\n<script>\nconst c=document.getElementById('c'),g=c.getContext('2d'),dpr=devicePixelRatio||1;\nlet dot={x:.5,y:.5,vx:.004,vy:.003},score=0,end=0,raf;\nfunction fit(){c.width=c.clientWidth*dpr;c.height=c.clientHeight*dpr}\nfunction draw(){fit();g.clearRect(0,0,c.width,c.height);g.fillStyle='#0285ff';g.beginPath();g.arc(dot.x*c.width,dot.y*c.height,16*dpr,0,7);g.fill()}\nfunction tick(){const left=Math.max(0,end-Date.now());document.getElementById('time').textContent=(left/1000).toFixed(1)+' s';\ndot.x+=dot.vx;dot.y+=dot.vy;if(dot.x<.05||dot.x>.95)dot.vx*=-1;if(dot.y<.08||dot.y>.92)dot.vy*=-1;draw();\nif(left>0)raf=requestAnimationFrame(tick);else document.getElementById('time').textContent='Time! Final score '+score}\nc.addEventListener('pointerdown',e=>{if(Date.now()>end)return;const r=c.getBoundingClientRect(),dx=(e.clientX-r.left)/r.width-dot.x,dy=(e.clientY-r.top)/r.height-dot.y;\nif(Math.hypot(dx*r.width,dy*r.height)<26){score++;document.getElementById('score').textContent='Score '+score;dot.vx=(Math.random()-.5)*.02;dot.vy=(Math.random()-.5)*.02;dot.x=.1+Math.random()*.8;dot.y=.15+Math.random()*.7}});\ndocument.getElementById('go').onclick=()=>{score=0;document.getElementById('score').textContent='Score 0';end=Date.now()+15000;cancelAnimationFrame(raf);tick()};\ndraw();\n</script>", null, "Catch the dot")
])

Case 39, Timer (timer)​

설명: Timer($left) counts the bound seconds down; preset buttons @Set it, and a statement reacts when $left reaches 0.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Card([Title("Steep your tea", "md"), presets, Timer($left, "Time left"), done])

// 상태 변수 ($left): 사용자 인터랙션에 따라 값이 변경되는 반응형 상태
$left = 180

// presets 정의
presets = Row([
Button("Green · 2 min", @Set($left, 120), "outline", null, "sm"),
Button("Black · 4 min", @Set($left, 240), "outline", null, "sm"),
Button("Herbal · 6 min", @Set($left, 360), "outline", null, "sm")
], 2, null, null, true)

// done 정의
done = $left == 0 ? Text("**Ready.** Take the leaves out now.", "sm", "success") : Caption("Start the timer when the water reaches the leaves.")

Case 40, Color scale (heatmap)​

설명: @MixColor turns a value into a color between two hex colors, for heatmaps and tints.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Card([
Title("When the café is busiest", "md"),
Caption("Average customers per hour, last 4 weeks"),
Grid(@Each(days, "d", Caption(d)), 7, null, 1),
Grid(@Each(visits, "v", Box([Text(v + "", "xs", null, "medium", false, false, true)], null, null, 1, @MixColor("#EEF6FF", "#0068BC", v / 60), "sm", null, null, 32, null, "center", "center")), 7, null, 1),
Row([
Caption("Quiet"),
Row(@Each([0, 0.25, 0.5, 0.75, 1], "t", Box([], null, null, null, @MixColor("#EEF6FF", "#0068BC", t), "sm", null, 20, 10)), 1),
Caption("Busy")
], 2)
])

// days 정의
days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]

// visits 정의
visits = [12, 14, 13, 15, 18, 34, 30, 28, 30, 29, 31, 36, 52, 47, 41, 44, 40, 45, 50, 58, 49, 22, 24, 23, 27, 38, 44, 35]

Case 41, Icons (icons)​

설명: Lucide names at each size and in a few colors. An unknown name renders nothing.

// 최상위 루트 레이아웃: 전체 화면 구조와 주요 컴포넌트 배치
root = Card([
Row([
Icon("sparkles", "sm"),
Icon("map-pin"),
Icon("camera", "lg"),
Icon("bike", "xl"),
Icon("heart", "lg", "danger"),
Icon("check-circle", "lg", "success"),
Icon("info", "lg", "info"),
Icon("alert-triangle", "lg", "warning"),
Icon("no-such-icon", "lg")
], 4, null, null, true)
])