ludic/packages/ludic.ui/inspect.ludic
Orkuncakilkaya 49b5e80af5 ludic ui-preview: ludic.ui components previewed for a studio over stdio (R5)
A prebuilt tool, bin/ludic-ui-preview (built by ludic-dev build, shipped beside ludic-lsp, run as
`ludic ui-preview [--font DIR]`): ludic.ui with a backend that records every draw call as a line,
and mock component classes the studio describes (load / model / calls). frame t runs ui_show at
interface time t; text is measured on the CPU with the game's font.json metrics; locale goes
through ludic.i18n; tree / box / rules inspect the frame. No game code, no GPU, no network. The wire
protocol is frozen as tools/ui-preview/protocol-v1.md; smoke.txt is a transcript to run.

ludic.ui gains, all additive: UiClass.make_of, UiBackend.said / emitted, UiRule.text / at (with
comment line breaks kept so rule lines count true, and a class's styles read under its .lss path),
and ui_root, ui_find, ui_rules_of, ui_rule_value, ui_building, ui_class, ui_class_load,
ui_file_forget, ui_errors_clear; ui_nine_cuts_into exported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:53:26 +03:00

71 lines
3.1 KiB
Text

# inspect.ludic - what an inspector asks of the last frame shown (ludic ui-preview, a developer's
# tools): a screen's root, an element by id, the rules that style it, and whether a build is running
# the root of the screen `screen` as it was last shown, or null
export function ui_root(ui_st: UiState, screen: string) -> UiNode {
for i in 0 .. len(ui_st.fb_roots) {
if ui_st.fb_roots[i].key == screen { return ui_st.fb_roots[i] }
}
return null
}
# the element with that id (or key) on any screen shown, or null
export function ui_find(ui_st: UiState, id: string) -> UiNode {
for i in 0 .. len(ui_st.fb_roots) {
let n = fb_find(ui_st.fb_roots[i], id)
if n != null { return n }
}
return null
}
# true while a frame's tree is being built (a model read), false while an action runs (a press)
export function ui_building(ui_st: UiState) -> bool { return ui_st.uv_on }
# the rules that match the element now, in the order the cascade applies them: the default sheet's,
# then its file's (merged with each user's sheet for a component's root); its style="..." is not one
@alloc_ok("an inspector's question, never a frame")
export function ui_rules_of(ui_st: mut UiState, n: UiNode, out: []UiRule) -> void {
List.clear(out)
ir_sheet(ui_st, n, ua_sheet(ui_st), out)
if n.ups == null {
if n.sheet != null { ir_sheet(ui_st, n, n.sheet, out) }
return
}
let at = len(out)
if n.sheet != null { ir_sheet(ui_st, n, n.sheet, out) }
for i in 0 .. len(n.ups) {
if n.ups[i].sheet != null and n.ups[i].sheet != n.sheet { ir_sheet(ui_st, n, n.ups[i].sheet, out) }
}
for i in at + 1 .. len(out) { # one order by specificity, stable, as cs_merged applies them
let r = out[i]
var j = i
while j > at and out[j - 1].spec > r.spec {
out[j] = out[j - 1]
j -= 1
}
out[j] = r
}
}
@alloc_ok("an inspector's question, never a frame")
function ir_sheet(ui_st: mut UiState, n: UiNode, sh: UiSheet, out: []UiRule) -> void {
for i in 0 .. len(sh.rules) {
let r = sh.rules[i]
if md_ok(ui_st, r) and sel_match(ui_st, r.sel, n) { push(out, r) }
}
}
# a declaration's value as written, when it is plain text (else "" - an expression, read per element)
export function ui_rule_value(r: UiRule, k: int) -> string {
let x = r.vals[k]
if x.op == X_LIT { return vl_text(x.v) }
return ""
}
# a class's template and styles read now (else at its first show), so a host hears their errors at once
export function ui_class_load(ui_st: mut UiState, k: UiClass) -> void { uc_load(ui_st, k) }
# a file read again the next time anything imports it (a host changed its text): every class too
@alloc_ok("an inspector's edit, never a frame")
export function ui_file_forget(ui_st: mut UiState, path: string) -> void {
let keep = new []UiFile
for i in 0 .. len(ui_st.lib_files) {
if ui_st.lib_files[i].path != path { push(keep, ui_st.lib_files[i]) }
}
ui_st.lib_files = keep
uc_invalidate(ui_st)
}
# every error forgotten, so each is said again the next time it is made (a host that reloads a class)
export function ui_errors_clear(ui_st: mut UiState) -> void { List.clear(ui_st.ui_errs) }