makepad/examples/ddgo/app.splash
Admin fbd4972450 gamemaker: full engine round — chase camera rig, racing-game APIs, script stdlib math, real error line numbers
Engine (examples/gamemaker): chase camera rig (camera({chase}) — ease-behind
with mouse-wins/recenter authority), writable camera + look deltas, spatial
queries (raycast/overlap_sphere/ground_normal), save/load, sustained tones,
rot_y + collide:false spawnables, HUD slots/bars, terrain noise shaping +
height bands, per-shape instanced render batching with static slabs (3.2x),
error push-loop into the agent chat, unknown-verb/option diagnostics with
game.splash:line:col positions, streaming tail-statement finalization, quiet
toolbar UI, Shh voice hush, fable voice.

Platform: runtime vector methods (.length/.normalized/.dot/.cross), scalar+
vector lerp, TAU; ScriptVm error capture sink; window frame capture API; four
headless-JIT fixes (scalar casts, mat4 mul, Id-arg expansion, commuted
scalar-vec ops) with regression test stages. Widgets: single-line TextInput
baseline centering, TextFlow inline-code baseline alignment, glass button
corner_radius uniform. Voice/ggml Metal backends: debug logs behind
GGML_METAL_TRACE. splashgame.md: the runtime-loaded game API contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 14:20:03 +02:00

181 lines
4.3 KiB
Text

let image_results = []
let ResultCard = glass.Card{
width: Fill
height: Fit
flow: Right
spacing: 12
padding: 10
thumb := Image{
width: 128
height: 92
fit: ImageFit.CropToFill
}
// Named, so `info.title` / `info.source` are addressable from an instantiation.
// Named children are only fields of their *direct* parent, so an unnamed wrapper
// would make `title`/`source` unreachable.
info := View{
width: Fill
height: Fit
flow: Down
spacing: 5
title := glass.Body{
text: ""
width: Fill
}
source := glass.Caption{
text: ""
width: Fill
}
}
}
fn fetch(url, extra_headers){
let p = promise()
let headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"
}
if extra_headers != nil {
for key, value in extra_headers {
headers[key] = value
}
}
net.http_request(net.HttpRequest{url: url method: net.HttpMethod.GET headers: headers}) do net.HttpEvents{
on_response: |res| p.resolve(res)
on_error: |_err| p.resolve(nil)
}
p
}
fn set_status(text){
ui.status.set_text(text)
}
fn search_images(query){
let clean = ("" + query).trim()
if clean == "" {
set_status("Enter a search query")
return
}
image_results.clear()
ui.results.render()
set_status("Searching DuckDuckGo images...")
let encoded = clean.url_encode()
let page = fetch("https://duckduckgo.com/?q=" + encoded + "&iax=images&ia=images", nil).await()
if page == nil {
set_status("Search failed")
return
}
let vqd = ""
let token_parts = page.body.to_string().split("vqd=\"")
if token_parts.len() > 1 {
vqd = token_parts[1].split("\"")[0]
}
if vqd == "" {
set_status("DuckDuckGo did not return an image token")
return
}
let response = fetch(
"https://duckduckgo.com/i.js?l=us-en&o=json&q=" + encoded + "&vqd=" + vqd + "&f=,,,,,&p=1",
{"Referer": "https://duckduckgo.com/"}
).await()
if response == nil {
set_status("Image request failed")
return
}
let data = response.body.to_string().parse_json()
if data != nil && data.results != nil {
for img in data.results {
if image_results.len() < 48 {
image_results.push({
title: img.title
source: img.source
thumbnail: img.thumbnail
})
}
}
}
if image_results.len() == 0 {
set_status("No images found")
} else {
set_status("" + image_results.len() + " images for " + clean)
}
ui.results.render()
}
View{
width: Fill
height: Fit
flow: Down
spacing: 14
padding: 4
View{
width: Fill
height: Fit
flow: Down
spacing: 4
glass.H1{text: "Picture Search"}
glass.Caption{text: "DUCKDUCKGO IMAGE RESULTS"}
}
glass.Card{
width: Fill
height: Fit
flow: Right
spacing: 10
padding: 12
query := glass.TextInput{
width: Fill
height: 38
empty_text: "Search for images..."
on_return: |text| search_images(text)
}
glass.GlassButtonProminent{
text: "Search"
height: 38
on_click: || search_images(ui.query.text())
}
}
status := glass.Caption{
text: "Type a query and press Search"
width: Fill
}
results := ScrollYView{
width: Fill
height: 610
flow: Down
spacing: 10
on_render: || {
if image_results.len() == 0 {
glass.Card{
width: Fill
height: 140
align: Align{x: 0.5 y: 0.5}
glass.Body{text: "Results will appear here."}
}
}
else for item in image_results {
ResultCard{
thumb.src: http_resource(item.thumbnail)
info.title.text: item.title
info.source.text: item.source
}
}
}
}
}