code wiki / _hdl_build / nx_studio_tiers.nx
nx_studio_tiers.nx source
↩ module page · 148 lines · 8052 B
1// nx_studio_tiers.nx -- CAPABILITY-ADAPTIVE WYSIWYG (operator's idea: let users select, by their capability,
2// HOW they want to build). The editor offers tiers -- GUIDED (novice/elderly: answer a few fields, the
3// structure is guaranteed), BLOCK (intermediate: arrange blocks), FREE-FORM (power: place freely) -- but ALL
4// tiers normalize to the ONE governed, portable .site data model and build through the SAME sovereign engine
5// (se_build_site). So escaping, the publish-time UX gate, and portability are INVARIANT across tiers: no
6// capability tier can escape governance. tier_normalize is the single funnel.
7// GUIDED : a compact answer set (brand|.. headline|.. cta|.. ..) -> a COMPLETE compliant .site by
8// construction (every required block is in the template; missing answers fall back to defaults),
9// so a novice CANNOT produce a broken page.
10// FREE-FORM: a .site plus layout-only `place|idx|x|y` lines -> the placement lines are stripped to the
11// governed block .site (free placement is a presentation hint, never a way around the engine).
12// BLOCK : already a .site -> passthrough.
13// license_tier: ORIGINAL
14import "nx_sitegen_studio.nx"
15import "nx_sitegen_freeform.nx"
16import "nx_syscalls.nx"
17const TIER_MAGIC_65536: i64 = 65536
18const TIER_MAGIC_65535: i64 = 65535
19
20const TIER_GUIDED: i64 = 0
21const TIER_BLOCK: i64 = 1
22const TIER_FREEFORM: i64 = 2
23
24func tn_strlen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
25func tn_puts(out: *u8, off: i64, s: *u8) -> i64 { var o: i64=off; var k: i64=0; while s[k]!=(0 as u8){out[o]=s[k];o=o+1;k=k+1} return o }
26func tn_putrange(out: *u8, off: i64, src: *u8, a: i64, b: i64) -> i64 { var o: i64=off; var j: i64=a; while j<b { out[o]=src[j]; o=o+1; j=j+1 } if o>off { if (out[o-1] as i64)==13 { o=o-1 } } return o }
27
28// does input[ls..le) start with key followed by '|'? return value-start index, or -1.
29func tn_linematch(input: *u8, ls: i64, le: i64, key: *u8, klen: i64) -> i64 {
30 if ls+klen+1 > le { return 0-1 }
31 var i: i64=0
32 while i<klen { if (input[ls+i] as i64)!=(key[i] as i64){return 0-1} i=i+1 }
33 if (input[ls+klen] as i64)!=124 { return 0-1 } // '|'
34 return ls+klen+1
35}
36
37// append the value of input's first "key|value" line, or `dflt` if the key is absent. returns new off.
38func tn_appget(out: *u8, off: i64, input: *u8, n: i64, key: *u8, dflt: *u8) -> i64 {
39 let klen: i64 = tn_strlen(key)
40 var ls: i64=0; var i: i64=0
41 var fvs: i64=0-1; var fve: i64=0; var done: i64=0
42 while i<=n {
43 if done==0 {
44 var eol: i64=0
45 if i==n { eol=1 }
46 if i<n { if (input[i] as i64)==10 { eol=1 } }
47 if eol==1 {
48 let vs: i64 = tn_linematch(input, ls, i, key, klen)
49 if vs>=0 { fvs=vs; fve=i; done=1 }
50 ls=i+1
51 }
52 }
53 i=i+1
54 }
55 if fvs>=0 { return tn_putrange(out, off, input, fvs, fve) }
56 return tn_puts(out, off, dflt)
57}
58
59// normalize a tier-specific editor input into the canonical governed .site text in out; return its length.
60func tier_normalize(tier: i64, input: *u8, n: i64, out: *u8, cap: i64) -> i64 {
61 if tier == TIER_GUIDED {
62 var o: i64 = 0
63 o = tn_puts(out, o, "title|" as *u8); o = tn_appget(out, o, input, n, "brand" as *u8, "Your Brand" as *u8)
64 o = tn_puts(out, o, "\nheader|" as *u8); o = tn_appget(out, o, input, n, "brand" as *u8, "Your Brand" as *u8)
65 o = tn_puts(out, o, "|" as *u8); o = tn_appget(out, o, input, n, "cta" as *u8, "Contact us" as *u8)
66 o = tn_puts(out, o, "|" as *u8); o = tn_appget(out, o, input, n, "cta_href" as *u8, "#contact" as *u8)
67 o = tn_puts(out, o, "\nhero|" as *u8); o = tn_appget(out, o, input, n, "headline" as *u8, "Welcome" as *u8)
68 o = tn_puts(out, o, "|" as *u8); o = tn_appget(out, o, input, n, "sub" as *u8, "How we can help you." as *u8)
69 o = tn_puts(out, o, "|" as *u8); o = tn_appget(out, o, input, n, "cta" as *u8, "Contact us" as *u8)
70 o = tn_puts(out, o, "|" as *u8); o = tn_appget(out, o, input, n, "cta_href" as *u8, "#contact" as *u8)
71 o = tn_puts(out, o, "\nsearch|" as *u8); o = tn_appget(out, o, input, n, "search" as *u8, "/search" as *u8)
72 o = tn_puts(out, o, "|Search...\ncards|What we offer\ncard|" as *u8); o = tn_appget(out, o, input, n, "card_title" as *u8, "Our service" as *u8)
73 o = tn_puts(out, o, "|" as *u8); o = tn_appget(out, o, input, n, "card_body" as *u8, "What we do for you." as *u8)
74 o = tn_puts(out, o, "\nsteps|How it works\nstep|" as *u8); o = tn_appget(out, o, input, n, "step1" as *u8, "Get in touch" as *u8)
75 o = tn_puts(out, o, "\nstep|" as *u8); o = tn_appget(out, o, input, n, "step2" as *u8, "We help you" as *u8)
76 o = tn_puts(out, o, "\ntrust|\nsig|" as *u8); o = tn_appget(out, o, input, n, "trust" as *u8, "Trusted service" as *u8)
77 o = tn_puts(out, o, "\nfooter|" as *u8); o = tn_appget(out, o, input, n, "footer" as *u8, "Your Brand" as *u8)
78 o = tn_puts(out, o, "\n" as *u8)
79 return o
80 }
81 if tier == TIER_FREEFORM {
82 // strip layout-only "place|..." lines; keep the governed block lines (placement = presentation hint)
83 var o: i64 = 0
84 var ls: i64=0; var i: i64=0
85 while i<=n {
86 var eol: i64=0
87 if i==n { eol=1 }
88 if i<n { if (input[i] as i64)==10 { eol=1 } }
89 if eol==1 {
90 if tn_linematch(input, ls, i, "place" as *u8, 5) < 0 {
91 o = tn_putrange(out, o, input, ls, i)
92 out[o]=10 as u8; o=o+1
93 }
94 ls=i+1
95 }
96 i=i+1
97 }
98 return o
99 }
100 // TIER_BLOCK (default): passthrough -- it is already a .site
101 var o: i64 = 0
102 var j: i64 = 0
103 while j<n { out[o]=input[j]; o=o+1; j=j+1 }
104 return o
105}
106
107// does s[a..b) (trailing CR trimmed) equal the null-terminated literal lit?
108func tn_range_eq(s: *u8, a: i64, b: i64, lit: *u8) -> i64 {
109 var e: i64 = b
110 if e>a { if (s[e-1] as i64)==13 { e=e-1 } }
111 var i: i64 = a
112 var k: i64 = 0
113 while i<e {
114 if lit[k]==(0 as u8) { return 0 }
115 if (s[i] as i64)!=(lit[k] as i64) { return 0 }
116 i=i+1; k=k+1
117 }
118 if lit[k]==(0 as u8) { return 1 }
119 return 0
120}
121
122// THE GOVERNED TIER-AWARE ENDPOINT: hand it the raw POST body. An optional first line `tier|<guided|block|
123// freeform>` selects the capability tier (default = block); the REST is that tier's input. It normalizes via
124// the single funnel (tier_normalize) and builds through the sovereign engine (se_build_site). So the daemon
125// route just forwards the request body here -- the tier is data, governance is invariant. Returns built length
126// (0 = refused).
127func se_build_tier(payload: *u8, n: i64, out: *u8, cap: i64) -> i64 {
128 var tier: i64 = TIER_BLOCK
129 var body_start: i64 = 0
130 var eol1: i64 = n
131 var i: i64 = 0
132 while i < n { if (payload[i] as i64)==10 { eol1=i; i=n } else { i=i+1 } }
133 let vs: i64 = tn_linematch(payload, 0, eol1, "tier" as *u8, 4)
134 if vs >= 0 {
135 if tn_range_eq(payload, vs, eol1, "guided" as *u8)==1 { tier=TIER_GUIDED }
136 if tn_range_eq(payload, vs, eol1, "freeform" as *u8)==1 { tier=TIER_FREEFORM }
137 if tn_range_eq(payload, vs, eol1, "block" as *u8)==1 { tier=TIER_BLOCK }
138 body_start = eol1 + 1
139 if body_start > n { body_start = n }
140 }
141 let bn: i64 = n - body_start
142 // FREE-FORM tier -> the canvas profile (its own page profile, free placement + escaped + portable).
143 // GUIDED/BLOCK (+ default) -> normalize to the governed block .site funnel, then the block engine.
144 if tier == TIER_FREEFORM { return se_build_freeform(payload + body_start, bn, out, cap) }
145 let site: *u8 = sys_mmap(TIER_MAGIC_65536)
146 let sn: i64 = tier_normalize(tier, payload + body_start, bn, site, TIER_MAGIC_65535)
147 return se_build_site(site, sn, out, cap)
148}