3. Replace src/main.luau
luastra/ui · semantic spatial navigation · about 25 minutesconstellations → nodes → Focus Surface
One small state machine controls active depth and focused leaf; every visual placement decision remains host-owned.
When to use it
Replace the complete entry module. Keep the component IDs stable: relationships, selection, focus restoration, tests, and future routes all depend on semantic identity.
Focused example
--!strict
local UI = require("luastra/ui")
local App = require("luastra/app")
local Root = {}
local depth = "root"
local visitedBuild = false
local selected: string? = nil
local details = {
["orbit/learn"] = "Learn the semantic model before adding host capabilities.",
["orbit/ship"] = "Verify each target separately before making a platform claim.",
["orbit/build/interface"] = "Compose meaning in Luau and let the host place it.",
["orbit/build/adaptive"] = "The same nodes become a readable list when space is constrained.",
["orbit/build/accessible"] = "Keyboard, pointer, and assistive technology reach the same actions.",
}
local function titleFor(id: string?): string
if id == "orbit/learn" then return "Learn" end
if id == "orbit/ship" then return "Ship" end
if id == "orbit/build/interface" then return "Interface" end
if id == "orbit/build/adaptive" then return "Adaptive" end
if id == "orbit/build/accessible" then return "Accessible" end
return "Node details"
end
function Root.render(): UI.Node
local focusTitle = titleFor(selected)
local children: {UI.Node} = {
UI.OrbitPath {
id = "orbit/path",
label = "Orbit path",
UI.OrbitReturn {
id = "orbit/path/root",
text = "Luastra",
onTap = "return-root",
disabled = depth == "root",
},
UI.Text {
id = "orbit/path/current",
text = depth == "root" and "Home" or "Build",
},
},
UI.Constellation {
id = "orbit/root",
label = "Luastra concepts",
depth = 0,
layerState = depth == "root" and "active" or "behind",
UI.OrbitCenter {
id = "orbit/root/center",
title = "Luastra",
description = "Build apps like games.",
},
UI.OrbitNode {
id = "orbit/build",
title = "Build",
description = "Enter the next constellation.",
nodeKind = "constellation",
priority = 1,
signalIcon = "compass",
relatedTo = { "orbit/learn" },
onTap = "open-build",
},
UI.OrbitNode {
id = "orbit/learn",
title = "Learn",
description = "Understand the semantic model.",
priority = 1,
signalIcon = "book",
status = "Guide",
selected = selected == "orbit/learn",
relatedTo = { "orbit/build", "orbit/ship" },
onTap = "open-focus",
},
UI.OrbitNode {
id = "orbit/ship",
title = "Ship",
description = "Verify web, desktop, and mobile.",
priority = 2,
signalIcon = "rocket",
selected = selected == "orbit/ship",
relatedTo = { "orbit/learn" },
onTap = "open-focus",
},
},
}
if visitedBuild then
table.insert(children, UI.Constellation {
id = "orbit/build-space",
label = "Build an interface",
depth = 1,
layerState = depth == "build" and "active" or "ahead",
UI.OrbitCenter {
id = "orbit/build-space/center",
title = "Build",
description = "Semantic, adaptive, accessible.",
},
UI.OrbitNode {
id = "orbit/build/interface",
title = "Interface",
description = "Describe content and actions.",
priority = 1,
signalIcon = "spark",
selected = selected == "orbit/build/interface",
relatedTo = { "orbit/build/adaptive", "orbit/build/accessible" },
onTap = "open-focus",
},
UI.OrbitNode {
id = "orbit/build/adaptive",
title = "Adaptive",
description = "Keep every node available in a list.",
priority = 2,
selected = selected == "orbit/build/adaptive",
relatedTo = { "orbit/build/interface" },
onTap = "open-focus",
},
UI.OrbitNode {
id = "orbit/build/accessible",
title = "Accessible",
description = "Preserve labels, focus, and actions.",
priority = 1,
signalIcon = "check",
status = "Required",
statusTone = "success",
selected = selected == "orbit/build/accessible",
relatedTo = { "orbit/build/interface" },
onTap = "open-focus",
},
})
end
table.insert(children, UI.FocusSurface {
id = "orbit/focus",
label = focusTitle .. " details",
open = selected ~= nil,
onDismiss = "close-focus",
UI.FocusHeader {
id = "orbit/focus/header",
UI.Text { id = "orbit/focus/title", text = focusTitle, variant = "heading" },
UI.Button {
id = "orbit/focus/close",
text = "Return to the constellation",
onTap = "close-focus",
},
},
UI.Stack {
id = "orbit/focus/content",
gap = "md",
UI.Text {
id = "orbit/focus/copy",
text = if selected == nil then "Choose a node." else details[selected],
},
},
})
return UI.Screen {
id = "orbit-recipe",
documentTitle = "Orbit recipe — Luastra",
width = "wide",
UI.Orbit {
id = "orbit",
label = "Luastra learning orbit",
presentation = "auto",
orbitTheme = "luastra",
orbitMotion = "system",
maxVisible = 8,
table.unpack(children),
},
}
end
function Root.handle(action: string, target: string, _value: string)
if action == "open-build" and target == "orbit/build" then
depth = "build"
visitedBuild = true
selected = nil
elseif action == "return-root" then
depth = "root"
selected = nil
elseif action == "open-focus" and details[target] ~= nil then
selected = target
elseif action == "close-focus" then
selected = nil
end
end
function Root.snapshot()
return { depth = depth, visitedBuild = visitedBuild, selected = selected }
end
return App.compose {
render = function(_outputs) return Root.render() end,
handle = Root.handle,
snapshot = Root.snapshot,
}