@tooey/ui 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,190 @@
1
+ /**
2
+ * tooey - token-efficient ui library for llms
3
+ *
4
+ * component types:
5
+ * layout: V (vstack), H (hstack), D (div), G (grid)
6
+ * text & buttons: T (text/span), B (button)
7
+ * inputs: I (input), Ta (textarea), S (select), C (checkbox), R (radio)
8
+ * tables: Tb (table), Th (thead), Tbd (tbody), Tr (tr), Td (td), Tc (th)
9
+ * lists: Ul (ul), Ol (ol), Li (li)
10
+ * media & links: M (image), L (link), Sv (svg)
11
+ *
12
+ * props (short keys):
13
+ * spacing: g (gap), p (padding), m (margin), w (width), h (height), mw, mh
14
+ * colors: bg (background), fg (color), o (opacity)
15
+ * borders: r (border-radius), bw (border-width), bc (border-color), bs (border-style)
16
+ * positioning: pos (position), z (z-index), t (top), l (left), rt (right)
17
+ * typography: fs (font-size), fw (font-weight), ff (font-family), ta, td, lh, ls
18
+ * layout: ai (align-items), jc (justify-content), flw (flex-wrap), cols, rows
19
+ * shortcuts: c=center, sb=space-between, fe=flex-end, fs=flex-start, st=stretch
20
+ * misc: cur (cursor), ov (overflow), pe (pointer-events), us (user-select), sh, tr
21
+ * element-specific: v (value), ph (placeholder), type, href, src, alt, dis, ch, ro, opts
22
+ *
23
+ * events: c (click), x (input/change), f (focus), bl (blur), k (keydown), ku, kp, e, lv, sub
24
+ * shorthand: "state+" (increment), "state-" (decrement), "state~" (toggle), "state!val" (set)
25
+ *
26
+ * state operations: + (increment), - (decrement), ! (set), ~ (toggle), < (append), > (prepend), X (remove), . (set prop)
27
+ *
28
+ * control flow (short form): {?: cond, t: [...], e: [...]} | {m: state, a: [...]}
29
+ * control flow (long form): {if: state, then: [...], else: [...]} | {map: state, as: [...]}
30
+ * equality check: {?: "state", is: 0, t: [...]} or {?: {$:"state"}, is: 0, t: [...]}
31
+ */
32
+ type StateValue = unknown;
33
+ type StateStore = Record<string, Signal<StateValue>>;
34
+ interface Signal<T> {
35
+ (): T;
36
+ set(v: T | ((prev: T) => T)): void;
37
+ sub(fn: () => void): () => void;
38
+ }
39
+ type Op = '+' | '-' | '!' | '~' | '<' | '>' | 'X' | '.';
40
+ type EventHandler = [string, Op, unknown?] | (() => void) | string;
41
+ interface Props {
42
+ g?: number | string;
43
+ p?: number | string;
44
+ m?: number | string;
45
+ w?: number | string;
46
+ h?: number | string;
47
+ mw?: number | string;
48
+ mh?: number | string;
49
+ bg?: string;
50
+ fg?: string;
51
+ o?: number;
52
+ r?: number | string;
53
+ bw?: number | string;
54
+ bc?: string;
55
+ bs?: string;
56
+ pos?: 'rel' | 'abs' | 'fix' | 'sticky' | string;
57
+ z?: number;
58
+ t?: number | string;
59
+ l?: number | string;
60
+ b?: number | string;
61
+ rt?: number | string;
62
+ fs?: number | string;
63
+ fw?: number | string;
64
+ ff?: string;
65
+ ta?: string;
66
+ td?: string;
67
+ lh?: number | string;
68
+ ls?: number | string;
69
+ ai?: string;
70
+ jc?: string;
71
+ flw?: string;
72
+ cols?: number | string;
73
+ rows?: number | string;
74
+ cur?: string;
75
+ ov?: string;
76
+ pe?: string;
77
+ us?: string;
78
+ sh?: string;
79
+ tr?: string;
80
+ v?: unknown;
81
+ ph?: string;
82
+ type?: string;
83
+ href?: string;
84
+ src?: string;
85
+ alt?: string;
86
+ dis?: boolean;
87
+ ch?: unknown;
88
+ ro?: boolean;
89
+ opts?: Array<{
90
+ v: string;
91
+ l: string;
92
+ }>;
93
+ cls?: string;
94
+ id?: string;
95
+ rw?: number;
96
+ sp?: number;
97
+ rsp?: number;
98
+ c?: EventHandler;
99
+ x?: EventHandler;
100
+ f?: EventHandler;
101
+ bl?: EventHandler;
102
+ k?: EventHandler;
103
+ ku?: EventHandler;
104
+ kp?: EventHandler;
105
+ e?: EventHandler;
106
+ lv?: EventHandler;
107
+ sub?: EventHandler;
108
+ s?: Record<string, unknown>;
109
+ }
110
+ type ComponentType = 'V' | 'H' | 'D' | 'G' | 'T' | 'B' | 'I' | 'Ta' | 'S' | 'C' | 'R' | 'Tb' | 'Th' | 'Tbd' | 'Tr' | 'Td' | 'Tc' | 'Ul' | 'Ol' | 'Li' | 'M' | 'L' | 'Sv';
111
+ type StateRef = {
112
+ $: string;
113
+ };
114
+ interface IfNode {
115
+ if?: StateRef | string;
116
+ then?: NodeSpec | NodeSpec[];
117
+ else?: NodeSpec | NodeSpec[];
118
+ '?'?: StateRef | string;
119
+ t?: NodeSpec | NodeSpec[];
120
+ e?: NodeSpec | NodeSpec[];
121
+ eq?: unknown;
122
+ is?: unknown;
123
+ }
124
+ interface MapNode {
125
+ map?: StateRef | string;
126
+ as?: NodeSpec;
127
+ m?: StateRef | string;
128
+ a?: NodeSpec;
129
+ key?: string;
130
+ }
131
+ type Content = string | number | StateRef | NodeSpec[] | IfNode | MapNode;
132
+ type NodeSpec = [ComponentType, Content?, Props?] | IfNode | MapNode;
133
+ interface TooeySpec {
134
+ s?: Record<string, StateValue>;
135
+ r: NodeSpec;
136
+ }
137
+ interface ErrorInfo {
138
+ message: string;
139
+ componentType?: string;
140
+ stack?: string;
141
+ }
142
+ type ErrorHandler = (error: ErrorInfo) => void;
143
+ interface RenderContext {
144
+ cleanups: Array<() => void>;
145
+ state: StateStore;
146
+ onError?: ErrorHandler;
147
+ }
148
+ interface ErrorBoundaryNode {
149
+ boundary: true;
150
+ child: NodeSpec;
151
+ fallback?: NodeSpec;
152
+ onError?: ErrorHandler;
153
+ }
154
+ declare function signal<T>(initial: T): Signal<T>;
155
+ declare function batch(fn: () => void): void;
156
+ declare function effect(fn: () => void, ctx?: RenderContext): () => void;
157
+ interface TooeyInstance {
158
+ state: StateStore;
159
+ el: HTMLElement | null;
160
+ destroy(): void;
161
+ update(newSpec: TooeySpec): void;
162
+ get(key: string): unknown;
163
+ set(key: string, value: unknown): void;
164
+ }
165
+ declare function render(container: HTMLElement, spec: TooeySpec): TooeyInstance;
166
+ declare function $(name: string): StateRef;
167
+ declare const V: "V";
168
+ declare const H: "H";
169
+ declare const D: "D";
170
+ declare const G: "G";
171
+ declare const T: "T";
172
+ declare const B: "B";
173
+ declare const I: "I";
174
+ declare const Ta: "Ta";
175
+ declare const S: "S";
176
+ declare const C: "C";
177
+ declare const R: "R";
178
+ declare const Tb: "Tb";
179
+ declare const Th: "Th";
180
+ declare const Tbd: "Tbd";
181
+ declare const Tr: "Tr";
182
+ declare const Td: "Td";
183
+ declare const Tc: "Tc";
184
+ declare const Ul: "Ul";
185
+ declare const Ol: "Ol";
186
+ declare const Li: "Li";
187
+ declare const M: "M";
188
+ declare const L: "L";
189
+ declare const Sv: "Sv";
190
+ export { render, signal, effect, batch, $, V, H, D, G, T, B, I, Ta, S, C, R, Tb, Th, Tbd, Tr, Td, Tc, Ul, Ol, Li, M, L, Sv, TooeySpec, NodeSpec, Props, StateRef, TooeyInstance, IfNode, MapNode, ErrorBoundaryNode, ErrorInfo, ErrorHandler };
@@ -0,0 +1,2 @@
1
+ function D(n){return typeof n=="object"&&n!==null&&"boundary"in n&&n.boundary===!0}function O(n){let e=document.createElement("div");return e.style.cssText="padding:12px;background:#fee;border:1px solid #fcc;border-radius:4px;color:#c00;font-family:monospace;font-size:12px",e.textContent=`[tooey error] ${n.message}`,e}var v=null,H=0,C=new Set;function $(n){let e=n,t=new Set,s=()=>(v&&t.add(v),e);return s.set=o=>{let l=typeof o=="function"?o(e):o;l!==e&&(e=l,H>0?t.forEach(r=>C.add(r)):t.forEach(r=>r()))},s.sub=o=>(t.add(o),()=>t.delete(o)),s}function W(n){H++;try{n()}finally{if(H--,H===0){let e=C;C=new Set,e.forEach(t=>t())}}}function w(n,e){let t=!0,s=()=>{if(t){v=s;try{n()}finally{v=null}}};s();let o=()=>{t=!1,v=null};return e&&e.cleanups.push(o),o}function j(n,e,t){try{switch(e){case"+":n.set(s=>s+(typeof t=="number"?t:1));break;case"-":n.set(s=>s-(typeof t=="number"?t:1));break;case"!":n.set(t);break;case"~":n.set(s=>!s);break;case"<":n.set(s=>[...s,t]);break;case">":n.set(s=>[t,...s]);break;case"X":n.set(s=>{let o=s;return typeof t=="number"?o.filter((l,r)=>r!==t):typeof t=="function"?o.filter((l,r)=>!t(l,r)):o.filter(l=>l!==t)});break;case".":Array.isArray(t)&&t.length===2&&n.set(s=>({...s,[t[0]]:t[1]}));break}}catch(s){console.warn("[tooey] state operation error:",e,s)}}function L(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)&&"$"in n}function N(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)&&("if"in n||"?"in n)}function R(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)&&("map"in n||"m"in n)}function p(n,e){if(L(n)){let t=e[n.$];if(!t){console.warn(`[tooey] unknown state key: "${n.$}"`);return}return t()}return n}function u(n){if(n!==void 0)return typeof n=="number"?`${n}px`:n}var z={c:"center",sb:"space-between",sa:"space-around",se:"space-evenly",fe:"flex-end",fs:"flex-start",st:"stretch",bl:"baseline"};function A(n){if(n!==void 0)return z[n]||n}function U(n){if(typeof n!="string"||n.length<2)return null;let e=n[n.length-1];if(e==="+"||e==="-"||e==="~")return[n.slice(0,-1),e];let t=n.indexOf("!");if(t>0){let s=n.slice(0,t),o=n.slice(t+1),l=o;return o==="true"?l=!0:o==="false"?l=!1:!isNaN(Number(o))&&o!==""&&(l=Number(o)),[s,"!",l]}return null}var P=["http:","https:","mailto:","tel:","ftp:"];function q(n){if(!n||typeof n!="string")return!1;if(n.startsWith("/")||n.startsWith("#")||n.startsWith("."))return!0;try{let e=new URL(n,window.location.href);return P.includes(e.protocol)}catch{let e=n.toLowerCase().trim();return!["javascript:","data:","vbscript:"].some(s=>e.startsWith(s))}}function B(n,e){return n?q(n)?n:(console.warn(`[tooey] blocked unsafe URL in ${e}: "${n.slice(0,50)}..."`),null):null}function b(n,e,t,s,o){if(typeof n=="function")return n;let l;if(typeof n=="string"){let h=U(n);h?l=h:o==="+"?l=[n,"+"]:o==="-"?l=[n,"-"]:l=[n,"~"]}else l=n;let[r,a,d]=l;return()=>{let h=e[r];if(!h){console.warn(`[tooey] click handler: unknown state key "${r}"`);return}let i=d;if(s&&typeof i=="string"){if(i==="$index")i=s.index;else if(i==="$item")i=s.item;else if(i.startsWith("$item.")){let c=i.substring(6);i=s.item?.[c]}}if(a==="!"&&d===void 0&&t){let c=t.target;i=c.type==="checkbox"?c.checked:c.value}j(h,a,i)}}function _(n,e){let t=n.style;if(e.g!==void 0&&(t.gap=u(e.g)),e.p!==void 0&&(t.padding=u(e.p)),e.m!==void 0&&(t.margin=u(e.m)),e.w!==void 0&&(t.width=u(e.w)),e.h!==void 0&&(t.height=u(e.h)),e.mw!==void 0&&(t.maxWidth=u(e.mw)),e.mh!==void 0&&(t.maxHeight=u(e.mh)),e.bg!==void 0&&(t.background=e.bg),e.fg!==void 0&&(t.color=e.fg),e.o!==void 0&&(t.opacity=String(e.o)),e.r!==void 0&&(t.borderRadius=u(e.r)),e.bw!==void 0&&(t.borderWidth=u(e.bw)),e.bc!==void 0&&(t.borderColor=e.bc),e.bs!==void 0&&(t.borderStyle=e.bs),e.pos!==void 0){let s={rel:"relative",abs:"absolute",fix:"fixed",sticky:"sticky"};t.position=s[e.pos]||e.pos}e.z!==void 0&&(t.zIndex=String(e.z)),e.t!==void 0&&(t.top=u(e.t)),e.l!==void 0&&(t.left=u(e.l)),(typeof e.b=="number"||typeof e.b=="string"&&!Array.isArray(e.b))&&(t.bottom=u(e.b)),e.rt!==void 0&&(t.right=u(e.rt)),e.fs!==void 0&&(t.fontSize=u(e.fs)),e.fw!==void 0&&(t.fontWeight=String(e.fw)),e.ff!==void 0&&(t.fontFamily=e.ff),e.ta!==void 0&&(t.textAlign=e.ta),e.td!==void 0&&(t.textDecoration=e.td),e.lh!==void 0&&(t.lineHeight=typeof e.lh=="number"?String(e.lh):e.lh),e.ls!==void 0&&(t.letterSpacing=u(e.ls)),e.ai!==void 0&&(t.alignItems=A(e.ai)),e.jc!==void 0&&(t.justifyContent=A(e.jc)),e.flw!==void 0&&(t.flexWrap=A(e.flw)),e.cur!==void 0&&(t.cursor=e.cur),e.ov!==void 0&&(t.overflow=e.ov),e.pe!==void 0&&(t.pointerEvents=e.pe),e.us!==void 0&&(t.userSelect=e.us),e.sh!==void 0&&(t.boxShadow=e.sh),e.tr!==void 0&&(t.transform=e.tr),e.s&&Object.entries(e.s).forEach(([s,o])=>{t[s]=String(o)})}function y(n,e,t){let{state:s}=e;if(D(n)){let i=document.createElement("div");i.style.display="contents";try{let c={cleanups:[],state:s,onError:n.onError||e.onError},f=y(n.child,c,t);f&&(i.appendChild(f),e.cleanups.push(...c.cleanups))}catch(c){let f={message:c instanceof Error?c.message:String(c),stack:c instanceof Error?c.stack:void 0};if(n.onError&&n.onError(f),n.fallback)try{let m=y(n.fallback,e,t);m&&i.appendChild(m)}catch{i.appendChild(O(f))}else i.appendChild(O(f))}return i}if(N(n)){let i=document.createElement("div");i.style.display="contents";let c=null,f=null,m=n.if??n["?"],S=n.then??n.t,g=n.else??n.e,E=n.eq??n.is;return w(()=>{f&&(f.cleanups.forEach(I=>I()),f.cleanups=[]),c&&(i.innerHTML="",c=null);let k=typeof m=="string"?s[m]?.():p(m,s),M;E!==void 0?M=k===E:M=!!k;let T=M?S:g;T&&(f={cleanups:[],state:s},Array.isArray(T)&&T.length>0&&Array.isArray(T[0])?T.forEach(I=>{let V=y(I,f,t);V&&i.appendChild(V)}):(c=y(T,f,t),c&&i.appendChild(c)))},e),i}if(R(n)){let i=document.createElement("div");i.style.display="contents";let c=null,f=n.map??n.m,m=n.as??n.a;return w(()=>{c&&(c.cleanups.forEach(E=>E()),c.cleanups=[]),i.innerHTML="";let g=typeof f=="string"?s[f]?.():p(f,s);!Array.isArray(g)||!m||(c={cleanups:[],state:s},g.forEach((E,x)=>{let k=y(m,c,{item:E,index:x});k&&i.appendChild(k)}))},e),i}if(!Array.isArray(n)||n.length===0)return console.warn("[tooey] invalid node spec:",n),null;let[o,l,r={}]=n,a;switch(o){case"V":a=document.createElement("div"),a.style.display="flex",a.style.flexDirection="column";break;case"H":a=document.createElement("div"),a.style.display="flex",a.style.flexDirection="row";break;case"G":a=document.createElement("div"),a.style.display="grid",r.cols&&(a.style.gridTemplateColumns=typeof r.cols=="number"?`repeat(${r.cols}, 1fr)`:r.cols),r.rows&&(a.style.gridTemplateRows=typeof r.rows=="number"?`repeat(${r.rows}, 1fr)`:r.rows);break;case"D":a=document.createElement("div");break;case"T":a=document.createElement("span");break;case"B":a=document.createElement("button");break;case"I":a=document.createElement("input"),a.type=r.type||"text",r.ph&&(a.placeholder=r.ph),r.ro&&(a.readOnly=!0);break;case"Ta":a=document.createElement("textarea"),r.ph&&(a.placeholder=r.ph),r.rw&&(a.rows=r.rw),r.ro&&(a.readOnly=!0);break;case"S":a=document.createElement("select"),r.opts&&r.opts.forEach(i=>{let c=document.createElement("option");c.value=i.v,c.textContent=i.l,a.appendChild(c)});break;case"C":a=document.createElement("input"),a.type="checkbox";break;case"R":a=document.createElement("input"),a.type="radio";break;case"Tb":a=document.createElement("table");break;case"Th":a=document.createElement("thead");break;case"Tbd":a=document.createElement("tbody");break;case"Tr":a=document.createElement("tr");break;case"Td":a=document.createElement("td"),r.sp&&(a.colSpan=r.sp),r.rsp&&(a.rowSpan=r.rsp);break;case"Tc":a=document.createElement("th"),r.sp&&(a.colSpan=r.sp),r.rsp&&(a.rowSpan=r.rsp);break;case"Ul":a=document.createElement("ul");break;case"Ol":a=document.createElement("ol");break;case"Li":a=document.createElement("li");break;case"M":if(a=document.createElement("img"),r.src){let i=B(r.src,"src");i&&(a.src=i)}r.alt&&(a.alt=r.alt);break;case"L":if(a=document.createElement("a"),r.href){let i=B(r.href,"href");i&&(a.href=i)}break;case"Sv":a=document.createElementNS("http://www.w3.org/2000/svg","svg");break;default:console.warn(`[tooey] unknown component type: ${o}`),a=document.createElement("div")}if(r.cls&&(a.className=r.cls),r.id&&(a.id=r.id),r.dis&&(a.disabled=!0),_(a,r),l!==void 0){if(Array.isArray(l)&&l.length>0&&(Array.isArray(l[0])||N(l[0])||R(l[0])))l.forEach(i=>{let c=y(i,e,t);c&&a.appendChild(c)});else if(N(l)||R(l)){let i=y(l,e,t);i&&a.appendChild(i)}else if(L(l))w(()=>{let i=p(l,s);o==="I"||o==="Ta"?a.value=String(i??""):o==="C"||o==="R"?a.checked=!!i:a.textContent=String(i??"")},e);else if(typeof l=="string"||typeof l=="number"){let i=String(l);t&&(typeof t.item=="object"&&t.item!==null&&(i=i.replace(/\$item\.(\w+)/g,(c,f)=>String(t.item[f]??""))),i=i.replace(/\$item/g,String(t.item)),i=i.replace(/\$index/g,String(t.index))),o==="I"||o==="Ta"?a.value=i:o!=="S"&&(a.textContent=i)}}r.v!==void 0&&L(r.v)&&w(()=>{let i=p(r.v,s);o==="I"||o==="S"||o==="Ta"?a.value=String(i??""):(o==="C"||o==="R")&&(a.checked=!!i)},e),r.ch!==void 0&&(L(r.ch)?w(()=>{let i=p(r.ch,s);a.checked=!!i},e):a.checked=!!r.ch);let d=(i,c)=>{a.addEventListener(i,c),e.cleanups.push(()=>a.removeEventListener(i,c))},h=o==="B"&&(typeof l=="string"||typeof l=="number")?String(l):void 0;if(r.c&&d("click",i=>b(r.c,s,i,t,h)()),r.x){let i=r.x;d("input",c=>{if(typeof i=="function")i();else{let f,m;if(typeof i=="string"){let g=U(i);g?[f,m]=g:(f=i,m="!")}else[f,m]=i;let S=s[f];if(S){let g=c.target,E=o==="C"||o==="R"?g.checked:g.value;j(S,m,E)}}})}return r.f&&d("focus",i=>b(r.f,s,i,t)()),r.bl&&d("blur",i=>b(r.bl,s,i,t)()),r.k&&d("keydown",i=>b(r.k,s,i,t)()),r.ku&&d("keyup",i=>b(r.ku,s,i,t)()),r.kp&&d("keypress",i=>b(r.kp,s,i,t)()),r.e&&d("mouseenter",i=>b(r.e,s,i,t)()),r.lv&&d("mouseleave",i=>b(r.lv,s,i,t)()),r.sub&&d("submit",i=>{i.preventDefault(),b(r.sub,s,i,t)()}),a}function G(n,e){if(!n)throw new Error("[tooey] render requires a valid container element");if(!e||!e.r)throw new Error("[tooey] render requires a spec with a root node (r)");let t={};e.s&&Object.entries(e.s).forEach(([r,a])=>{t[r]=$(a)});let s={cleanups:[],state:t};n.innerHTML="";let o=y(e.r,s);o&&n.appendChild(o);let l={state:t,el:o,destroy(){s.cleanups.forEach(r=>r()),s.cleanups=[],n.innerHTML=""},update(r){if(r.s&&W(()=>{Object.entries(r.s).forEach(([a,d])=>{t[a]?t[a].set(d):t[a]=$(d)})}),r.r){s.cleanups.forEach(d=>d()),s.cleanups=[],n.innerHTML="";let a=y(r.r,s);a&&n.appendChild(a),l.el=a}},get(r){return t[r]?.()},set(r,a){t[r]&&t[r].set(a)}};return l}function F(n){return{$:n}}var K="V",X="H",J="D",Q="G",Y="T",Z="B",ee="I",ne="Ta",te="S",re="C",ie="R",ae="Tb",se="Th",oe="Tbd",le="Tr",ce="Td",fe="Tc",de="Ul",ue="Ol",me="Li",ge="M",be="L",ye="Sv";export{F as $,Z as B,re as C,J as D,Q as G,X as H,ee as I,be as L,me as Li,ge as M,ue as Ol,ie as R,te as S,ye as Sv,Y as T,ne as Ta,ae as Tb,oe as Tbd,fe as Tc,ce as Td,se as Th,le as Tr,de as Ul,K as V,W as batch,w as effect,G as render,$ as signal};
2
+ //# sourceMappingURL=tooey.esm.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/tooey.ts"],
4
+ "sourcesContent": ["/**\n * tooey - token-efficient ui library for llms\n *\n * component types:\n * layout: V (vstack), H (hstack), D (div), G (grid)\n * text & buttons: T (text/span), B (button)\n * inputs: I (input), Ta (textarea), S (select), C (checkbox), R (radio)\n * tables: Tb (table), Th (thead), Tbd (tbody), Tr (tr), Td (td), Tc (th)\n * lists: Ul (ul), Ol (ol), Li (li)\n * media & links: M (image), L (link), Sv (svg)\n *\n * props (short keys):\n * spacing: g (gap), p (padding), m (margin), w (width), h (height), mw, mh\n * colors: bg (background), fg (color), o (opacity)\n * borders: r (border-radius), bw (border-width), bc (border-color), bs (border-style)\n * positioning: pos (position), z (z-index), t (top), l (left), rt (right)\n * typography: fs (font-size), fw (font-weight), ff (font-family), ta, td, lh, ls\n * layout: ai (align-items), jc (justify-content), flw (flex-wrap), cols, rows\n * shortcuts: c=center, sb=space-between, fe=flex-end, fs=flex-start, st=stretch\n * misc: cur (cursor), ov (overflow), pe (pointer-events), us (user-select), sh, tr\n * element-specific: v (value), ph (placeholder), type, href, src, alt, dis, ch, ro, opts\n *\n * events: c (click), x (input/change), f (focus), bl (blur), k (keydown), ku, kp, e, lv, sub\n * shorthand: \"state+\" (increment), \"state-\" (decrement), \"state~\" (toggle), \"state!val\" (set)\n *\n * state operations: + (increment), - (decrement), ! (set), ~ (toggle), < (append), > (prepend), X (remove), . (set prop)\n *\n * control flow (short form): {?: cond, t: [...], e: [...]} | {m: state, a: [...]}\n * control flow (long form): {if: state, then: [...], else: [...]} | {map: state, as: [...]}\n * equality check: {?: \"state\", is: 0, t: [...]} or {?: {$:\"state\"}, is: 0, t: [...]}\n */\n\n// ============ types ============\n\ntype StateValue = unknown;\ntype StateStore = Record<string, Signal<StateValue>>;\n\ninterface Signal<T> {\n (): T;\n set(v: T | ((prev: T) => T)): void;\n sub(fn: () => void): () => void;\n}\n\ntype Op = '+' | '-' | '!' | '~' | '<' | '>' | 'X' | '.';\ntype EventHandler = [string, Op, unknown?] | (() => void) | string;\n\ninterface Props {\n // spacing/sizing\n g?: number | string;\n p?: number | string;\n m?: number | string;\n w?: number | string;\n h?: number | string;\n mw?: number | string;\n mh?: number | string;\n // colors\n bg?: string;\n fg?: string;\n o?: number;\n // borders\n r?: number | string;\n bw?: number | string;\n bc?: string;\n bs?: string;\n // positioning\n pos?: 'rel' | 'abs' | 'fix' | 'sticky' | string;\n z?: number;\n t?: number | string;\n l?: number | string;\n b?: number | string;\n rt?: number | string;\n // typography\n fs?: number | string;\n fw?: number | string;\n ff?: string;\n ta?: string;\n td?: string;\n lh?: number | string;\n ls?: number | string;\n // layout\n ai?: string;\n jc?: string;\n flw?: string;\n cols?: number | string;\n rows?: number | string;\n // misc\n cur?: string;\n ov?: string;\n pe?: string;\n us?: string;\n sh?: string;\n tr?: string;\n // element-specific\n v?: unknown;\n ph?: string;\n type?: string;\n href?: string;\n src?: string;\n alt?: string;\n dis?: boolean;\n ch?: unknown;\n ro?: boolean;\n opts?: Array<{v: string, l: string}>;\n cls?: string;\n id?: string;\n rw?: number;\n sp?: number;\n rsp?: number;\n // events\n c?: EventHandler;\n x?: EventHandler;\n f?: EventHandler;\n bl?: EventHandler;\n k?: EventHandler;\n ku?: EventHandler;\n kp?: EventHandler;\n e?: EventHandler;\n lv?: EventHandler;\n sub?: EventHandler;\n // custom styles\n s?: Record<string, unknown>;\n}\n\ntype ComponentType =\n | 'V' | 'H' | 'D' | 'G'\n | 'T' | 'B'\n | 'I' | 'Ta' | 'S' | 'C' | 'R'\n | 'Tb' | 'Th' | 'Tbd' | 'Tr' | 'Td' | 'Tc'\n | 'Ul' | 'Ol' | 'Li'\n | 'M' | 'L' | 'Sv';\n\ntype StateRef = { $: string };\n\ninterface IfNode {\n // Long form\n if?: StateRef | string;\n then?: NodeSpec | NodeSpec[];\n else?: NodeSpec | NodeSpec[];\n // Short form\n '?'?: StateRef | string;\n t?: NodeSpec | NodeSpec[];\n e?: NodeSpec | NodeSpec[];\n // Equality check (works with both forms)\n eq?: unknown;\n is?: unknown;\n}\n\ninterface MapNode {\n // Long form\n map?: StateRef | string;\n as?: NodeSpec;\n // Short form\n m?: StateRef | string;\n a?: NodeSpec;\n key?: string;\n}\n\ntype Content = string | number | StateRef | NodeSpec[] | IfNode | MapNode;\ntype NodeSpec = [ComponentType, Content?, Props?] | IfNode | MapNode;\n\ninterface TooeySpec {\n s?: Record<string, StateValue>;\n r: NodeSpec;\n}\n\n// ============ render context for cleanup ============\n\ninterface ErrorInfo {\n message: string;\n componentType?: string;\n stack?: string;\n}\n\ntype ErrorHandler = (error: ErrorInfo) => void;\n\ninterface RenderContext {\n cleanups: Array<() => void>;\n state: StateStore;\n onError?: ErrorHandler;\n}\n\n// ============ error boundary ============\n\ninterface ErrorBoundaryNode {\n boundary: true;\n child: NodeSpec;\n fallback?: NodeSpec;\n onError?: ErrorHandler;\n}\n\nfunction isErrorBoundaryNode(v: unknown): v is ErrorBoundaryNode {\n return typeof v === 'object' && v !== null && 'boundary' in v && (v as ErrorBoundaryNode).boundary === true;\n}\n\nfunction createErrorFallback(error: ErrorInfo): HTMLElement {\n const el = document.createElement('div');\n el.style.cssText = 'padding:12px;background:#fee;border:1px solid #fcc;border-radius:4px;color:#c00;font-family:monospace;font-size:12px';\n el.textContent = `[tooey error] ${error.message}`;\n return el;\n}\n\n// ============ signals ============\n\nlet currentEffect: (() => void) | null = null;\nlet batchDepth = 0;\nlet pendingEffects = new Set<() => void>();\n\nfunction signal<T>(initial: T): Signal<T> {\n let value = initial;\n const subscribers = new Set<() => void>();\n\n const sig = (() => {\n if (currentEffect) {\n subscribers.add(currentEffect);\n }\n return value;\n }) as Signal<T>;\n\n sig.set = (v: T | ((prev: T) => T)) => {\n const newValue = typeof v === 'function' ? (v as (prev: T) => T)(value) : v;\n if (newValue !== value) {\n value = newValue;\n if (batchDepth > 0) {\n subscribers.forEach(fn => pendingEffects.add(fn));\n } else {\n subscribers.forEach(fn => fn());\n }\n }\n };\n\n sig.sub = (fn: () => void) => {\n subscribers.add(fn);\n return () => subscribers.delete(fn);\n };\n\n return sig;\n}\n\nfunction batch(fn: () => void): void {\n batchDepth++;\n try {\n fn();\n } finally {\n batchDepth--;\n if (batchDepth === 0) {\n const effects = pendingEffects;\n pendingEffects = new Set();\n effects.forEach(fn => fn());\n }\n }\n}\n\nfunction effect(fn: () => void, ctx?: RenderContext): () => void {\n let isActive = true;\n\n const execute = () => {\n if (!isActive) return;\n currentEffect = execute;\n try {\n fn();\n } finally {\n currentEffect = null;\n }\n };\n\n execute();\n\n const cleanup = () => {\n isActive = false;\n currentEffect = null;\n };\n\n if (ctx) {\n ctx.cleanups.push(cleanup);\n }\n\n return cleanup;\n}\n\n// ============ state operations ============\n\nfunction applyOp(state: Signal<StateValue>, op: Op, val?: unknown): void {\n try {\n switch (op) {\n case '+':\n state.set((v: StateValue) => (v as number) + (typeof val === 'number' ? val : 1));\n break;\n case '-':\n state.set((v: StateValue) => (v as number) - (typeof val === 'number' ? val : 1));\n break;\n case '!':\n state.set(val);\n break;\n case '~':\n state.set((v: StateValue) => !v);\n break;\n case '<':\n state.set((v: StateValue) => [...(v as unknown[]), val]);\n break;\n case '>':\n state.set((v: StateValue) => [val, ...(v as unknown[])]);\n break;\n case 'X':\n state.set((v: StateValue) => {\n const arr = v as unknown[];\n if (typeof val === 'number') {\n return arr.filter((_, i) => i !== val);\n } else if (typeof val === 'function') {\n return arr.filter((item, i) => !(val as (item: unknown, i: number) => boolean)(item, i));\n }\n return arr.filter(item => item !== val);\n });\n break;\n case '.':\n if (Array.isArray(val) && val.length === 2) {\n state.set((v: StateValue) => ({ ...(v as Record<string, unknown>), [val[0]]: val[1] }));\n }\n break;\n }\n } catch (err) {\n console.warn('[tooey] state operation error:', op, err);\n }\n}\n\n// ============ helpers ============\n\nfunction isStateRef(v: unknown): v is StateRef {\n return typeof v === 'object' && v !== null && !Array.isArray(v) && '$' in v;\n}\n\nfunction isIfNode(v: unknown): v is IfNode {\n return typeof v === 'object' && v !== null && !Array.isArray(v) && ('if' in v || '?' in v);\n}\n\nfunction isMapNode(v: unknown): v is MapNode {\n return typeof v === 'object' && v !== null && !Array.isArray(v) && ('map' in v || 'm' in v);\n}\n\nfunction resolveValue(content: unknown, state: StateStore): unknown {\n if (isStateRef(content)) {\n const sig = state[content.$];\n if (!sig) {\n console.warn(`[tooey] unknown state key: \"${content.$}\"`);\n return undefined;\n }\n return sig();\n }\n return content;\n}\n\nfunction resolveCssValue(val: number | string | undefined): string | undefined {\n if (val === undefined) return undefined;\n if (typeof val === 'number') return `${val}px`;\n return val;\n}\n\n// Style value shortcuts for common layout values\nconst styleShortcuts: Record<string, string> = {\n 'c': 'center',\n 'sb': 'space-between',\n 'sa': 'space-around',\n 'se': 'space-evenly',\n 'fe': 'flex-end',\n 'fs': 'flex-start',\n 'st': 'stretch',\n 'bl': 'baseline',\n};\n\nfunction expandStyleValue(val: string | undefined): string | undefined {\n if (val === undefined) return undefined;\n return styleShortcuts[val] || val;\n}\n\n// Parse string-based event handler shorthand\n// Format: \"stateName+\" | \"stateName-\" | \"stateName~\" | \"stateName!value\"\nfunction parseEventShorthand(str: string): [string, Op, unknown?] | null {\n if (typeof str !== 'string' || str.length < 2) return null;\n\n const lastChar = str[str.length - 1];\n\n // Simple ops: +, -, ~\n if (lastChar === '+' || lastChar === '-' || lastChar === '~') {\n return [str.slice(0, -1), lastChar as Op];\n }\n\n // Set operation with value: \"state!value\"\n const bangIdx = str.indexOf('!');\n if (bangIdx > 0) {\n const stateKey = str.slice(0, bangIdx);\n const valStr = str.slice(bangIdx + 1);\n // Try to parse as number or boolean\n let val: unknown = valStr;\n if (valStr === 'true') val = true;\n else if (valStr === 'false') val = false;\n else if (!isNaN(Number(valStr)) && valStr !== '') val = Number(valStr);\n return [stateKey, '!', val];\n }\n\n return null;\n}\n\n// XSS protection - escape HTML entities (kept for future use, textContent provides protection now)\nfunction _escapeHtml(str: string): string {\n const div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n}\n\n// URL validation - prevent dangerous protocols\nconst SAFE_URL_PROTOCOLS = ['http:', 'https:', 'mailto:', 'tel:', 'ftp:'];\n\nfunction isValidUrl(url: string): boolean {\n if (!url || typeof url !== 'string') return false;\n\n // Allow relative URLs and anchors\n if (url.startsWith('/') || url.startsWith('#') || url.startsWith('.')) {\n return true;\n }\n\n try {\n const parsed = new URL(url, window.location.href);\n return SAFE_URL_PROTOCOLS.includes(parsed.protocol);\n } catch {\n // If URL parsing fails, check for dangerous patterns directly\n const lowerUrl = url.toLowerCase().trim();\n const dangerousPatterns = ['javascript:', 'data:', 'vbscript:'];\n return !dangerousPatterns.some(pattern => lowerUrl.startsWith(pattern));\n }\n}\n\nfunction sanitizeUrl(url: string, propName: string): string | null {\n if (!url) return null;\n\n if (!isValidUrl(url)) {\n console.warn(`[tooey] blocked unsafe URL in ${propName}: \"${url.slice(0, 50)}...\"`);\n return null;\n }\n\n return url;\n}\n\nfunction createHandler(\n handler: EventHandler,\n state: StateStore,\n event?: Event,\n itemContext?: { item: unknown, index: number },\n buttonText?: string\n): () => void {\n if (typeof handler === 'function') {\n return handler;\n }\n\n // Handle string shorthand: \"state+\", \"state-\", \"state~\", \"state!value\"\n let normalizedHandler: [string, Op, unknown?];\n if (typeof handler === 'string') {\n const parsed = parseEventShorthand(handler);\n if (parsed) {\n normalizedHandler = parsed;\n } else {\n // Plain state key - infer operation from button text if available\n if (buttonText === '+') {\n normalizedHandler = [handler, '+'];\n } else if (buttonText === '-') {\n normalizedHandler = [handler, '-'];\n } else {\n // Default to toggle for plain state key\n normalizedHandler = [handler, '~'];\n }\n }\n } else {\n normalizedHandler = handler;\n }\n\n const [stateKey, op, val] = normalizedHandler;\n return () => {\n const sig = state[stateKey];\n if (!sig) {\n console.warn(`[tooey] click handler: unknown state key \"${stateKey}\"`);\n return;\n }\n let actualVal = val;\n // Resolve $item and $index in event handler values\n if (itemContext && typeof actualVal === 'string') {\n if (actualVal === '$index') {\n actualVal = itemContext.index;\n } else if (actualVal === '$item') {\n actualVal = itemContext.item;\n } else if (actualVal.startsWith('$item.')) {\n const key = actualVal.substring(6);\n actualVal = (itemContext.item as Record<string, unknown>)?.[key];\n }\n }\n if (op === '!' && val === undefined && event) {\n const target = event.target as HTMLInputElement;\n actualVal = target.type === 'checkbox' ? target.checked : target.value;\n }\n applyOp(sig, op, actualVal);\n };\n}\n\n// ============ styles ============\n\nfunction applyStyles(el: HTMLElement, props: Props): void {\n const style = el.style;\n\n if (props.g !== undefined) style.gap = resolveCssValue(props.g)!;\n if (props.p !== undefined) style.padding = resolveCssValue(props.p)!;\n if (props.m !== undefined) style.margin = resolveCssValue(props.m)!;\n if (props.w !== undefined) style.width = resolveCssValue(props.w)!;\n if (props.h !== undefined) style.height = resolveCssValue(props.h)!;\n if (props.mw !== undefined) style.maxWidth = resolveCssValue(props.mw)!;\n if (props.mh !== undefined) style.maxHeight = resolveCssValue(props.mh)!;\n\n if (props.bg !== undefined) style.background = props.bg;\n if (props.fg !== undefined) style.color = props.fg;\n if (props.o !== undefined) style.opacity = String(props.o);\n\n if (props.r !== undefined) style.borderRadius = resolveCssValue(props.r)!;\n if (props.bw !== undefined) style.borderWidth = resolveCssValue(props.bw)!;\n if (props.bc !== undefined) style.borderColor = props.bc;\n if (props.bs !== undefined) style.borderStyle = props.bs;\n\n if (props.pos !== undefined) {\n const posMap: Record<string, string> = {\n rel: 'relative', abs: 'absolute', fix: 'fixed', sticky: 'sticky'\n };\n style.position = posMap[props.pos] || props.pos;\n }\n if (props.z !== undefined) style.zIndex = String(props.z);\n if (props.t !== undefined) style.top = resolveCssValue(props.t)!;\n if (props.l !== undefined) style.left = resolveCssValue(props.l)!;\n if (typeof props.b === 'number' || (typeof props.b === 'string' && !Array.isArray(props.b))) {\n style.bottom = resolveCssValue(props.b as number | string)!;\n }\n if (props.rt !== undefined) style.right = resolveCssValue(props.rt)!;\n\n if (props.fs !== undefined) style.fontSize = resolveCssValue(props.fs)!;\n if (props.fw !== undefined) style.fontWeight = String(props.fw);\n if (props.ff !== undefined) style.fontFamily = props.ff;\n if (props.ta !== undefined) style.textAlign = props.ta;\n if (props.td !== undefined) style.textDecoration = props.td;\n if (props.lh !== undefined) style.lineHeight = typeof props.lh === 'number' ? String(props.lh) : props.lh;\n if (props.ls !== undefined) style.letterSpacing = resolveCssValue(props.ls)!;\n\n if (props.ai !== undefined) style.alignItems = expandStyleValue(props.ai)!;\n if (props.jc !== undefined) style.justifyContent = expandStyleValue(props.jc)!;\n if (props.flw !== undefined) style.flexWrap = expandStyleValue(props.flw)!\n\n if (props.cur !== undefined) style.cursor = props.cur;\n if (props.ov !== undefined) style.overflow = props.ov;\n if (props.pe !== undefined) style.pointerEvents = props.pe;\n if (props.us !== undefined) style.userSelect = props.us;\n if (props.sh !== undefined) style.boxShadow = props.sh;\n if (props.tr !== undefined) style.transform = props.tr;\n\n if (props.s) {\n Object.entries(props.s).forEach(([key, val]) => {\n (style as unknown as Record<string, string>)[key] = String(val);\n });\n }\n}\n\n// ============ renderer ============\n\nfunction createElement(\n spec: NodeSpec,\n ctx: RenderContext,\n itemContext?: { item: unknown, index: number }\n): HTMLElement | null {\n const { state } = ctx;\n\n // handle error boundary node\n if (isErrorBoundaryNode(spec)) {\n const placeholder = document.createElement('div');\n placeholder.style.display = 'contents';\n\n try {\n const childCtx: RenderContext = {\n cleanups: [],\n state,\n onError: spec.onError || ctx.onError,\n };\n const child = createElement(spec.child, childCtx, itemContext);\n if (child) {\n placeholder.appendChild(child);\n ctx.cleanups.push(...childCtx.cleanups);\n }\n } catch (err) {\n const errorInfo: ErrorInfo = {\n message: err instanceof Error ? err.message : String(err),\n stack: err instanceof Error ? err.stack : undefined,\n };\n\n if (spec.onError) {\n spec.onError(errorInfo);\n }\n\n if (spec.fallback) {\n try {\n const fallbackEl = createElement(spec.fallback, ctx, itemContext);\n if (fallbackEl) placeholder.appendChild(fallbackEl);\n } catch {\n placeholder.appendChild(createErrorFallback(errorInfo));\n }\n } else {\n placeholder.appendChild(createErrorFallback(errorInfo));\n }\n }\n\n return placeholder;\n }\n\n // handle reactive if node (supports both long and short form)\n if (isIfNode(spec)) {\n const placeholder = document.createElement('div');\n placeholder.style.display = 'contents';\n\n let currentEl: HTMLElement | null = null;\n let childCtx: RenderContext | null = null;\n\n // Normalize short form to long form\n const ifCond = spec.if ?? spec['?'];\n const thenBranch = spec.then ?? spec.t;\n const elseBranch = spec.else ?? spec.e;\n const eqValue = spec.eq ?? spec.is;\n\n const updateIf = () => {\n // cleanup previous render\n if (childCtx) {\n childCtx.cleanups.forEach(fn => fn());\n childCtx.cleanups = [];\n }\n if (currentEl) {\n placeholder.innerHTML = '';\n currentEl = null;\n }\n\n const rawValue = typeof ifCond === 'string'\n ? state[ifCond]?.()\n : resolveValue(ifCond, state);\n\n // Handle equality check (eq or is)\n let condition: boolean;\n if (eqValue !== undefined) {\n condition = rawValue === eqValue;\n } else {\n condition = Boolean(rawValue);\n }\n\n const branch = condition ? thenBranch : elseBranch;\n if (!branch) return;\n\n childCtx = { cleanups: [], state };\n\n if (Array.isArray(branch) && branch.length > 0 && Array.isArray(branch[0])) {\n (branch as NodeSpec[]).forEach(child => {\n const el = createElement(child, childCtx!, itemContext);\n if (el) placeholder.appendChild(el);\n });\n } else {\n currentEl = createElement(branch as NodeSpec, childCtx, itemContext);\n if (currentEl) placeholder.appendChild(currentEl);\n }\n };\n\n effect(updateIf, ctx);\n return placeholder;\n }\n\n // handle reactive map node (supports both long and short form)\n if (isMapNode(spec)) {\n const placeholder = document.createElement('div');\n placeholder.style.display = 'contents';\n\n let childCtx: RenderContext | null = null;\n\n // Normalize short form to long form\n const mapSource = spec.map ?? spec.m;\n const asTemplate = spec.as ?? spec.a;\n\n const updateMap = () => {\n // cleanup previous render\n if (childCtx) {\n childCtx.cleanups.forEach(fn => fn());\n childCtx.cleanups = [];\n }\n placeholder.innerHTML = '';\n\n const arr = (typeof mapSource === 'string'\n ? state[mapSource]?.()\n : resolveValue(mapSource, state)) as unknown[];\n\n if (!Array.isArray(arr) || !asTemplate) return;\n\n childCtx = { cleanups: [], state };\n\n arr.forEach((item, index) => {\n const el = createElement(asTemplate, childCtx!, { item, index });\n if (el) placeholder.appendChild(el);\n });\n };\n\n effect(updateMap, ctx);\n return placeholder;\n }\n\n // validate spec structure\n if (!Array.isArray(spec) || (spec as unknown[]).length === 0) {\n console.warn('[tooey] invalid node spec:', spec);\n return null;\n }\n\n const [type, content, props = {}] = spec as [ComponentType, Content?, Props?];\n let el: HTMLElement;\n\n switch (type) {\n case 'V':\n el = document.createElement('div');\n el.style.display = 'flex';\n el.style.flexDirection = 'column';\n break;\n case 'H':\n el = document.createElement('div');\n el.style.display = 'flex';\n el.style.flexDirection = 'row';\n break;\n case 'G':\n el = document.createElement('div');\n el.style.display = 'grid';\n if (props.cols) {\n el.style.gridTemplateColumns = typeof props.cols === 'number'\n ? `repeat(${props.cols}, 1fr)` : props.cols;\n }\n if (props.rows) {\n el.style.gridTemplateRows = typeof props.rows === 'number'\n ? `repeat(${props.rows}, 1fr)` : props.rows;\n }\n break;\n case 'D':\n el = document.createElement('div');\n break;\n case 'T':\n el = document.createElement('span');\n break;\n case 'B':\n el = document.createElement('button');\n break;\n case 'I':\n el = document.createElement('input');\n (el as HTMLInputElement).type = props.type || 'text';\n if (props.ph) (el as HTMLInputElement).placeholder = props.ph;\n if (props.ro) (el as HTMLInputElement).readOnly = true;\n break;\n case 'Ta':\n el = document.createElement('textarea');\n if (props.ph) (el as HTMLTextAreaElement).placeholder = props.ph;\n if (props.rw) (el as HTMLTextAreaElement).rows = props.rw;\n if (props.ro) (el as HTMLTextAreaElement).readOnly = true;\n break;\n case 'S':\n el = document.createElement('select');\n if (props.opts) {\n props.opts.forEach(opt => {\n const option = document.createElement('option');\n option.value = opt.v;\n option.textContent = opt.l;\n el.appendChild(option);\n });\n }\n break;\n case 'C':\n el = document.createElement('input');\n (el as HTMLInputElement).type = 'checkbox';\n break;\n case 'R':\n el = document.createElement('input');\n (el as HTMLInputElement).type = 'radio';\n break;\n case 'Tb':\n el = document.createElement('table');\n break;\n case 'Th':\n el = document.createElement('thead');\n break;\n case 'Tbd':\n el = document.createElement('tbody');\n break;\n case 'Tr':\n el = document.createElement('tr');\n break;\n case 'Td':\n el = document.createElement('td');\n if (props.sp) (el as HTMLTableCellElement).colSpan = props.sp;\n if (props.rsp) (el as HTMLTableCellElement).rowSpan = props.rsp;\n break;\n case 'Tc':\n el = document.createElement('th');\n if (props.sp) (el as HTMLTableCellElement).colSpan = props.sp;\n if (props.rsp) (el as HTMLTableCellElement).rowSpan = props.rsp;\n break;\n case 'Ul':\n el = document.createElement('ul');\n break;\n case 'Ol':\n el = document.createElement('ol');\n break;\n case 'Li':\n el = document.createElement('li');\n break;\n case 'M':\n el = document.createElement('img');\n if (props.src) {\n const safeSrc = sanitizeUrl(props.src, 'src');\n if (safeSrc) (el as HTMLImageElement).src = safeSrc;\n }\n if (props.alt) (el as HTMLImageElement).alt = props.alt;\n break;\n case 'L':\n el = document.createElement('a');\n if (props.href) {\n const safeHref = sanitizeUrl(props.href, 'href');\n if (safeHref) (el as HTMLAnchorElement).href = safeHref;\n }\n break;\n case 'Sv':\n el = document.createElementNS('http://www.w3.org/2000/svg', 'svg') as unknown as HTMLElement;\n break;\n default:\n console.warn(`[tooey] unknown component type: ${type}`);\n el = document.createElement('div');\n }\n\n if (props.cls) el.className = props.cls;\n if (props.id) el.id = props.id;\n if (props.dis) (el as HTMLButtonElement).disabled = true;\n\n applyStyles(el, props);\n\n // handle content\n if (content !== undefined) {\n if (Array.isArray(content) && content.length > 0 && (Array.isArray(content[0]) || isIfNode(content[0]) || isMapNode(content[0]))) {\n (content as NodeSpec[]).forEach(childSpec => {\n const child = createElement(childSpec, ctx, itemContext);\n if (child) el.appendChild(child);\n });\n } else if (isIfNode(content) || isMapNode(content)) {\n const child = createElement(content as NodeSpec, ctx, itemContext);\n if (child) el.appendChild(child);\n } else if (isStateRef(content)) {\n effect(() => {\n const val = resolveValue(content, state);\n if (type === 'I') {\n (el as HTMLInputElement).value = String(val ?? '');\n } else if (type === 'Ta') {\n (el as HTMLTextAreaElement).value = String(val ?? '');\n } else if (type === 'C' || type === 'R') {\n (el as HTMLInputElement).checked = Boolean(val);\n } else {\n // textContent provides XSS protection by not parsing HTML\n el.textContent = String(val ?? '');\n }\n }, ctx);\n } else if (typeof content === 'string' || typeof content === 'number') {\n let textContent = String(content);\n if (itemContext) {\n // handle object property access first (before $item replacement)\n if (typeof itemContext.item === 'object' && itemContext.item !== null) {\n textContent = textContent.replace(/\\$item\\.(\\w+)/g, (_, key) => {\n return String((itemContext.item as Record<string, unknown>)[key] ?? '');\n });\n }\n textContent = textContent.replace(/\\$item/g, String(itemContext.item));\n textContent = textContent.replace(/\\$index/g, String(itemContext.index));\n }\n // XSS protection for static content\n if (type === 'I') {\n (el as HTMLInputElement).value = textContent;\n } else if (type === 'Ta') {\n (el as HTMLTextAreaElement).value = textContent;\n } else if (type !== 'S') {\n // don't set textContent for select (would clear options)\n el.textContent = textContent;\n }\n }\n }\n\n // handle value binding for inputs\n if (props.v !== undefined && isStateRef(props.v)) {\n effect(() => {\n const val = resolveValue(props.v!, state);\n if (type === 'I' || type === 'S' || type === 'Ta') {\n (el as HTMLInputElement).value = String(val ?? '');\n } else if (type === 'C' || type === 'R') {\n (el as HTMLInputElement).checked = Boolean(val);\n }\n }, ctx);\n }\n\n // handle checked binding\n if (props.ch !== undefined) {\n if (isStateRef(props.ch as unknown)) {\n effect(() => {\n const val = resolveValue(props.ch as unknown as StateRef, state);\n (el as HTMLInputElement).checked = Boolean(val);\n }, ctx);\n } else {\n (el as HTMLInputElement).checked = Boolean(props.ch);\n }\n }\n\n // event handlers with cleanup tracking\n const addEventListener = (event: string, handler: (e: Event) => void) => {\n el.addEventListener(event, handler);\n ctx.cleanups.push(() => el.removeEventListener(event, handler));\n };\n\n // Get button text for implicit operation inference\n const buttonText = type === 'B' && (typeof content === 'string' || typeof content === 'number')\n ? String(content)\n : undefined;\n\n if (props.c) {\n addEventListener('click', (e) => createHandler(props.c!, state, e, itemContext, buttonText)());\n }\n if (props.x) {\n const handler = props.x;\n addEventListener('input', (e) => {\n if (typeof handler === 'function') {\n handler();\n } else {\n // Normalize string shorthand to array form\n let stateKey: string;\n let op: Op;\n if (typeof handler === 'string') {\n const parsed = parseEventShorthand(handler);\n if (parsed) {\n [stateKey, op] = parsed;\n } else {\n // Plain state key defaults to set (!) for input\n stateKey = handler;\n op = '!';\n }\n } else {\n [stateKey, op] = handler;\n }\n const sig = state[stateKey];\n if (sig) {\n const target = e.target as HTMLInputElement;\n const val = (type === 'C' || type === 'R') ? target.checked : target.value;\n applyOp(sig, op, val);\n }\n }\n });\n }\n if (props.f) {\n addEventListener('focus', (e) => createHandler(props.f!, state, e, itemContext)());\n }\n if (props.bl) {\n addEventListener('blur', (e) => createHandler(props.bl!, state, e, itemContext)());\n }\n if (props.k) {\n addEventListener('keydown', (e) => createHandler(props.k!, state, e, itemContext)());\n }\n if (props.ku) {\n addEventListener('keyup', (e) => createHandler(props.ku!, state, e, itemContext)());\n }\n if (props.kp) {\n addEventListener('keypress', (e) => createHandler(props.kp!, state, e, itemContext)());\n }\n if (props.e) {\n addEventListener('mouseenter', (e) => createHandler(props.e!, state, e, itemContext)());\n }\n if (props.lv) {\n addEventListener('mouseleave', (e) => createHandler(props.lv!, state, e, itemContext)());\n }\n if (props.sub) {\n addEventListener('submit', (e) => {\n e.preventDefault();\n createHandler(props.sub!, state, e, itemContext)();\n });\n }\n\n return el;\n}\n\n// ============ main api ============\n\ninterface TooeyInstance {\n state: StateStore;\n el: HTMLElement | null;\n destroy(): void;\n update(newSpec: TooeySpec): void;\n get(key: string): unknown;\n set(key: string, value: unknown): void;\n}\n\nfunction render(container: HTMLElement, spec: TooeySpec): TooeyInstance {\n if (!container) {\n throw new Error('[tooey] render requires a valid container element');\n }\n if (!spec || !spec.r) {\n throw new Error('[tooey] render requires a spec with a root node (r)');\n }\n\n const state: StateStore = {};\n if (spec.s) {\n Object.entries(spec.s).forEach(([key, val]) => {\n state[key] = signal(val);\n });\n }\n\n const ctx: RenderContext = { cleanups: [], state };\n\n container.innerHTML = '';\n const el = createElement(spec.r, ctx);\n if (el) container.appendChild(el);\n\n const instance: TooeyInstance = {\n state,\n el,\n destroy() {\n ctx.cleanups.forEach(fn => fn());\n ctx.cleanups = [];\n container.innerHTML = '';\n },\n update(newSpec: TooeySpec) {\n if (newSpec.s) {\n batch(() => {\n Object.entries(newSpec.s!).forEach(([key, val]) => {\n if (state[key]) {\n state[key].set(val);\n } else {\n state[key] = signal(val);\n }\n });\n });\n }\n if (newSpec.r) {\n ctx.cleanups.forEach(fn => fn());\n ctx.cleanups = [];\n container.innerHTML = '';\n const newEl = createElement(newSpec.r, ctx);\n if (newEl) container.appendChild(newEl);\n instance.el = newEl;\n }\n },\n get(key: string) {\n return state[key]?.();\n },\n set(key: string, value: unknown) {\n if (state[key]) {\n state[key].set(value);\n }\n }\n };\n\n return instance;\n}\n\n// ============ convenience helpers ============\n\nfunction $(name: string): StateRef {\n return { $: name };\n}\n\n// component type constants\nconst V = 'V' as const;\nconst H = 'H' as const;\nconst D = 'D' as const;\nconst G = 'G' as const;\nconst T = 'T' as const;\nconst B = 'B' as const;\nconst I = 'I' as const;\nconst Ta = 'Ta' as const;\nconst S = 'S' as const;\nconst C = 'C' as const;\nconst R = 'R' as const;\nconst Tb = 'Tb' as const;\nconst Th = 'Th' as const;\nconst Tbd = 'Tbd' as const;\nconst Tr = 'Tr' as const;\nconst Td = 'Td' as const;\nconst Tc = 'Tc' as const;\nconst Ul = 'Ul' as const;\nconst Ol = 'Ol' as const;\nconst Li = 'Li' as const;\nconst M = 'M' as const;\nconst L = 'L' as const;\nconst Sv = 'Sv' as const;\n\nexport {\n render,\n signal,\n effect,\n batch,\n $,\n V, H, D, G,\n T, B,\n I, Ta, S, C, R,\n Tb, Th, Tbd, Tr, Td, Tc,\n Ul, Ol, Li,\n M, L, Sv,\n TooeySpec,\n NodeSpec,\n Props,\n StateRef,\n TooeyInstance,\n IfNode,\n MapNode,\n ErrorBoundaryNode,\n ErrorInfo,\n ErrorHandler\n};\n"],
5
+ "mappings": "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",
6
+ "names": ["isErrorBoundaryNode", "v", "createErrorFallback", "error", "el", "currentEffect", "batchDepth", "pendingEffects", "signal", "initial", "value", "subscribers", "sig", "newValue", "fn", "batch", "effects", "effect", "ctx", "isActive", "execute", "cleanup", "applyOp", "state", "op", "val", "arr", "_", "i", "item", "err", "isStateRef", "isIfNode", "isMapNode", "resolveValue", "content", "resolveCssValue", "styleShortcuts", "expandStyleValue", "parseEventShorthand", "str", "lastChar", "bangIdx", "stateKey", "valStr", "SAFE_URL_PROTOCOLS", "isValidUrl", "url", "parsed", "lowerUrl", "pattern", "sanitizeUrl", "propName", "createHandler", "handler", "state", "event", "itemContext", "buttonText", "normalizedHandler", "parseEventShorthand", "stateKey", "op", "val", "sig", "actualVal", "key", "target", "applyOp", "applyStyles", "el", "props", "style", "resolveCssValue", "posMap", "expandStyleValue", "createElement", "spec", "ctx", "isErrorBoundaryNode", "placeholder", "childCtx", "child", "err", "errorInfo", "fallbackEl", "createErrorFallback", "isIfNode", "currentEl", "ifCond", "thenBranch", "elseBranch", "eqValue", "effect", "fn", "rawValue", "resolveValue", "condition", "branch", "isMapNode", "mapSource", "asTemplate", "arr", "item", "index", "type", "content", "opt", "option", "safeSrc", "safeHref", "childSpec", "isStateRef", "textContent", "_", "addEventListener", "e", "render", "container", "signal", "instance", "newSpec", "batch", "newEl", "value", "$", "name", "V", "H", "D", "G", "T", "B", "I", "Ta", "S", "C", "R", "Tb", "Th", "Tbd", "Tr", "Td", "Tc", "Ul", "Ol", "Li", "M", "L", "Sv"]
7
+ }
package/dist/tooey.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";var tooey=(()=>{var N=Object.defineProperty;var z=Object.getOwnPropertyDescriptor;var P=Object.getOwnPropertyNames;var q=Object.prototype.hasOwnProperty;var _=(n,e)=>{for(var t in e)N(n,t,{get:e[t],enumerable:!0})},G=(n,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of P(e))!q.call(n,o)&&o!==t&&N(n,o,{get:()=>e[o],enumerable:!(s=z(e,o))||s.enumerable});return n};var F=n=>G(N({},"__esModule",{value:!0}),n);var ve={};_(ve,{$:()=>ee,B:()=>se,C:()=>fe,D:()=>re,G:()=>ie,H:()=>te,I:()=>oe,L:()=>we,Li:()=>Se,M:()=>ke,Ol:()=>Te,R:()=>de,S:()=>ce,Sv:()=>pe,T:()=>ae,Ta:()=>le,Tb:()=>ue,Tbd:()=>ge,Tc:()=>Ee,Td:()=>ye,Th:()=>me,Tr:()=>be,Ul:()=>he,V:()=>ne,batch:()=>U,effect:()=>S,render:()=>Z,signal:()=>V});function K(n){return typeof n=="object"&&n!==null&&"boundary"in n&&n.boundary===!0}function B(n){let e=document.createElement("div");return e.style.cssText="padding:12px;background:#fee;border:1px solid #fcc;border-radius:4px;color:#c00;font-family:monospace;font-size:12px",e.textContent=`[tooey error] ${n.message}`,e}var v=null,H=0,x=new Set;function V(n){let e=n,t=new Set,s=()=>(v&&t.add(v),e);return s.set=o=>{let l=typeof o=="function"?o(e):o;l!==e&&(e=l,H>0?t.forEach(r=>x.add(r)):t.forEach(r=>r()))},s.sub=o=>(t.add(o),()=>t.delete(o)),s}function U(n){H++;try{n()}finally{if(H--,H===0){let e=x;x=new Set,e.forEach(t=>t())}}}function S(n,e){let t=!0,s=()=>{if(t){v=s;try{n()}finally{v=null}}};s();let o=()=>{t=!1,v=null};return e&&e.cleanups.push(o),o}function D(n,e,t){try{switch(e){case"+":n.set(s=>s+(typeof t=="number"?t:1));break;case"-":n.set(s=>s-(typeof t=="number"?t:1));break;case"!":n.set(t);break;case"~":n.set(s=>!s);break;case"<":n.set(s=>[...s,t]);break;case">":n.set(s=>[t,...s]);break;case"X":n.set(s=>{let o=s;return typeof t=="number"?o.filter((l,r)=>r!==t):typeof t=="function"?o.filter((l,r)=>!t(l,r)):o.filter(l=>l!==t)});break;case".":Array.isArray(t)&&t.length===2&&n.set(s=>({...s,[t[0]]:t[1]}));break}}catch(s){console.warn("[tooey] state operation error:",e,s)}}function L(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)&&"$"in n}function R(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)&&("if"in n||"?"in n)}function A(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)&&("map"in n||"m"in n)}function p(n,e){if(L(n)){let t=e[n.$];if(!t){console.warn(`[tooey] unknown state key: "${n.$}"`);return}return t()}return n}function u(n){if(n!==void 0)return typeof n=="number"?`${n}px`:n}var X={c:"center",sb:"space-between",sa:"space-around",se:"space-evenly",fe:"flex-end",fs:"flex-start",st:"stretch",bl:"baseline"};function C(n){if(n!==void 0)return X[n]||n}function W(n){if(typeof n!="string"||n.length<2)return null;let e=n[n.length-1];if(e==="+"||e==="-"||e==="~")return[n.slice(0,-1),e];let t=n.indexOf("!");if(t>0){let s=n.slice(0,t),o=n.slice(t+1),l=o;return o==="true"?l=!0:o==="false"?l=!1:!isNaN(Number(o))&&o!==""&&(l=Number(o)),[s,"!",l]}return null}var J=["http:","https:","mailto:","tel:","ftp:"];function Q(n){if(!n||typeof n!="string")return!1;if(n.startsWith("/")||n.startsWith("#")||n.startsWith("."))return!0;try{let e=new URL(n,window.location.href);return J.includes(e.protocol)}catch{let e=n.toLowerCase().trim();return!["javascript:","data:","vbscript:"].some(s=>e.startsWith(s))}}function j(n,e){return n?Q(n)?n:(console.warn(`[tooey] blocked unsafe URL in ${e}: "${n.slice(0,50)}..."`),null):null}function b(n,e,t,s,o){if(typeof n=="function")return n;let l;if(typeof n=="string"){let h=W(n);h?l=h:o==="+"?l=[n,"+"]:o==="-"?l=[n,"-"]:l=[n,"~"]}else l=n;let[r,a,d]=l;return()=>{let h=e[r];if(!h){console.warn(`[tooey] click handler: unknown state key "${r}"`);return}let i=d;if(s&&typeof i=="string"){if(i==="$index")i=s.index;else if(i==="$item")i=s.item;else if(i.startsWith("$item.")){let c=i.substring(6);i=s.item?.[c]}}if(a==="!"&&d===void 0&&t){let c=t.target;i=c.type==="checkbox"?c.checked:c.value}D(h,a,i)}}function Y(n,e){let t=n.style;if(e.g!==void 0&&(t.gap=u(e.g)),e.p!==void 0&&(t.padding=u(e.p)),e.m!==void 0&&(t.margin=u(e.m)),e.w!==void 0&&(t.width=u(e.w)),e.h!==void 0&&(t.height=u(e.h)),e.mw!==void 0&&(t.maxWidth=u(e.mw)),e.mh!==void 0&&(t.maxHeight=u(e.mh)),e.bg!==void 0&&(t.background=e.bg),e.fg!==void 0&&(t.color=e.fg),e.o!==void 0&&(t.opacity=String(e.o)),e.r!==void 0&&(t.borderRadius=u(e.r)),e.bw!==void 0&&(t.borderWidth=u(e.bw)),e.bc!==void 0&&(t.borderColor=e.bc),e.bs!==void 0&&(t.borderStyle=e.bs),e.pos!==void 0){let s={rel:"relative",abs:"absolute",fix:"fixed",sticky:"sticky"};t.position=s[e.pos]||e.pos}e.z!==void 0&&(t.zIndex=String(e.z)),e.t!==void 0&&(t.top=u(e.t)),e.l!==void 0&&(t.left=u(e.l)),(typeof e.b=="number"||typeof e.b=="string"&&!Array.isArray(e.b))&&(t.bottom=u(e.b)),e.rt!==void 0&&(t.right=u(e.rt)),e.fs!==void 0&&(t.fontSize=u(e.fs)),e.fw!==void 0&&(t.fontWeight=String(e.fw)),e.ff!==void 0&&(t.fontFamily=e.ff),e.ta!==void 0&&(t.textAlign=e.ta),e.td!==void 0&&(t.textDecoration=e.td),e.lh!==void 0&&(t.lineHeight=typeof e.lh=="number"?String(e.lh):e.lh),e.ls!==void 0&&(t.letterSpacing=u(e.ls)),e.ai!==void 0&&(t.alignItems=C(e.ai)),e.jc!==void 0&&(t.justifyContent=C(e.jc)),e.flw!==void 0&&(t.flexWrap=C(e.flw)),e.cur!==void 0&&(t.cursor=e.cur),e.ov!==void 0&&(t.overflow=e.ov),e.pe!==void 0&&(t.pointerEvents=e.pe),e.us!==void 0&&(t.userSelect=e.us),e.sh!==void 0&&(t.boxShadow=e.sh),e.tr!==void 0&&(t.transform=e.tr),e.s&&Object.entries(e.s).forEach(([s,o])=>{t[s]=String(o)})}function y(n,e,t){let{state:s}=e;if(K(n)){let i=document.createElement("div");i.style.display="contents";try{let c={cleanups:[],state:s,onError:n.onError||e.onError},f=y(n.child,c,t);f&&(i.appendChild(f),e.cleanups.push(...c.cleanups))}catch(c){let f={message:c instanceof Error?c.message:String(c),stack:c instanceof Error?c.stack:void 0};if(n.onError&&n.onError(f),n.fallback)try{let m=y(n.fallback,e,t);m&&i.appendChild(m)}catch{i.appendChild(B(f))}else i.appendChild(B(f))}return i}if(R(n)){let i=document.createElement("div");i.style.display="contents";let c=null,f=null,m=n.if??n["?"],k=n.then??n.t,g=n.else??n.e,E=n.eq??n.is;return S(()=>{f&&(f.cleanups.forEach(I=>I()),f.cleanups=[]),c&&(i.innerHTML="",c=null);let w=typeof m=="string"?s[m]?.():p(m,s),M;E!==void 0?M=w===E:M=!!w;let T=M?k:g;T&&(f={cleanups:[],state:s},Array.isArray(T)&&T.length>0&&Array.isArray(T[0])?T.forEach(I=>{let $=y(I,f,t);$&&i.appendChild($)}):(c=y(T,f,t),c&&i.appendChild(c)))},e),i}if(A(n)){let i=document.createElement("div");i.style.display="contents";let c=null,f=n.map??n.m,m=n.as??n.a;return S(()=>{c&&(c.cleanups.forEach(E=>E()),c.cleanups=[]),i.innerHTML="";let g=typeof f=="string"?s[f]?.():p(f,s);!Array.isArray(g)||!m||(c={cleanups:[],state:s},g.forEach((E,O)=>{let w=y(m,c,{item:E,index:O});w&&i.appendChild(w)}))},e),i}if(!Array.isArray(n)||n.length===0)return console.warn("[tooey] invalid node spec:",n),null;let[o,l,r={}]=n,a;switch(o){case"V":a=document.createElement("div"),a.style.display="flex",a.style.flexDirection="column";break;case"H":a=document.createElement("div"),a.style.display="flex",a.style.flexDirection="row";break;case"G":a=document.createElement("div"),a.style.display="grid",r.cols&&(a.style.gridTemplateColumns=typeof r.cols=="number"?`repeat(${r.cols}, 1fr)`:r.cols),r.rows&&(a.style.gridTemplateRows=typeof r.rows=="number"?`repeat(${r.rows}, 1fr)`:r.rows);break;case"D":a=document.createElement("div");break;case"T":a=document.createElement("span");break;case"B":a=document.createElement("button");break;case"I":a=document.createElement("input"),a.type=r.type||"text",r.ph&&(a.placeholder=r.ph),r.ro&&(a.readOnly=!0);break;case"Ta":a=document.createElement("textarea"),r.ph&&(a.placeholder=r.ph),r.rw&&(a.rows=r.rw),r.ro&&(a.readOnly=!0);break;case"S":a=document.createElement("select"),r.opts&&r.opts.forEach(i=>{let c=document.createElement("option");c.value=i.v,c.textContent=i.l,a.appendChild(c)});break;case"C":a=document.createElement("input"),a.type="checkbox";break;case"R":a=document.createElement("input"),a.type="radio";break;case"Tb":a=document.createElement("table");break;case"Th":a=document.createElement("thead");break;case"Tbd":a=document.createElement("tbody");break;case"Tr":a=document.createElement("tr");break;case"Td":a=document.createElement("td"),r.sp&&(a.colSpan=r.sp),r.rsp&&(a.rowSpan=r.rsp);break;case"Tc":a=document.createElement("th"),r.sp&&(a.colSpan=r.sp),r.rsp&&(a.rowSpan=r.rsp);break;case"Ul":a=document.createElement("ul");break;case"Ol":a=document.createElement("ol");break;case"Li":a=document.createElement("li");break;case"M":if(a=document.createElement("img"),r.src){let i=j(r.src,"src");i&&(a.src=i)}r.alt&&(a.alt=r.alt);break;case"L":if(a=document.createElement("a"),r.href){let i=j(r.href,"href");i&&(a.href=i)}break;case"Sv":a=document.createElementNS("http://www.w3.org/2000/svg","svg");break;default:console.warn(`[tooey] unknown component type: ${o}`),a=document.createElement("div")}if(r.cls&&(a.className=r.cls),r.id&&(a.id=r.id),r.dis&&(a.disabled=!0),Y(a,r),l!==void 0){if(Array.isArray(l)&&l.length>0&&(Array.isArray(l[0])||R(l[0])||A(l[0])))l.forEach(i=>{let c=y(i,e,t);c&&a.appendChild(c)});else if(R(l)||A(l)){let i=y(l,e,t);i&&a.appendChild(i)}else if(L(l))S(()=>{let i=p(l,s);o==="I"||o==="Ta"?a.value=String(i??""):o==="C"||o==="R"?a.checked=!!i:a.textContent=String(i??"")},e);else if(typeof l=="string"||typeof l=="number"){let i=String(l);t&&(typeof t.item=="object"&&t.item!==null&&(i=i.replace(/\$item\.(\w+)/g,(c,f)=>String(t.item[f]??""))),i=i.replace(/\$item/g,String(t.item)),i=i.replace(/\$index/g,String(t.index))),o==="I"||o==="Ta"?a.value=i:o!=="S"&&(a.textContent=i)}}r.v!==void 0&&L(r.v)&&S(()=>{let i=p(r.v,s);o==="I"||o==="S"||o==="Ta"?a.value=String(i??""):(o==="C"||o==="R")&&(a.checked=!!i)},e),r.ch!==void 0&&(L(r.ch)?S(()=>{let i=p(r.ch,s);a.checked=!!i},e):a.checked=!!r.ch);let d=(i,c)=>{a.addEventListener(i,c),e.cleanups.push(()=>a.removeEventListener(i,c))},h=o==="B"&&(typeof l=="string"||typeof l=="number")?String(l):void 0;if(r.c&&d("click",i=>b(r.c,s,i,t,h)()),r.x){let i=r.x;d("input",c=>{if(typeof i=="function")i();else{let f,m;if(typeof i=="string"){let g=W(i);g?[f,m]=g:(f=i,m="!")}else[f,m]=i;let k=s[f];if(k){let g=c.target,E=o==="C"||o==="R"?g.checked:g.value;D(k,m,E)}}})}return r.f&&d("focus",i=>b(r.f,s,i,t)()),r.bl&&d("blur",i=>b(r.bl,s,i,t)()),r.k&&d("keydown",i=>b(r.k,s,i,t)()),r.ku&&d("keyup",i=>b(r.ku,s,i,t)()),r.kp&&d("keypress",i=>b(r.kp,s,i,t)()),r.e&&d("mouseenter",i=>b(r.e,s,i,t)()),r.lv&&d("mouseleave",i=>b(r.lv,s,i,t)()),r.sub&&d("submit",i=>{i.preventDefault(),b(r.sub,s,i,t)()}),a}function Z(n,e){if(!n)throw new Error("[tooey] render requires a valid container element");if(!e||!e.r)throw new Error("[tooey] render requires a spec with a root node (r)");let t={};e.s&&Object.entries(e.s).forEach(([r,a])=>{t[r]=V(a)});let s={cleanups:[],state:t};n.innerHTML="";let o=y(e.r,s);o&&n.appendChild(o);let l={state:t,el:o,destroy(){s.cleanups.forEach(r=>r()),s.cleanups=[],n.innerHTML=""},update(r){if(r.s&&U(()=>{Object.entries(r.s).forEach(([a,d])=>{t[a]?t[a].set(d):t[a]=V(d)})}),r.r){s.cleanups.forEach(d=>d()),s.cleanups=[],n.innerHTML="";let a=y(r.r,s);a&&n.appendChild(a),l.el=a}},get(r){return t[r]?.()},set(r,a){t[r]&&t[r].set(a)}};return l}function ee(n){return{$:n}}var ne="V",te="H",re="D",ie="G",ae="T",se="B",oe="I",le="Ta",ce="S",fe="C",de="R",ue="Tb",me="Th",ge="Tbd",be="Tr",ye="Td",Ee="Tc",he="Ul",Te="Ol",Se="Li",ke="M",we="L",pe="Sv";return F(ve);})();
2
+ if(typeof module!=="undefined")module.exports=tooey;
3
+ //# sourceMappingURL=tooey.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/tooey.ts"],
4
+ "sourcesContent": ["/**\n * tooey - token-efficient ui library for llms\n *\n * component types:\n * layout: V (vstack), H (hstack), D (div), G (grid)\n * text & buttons: T (text/span), B (button)\n * inputs: I (input), Ta (textarea), S (select), C (checkbox), R (radio)\n * tables: Tb (table), Th (thead), Tbd (tbody), Tr (tr), Td (td), Tc (th)\n * lists: Ul (ul), Ol (ol), Li (li)\n * media & links: M (image), L (link), Sv (svg)\n *\n * props (short keys):\n * spacing: g (gap), p (padding), m (margin), w (width), h (height), mw, mh\n * colors: bg (background), fg (color), o (opacity)\n * borders: r (border-radius), bw (border-width), bc (border-color), bs (border-style)\n * positioning: pos (position), z (z-index), t (top), l (left), rt (right)\n * typography: fs (font-size), fw (font-weight), ff (font-family), ta, td, lh, ls\n * layout: ai (align-items), jc (justify-content), flw (flex-wrap), cols, rows\n * shortcuts: c=center, sb=space-between, fe=flex-end, fs=flex-start, st=stretch\n * misc: cur (cursor), ov (overflow), pe (pointer-events), us (user-select), sh, tr\n * element-specific: v (value), ph (placeholder), type, href, src, alt, dis, ch, ro, opts\n *\n * events: c (click), x (input/change), f (focus), bl (blur), k (keydown), ku, kp, e, lv, sub\n * shorthand: \"state+\" (increment), \"state-\" (decrement), \"state~\" (toggle), \"state!val\" (set)\n *\n * state operations: + (increment), - (decrement), ! (set), ~ (toggle), < (append), > (prepend), X (remove), . (set prop)\n *\n * control flow (short form): {?: cond, t: [...], e: [...]} | {m: state, a: [...]}\n * control flow (long form): {if: state, then: [...], else: [...]} | {map: state, as: [...]}\n * equality check: {?: \"state\", is: 0, t: [...]} or {?: {$:\"state\"}, is: 0, t: [...]}\n */\n\n// ============ types ============\n\ntype StateValue = unknown;\ntype StateStore = Record<string, Signal<StateValue>>;\n\ninterface Signal<T> {\n (): T;\n set(v: T | ((prev: T) => T)): void;\n sub(fn: () => void): () => void;\n}\n\ntype Op = '+' | '-' | '!' | '~' | '<' | '>' | 'X' | '.';\ntype EventHandler = [string, Op, unknown?] | (() => void) | string;\n\ninterface Props {\n // spacing/sizing\n g?: number | string;\n p?: number | string;\n m?: number | string;\n w?: number | string;\n h?: number | string;\n mw?: number | string;\n mh?: number | string;\n // colors\n bg?: string;\n fg?: string;\n o?: number;\n // borders\n r?: number | string;\n bw?: number | string;\n bc?: string;\n bs?: string;\n // positioning\n pos?: 'rel' | 'abs' | 'fix' | 'sticky' | string;\n z?: number;\n t?: number | string;\n l?: number | string;\n b?: number | string;\n rt?: number | string;\n // typography\n fs?: number | string;\n fw?: number | string;\n ff?: string;\n ta?: string;\n td?: string;\n lh?: number | string;\n ls?: number | string;\n // layout\n ai?: string;\n jc?: string;\n flw?: string;\n cols?: number | string;\n rows?: number | string;\n // misc\n cur?: string;\n ov?: string;\n pe?: string;\n us?: string;\n sh?: string;\n tr?: string;\n // element-specific\n v?: unknown;\n ph?: string;\n type?: string;\n href?: string;\n src?: string;\n alt?: string;\n dis?: boolean;\n ch?: unknown;\n ro?: boolean;\n opts?: Array<{v: string, l: string}>;\n cls?: string;\n id?: string;\n rw?: number;\n sp?: number;\n rsp?: number;\n // events\n c?: EventHandler;\n x?: EventHandler;\n f?: EventHandler;\n bl?: EventHandler;\n k?: EventHandler;\n ku?: EventHandler;\n kp?: EventHandler;\n e?: EventHandler;\n lv?: EventHandler;\n sub?: EventHandler;\n // custom styles\n s?: Record<string, unknown>;\n}\n\ntype ComponentType =\n | 'V' | 'H' | 'D' | 'G'\n | 'T' | 'B'\n | 'I' | 'Ta' | 'S' | 'C' | 'R'\n | 'Tb' | 'Th' | 'Tbd' | 'Tr' | 'Td' | 'Tc'\n | 'Ul' | 'Ol' | 'Li'\n | 'M' | 'L' | 'Sv';\n\ntype StateRef = { $: string };\n\ninterface IfNode {\n // Long form\n if?: StateRef | string;\n then?: NodeSpec | NodeSpec[];\n else?: NodeSpec | NodeSpec[];\n // Short form\n '?'?: StateRef | string;\n t?: NodeSpec | NodeSpec[];\n e?: NodeSpec | NodeSpec[];\n // Equality check (works with both forms)\n eq?: unknown;\n is?: unknown;\n}\n\ninterface MapNode {\n // Long form\n map?: StateRef | string;\n as?: NodeSpec;\n // Short form\n m?: StateRef | string;\n a?: NodeSpec;\n key?: string;\n}\n\ntype Content = string | number | StateRef | NodeSpec[] | IfNode | MapNode;\ntype NodeSpec = [ComponentType, Content?, Props?] | IfNode | MapNode;\n\ninterface TooeySpec {\n s?: Record<string, StateValue>;\n r: NodeSpec;\n}\n\n// ============ render context for cleanup ============\n\ninterface ErrorInfo {\n message: string;\n componentType?: string;\n stack?: string;\n}\n\ntype ErrorHandler = (error: ErrorInfo) => void;\n\ninterface RenderContext {\n cleanups: Array<() => void>;\n state: StateStore;\n onError?: ErrorHandler;\n}\n\n// ============ error boundary ============\n\ninterface ErrorBoundaryNode {\n boundary: true;\n child: NodeSpec;\n fallback?: NodeSpec;\n onError?: ErrorHandler;\n}\n\nfunction isErrorBoundaryNode(v: unknown): v is ErrorBoundaryNode {\n return typeof v === 'object' && v !== null && 'boundary' in v && (v as ErrorBoundaryNode).boundary === true;\n}\n\nfunction createErrorFallback(error: ErrorInfo): HTMLElement {\n const el = document.createElement('div');\n el.style.cssText = 'padding:12px;background:#fee;border:1px solid #fcc;border-radius:4px;color:#c00;font-family:monospace;font-size:12px';\n el.textContent = `[tooey error] ${error.message}`;\n return el;\n}\n\n// ============ signals ============\n\nlet currentEffect: (() => void) | null = null;\nlet batchDepth = 0;\nlet pendingEffects = new Set<() => void>();\n\nfunction signal<T>(initial: T): Signal<T> {\n let value = initial;\n const subscribers = new Set<() => void>();\n\n const sig = (() => {\n if (currentEffect) {\n subscribers.add(currentEffect);\n }\n return value;\n }) as Signal<T>;\n\n sig.set = (v: T | ((prev: T) => T)) => {\n const newValue = typeof v === 'function' ? (v as (prev: T) => T)(value) : v;\n if (newValue !== value) {\n value = newValue;\n if (batchDepth > 0) {\n subscribers.forEach(fn => pendingEffects.add(fn));\n } else {\n subscribers.forEach(fn => fn());\n }\n }\n };\n\n sig.sub = (fn: () => void) => {\n subscribers.add(fn);\n return () => subscribers.delete(fn);\n };\n\n return sig;\n}\n\nfunction batch(fn: () => void): void {\n batchDepth++;\n try {\n fn();\n } finally {\n batchDepth--;\n if (batchDepth === 0) {\n const effects = pendingEffects;\n pendingEffects = new Set();\n effects.forEach(fn => fn());\n }\n }\n}\n\nfunction effect(fn: () => void, ctx?: RenderContext): () => void {\n let isActive = true;\n\n const execute = () => {\n if (!isActive) return;\n currentEffect = execute;\n try {\n fn();\n } finally {\n currentEffect = null;\n }\n };\n\n execute();\n\n const cleanup = () => {\n isActive = false;\n currentEffect = null;\n };\n\n if (ctx) {\n ctx.cleanups.push(cleanup);\n }\n\n return cleanup;\n}\n\n// ============ state operations ============\n\nfunction applyOp(state: Signal<StateValue>, op: Op, val?: unknown): void {\n try {\n switch (op) {\n case '+':\n state.set((v: StateValue) => (v as number) + (typeof val === 'number' ? val : 1));\n break;\n case '-':\n state.set((v: StateValue) => (v as number) - (typeof val === 'number' ? val : 1));\n break;\n case '!':\n state.set(val);\n break;\n case '~':\n state.set((v: StateValue) => !v);\n break;\n case '<':\n state.set((v: StateValue) => [...(v as unknown[]), val]);\n break;\n case '>':\n state.set((v: StateValue) => [val, ...(v as unknown[])]);\n break;\n case 'X':\n state.set((v: StateValue) => {\n const arr = v as unknown[];\n if (typeof val === 'number') {\n return arr.filter((_, i) => i !== val);\n } else if (typeof val === 'function') {\n return arr.filter((item, i) => !(val as (item: unknown, i: number) => boolean)(item, i));\n }\n return arr.filter(item => item !== val);\n });\n break;\n case '.':\n if (Array.isArray(val) && val.length === 2) {\n state.set((v: StateValue) => ({ ...(v as Record<string, unknown>), [val[0]]: val[1] }));\n }\n break;\n }\n } catch (err) {\n console.warn('[tooey] state operation error:', op, err);\n }\n}\n\n// ============ helpers ============\n\nfunction isStateRef(v: unknown): v is StateRef {\n return typeof v === 'object' && v !== null && !Array.isArray(v) && '$' in v;\n}\n\nfunction isIfNode(v: unknown): v is IfNode {\n return typeof v === 'object' && v !== null && !Array.isArray(v) && ('if' in v || '?' in v);\n}\n\nfunction isMapNode(v: unknown): v is MapNode {\n return typeof v === 'object' && v !== null && !Array.isArray(v) && ('map' in v || 'm' in v);\n}\n\nfunction resolveValue(content: unknown, state: StateStore): unknown {\n if (isStateRef(content)) {\n const sig = state[content.$];\n if (!sig) {\n console.warn(`[tooey] unknown state key: \"${content.$}\"`);\n return undefined;\n }\n return sig();\n }\n return content;\n}\n\nfunction resolveCssValue(val: number | string | undefined): string | undefined {\n if (val === undefined) return undefined;\n if (typeof val === 'number') return `${val}px`;\n return val;\n}\n\n// Style value shortcuts for common layout values\nconst styleShortcuts: Record<string, string> = {\n 'c': 'center',\n 'sb': 'space-between',\n 'sa': 'space-around',\n 'se': 'space-evenly',\n 'fe': 'flex-end',\n 'fs': 'flex-start',\n 'st': 'stretch',\n 'bl': 'baseline',\n};\n\nfunction expandStyleValue(val: string | undefined): string | undefined {\n if (val === undefined) return undefined;\n return styleShortcuts[val] || val;\n}\n\n// Parse string-based event handler shorthand\n// Format: \"stateName+\" | \"stateName-\" | \"stateName~\" | \"stateName!value\"\nfunction parseEventShorthand(str: string): [string, Op, unknown?] | null {\n if (typeof str !== 'string' || str.length < 2) return null;\n\n const lastChar = str[str.length - 1];\n\n // Simple ops: +, -, ~\n if (lastChar === '+' || lastChar === '-' || lastChar === '~') {\n return [str.slice(0, -1), lastChar as Op];\n }\n\n // Set operation with value: \"state!value\"\n const bangIdx = str.indexOf('!');\n if (bangIdx > 0) {\n const stateKey = str.slice(0, bangIdx);\n const valStr = str.slice(bangIdx + 1);\n // Try to parse as number or boolean\n let val: unknown = valStr;\n if (valStr === 'true') val = true;\n else if (valStr === 'false') val = false;\n else if (!isNaN(Number(valStr)) && valStr !== '') val = Number(valStr);\n return [stateKey, '!', val];\n }\n\n return null;\n}\n\n// XSS protection - escape HTML entities (kept for future use, textContent provides protection now)\nfunction _escapeHtml(str: string): string {\n const div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n}\n\n// URL validation - prevent dangerous protocols\nconst SAFE_URL_PROTOCOLS = ['http:', 'https:', 'mailto:', 'tel:', 'ftp:'];\n\nfunction isValidUrl(url: string): boolean {\n if (!url || typeof url !== 'string') return false;\n\n // Allow relative URLs and anchors\n if (url.startsWith('/') || url.startsWith('#') || url.startsWith('.')) {\n return true;\n }\n\n try {\n const parsed = new URL(url, window.location.href);\n return SAFE_URL_PROTOCOLS.includes(parsed.protocol);\n } catch {\n // If URL parsing fails, check for dangerous patterns directly\n const lowerUrl = url.toLowerCase().trim();\n const dangerousPatterns = ['javascript:', 'data:', 'vbscript:'];\n return !dangerousPatterns.some(pattern => lowerUrl.startsWith(pattern));\n }\n}\n\nfunction sanitizeUrl(url: string, propName: string): string | null {\n if (!url) return null;\n\n if (!isValidUrl(url)) {\n console.warn(`[tooey] blocked unsafe URL in ${propName}: \"${url.slice(0, 50)}...\"`);\n return null;\n }\n\n return url;\n}\n\nfunction createHandler(\n handler: EventHandler,\n state: StateStore,\n event?: Event,\n itemContext?: { item: unknown, index: number },\n buttonText?: string\n): () => void {\n if (typeof handler === 'function') {\n return handler;\n }\n\n // Handle string shorthand: \"state+\", \"state-\", \"state~\", \"state!value\"\n let normalizedHandler: [string, Op, unknown?];\n if (typeof handler === 'string') {\n const parsed = parseEventShorthand(handler);\n if (parsed) {\n normalizedHandler = parsed;\n } else {\n // Plain state key - infer operation from button text if available\n if (buttonText === '+') {\n normalizedHandler = [handler, '+'];\n } else if (buttonText === '-') {\n normalizedHandler = [handler, '-'];\n } else {\n // Default to toggle for plain state key\n normalizedHandler = [handler, '~'];\n }\n }\n } else {\n normalizedHandler = handler;\n }\n\n const [stateKey, op, val] = normalizedHandler;\n return () => {\n const sig = state[stateKey];\n if (!sig) {\n console.warn(`[tooey] click handler: unknown state key \"${stateKey}\"`);\n return;\n }\n let actualVal = val;\n // Resolve $item and $index in event handler values\n if (itemContext && typeof actualVal === 'string') {\n if (actualVal === '$index') {\n actualVal = itemContext.index;\n } else if (actualVal === '$item') {\n actualVal = itemContext.item;\n } else if (actualVal.startsWith('$item.')) {\n const key = actualVal.substring(6);\n actualVal = (itemContext.item as Record<string, unknown>)?.[key];\n }\n }\n if (op === '!' && val === undefined && event) {\n const target = event.target as HTMLInputElement;\n actualVal = target.type === 'checkbox' ? target.checked : target.value;\n }\n applyOp(sig, op, actualVal);\n };\n}\n\n// ============ styles ============\n\nfunction applyStyles(el: HTMLElement, props: Props): void {\n const style = el.style;\n\n if (props.g !== undefined) style.gap = resolveCssValue(props.g)!;\n if (props.p !== undefined) style.padding = resolveCssValue(props.p)!;\n if (props.m !== undefined) style.margin = resolveCssValue(props.m)!;\n if (props.w !== undefined) style.width = resolveCssValue(props.w)!;\n if (props.h !== undefined) style.height = resolveCssValue(props.h)!;\n if (props.mw !== undefined) style.maxWidth = resolveCssValue(props.mw)!;\n if (props.mh !== undefined) style.maxHeight = resolveCssValue(props.mh)!;\n\n if (props.bg !== undefined) style.background = props.bg;\n if (props.fg !== undefined) style.color = props.fg;\n if (props.o !== undefined) style.opacity = String(props.o);\n\n if (props.r !== undefined) style.borderRadius = resolveCssValue(props.r)!;\n if (props.bw !== undefined) style.borderWidth = resolveCssValue(props.bw)!;\n if (props.bc !== undefined) style.borderColor = props.bc;\n if (props.bs !== undefined) style.borderStyle = props.bs;\n\n if (props.pos !== undefined) {\n const posMap: Record<string, string> = {\n rel: 'relative', abs: 'absolute', fix: 'fixed', sticky: 'sticky'\n };\n style.position = posMap[props.pos] || props.pos;\n }\n if (props.z !== undefined) style.zIndex = String(props.z);\n if (props.t !== undefined) style.top = resolveCssValue(props.t)!;\n if (props.l !== undefined) style.left = resolveCssValue(props.l)!;\n if (typeof props.b === 'number' || (typeof props.b === 'string' && !Array.isArray(props.b))) {\n style.bottom = resolveCssValue(props.b as number | string)!;\n }\n if (props.rt !== undefined) style.right = resolveCssValue(props.rt)!;\n\n if (props.fs !== undefined) style.fontSize = resolveCssValue(props.fs)!;\n if (props.fw !== undefined) style.fontWeight = String(props.fw);\n if (props.ff !== undefined) style.fontFamily = props.ff;\n if (props.ta !== undefined) style.textAlign = props.ta;\n if (props.td !== undefined) style.textDecoration = props.td;\n if (props.lh !== undefined) style.lineHeight = typeof props.lh === 'number' ? String(props.lh) : props.lh;\n if (props.ls !== undefined) style.letterSpacing = resolveCssValue(props.ls)!;\n\n if (props.ai !== undefined) style.alignItems = expandStyleValue(props.ai)!;\n if (props.jc !== undefined) style.justifyContent = expandStyleValue(props.jc)!;\n if (props.flw !== undefined) style.flexWrap = expandStyleValue(props.flw)!\n\n if (props.cur !== undefined) style.cursor = props.cur;\n if (props.ov !== undefined) style.overflow = props.ov;\n if (props.pe !== undefined) style.pointerEvents = props.pe;\n if (props.us !== undefined) style.userSelect = props.us;\n if (props.sh !== undefined) style.boxShadow = props.sh;\n if (props.tr !== undefined) style.transform = props.tr;\n\n if (props.s) {\n Object.entries(props.s).forEach(([key, val]) => {\n (style as unknown as Record<string, string>)[key] = String(val);\n });\n }\n}\n\n// ============ renderer ============\n\nfunction createElement(\n spec: NodeSpec,\n ctx: RenderContext,\n itemContext?: { item: unknown, index: number }\n): HTMLElement | null {\n const { state } = ctx;\n\n // handle error boundary node\n if (isErrorBoundaryNode(spec)) {\n const placeholder = document.createElement('div');\n placeholder.style.display = 'contents';\n\n try {\n const childCtx: RenderContext = {\n cleanups: [],\n state,\n onError: spec.onError || ctx.onError,\n };\n const child = createElement(spec.child, childCtx, itemContext);\n if (child) {\n placeholder.appendChild(child);\n ctx.cleanups.push(...childCtx.cleanups);\n }\n } catch (err) {\n const errorInfo: ErrorInfo = {\n message: err instanceof Error ? err.message : String(err),\n stack: err instanceof Error ? err.stack : undefined,\n };\n\n if (spec.onError) {\n spec.onError(errorInfo);\n }\n\n if (spec.fallback) {\n try {\n const fallbackEl = createElement(spec.fallback, ctx, itemContext);\n if (fallbackEl) placeholder.appendChild(fallbackEl);\n } catch {\n placeholder.appendChild(createErrorFallback(errorInfo));\n }\n } else {\n placeholder.appendChild(createErrorFallback(errorInfo));\n }\n }\n\n return placeholder;\n }\n\n // handle reactive if node (supports both long and short form)\n if (isIfNode(spec)) {\n const placeholder = document.createElement('div');\n placeholder.style.display = 'contents';\n\n let currentEl: HTMLElement | null = null;\n let childCtx: RenderContext | null = null;\n\n // Normalize short form to long form\n const ifCond = spec.if ?? spec['?'];\n const thenBranch = spec.then ?? spec.t;\n const elseBranch = spec.else ?? spec.e;\n const eqValue = spec.eq ?? spec.is;\n\n const updateIf = () => {\n // cleanup previous render\n if (childCtx) {\n childCtx.cleanups.forEach(fn => fn());\n childCtx.cleanups = [];\n }\n if (currentEl) {\n placeholder.innerHTML = '';\n currentEl = null;\n }\n\n const rawValue = typeof ifCond === 'string'\n ? state[ifCond]?.()\n : resolveValue(ifCond, state);\n\n // Handle equality check (eq or is)\n let condition: boolean;\n if (eqValue !== undefined) {\n condition = rawValue === eqValue;\n } else {\n condition = Boolean(rawValue);\n }\n\n const branch = condition ? thenBranch : elseBranch;\n if (!branch) return;\n\n childCtx = { cleanups: [], state };\n\n if (Array.isArray(branch) && branch.length > 0 && Array.isArray(branch[0])) {\n (branch as NodeSpec[]).forEach(child => {\n const el = createElement(child, childCtx!, itemContext);\n if (el) placeholder.appendChild(el);\n });\n } else {\n currentEl = createElement(branch as NodeSpec, childCtx, itemContext);\n if (currentEl) placeholder.appendChild(currentEl);\n }\n };\n\n effect(updateIf, ctx);\n return placeholder;\n }\n\n // handle reactive map node (supports both long and short form)\n if (isMapNode(spec)) {\n const placeholder = document.createElement('div');\n placeholder.style.display = 'contents';\n\n let childCtx: RenderContext | null = null;\n\n // Normalize short form to long form\n const mapSource = spec.map ?? spec.m;\n const asTemplate = spec.as ?? spec.a;\n\n const updateMap = () => {\n // cleanup previous render\n if (childCtx) {\n childCtx.cleanups.forEach(fn => fn());\n childCtx.cleanups = [];\n }\n placeholder.innerHTML = '';\n\n const arr = (typeof mapSource === 'string'\n ? state[mapSource]?.()\n : resolveValue(mapSource, state)) as unknown[];\n\n if (!Array.isArray(arr) || !asTemplate) return;\n\n childCtx = { cleanups: [], state };\n\n arr.forEach((item, index) => {\n const el = createElement(asTemplate, childCtx!, { item, index });\n if (el) placeholder.appendChild(el);\n });\n };\n\n effect(updateMap, ctx);\n return placeholder;\n }\n\n // validate spec structure\n if (!Array.isArray(spec) || (spec as unknown[]).length === 0) {\n console.warn('[tooey] invalid node spec:', spec);\n return null;\n }\n\n const [type, content, props = {}] = spec as [ComponentType, Content?, Props?];\n let el: HTMLElement;\n\n switch (type) {\n case 'V':\n el = document.createElement('div');\n el.style.display = 'flex';\n el.style.flexDirection = 'column';\n break;\n case 'H':\n el = document.createElement('div');\n el.style.display = 'flex';\n el.style.flexDirection = 'row';\n break;\n case 'G':\n el = document.createElement('div');\n el.style.display = 'grid';\n if (props.cols) {\n el.style.gridTemplateColumns = typeof props.cols === 'number'\n ? `repeat(${props.cols}, 1fr)` : props.cols;\n }\n if (props.rows) {\n el.style.gridTemplateRows = typeof props.rows === 'number'\n ? `repeat(${props.rows}, 1fr)` : props.rows;\n }\n break;\n case 'D':\n el = document.createElement('div');\n break;\n case 'T':\n el = document.createElement('span');\n break;\n case 'B':\n el = document.createElement('button');\n break;\n case 'I':\n el = document.createElement('input');\n (el as HTMLInputElement).type = props.type || 'text';\n if (props.ph) (el as HTMLInputElement).placeholder = props.ph;\n if (props.ro) (el as HTMLInputElement).readOnly = true;\n break;\n case 'Ta':\n el = document.createElement('textarea');\n if (props.ph) (el as HTMLTextAreaElement).placeholder = props.ph;\n if (props.rw) (el as HTMLTextAreaElement).rows = props.rw;\n if (props.ro) (el as HTMLTextAreaElement).readOnly = true;\n break;\n case 'S':\n el = document.createElement('select');\n if (props.opts) {\n props.opts.forEach(opt => {\n const option = document.createElement('option');\n option.value = opt.v;\n option.textContent = opt.l;\n el.appendChild(option);\n });\n }\n break;\n case 'C':\n el = document.createElement('input');\n (el as HTMLInputElement).type = 'checkbox';\n break;\n case 'R':\n el = document.createElement('input');\n (el as HTMLInputElement).type = 'radio';\n break;\n case 'Tb':\n el = document.createElement('table');\n break;\n case 'Th':\n el = document.createElement('thead');\n break;\n case 'Tbd':\n el = document.createElement('tbody');\n break;\n case 'Tr':\n el = document.createElement('tr');\n break;\n case 'Td':\n el = document.createElement('td');\n if (props.sp) (el as HTMLTableCellElement).colSpan = props.sp;\n if (props.rsp) (el as HTMLTableCellElement).rowSpan = props.rsp;\n break;\n case 'Tc':\n el = document.createElement('th');\n if (props.sp) (el as HTMLTableCellElement).colSpan = props.sp;\n if (props.rsp) (el as HTMLTableCellElement).rowSpan = props.rsp;\n break;\n case 'Ul':\n el = document.createElement('ul');\n break;\n case 'Ol':\n el = document.createElement('ol');\n break;\n case 'Li':\n el = document.createElement('li');\n break;\n case 'M':\n el = document.createElement('img');\n if (props.src) {\n const safeSrc = sanitizeUrl(props.src, 'src');\n if (safeSrc) (el as HTMLImageElement).src = safeSrc;\n }\n if (props.alt) (el as HTMLImageElement).alt = props.alt;\n break;\n case 'L':\n el = document.createElement('a');\n if (props.href) {\n const safeHref = sanitizeUrl(props.href, 'href');\n if (safeHref) (el as HTMLAnchorElement).href = safeHref;\n }\n break;\n case 'Sv':\n el = document.createElementNS('http://www.w3.org/2000/svg', 'svg') as unknown as HTMLElement;\n break;\n default:\n console.warn(`[tooey] unknown component type: ${type}`);\n el = document.createElement('div');\n }\n\n if (props.cls) el.className = props.cls;\n if (props.id) el.id = props.id;\n if (props.dis) (el as HTMLButtonElement).disabled = true;\n\n applyStyles(el, props);\n\n // handle content\n if (content !== undefined) {\n if (Array.isArray(content) && content.length > 0 && (Array.isArray(content[0]) || isIfNode(content[0]) || isMapNode(content[0]))) {\n (content as NodeSpec[]).forEach(childSpec => {\n const child = createElement(childSpec, ctx, itemContext);\n if (child) el.appendChild(child);\n });\n } else if (isIfNode(content) || isMapNode(content)) {\n const child = createElement(content as NodeSpec, ctx, itemContext);\n if (child) el.appendChild(child);\n } else if (isStateRef(content)) {\n effect(() => {\n const val = resolveValue(content, state);\n if (type === 'I') {\n (el as HTMLInputElement).value = String(val ?? '');\n } else if (type === 'Ta') {\n (el as HTMLTextAreaElement).value = String(val ?? '');\n } else if (type === 'C' || type === 'R') {\n (el as HTMLInputElement).checked = Boolean(val);\n } else {\n // textContent provides XSS protection by not parsing HTML\n el.textContent = String(val ?? '');\n }\n }, ctx);\n } else if (typeof content === 'string' || typeof content === 'number') {\n let textContent = String(content);\n if (itemContext) {\n // handle object property access first (before $item replacement)\n if (typeof itemContext.item === 'object' && itemContext.item !== null) {\n textContent = textContent.replace(/\\$item\\.(\\w+)/g, (_, key) => {\n return String((itemContext.item as Record<string, unknown>)[key] ?? '');\n });\n }\n textContent = textContent.replace(/\\$item/g, String(itemContext.item));\n textContent = textContent.replace(/\\$index/g, String(itemContext.index));\n }\n // XSS protection for static content\n if (type === 'I') {\n (el as HTMLInputElement).value = textContent;\n } else if (type === 'Ta') {\n (el as HTMLTextAreaElement).value = textContent;\n } else if (type !== 'S') {\n // don't set textContent for select (would clear options)\n el.textContent = textContent;\n }\n }\n }\n\n // handle value binding for inputs\n if (props.v !== undefined && isStateRef(props.v)) {\n effect(() => {\n const val = resolveValue(props.v!, state);\n if (type === 'I' || type === 'S' || type === 'Ta') {\n (el as HTMLInputElement).value = String(val ?? '');\n } else if (type === 'C' || type === 'R') {\n (el as HTMLInputElement).checked = Boolean(val);\n }\n }, ctx);\n }\n\n // handle checked binding\n if (props.ch !== undefined) {\n if (isStateRef(props.ch as unknown)) {\n effect(() => {\n const val = resolveValue(props.ch as unknown as StateRef, state);\n (el as HTMLInputElement).checked = Boolean(val);\n }, ctx);\n } else {\n (el as HTMLInputElement).checked = Boolean(props.ch);\n }\n }\n\n // event handlers with cleanup tracking\n const addEventListener = (event: string, handler: (e: Event) => void) => {\n el.addEventListener(event, handler);\n ctx.cleanups.push(() => el.removeEventListener(event, handler));\n };\n\n // Get button text for implicit operation inference\n const buttonText = type === 'B' && (typeof content === 'string' || typeof content === 'number')\n ? String(content)\n : undefined;\n\n if (props.c) {\n addEventListener('click', (e) => createHandler(props.c!, state, e, itemContext, buttonText)());\n }\n if (props.x) {\n const handler = props.x;\n addEventListener('input', (e) => {\n if (typeof handler === 'function') {\n handler();\n } else {\n // Normalize string shorthand to array form\n let stateKey: string;\n let op: Op;\n if (typeof handler === 'string') {\n const parsed = parseEventShorthand(handler);\n if (parsed) {\n [stateKey, op] = parsed;\n } else {\n // Plain state key defaults to set (!) for input\n stateKey = handler;\n op = '!';\n }\n } else {\n [stateKey, op] = handler;\n }\n const sig = state[stateKey];\n if (sig) {\n const target = e.target as HTMLInputElement;\n const val = (type === 'C' || type === 'R') ? target.checked : target.value;\n applyOp(sig, op, val);\n }\n }\n });\n }\n if (props.f) {\n addEventListener('focus', (e) => createHandler(props.f!, state, e, itemContext)());\n }\n if (props.bl) {\n addEventListener('blur', (e) => createHandler(props.bl!, state, e, itemContext)());\n }\n if (props.k) {\n addEventListener('keydown', (e) => createHandler(props.k!, state, e, itemContext)());\n }\n if (props.ku) {\n addEventListener('keyup', (e) => createHandler(props.ku!, state, e, itemContext)());\n }\n if (props.kp) {\n addEventListener('keypress', (e) => createHandler(props.kp!, state, e, itemContext)());\n }\n if (props.e) {\n addEventListener('mouseenter', (e) => createHandler(props.e!, state, e, itemContext)());\n }\n if (props.lv) {\n addEventListener('mouseleave', (e) => createHandler(props.lv!, state, e, itemContext)());\n }\n if (props.sub) {\n addEventListener('submit', (e) => {\n e.preventDefault();\n createHandler(props.sub!, state, e, itemContext)();\n });\n }\n\n return el;\n}\n\n// ============ main api ============\n\ninterface TooeyInstance {\n state: StateStore;\n el: HTMLElement | null;\n destroy(): void;\n update(newSpec: TooeySpec): void;\n get(key: string): unknown;\n set(key: string, value: unknown): void;\n}\n\nfunction render(container: HTMLElement, spec: TooeySpec): TooeyInstance {\n if (!container) {\n throw new Error('[tooey] render requires a valid container element');\n }\n if (!spec || !spec.r) {\n throw new Error('[tooey] render requires a spec with a root node (r)');\n }\n\n const state: StateStore = {};\n if (spec.s) {\n Object.entries(spec.s).forEach(([key, val]) => {\n state[key] = signal(val);\n });\n }\n\n const ctx: RenderContext = { cleanups: [], state };\n\n container.innerHTML = '';\n const el = createElement(spec.r, ctx);\n if (el) container.appendChild(el);\n\n const instance: TooeyInstance = {\n state,\n el,\n destroy() {\n ctx.cleanups.forEach(fn => fn());\n ctx.cleanups = [];\n container.innerHTML = '';\n },\n update(newSpec: TooeySpec) {\n if (newSpec.s) {\n batch(() => {\n Object.entries(newSpec.s!).forEach(([key, val]) => {\n if (state[key]) {\n state[key].set(val);\n } else {\n state[key] = signal(val);\n }\n });\n });\n }\n if (newSpec.r) {\n ctx.cleanups.forEach(fn => fn());\n ctx.cleanups = [];\n container.innerHTML = '';\n const newEl = createElement(newSpec.r, ctx);\n if (newEl) container.appendChild(newEl);\n instance.el = newEl;\n }\n },\n get(key: string) {\n return state[key]?.();\n },\n set(key: string, value: unknown) {\n if (state[key]) {\n state[key].set(value);\n }\n }\n };\n\n return instance;\n}\n\n// ============ convenience helpers ============\n\nfunction $(name: string): StateRef {\n return { $: name };\n}\n\n// component type constants\nconst V = 'V' as const;\nconst H = 'H' as const;\nconst D = 'D' as const;\nconst G = 'G' as const;\nconst T = 'T' as const;\nconst B = 'B' as const;\nconst I = 'I' as const;\nconst Ta = 'Ta' as const;\nconst S = 'S' as const;\nconst C = 'C' as const;\nconst R = 'R' as const;\nconst Tb = 'Tb' as const;\nconst Th = 'Th' as const;\nconst Tbd = 'Tbd' as const;\nconst Tr = 'Tr' as const;\nconst Td = 'Td' as const;\nconst Tc = 'Tc' as const;\nconst Ul = 'Ul' as const;\nconst Ol = 'Ol' as const;\nconst Li = 'Li' as const;\nconst M = 'M' as const;\nconst L = 'L' as const;\nconst Sv = 'Sv' as const;\n\nexport {\n render,\n signal,\n effect,\n batch,\n $,\n V, H, D, G,\n T, B,\n I, Ta, S, C, R,\n Tb, Th, Tbd, Tr, Td, Tc,\n Ul, Ol, Li,\n M, L, Sv,\n TooeySpec,\n NodeSpec,\n Props,\n StateRef,\n TooeyInstance,\n IfNode,\n MapNode,\n ErrorBoundaryNode,\n ErrorInfo,\n ErrorHandler\n};\n"],
5
+ "mappings": "ybAAA,IAAAA,GAAA,GAAAC,EAAAD,GAAA,OAAAE,GAAA,MAAAC,GAAA,MAAAC,GAAA,MAAAC,GAAA,MAAAC,GAAA,MAAAC,GAAA,MAAAC,GAAA,MAAAC,GAAA,OAAAC,GAAA,MAAAC,GAAA,OAAAC,GAAA,MAAAC,GAAA,MAAAC,GAAA,OAAAC,GAAA,MAAAC,GAAA,OAAAC,GAAA,OAAAC,GAAA,QAAAC,GAAA,OAAAC,GAAA,OAAAC,GAAA,OAAAC,GAAA,OAAAC,GAAA,OAAAC,GAAA,MAAAC,GAAA,UAAAC,EAAA,WAAAC,EAAA,WAAAC,EAAA,WAAAC,IA8LA,SAASC,EAAoBC,EAAoC,CAC/D,OAAO,OAAOA,GAAM,UAAYA,IAAM,MAAQ,aAAcA,GAAMA,EAAwB,WAAa,EACzG,CAEA,SAASC,EAAoBC,EAA+B,CAC1D,IAAMC,EAAK,SAAS,cAAc,KAAK,EACvC,OAAAA,EAAG,MAAM,QAAU,uHACnBA,EAAG,YAAc,iBAAiBD,EAAM,OAAO,GACxCC,CACT,CAIA,IAAIC,EAAqC,KACrCC,EAAa,EACbC,EAAiB,IAAI,IAEzB,SAASR,EAAUS,EAAuB,CACxC,IAAIC,EAAQD,EACNE,EAAc,IAAI,IAElBC,EAAO,KACPN,GACFK,EAAY,IAAIL,CAAa,EAExBI,GAGT,OAAAE,EAAI,IAAOV,GAA4B,CACrC,IAAMW,EAAW,OAAOX,GAAM,WAAcA,EAAqBQ,CAAK,EAAIR,EACtEW,IAAaH,IACfA,EAAQG,EACJN,EAAa,EACfI,EAAY,QAAQG,GAAMN,EAAe,IAAIM,CAAE,CAAC,EAEhDH,EAAY,QAAQG,GAAMA,EAAG,CAAC,EAGpC,EAEAF,EAAI,IAAOE,IACTH,EAAY,IAAIG,CAAE,EACX,IAAMH,EAAY,OAAOG,CAAE,GAG7BF,CACT,CAEA,SAASf,EAAMiB,EAAsB,CACnCP,IACA,GAAI,CACFO,EAAG,CACL,QAAE,CAEA,GADAP,IACIA,IAAe,EAAG,CACpB,IAAMQ,EAAUP,EAChBA,EAAiB,IAAI,IACrBO,EAAQ,QAAQD,GAAMA,EAAG,CAAC,CAC5B,CACF,CACF,CAEA,SAAShB,EAAOgB,EAAgBE,EAAiC,CAC/D,IAAIC,EAAW,GAETC,EAAU,IAAM,CACpB,GAAKD,EACL,CAAAX,EAAgBY,EAChB,GAAI,CACFJ,EAAG,CACL,QAAE,CACAR,EAAgB,IAClB,EACF,EAEAY,EAAQ,EAER,IAAMC,EAAU,IAAM,CACpBF,EAAW,GACXX,EAAgB,IAClB,EAEA,OAAIU,GACFA,EAAI,SAAS,KAAKG,CAAO,EAGpBA,CACT,CAIA,SAASC,EAAQC,EAA2BC,EAAQC,EAAqB,CACvE,GAAI,CACF,OAAQD,EAAI,CACV,IAAK,IACHD,EAAM,IAAKnB,GAAmBA,GAAgB,OAAOqB,GAAQ,SAAWA,EAAM,EAAE,EAChF,MACF,IAAK,IACHF,EAAM,IAAKnB,GAAmBA,GAAgB,OAAOqB,GAAQ,SAAWA,EAAM,EAAE,EAChF,MACF,IAAK,IACHF,EAAM,IAAIE,CAAG,EACb,MACF,IAAK,IACHF,EAAM,IAAKnB,GAAkB,CAACA,CAAC,EAC/B,MACF,IAAK,IACHmB,EAAM,IAAKnB,GAAkB,CAAC,GAAIA,EAAiBqB,CAAG,CAAC,EACvD,MACF,IAAK,IACHF,EAAM,IAAKnB,GAAkB,CAACqB,EAAK,GAAIrB,CAAe,CAAC,EACvD,MACF,IAAK,IACHmB,EAAM,IAAKnB,GAAkB,CAC3B,IAAMsB,EAAMtB,EACZ,OAAI,OAAOqB,GAAQ,SACVC,EAAI,OAAO,CAACC,EAAGC,IAAMA,IAAMH,CAAG,EAC5B,OAAOA,GAAQ,WACjBC,EAAI,OAAO,CAACG,EAAMD,IAAM,CAAEH,EAA8CI,EAAMD,CAAC,CAAC,EAElFF,EAAI,OAAOG,GAAQA,IAASJ,CAAG,CACxC,CAAC,EACD,MACF,IAAK,IACC,MAAM,QAAQA,CAAG,GAAKA,EAAI,SAAW,GACvCF,EAAM,IAAKnB,IAAmB,CAAE,GAAIA,EAA+B,CAACqB,EAAI,CAAC,CAAC,EAAGA,EAAI,CAAC,CAAE,EAAE,EAExF,KACJ,CACF,OAASK,EAAK,CACZ,QAAQ,KAAK,iCAAkCN,EAAIM,CAAG,CACxD,CACF,CAIA,SAASC,EAAW3B,EAA2B,CAC7C,OAAO,OAAOA,GAAM,UAAYA,IAAM,MAAQ,CAAC,MAAM,QAAQA,CAAC,GAAK,MAAOA,CAC5E,CAEA,SAAS4B,EAAS5B,EAAyB,CACzC,OAAO,OAAOA,GAAM,UAAYA,IAAM,MAAQ,CAAC,MAAM,QAAQA,CAAC,IAAM,OAAQA,GAAK,MAAOA,EAC1F,CAEA,SAAS6B,EAAU7B,EAA0B,CAC3C,OAAO,OAAOA,GAAM,UAAYA,IAAM,MAAQ,CAAC,MAAM,QAAQA,CAAC,IAAM,QAASA,GAAK,MAAOA,EAC3F,CAEA,SAAS8B,EAAaC,EAAkBZ,EAA4B,CAClE,GAAIQ,EAAWI,CAAO,EAAG,CACvB,IAAMrB,EAAMS,EAAMY,EAAQ,CAAC,EAC3B,GAAI,CAACrB,EAAK,CACR,QAAQ,KAAK,+BAA+BqB,EAAQ,CAAC,GAAG,EACxD,MACF,CACA,OAAOrB,EAAI,CACb,CACA,OAAOqB,CACT,CAEA,SAASC,EAAgBX,EAAsD,CAC7E,GAAIA,IAAQ,OACZ,OAAI,OAAOA,GAAQ,SAAiB,GAAGA,CAAG,KACnCA,CACT,CAGA,IAAMY,EAAyC,CAC7C,EAAK,SACL,GAAM,gBACN,GAAM,eACN,GAAM,eACN,GAAM,WACN,GAAM,aACN,GAAM,UACN,GAAM,UACR,EAEA,SAASC,EAAiBb,EAA6C,CACrE,GAAIA,IAAQ,OACZ,OAAOY,EAAeZ,CAAG,GAAKA,CAChC,CAIA,SAASc,EAAoBC,EAA4C,CACvE,GAAI,OAAOA,GAAQ,UAAYA,EAAI,OAAS,EAAG,OAAO,KAEtD,IAAMC,EAAWD,EAAIA,EAAI,OAAS,CAAC,EAGnC,GAAIC,IAAa,KAAOA,IAAa,KAAOA,IAAa,IACvD,MAAO,CAACD,EAAI,MAAM,EAAG,EAAE,EAAGC,CAAc,EAI1C,IAAMC,EAAUF,EAAI,QAAQ,GAAG,EAC/B,GAAIE,EAAU,EAAG,CACf,IAAMC,EAAWH,EAAI,MAAM,EAAGE,CAAO,EAC/BE,EAASJ,EAAI,MAAME,EAAU,CAAC,EAEhCjB,EAAemB,EACnB,OAAIA,IAAW,OAAQnB,EAAM,GACpBmB,IAAW,QAASnB,EAAM,GAC1B,CAAC,MAAM,OAAOmB,CAAM,CAAC,GAAKA,IAAW,KAAInB,EAAM,OAAOmB,CAAM,GAC9D,CAACD,EAAU,IAAKlB,CAAG,CAC5B,CAEA,OAAO,IACT,CAUA,IAAMoB,EAAqB,CAAC,QAAS,SAAU,UAAW,OAAQ,MAAM,EAExE,SAASC,EAAWC,EAAsB,CACxC,GAAI,CAACA,GAAO,OAAOA,GAAQ,SAAU,MAAO,GAG5C,GAAIA,EAAI,WAAW,GAAG,GAAKA,EAAI,WAAW,GAAG,GAAKA,EAAI,WAAW,GAAG,EAClE,MAAO,GAGT,GAAI,CACF,IAAMC,EAAS,IAAI,IAAID,EAAK,OAAO,SAAS,IAAI,EAChD,OAAOF,EAAmB,SAASG,EAAO,QAAQ,CACpD,MAAQ,CAEN,IAAMC,EAAWF,EAAI,YAAY,EAAE,KAAK,EAExC,MAAO,CADmB,CAAC,cAAe,QAAS,WAAW,EACpC,KAAKG,GAAWD,EAAS,WAAWC,CAAO,CAAC,CACxE,CACF,CAEA,SAASC,EAAYJ,EAAaK,EAAiC,CACjE,OAAKL,EAEAD,EAAWC,CAAG,EAKZA,GAJL,QAAQ,KAAK,iCAAiCK,CAAQ,MAAML,EAAI,MAAM,EAAG,EAAE,CAAC,MAAM,EAC3E,MAJQ,IAQnB,CAEA,SAASM,EACPC,EACAC,EACAC,EACAC,EACAC,EACY,CACZ,GAAI,OAAOJ,GAAY,WACrB,OAAOA,EAIT,IAAIK,EACJ,GAAI,OAAOL,GAAY,SAAU,CAC/B,IAAMN,EAASY,EAAoBN,CAAO,EACtCN,EACFW,EAAoBX,EAGhBU,IAAe,IACjBC,EAAoB,CAACL,EAAS,GAAG,EACxBI,IAAe,IACxBC,EAAoB,CAACL,EAAS,GAAG,EAGjCK,EAAoB,CAACL,EAAS,GAAG,CAGvC,MACEK,EAAoBL,EAGtB,GAAM,CAACO,EAAUC,EAAIC,CAAG,EAAIJ,EAC5B,MAAO,IAAM,CACX,IAAMK,EAAMT,EAAMM,CAAQ,EAC1B,GAAI,CAACG,EAAK,CACR,QAAQ,KAAK,6CAA6CH,CAAQ,GAAG,EACrE,MACF,CACA,IAAII,EAAYF,EAEhB,GAAIN,GAAe,OAAOQ,GAAc,UACtC,GAAIA,IAAc,SAChBA,EAAYR,EAAY,cACfQ,IAAc,QACvBA,EAAYR,EAAY,aACfQ,EAAU,WAAW,QAAQ,EAAG,CACzC,IAAMC,EAAMD,EAAU,UAAU,CAAC,EACjCA,EAAaR,EAAY,OAAmCS,CAAG,CACjE,EAEF,GAAIJ,IAAO,KAAOC,IAAQ,QAAaP,EAAO,CAC5C,IAAMW,EAASX,EAAM,OACrBS,EAAYE,EAAO,OAAS,WAAaA,EAAO,QAAUA,EAAO,KACnE,CACAC,EAAQJ,EAAKF,EAAIG,CAAS,CAC5B,CACF,CAIA,SAASI,EAAYC,EAAiBC,EAAoB,CACxD,IAAMC,EAAQF,EAAG,MAmBjB,GAjBIC,EAAM,IAAM,SAAWC,EAAM,IAAMC,EAAgBF,EAAM,CAAC,GAC1DA,EAAM,IAAM,SAAWC,EAAM,QAAUC,EAAgBF,EAAM,CAAC,GAC9DA,EAAM,IAAM,SAAWC,EAAM,OAASC,EAAgBF,EAAM,CAAC,GAC7DA,EAAM,IAAM,SAAWC,EAAM,MAAQC,EAAgBF,EAAM,CAAC,GAC5DA,EAAM,IAAM,SAAWC,EAAM,OAASC,EAAgBF,EAAM,CAAC,GAC7DA,EAAM,KAAO,SAAWC,EAAM,SAAWC,EAAgBF,EAAM,EAAE,GACjEA,EAAM,KAAO,SAAWC,EAAM,UAAYC,EAAgBF,EAAM,EAAE,GAElEA,EAAM,KAAO,SAAWC,EAAM,WAAaD,EAAM,IACjDA,EAAM,KAAO,SAAWC,EAAM,MAAQD,EAAM,IAC5CA,EAAM,IAAM,SAAWC,EAAM,QAAU,OAAOD,EAAM,CAAC,GAErDA,EAAM,IAAM,SAAWC,EAAM,aAAeC,EAAgBF,EAAM,CAAC,GACnEA,EAAM,KAAO,SAAWC,EAAM,YAAcC,EAAgBF,EAAM,EAAE,GACpEA,EAAM,KAAO,SAAWC,EAAM,YAAcD,EAAM,IAClDA,EAAM,KAAO,SAAWC,EAAM,YAAcD,EAAM,IAElDA,EAAM,MAAQ,OAAW,CAC3B,IAAMG,EAAiC,CACrC,IAAK,WAAY,IAAK,WAAY,IAAK,QAAS,OAAQ,QAC1D,EACAF,EAAM,SAAWE,EAAOH,EAAM,GAAG,GAAKA,EAAM,GAC9C,CACIA,EAAM,IAAM,SAAWC,EAAM,OAAS,OAAOD,EAAM,CAAC,GACpDA,EAAM,IAAM,SAAWC,EAAM,IAAMC,EAAgBF,EAAM,CAAC,GAC1DA,EAAM,IAAM,SAAWC,EAAM,KAAOC,EAAgBF,EAAM,CAAC,IAC3D,OAAOA,EAAM,GAAM,UAAa,OAAOA,EAAM,GAAM,UAAY,CAAC,MAAM,QAAQA,EAAM,CAAC,KACvFC,EAAM,OAASC,EAAgBF,EAAM,CAAoB,GAEvDA,EAAM,KAAO,SAAWC,EAAM,MAAQC,EAAgBF,EAAM,EAAE,GAE9DA,EAAM,KAAO,SAAWC,EAAM,SAAWC,EAAgBF,EAAM,EAAE,GACjEA,EAAM,KAAO,SAAWC,EAAM,WAAa,OAAOD,EAAM,EAAE,GAC1DA,EAAM,KAAO,SAAWC,EAAM,WAAaD,EAAM,IACjDA,EAAM,KAAO,SAAWC,EAAM,UAAYD,EAAM,IAChDA,EAAM,KAAO,SAAWC,EAAM,eAAiBD,EAAM,IACrDA,EAAM,KAAO,SAAWC,EAAM,WAAa,OAAOD,EAAM,IAAO,SAAW,OAAOA,EAAM,EAAE,EAAIA,EAAM,IACnGA,EAAM,KAAO,SAAWC,EAAM,cAAgBC,EAAgBF,EAAM,EAAE,GAEtEA,EAAM,KAAO,SAAWC,EAAM,WAAaG,EAAiBJ,EAAM,EAAE,GACpEA,EAAM,KAAO,SAAWC,EAAM,eAAiBG,EAAiBJ,EAAM,EAAE,GACxEA,EAAM,MAAQ,SAAWC,EAAM,SAAWG,EAAiBJ,EAAM,GAAG,GAEpEA,EAAM,MAAQ,SAAWC,EAAM,OAASD,EAAM,KAC9CA,EAAM,KAAO,SAAWC,EAAM,SAAWD,EAAM,IAC/CA,EAAM,KAAO,SAAWC,EAAM,cAAgBD,EAAM,IACpDA,EAAM,KAAO,SAAWC,EAAM,WAAaD,EAAM,IACjDA,EAAM,KAAO,SAAWC,EAAM,UAAYD,EAAM,IAChDA,EAAM,KAAO,SAAWC,EAAM,UAAYD,EAAM,IAEhDA,EAAM,GACR,OAAO,QAAQA,EAAM,CAAC,EAAE,QAAQ,CAAC,CAACL,EAAKH,CAAG,IAAM,CAC7CS,EAA4CN,CAAG,EAAI,OAAOH,CAAG,CAChE,CAAC,CAEL,CAIA,SAASa,EACPC,EACAC,EACArB,EACoB,CACpB,GAAM,CAAE,MAAAF,CAAM,EAAIuB,EAGlB,GAAIC,EAAoBF,CAAI,EAAG,CAC7B,IAAMG,EAAc,SAAS,cAAc,KAAK,EAChDA,EAAY,MAAM,QAAU,WAE5B,GAAI,CACF,IAAMC,EAA0B,CAC9B,SAAU,CAAC,EACX,MAAA1B,EACA,QAASsB,EAAK,SAAWC,EAAI,OAC/B,EACMI,EAAQN,EAAcC,EAAK,MAAOI,EAAUxB,CAAW,EACzDyB,IACFF,EAAY,YAAYE,CAAK,EAC7BJ,EAAI,SAAS,KAAK,GAAGG,EAAS,QAAQ,EAE1C,OAASE,EAAK,CACZ,IAAMC,EAAuB,CAC3B,QAASD,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,EACxD,MAAOA,aAAe,MAAQA,EAAI,MAAQ,MAC5C,EAMA,GAJIN,EAAK,SACPA,EAAK,QAAQO,CAAS,EAGpBP,EAAK,SACP,GAAI,CACF,IAAMQ,EAAaT,EAAcC,EAAK,SAAUC,EAAKrB,CAAW,EAC5D4B,GAAYL,EAAY,YAAYK,CAAU,CACpD,MAAQ,CACNL,EAAY,YAAYM,EAAoBF,CAAS,CAAC,CACxD,MAEAJ,EAAY,YAAYM,EAAoBF,CAAS,CAAC,CAE1D,CAEA,OAAOJ,CACT,CAGA,GAAIO,EAASV,CAAI,EAAG,CAClB,IAAMG,EAAc,SAAS,cAAc,KAAK,EAChDA,EAAY,MAAM,QAAU,WAE5B,IAAIQ,EAAgC,KAChCP,EAAiC,KAG/BQ,EAASZ,EAAK,IAAMA,EAAK,GAAG,EAC5Ba,EAAab,EAAK,MAAQA,EAAK,EAC/Bc,EAAad,EAAK,MAAQA,EAAK,EAC/Be,EAAUf,EAAK,IAAMA,EAAK,GAyChC,OAAAgB,EAvCiB,IAAM,CAEjBZ,IACFA,EAAS,SAAS,QAAQa,GAAMA,EAAG,CAAC,EACpCb,EAAS,SAAW,CAAC,GAEnBO,IACFR,EAAY,UAAY,GACxBQ,EAAY,MAGd,IAAMO,EAAW,OAAON,GAAW,SAC/BlC,EAAMkC,CAAM,IAAI,EAChBO,EAAaP,EAAQlC,CAAK,EAG1B0C,EACAL,IAAY,OACdK,EAAYF,IAAaH,EAEzBK,EAAY,EAAQF,EAGtB,IAAMG,EAASD,EAAYP,EAAaC,EACnCO,IAELjB,EAAW,CAAE,SAAU,CAAC,EAAG,MAAA1B,CAAM,EAE7B,MAAM,QAAQ2C,CAAM,GAAKA,EAAO,OAAS,GAAK,MAAM,QAAQA,EAAO,CAAC,CAAC,EACtEA,EAAsB,QAAQhB,GAAS,CACtC,IAAMZ,EAAKM,EAAcM,EAAOD,EAAWxB,CAAW,EAClDa,GAAIU,EAAY,YAAYV,CAAE,CACpC,CAAC,GAEDkB,EAAYZ,EAAcsB,EAAoBjB,EAAUxB,CAAW,EAC/D+B,GAAWR,EAAY,YAAYQ,CAAS,GAEpD,EAEiBV,CAAG,EACbE,CACT,CAGA,GAAImB,EAAUtB,CAAI,EAAG,CACnB,IAAMG,EAAc,SAAS,cAAc,KAAK,EAChDA,EAAY,MAAM,QAAU,WAE5B,IAAIC,EAAiC,KAG/BmB,EAAYvB,EAAK,KAAOA,EAAK,EAC7BwB,EAAaxB,EAAK,IAAMA,EAAK,EAwBnC,OAAAgB,EAtBkB,IAAM,CAElBZ,IACFA,EAAS,SAAS,QAAQa,GAAMA,EAAG,CAAC,EACpCb,EAAS,SAAW,CAAC,GAEvBD,EAAY,UAAY,GAExB,IAAMsB,EAAO,OAAOF,GAAc,SAC9B7C,EAAM6C,CAAS,IAAI,EACnBJ,EAAaI,EAAW7C,CAAK,EAE7B,CAAC,MAAM,QAAQ+C,CAAG,GAAK,CAACD,IAE5BpB,EAAW,CAAE,SAAU,CAAC,EAAG,MAAA1B,CAAM,EAEjC+C,EAAI,QAAQ,CAACC,EAAMC,IAAU,CAC3B,IAAMlC,EAAKM,EAAcyB,EAAYpB,EAAW,CAAE,KAAAsB,EAAM,MAAAC,CAAM,CAAC,EAC3DlC,GAAIU,EAAY,YAAYV,CAAE,CACpC,CAAC,EACH,EAEkBQ,CAAG,EACdE,CACT,CAGA,GAAI,CAAC,MAAM,QAAQH,CAAI,GAAMA,EAAmB,SAAW,EACzD,eAAQ,KAAK,6BAA8BA,CAAI,EACxC,KAGT,GAAM,CAAC4B,EAAMC,EAASnC,EAAQ,CAAC,CAAC,EAAIM,EAChCP,EAEJ,OAAQmC,EAAM,CACZ,IAAK,IACHnC,EAAK,SAAS,cAAc,KAAK,EACjCA,EAAG,MAAM,QAAU,OACnBA,EAAG,MAAM,cAAgB,SACzB,MACF,IAAK,IACHA,EAAK,SAAS,cAAc,KAAK,EACjCA,EAAG,MAAM,QAAU,OACnBA,EAAG,MAAM,cAAgB,MACzB,MACF,IAAK,IACHA,EAAK,SAAS,cAAc,KAAK,EACjCA,EAAG,MAAM,QAAU,OACfC,EAAM,OACRD,EAAG,MAAM,oBAAsB,OAAOC,EAAM,MAAS,SACjD,UAAUA,EAAM,IAAI,SAAWA,EAAM,MAEvCA,EAAM,OACRD,EAAG,MAAM,iBAAmB,OAAOC,EAAM,MAAS,SAC9C,UAAUA,EAAM,IAAI,SAAWA,EAAM,MAE3C,MACF,IAAK,IACHD,EAAK,SAAS,cAAc,KAAK,EACjC,MACF,IAAK,IACHA,EAAK,SAAS,cAAc,MAAM,EAClC,MACF,IAAK,IACHA,EAAK,SAAS,cAAc,QAAQ,EACpC,MACF,IAAK,IACHA,EAAK,SAAS,cAAc,OAAO,EAClCA,EAAwB,KAAOC,EAAM,MAAQ,OAC1CA,EAAM,KAAKD,EAAwB,YAAcC,EAAM,IACvDA,EAAM,KAAKD,EAAwB,SAAW,IAClD,MACF,IAAK,KACHA,EAAK,SAAS,cAAc,UAAU,EAClCC,EAAM,KAAKD,EAA2B,YAAcC,EAAM,IAC1DA,EAAM,KAAKD,EAA2B,KAAOC,EAAM,IACnDA,EAAM,KAAKD,EAA2B,SAAW,IACrD,MACF,IAAK,IACHA,EAAK,SAAS,cAAc,QAAQ,EAChCC,EAAM,MACRA,EAAM,KAAK,QAAQoC,GAAO,CACxB,IAAMC,EAAS,SAAS,cAAc,QAAQ,EAC9CA,EAAO,MAAQD,EAAI,EACnBC,EAAO,YAAcD,EAAI,EACzBrC,EAAG,YAAYsC,CAAM,CACvB,CAAC,EAEH,MACF,IAAK,IACHtC,EAAK,SAAS,cAAc,OAAO,EAClCA,EAAwB,KAAO,WAChC,MACF,IAAK,IACHA,EAAK,SAAS,cAAc,OAAO,EAClCA,EAAwB,KAAO,QAChC,MACF,IAAK,KACHA,EAAK,SAAS,cAAc,OAAO,EACnC,MACF,IAAK,KACHA,EAAK,SAAS,cAAc,OAAO,EACnC,MACF,IAAK,MACHA,EAAK,SAAS,cAAc,OAAO,EACnC,MACF,IAAK,KACHA,EAAK,SAAS,cAAc,IAAI,EAChC,MACF,IAAK,KACHA,EAAK,SAAS,cAAc,IAAI,EAC5BC,EAAM,KAAKD,EAA4B,QAAUC,EAAM,IACvDA,EAAM,MAAMD,EAA4B,QAAUC,EAAM,KAC5D,MACF,IAAK,KACHD,EAAK,SAAS,cAAc,IAAI,EAC5BC,EAAM,KAAKD,EAA4B,QAAUC,EAAM,IACvDA,EAAM,MAAMD,EAA4B,QAAUC,EAAM,KAC5D,MACF,IAAK,KACHD,EAAK,SAAS,cAAc,IAAI,EAChC,MACF,IAAK,KACHA,EAAK,SAAS,cAAc,IAAI,EAChC,MACF,IAAK,KACHA,EAAK,SAAS,cAAc,IAAI,EAChC,MACF,IAAK,IAEH,GADAA,EAAK,SAAS,cAAc,KAAK,EAC7BC,EAAM,IAAK,CACb,IAAMsC,EAAU1D,EAAYoB,EAAM,IAAK,KAAK,EACxCsC,IAAUvC,EAAwB,IAAMuC,EAC9C,CACItC,EAAM,MAAMD,EAAwB,IAAMC,EAAM,KACpD,MACF,IAAK,IAEH,GADAD,EAAK,SAAS,cAAc,GAAG,EAC3BC,EAAM,KAAM,CACd,IAAMuC,EAAW3D,EAAYoB,EAAM,KAAM,MAAM,EAC3CuC,IAAWxC,EAAyB,KAAOwC,EACjD,CACA,MACF,IAAK,KACHxC,EAAK,SAAS,gBAAgB,6BAA8B,KAAK,EACjE,MACF,QACE,QAAQ,KAAK,mCAAmCmC,CAAI,EAAE,EACtDnC,EAAK,SAAS,cAAc,KAAK,CACrC,CASA,GAPIC,EAAM,MAAKD,EAAG,UAAYC,EAAM,KAChCA,EAAM,KAAID,EAAG,GAAKC,EAAM,IACxBA,EAAM,MAAMD,EAAyB,SAAW,IAEpDD,EAAYC,EAAIC,CAAK,EAGjBmC,IAAY,QACd,GAAI,MAAM,QAAQA,CAAO,GAAKA,EAAQ,OAAS,IAAM,MAAM,QAAQA,EAAQ,CAAC,CAAC,GAAKnB,EAASmB,EAAQ,CAAC,CAAC,GAAKP,EAAUO,EAAQ,CAAC,CAAC,GAC3HA,EAAuB,QAAQK,GAAa,CAC3C,IAAM7B,EAAQN,EAAcmC,EAAWjC,EAAKrB,CAAW,EACnDyB,GAAOZ,EAAG,YAAYY,CAAK,CACjC,CAAC,UACQK,EAASmB,CAAO,GAAKP,EAAUO,CAAO,EAAG,CAClD,IAAMxB,EAAQN,EAAc8B,EAAqB5B,EAAKrB,CAAW,EAC7DyB,GAAOZ,EAAG,YAAYY,CAAK,CACjC,SAAW8B,EAAWN,CAAO,EAC3Bb,EAAO,IAAM,CACX,IAAM9B,EAAMiC,EAAaU,EAASnD,CAAK,EACnCkD,IAAS,KAEFA,IAAS,KADjBnC,EAAwB,MAAQ,OAAOP,GAAO,EAAE,EAGxC0C,IAAS,KAAOA,IAAS,IACjCnC,EAAwB,QAAU,EAAQP,EAG3CO,EAAG,YAAc,OAAOP,GAAO,EAAE,CAErC,EAAGe,CAAG,UACG,OAAO4B,GAAY,UAAY,OAAOA,GAAY,SAAU,CACrE,IAAIO,EAAc,OAAOP,CAAO,EAC5BjD,IAEE,OAAOA,EAAY,MAAS,UAAYA,EAAY,OAAS,OAC/DwD,EAAcA,EAAY,QAAQ,iBAAkB,CAACC,EAAGhD,IAC/C,OAAQT,EAAY,KAAiCS,CAAG,GAAK,EAAE,CACvE,GAEH+C,EAAcA,EAAY,QAAQ,UAAW,OAAOxD,EAAY,IAAI,CAAC,EACrEwD,EAAcA,EAAY,QAAQ,WAAY,OAAOxD,EAAY,KAAK,CAAC,GAGrEgD,IAAS,KAEFA,IAAS,KADjBnC,EAAwB,MAAQ2C,EAGxBR,IAAS,MAElBnC,EAAG,YAAc2C,EAErB,EAIE1C,EAAM,IAAM,QAAayC,EAAWzC,EAAM,CAAC,GAC7CsB,EAAO,IAAM,CACX,IAAM9B,EAAMiC,EAAazB,EAAM,EAAIhB,CAAK,EACpCkD,IAAS,KAAOA,IAAS,KAAOA,IAAS,KAC1CnC,EAAwB,MAAQ,OAAOP,GAAO,EAAE,GACxC0C,IAAS,KAAOA,IAAS,OACjCnC,EAAwB,QAAU,EAAQP,EAE/C,EAAGe,CAAG,EAIJP,EAAM,KAAO,SACXyC,EAAWzC,EAAM,EAAa,EAChCsB,EAAO,IAAM,CACX,IAAM9B,EAAMiC,EAAazB,EAAM,GAA2BhB,CAAK,EAC9De,EAAwB,QAAU,EAAQP,CAC7C,EAAGe,CAAG,EAELR,EAAwB,QAAU,EAAQC,EAAM,IAKrD,IAAM4C,EAAmB,CAAC3D,EAAeF,IAAgC,CACvEgB,EAAG,iBAAiBd,EAAOF,CAAO,EAClCwB,EAAI,SAAS,KAAK,IAAMR,EAAG,oBAAoBd,EAAOF,CAAO,CAAC,CAChE,EAGMI,EAAa+C,IAAS,MAAQ,OAAOC,GAAY,UAAY,OAAOA,GAAY,UAClF,OAAOA,CAAO,EACd,OAKJ,GAHInC,EAAM,GACR4C,EAAiB,QAAUC,GAAM/D,EAAckB,EAAM,EAAIhB,EAAO6D,EAAG3D,EAAaC,CAAU,EAAE,CAAC,EAE3Fa,EAAM,EAAG,CACX,IAAMjB,EAAUiB,EAAM,EACtB4C,EAAiB,QAAUC,GAAM,CAC/B,GAAI,OAAO9D,GAAY,WACrBA,EAAQ,MACH,CAEL,IAAIO,EACAC,EACJ,GAAI,OAAOR,GAAY,SAAU,CAC/B,IAAMN,EAASY,EAAoBN,CAAO,EACtCN,EACF,CAACa,EAAUC,CAAE,EAAId,GAGjBa,EAAWP,EACXQ,EAAK,IAET,KACE,CAACD,EAAUC,CAAE,EAAIR,EAEnB,IAAMU,EAAMT,EAAMM,CAAQ,EAC1B,GAAIG,EAAK,CACP,IAAMG,EAASiD,EAAE,OACXrD,EAAO0C,IAAS,KAAOA,IAAS,IAAOtC,EAAO,QAAUA,EAAO,MACrEC,EAAQJ,EAAKF,EAAIC,CAAG,CACtB,CACF,CACF,CAAC,CACH,CACA,OAAIQ,EAAM,GACR4C,EAAiB,QAAUC,GAAM/D,EAAckB,EAAM,EAAIhB,EAAO6D,EAAG3D,CAAW,EAAE,CAAC,EAE/Ec,EAAM,IACR4C,EAAiB,OAASC,GAAM/D,EAAckB,EAAM,GAAKhB,EAAO6D,EAAG3D,CAAW,EAAE,CAAC,EAE/Ec,EAAM,GACR4C,EAAiB,UAAYC,GAAM/D,EAAckB,EAAM,EAAIhB,EAAO6D,EAAG3D,CAAW,EAAE,CAAC,EAEjFc,EAAM,IACR4C,EAAiB,QAAUC,GAAM/D,EAAckB,EAAM,GAAKhB,EAAO6D,EAAG3D,CAAW,EAAE,CAAC,EAEhFc,EAAM,IACR4C,EAAiB,WAAaC,GAAM/D,EAAckB,EAAM,GAAKhB,EAAO6D,EAAG3D,CAAW,EAAE,CAAC,EAEnFc,EAAM,GACR4C,EAAiB,aAAeC,GAAM/D,EAAckB,EAAM,EAAIhB,EAAO6D,EAAG3D,CAAW,EAAE,CAAC,EAEpFc,EAAM,IACR4C,EAAiB,aAAeC,GAAM/D,EAAckB,EAAM,GAAKhB,EAAO6D,EAAG3D,CAAW,EAAE,CAAC,EAErFc,EAAM,KACR4C,EAAiB,SAAWC,GAAM,CAChCA,EAAE,eAAe,EACjB/D,EAAckB,EAAM,IAAMhB,EAAO6D,EAAG3D,CAAW,EAAE,CACnD,CAAC,EAGIa,CACT,CAaA,SAAS+C,EAAOC,EAAwBzC,EAAgC,CACtE,GAAI,CAACyC,EACH,MAAM,IAAI,MAAM,mDAAmD,EAErE,GAAI,CAACzC,GAAQ,CAACA,EAAK,EACjB,MAAM,IAAI,MAAM,qDAAqD,EAGvE,IAAMtB,EAAoB,CAAC,EACvBsB,EAAK,GACP,OAAO,QAAQA,EAAK,CAAC,EAAE,QAAQ,CAAC,CAACX,EAAKH,CAAG,IAAM,CAC7CR,EAAMW,CAAG,EAAIqD,EAAOxD,CAAG,CACzB,CAAC,EAGH,IAAMe,EAAqB,CAAE,SAAU,CAAC,EAAG,MAAAvB,CAAM,EAEjD+D,EAAU,UAAY,GACtB,IAAMhD,EAAKM,EAAcC,EAAK,EAAGC,CAAG,EAChCR,GAAIgD,EAAU,YAAYhD,CAAE,EAEhC,IAAMkD,EAA0B,CAC9B,MAAAjE,EACA,GAAAe,EACA,SAAU,CACRQ,EAAI,SAAS,QAAQgB,GAAMA,EAAG,CAAC,EAC/BhB,EAAI,SAAW,CAAC,EAChBwC,EAAU,UAAY,EACxB,EACA,OAAOG,EAAoB,CAYzB,GAXIA,EAAQ,GACVC,EAAM,IAAM,CACV,OAAO,QAAQD,EAAQ,CAAE,EAAE,QAAQ,CAAC,CAACvD,EAAKH,CAAG,IAAM,CAC7CR,EAAMW,CAAG,EACXX,EAAMW,CAAG,EAAE,IAAIH,CAAG,EAElBR,EAAMW,CAAG,EAAIqD,EAAOxD,CAAG,CAE3B,CAAC,CACH,CAAC,EAEC0D,EAAQ,EAAG,CACb3C,EAAI,SAAS,QAAQgB,GAAMA,EAAG,CAAC,EAC/BhB,EAAI,SAAW,CAAC,EAChBwC,EAAU,UAAY,GACtB,IAAMK,EAAQ/C,EAAc6C,EAAQ,EAAG3C,CAAG,EACtC6C,GAAOL,EAAU,YAAYK,CAAK,EACtCH,EAAS,GAAKG,CAChB,CACF,EACA,IAAIzD,EAAa,CACf,OAAOX,EAAMW,CAAG,IAAI,CACtB,EACA,IAAIA,EAAa0D,EAAgB,CAC3BrE,EAAMW,CAAG,GACXX,EAAMW,CAAG,EAAE,IAAI0D,CAAK,CAExB,CACF,EAEA,OAAOJ,CACT,CAIA,SAASK,GAAEC,EAAwB,CACjC,MAAO,CAAE,EAAGA,CAAK,CACnB,CAGA,IAAMC,GAAI,IACJC,GAAI,IACJC,GAAI,IACJC,GAAI,IACJC,GAAI,IACJC,GAAI,IACJC,GAAI,IACJC,GAAK,KACLC,GAAI,IACJC,GAAI,IACJC,GAAI,IACJC,GAAK,KACLC,GAAK,KACLC,GAAM,MACNC,GAAK,KACLC,GAAK,KACLC,GAAK,KACLC,GAAK,KACLC,GAAK,KACLC,GAAK,KACLC,GAAI,IACJC,GAAI,IACJC,GAAK",
6
+ "names": ["tooey_exports", "__export", "$", "B", "C", "D", "G", "H", "I", "L", "Li", "M", "Ol", "R", "S", "Sv", "T", "Ta", "Tb", "Tbd", "Tc", "Td", "Th", "Tr", "Ul", "V", "batch", "effect", "render", "signal", "isErrorBoundaryNode", "v", "createErrorFallback", "error", "el", "currentEffect", "batchDepth", "pendingEffects", "initial", "value", "subscribers", "sig", "newValue", "fn", "effects", "ctx", "isActive", "execute", "cleanup", "applyOp", "state", "op", "val", "arr", "_", "i", "item", "err", "isStateRef", "isIfNode", "isMapNode", "resolveValue", "content", "resolveCssValue", "styleShortcuts", "expandStyleValue", "parseEventShorthand", "str", "lastChar", "bangIdx", "stateKey", "valStr", "SAFE_URL_PROTOCOLS", "isValidUrl", "url", "parsed", "lowerUrl", "pattern", "sanitizeUrl", "propName", "createHandler", "handler", "state", "event", "itemContext", "buttonText", "normalizedHandler", "parseEventShorthand", "stateKey", "op", "val", "sig", "actualVal", "key", "target", "applyOp", "applyStyles", "el", "props", "style", "resolveCssValue", "posMap", "expandStyleValue", "createElement", "spec", "ctx", "isErrorBoundaryNode", "placeholder", "childCtx", "child", "err", "errorInfo", "fallbackEl", "createErrorFallback", "isIfNode", "currentEl", "ifCond", "thenBranch", "elseBranch", "eqValue", "effect", "fn", "rawValue", "resolveValue", "condition", "branch", "isMapNode", "mapSource", "asTemplate", "arr", "item", "index", "type", "content", "opt", "option", "safeSrc", "safeHref", "childSpec", "isStateRef", "textContent", "_", "addEventListener", "e", "render", "container", "signal", "instance", "newSpec", "batch", "newEl", "value", "$", "name", "V", "H", "D", "G", "T", "B", "I", "Ta", "S", "C", "R", "Tb", "Th", "Tbd", "Tr", "Td", "Tc", "Ul", "Ol", "Li", "M", "L", "Sv"]
7
+ }
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@tooey/ui",
3
+ "version": "1.0.0",
4
+ "description": "Token-efficient UI library for LLMs",
5
+ "main": "dist/tooey.js",
6
+ "module": "dist/tooey.esm.js",
7
+ "types": "dist/tooey.d.ts",
8
+ "type": "module",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/tooey.d.ts",
12
+ "import": "./dist/tooey.esm.js",
13
+ "require": "./dist/tooey.js",
14
+ "default": "./dist/tooey.esm.js"
15
+ }
16
+ },
17
+ "sideEffects": false,
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "scripts": {
22
+ "build": "node build.js",
23
+ "dev": "node build.js --watch",
24
+ "serve": "npx serve .",
25
+ "test": "vitest run",
26
+ "test:watch": "vitest",
27
+ "test:coverage": "vitest run --coverage",
28
+ "lint": "eslint src tests",
29
+ "lint:fix": "eslint src tests --fix",
30
+ "typecheck": "tsc --noEmit",
31
+ "count-tokens": "node count-tokens.js"
32
+ },
33
+ "keywords": [
34
+ "ui",
35
+ "llm",
36
+ "token-efficient",
37
+ "declarative",
38
+ "reactive",
39
+ "signals"
40
+ ],
41
+ "author": "Vijay Pemmaraju",
42
+ "license": "MIT",
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "git+https://github.com/vijaypemmaraju/tooey.git"
46
+ },
47
+ "bugs": {
48
+ "url": "https://github.com/vijaypemmaraju/tooey/issues"
49
+ },
50
+ "homepage": "https://github.com/vijaypemmaraju/tooey#readme",
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
54
+ "devDependencies": {
55
+ "@eslint/js": "^9.0.0",
56
+ "@types/node": "^20.0.0",
57
+ "@vitest/coverage-v8": "^3.0.0",
58
+ "esbuild": "^0.25.0",
59
+ "eslint": "^9.0.0",
60
+ "gpt-tokenizer": "^3.4.0",
61
+ "jsdom": "^24.0.0",
62
+ "typescript": "^5.3.0",
63
+ "typescript-eslint": "^8.0.0",
64
+ "vitest": "^3.0.0"
65
+ }
66
+ }
package/readme.md ADDED
@@ -0,0 +1,168 @@
1
+ <p align="center">
2
+ <img src="logo.svg" width="64" height="64" alt="tooey">
3
+ </p>
4
+
5
+ # tooey
6
+
7
+ token-efficient ui library for llm output
8
+
9
+ ```
10
+ ~39% fewer tokens than react | ~10kb minified | 0 deps
11
+ ```
12
+
13
+ ## install
14
+
15
+ ```bash
16
+ npm install @tooey/ui
17
+ ```
18
+
19
+ or via CDN:
20
+
21
+ ```html
22
+ <script src="https://unpkg.com/@tooey/ui/dist/tooey.js"></script>
23
+ ```
24
+
25
+ ## usage
26
+
27
+ ```javascript
28
+ tooey.render(document.getElementById('app'), {
29
+ s: {n: 0},
30
+ r: [V,[[T,{$:"n"}],[H,[[B,"-",{c:"n"}],[B,"+",{c:"n"}]],{g:8}]],{g:8}]
31
+ });
32
+ ```
33
+
34
+ ## components
35
+
36
+ ```
37
+ layout V H D G
38
+ text T B
39
+ input I Ta S C R
40
+ table Tb Th Tbd Tr Td Tc
41
+ list Ul Ol Li
42
+ media M L Sv
43
+ ```
44
+
45
+ ## props
46
+
47
+ ```
48
+ spacing g p m w h mw mh
49
+ color bg fg o
50
+ border r bw bc bs
51
+ position pos z t l rt
52
+ type fs fw ff ta td lh ls
53
+ layout ai jc flw cols rows
54
+ misc cur ov sh tr pe us
55
+ element v ph type href src alt dis ch ro opts rw sp rsp cls id
56
+
57
+ layout shortcuts: c=center sb=space-between fe=flex-end fs=flex-start st=stretch
58
+ ```
59
+
60
+ ## events
61
+
62
+ ```
63
+ c click
64
+ x input/change
65
+ f focus
66
+ bl blur
67
+ k keydown
68
+ e mouseenter
69
+ lv mouseleave
70
+ sub submit
71
+ ```
72
+
73
+ ## state ops
74
+
75
+ ```javascript
76
+ // array form
77
+ ["state", "+"] // increment
78
+ ["state", "-"] // decrement
79
+ ["state", "!", val] // set
80
+ ["state", "~"] // toggle
81
+ ["state", "<", item] // append
82
+ ["state", ">", item] // prepend
83
+ ["state", "X", idx] // remove
84
+ ["state", ".", [k,v]] // set prop
85
+
86
+ // string shorthand (for events)
87
+ "state+" // increment
88
+ "state-" // decrement
89
+ "state~" // toggle
90
+ "state!val" // set value
91
+ "state" // for inputs: set, for +/- buttons: infers op
92
+ ```
93
+
94
+ ## control flow
95
+
96
+ ```javascript
97
+ // long form
98
+ {if: "show", then: [T, "yes"], else: [T, "no"]}
99
+ {map: "items", as: [Li, "$item"]}
100
+
101
+ // short form (saves tokens)
102
+ {?: "show", t: [T, "yes"], e: [T, "no"]}
103
+ {m: "items", a: [Li, "$item"]}
104
+
105
+ // equality check
106
+ {?: "step", is: 0, t: [T, "step 1"]}
107
+ ```
108
+
109
+ ## api
110
+
111
+ ```javascript
112
+ const app = tooey.render(el, spec);
113
+ app.get("key") // read state
114
+ app.set("key", value) // write state
115
+ app.destroy() // cleanup
116
+ ```
117
+
118
+ ## examples
119
+
120
+ [/examples](./examples) - counter, todo, form, converter, table, tabs, modal, cart, wizard
121
+
122
+ ## security
123
+
124
+ tooey includes several security features to protect against common vulnerabilities:
125
+
126
+ ### xss protection
127
+ - all text content is escaped using `textContent` instead of `innerHTML`
128
+ - dynamic state values are sanitized before rendering
129
+
130
+ ### url validation
131
+ - `href` and `src` props are validated against safe protocols
132
+ - blocked protocols: `javascript:`, `data:`, `vbscript:`
133
+ - allowed protocols: `http:`, `https:`, `mailto:`, `tel:`, `ftp:`
134
+ - relative urls and anchors are allowed
135
+
136
+ ### best practices
137
+
138
+ 1. **content security policy**: add csp headers to your deployment:
139
+ ```
140
+ Content-Security-Policy: default-src 'self'; script-src 'self' https://unpkg.com/@tooey/ui
141
+ ```
142
+
143
+ 2. **user input**: always validate user input before passing to tooey specs
144
+ 3. **state values**: don't store sensitive data in state that gets rendered
145
+
146
+ ### reporting security issues
147
+
148
+ report security vulnerabilities via github issues with the `security` label.
149
+
150
+ ## llm reference
151
+
152
+ copy this into your system prompt for llms to generate tooey specs:
153
+
154
+ ```
155
+ tooey: token-efficient UI lib. spec={s:{state},r:[component,content?,props?]}
156
+ components: V(vstack) H(hstack) D(div) G(grid) T(text) B(button) I(input) Ta(textarea) S(select) C(checkbox) R(radio) Tb/Th/Tbd/Tr/Td/Tc(table) Ul/Ol/Li(list) M(img) L(a) Sv(svg)
157
+ props: g(gap) p(padding) m(margin) w(width) h(height) mw mh bg(background) fg(color) o(opacity) r(radius) bw bc bs pos(position:rel/abs/fix) z t l rt b fs(font-size) fw ff ta td lh ls ai(align-items) jc(justify-content) flw cols rows cur ov sh tr pe us v(value) ph(placeholder) type href src alt dis(disabled) ch(checked) ro(readonly) opts cls id
158
+ layout shortcuts: c=center sb=space-between fe=flex-end fs=flex-start st=stretch
159
+ events: c(click) x(input) f(focus) bl(blur) k(keydown) e(mouseenter) lv(mouseleave) sub(submit)
160
+ state ops: "key+"(inc) "key-"(dec) "key~"(toggle) "key!val"(set) or ["key","op",val?] where op=+/-/!/~/</>/X/.
161
+ control: {?:"key",t:[...],e:[...]} {?:"key",is:val,t:[...]} {m:"arr",a:[...]}
162
+ state ref: {$:"key"} | in map: $item $index $item.prop
163
+ example: {s:{n:0},r:[V,[[T,{$:"n"}],[H,[[B,"-",{c:"n"}],[B,"+",{c:"n"}]],{g:8}]],{g:8}]}
164
+ ```
165
+
166
+ ## license
167
+
168
+ mit