feat(ui): ludic.ui grows toward HTML, CSS and React (Phase 0.U1-U4)

- Natives: ui_native(tag, measure, draw) makes an element the program draws itself; ui_fire
  runs its on-<event> with event.value. input, select and textarea have simple defaults.
- React:
  - keyed <each>, <let>, <provide> context through components, <fragment>;
  - named slots, default props on <component>;
  - on-mount / on-unmount;
  - inline text inside text elements.
- CSS:
  - custom properties and var();
  - position relative/absolute/fixed with insets and z-index;
  - em/rem/vw/vh and @media;
  - wrapping text and ellipsis, overflow;
  - + and ~ combinators, :nth-child(an+b), :checked, :active.
- Developing: errors with file:line, ui_errors(), ui_reload() keeping state, and an
  inspector-style ui_dump.
- view fields infer the type of a literal or a named function's result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-24 16:17:25 +03:00
parent 66a2bc2214
commit 637d07400e
39 changed files with 42012 additions and 40795 deletions

View file

@ -1,16 +1,23 @@
# attr.ludic - what a property sets, by its CSS name or the short one (width or w, background or
# bg, font-size or size, ...). An attribute on an element is the same property, applied last.
const AT_ALIAS: string = " width=w height=h min-width=min-w max-width=max-w min-height=min-h max-height=max-h padding=pad background=bg background-color=bg font-size=size opacity=alpha flex-grow=grow flex-direction=dir flex-wrap=wrap justify-content=justify align-items=align align-self=self row-gap=gap column-gap=gap text-align=talign "
const AT_UNDRAWN: string = " border-radius font-weight font-family font-style box-shadow text-shadow cursor transition line-height letter-spacing text-decoration overflow z-index position "
const AT_UNDRAWN: string = " border-radius font-weight font-family font-style box-shadow text-shadow cursor transition animation line-height letter-spacing text-decoration "
# a stylesheet's property: one that means nothing is said
function el_attr(n: UiNode, key: string, v: Val) -> void {
if el_apply(n, key, v) { return }
if lk_in(AT_UNDRAWN, at_alias(key)) { ui_err(`{key}: this renderer does not draw it`) } else { ui_err(`{key}: there is no such property`) }
}
# an element's attribute: a property when it names one, otherwise only there for [selectors]
function el_apply(n: UiNode, key: string, v: Val) -> bool {
function el_apply(n: UiNode, key: string, v0: Val) -> bool {
let k = at_alias(key)
return el_size(n, k, v) or el_box(n, k, v) or el_flex(n, k, v) or el_look(n, k, v)
el_cur = n
el_key = k
if len(k) > 2 and k[0..2] == "--" {
cs_var_set(n, k, vl_text(v0))
return true
}
let v = cs_vars_in(n, v0)
return el_size(n, k, v) or el_box(n, k, v) or el_flex(n, k, v) or el_look(n, k, v) or el_place_prop(n, k, v)
}
function at_alias(key: string) -> string {
let pat = ` {key}=`
@ -52,7 +59,7 @@ function el_border(n: UiNode, s: string) -> void {
let parts = lss_split(s, 32)
for i in 0 .. len(parts) {
let p = parts[i]
if len(p) > 0 and ((p[0] >= 48 and p[0] <= 57) or p[0] == 46) { n.border = ui_px(el_num(p)) } else if p == "none" { n.border = 0.0 } else if len(p) > 0 and p != "solid" { n.border_c = ui_color(p) }
if len(p) > 0 and ((p[0] >= 48 and p[0] <= 57) or p[0] == 46) { n.border = el_unit(p) } else if p == "none" { n.border = 0.0 } else if len(p) > 0 and p != "solid" { n.border_c = ui_color(p) }
}
if n.border_c == UI_NONE { n.border_c = n.rfg }
}