lecodes-cli 2.0.13 → 2.1.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.
@@ -1,5 +1,5 @@
1
1
  var Dn=Object.defineProperty;var Bn=(e,t,n)=>t in e?Dn(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var je=(e,t,n)=>Bn(e,typeof t!="symbol"?t+"":t,n);(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))r(i);new MutationObserver(i=>{for(const s of i)if(s.type==="childList")for(const o of s.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&r(o)}).observe(document,{childList:!0,subtree:!0});function n(i){const s={};return i.integrity&&(s.integrity=i.integrity),i.referrerPolicy&&(s.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?s.credentials="include":i.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function r(i){if(i.ep)return;i.ep=!0;const s=n(i);fetch(i.href,s)}})();const Vn=e=>{if(typeof e=="string")return e;if(e instanceof Error)return e.stack?`${String(e)}
2
- ${e.stack}`:String(e);try{return JSON.stringify(e)}catch{return String(e)}},zn=(e,t)=>{const n=o=>{fetch(`${e}/log`,{method:"POST",body:JSON.stringify(o),headers:{"content-type":"application/json"},keepalive:!0}).catch(()=>{})};let r=-1;const i=async()=>{try{const{v:o}=await(await fetch(`${e}/version`,{cache:"no-store"})).json();typeof o=="number"&&t(o)}catch{}setTimeout(i,2500)};setTimeout(i,2500);const s=()=>{let o;try{o=new WebSocket(e.replace(/^http/,"ws")+"/ws")}catch{return}o.onopen=()=>o.send(JSON.stringify({type:"hello",v:r})),o.onmessage=l=>{let d={};try{d=JSON.parse(String(l.data))}catch{return}d.type==="reload"&&typeof d.v=="number"&&t(d.v)},o.onclose=()=>{setTimeout(s,2e3)}};return s(),{bundleUrl:o=>`${e}/bundle.js?t=web&v=${o}`,versionOf(o,l){const d=Number(o.headers.get("x-lecodes-v"));return o.headers.has("x-lecodes-v")&&Number.isFinite(d)?d:l},ran(o){r=o},log:(o,l)=>n({type:"log",level:o,text:l.map(Vn).join(" ")}),error:o=>n({type:"error",text:o})}},Wn=(e,t)=>{try{const n=new URL(e,t);return n.origin===new URL(t).origin&&(n.protocol==="http:"||n.protocol==="https:")?n.href:null}catch{return null}},Gn="modulepreload",Hn=function(e,t){return new URL(e,t).href},Et={},ce=function(t,n,r){let i=Promise.resolve();if(n&&n.length>0){let o=function(y){return Promise.all(y.map(k=>Promise.resolve(k).then(A=>({status:"fulfilled",value:A}),A=>({status:"rejected",reason:A}))))};const l=document.getElementsByTagName("link"),d=document.querySelector("meta[property=csp-nonce]"),f=(d==null?void 0:d.nonce)||(d==null?void 0:d.getAttribute("nonce"));i=o(n.map(y=>{if(y=Hn(y,r),y in Et)return;Et[y]=!0;const k=y.endsWith(".css"),A=k?'[rel="stylesheet"]':"";if(!!r)for(let E=l.length-1;E>=0;E--){const b=l[E];if(b.href===y&&(!k||b.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${y}"]${A}`))return;const p=document.createElement("link");if(p.rel=k?"stylesheet":Gn,k||(p.as="script"),p.crossOrigin="",p.href=y,f&&p.setAttribute("nonce",f),document.head.appendChild(p),k)return new Promise((E,b)=>{p.addEventListener("load",E),p.addEventListener("error",()=>b(new Error(`Unable to preload CSS for ${y}`)))})}))}function s(o){const l=new Event("vite:preloadError",{cancelable:!0});if(l.payload=o,window.dispatchEvent(l),!l.defaultPrevented)throw o}return i.then(o=>{for(const l of o||[])l.status==="rejected"&&s(l.reason);return t().catch(s)})},wt="2.0.13",Ke=e=>{const t=e==null?void 0:e.match(/^(\d+)\.\d+\.\d+/);return t?Number(t[1]):null},jn=(e,t)=>Ke(e)!==null&&Ke(e)===Ke(t),Kn=(e,t="woff2")=>{const n=/^\/\/ fonts: (.+)$/m.exec(e);if(!n)return[];const r=[];for(const i of n[1].split(";")){const s=i.indexOf("=");if(s===-1)continue;const o=/^(.+)@(\d+)(i?)$/.exec(i.slice(0,s));o&&r.push({family:o[1],weight:parseInt(o[2],10),italic:o[3]==="i",url:i.slice(s+1).replace("{fmt}",t)})}return r},Yn=async(e,t,n=2e3)=>{const r=t??(typeof document>"u"?void 0:document);if(!(r!=null&&r.fonts)||e.length===0)return;const i=[];for(const s of e)try{const o=new FontFace(s.family,`url("${s.url}")`,{weight:String(s.weight),style:s.italic?"italic":"normal"});r.fonts.add(o),i.push(o.load().catch(()=>{}))}catch{}await Promise.race([Promise.all(i),new Promise(s=>setTimeout(s,n))])},qn=e=>{var d,f,y,k;const t=e.slice(0,2e3),n=new Set,r=t.match(/\/\/ preload: (.+)/);if(r)for(const A of r[1].split(";"))n.add(A);const i=t.includes("// gl: disable")?"off":t.includes("// gl: defer")?"defer":"on",s=((d=t.match(/^\/\/ sdk: (\d+\.\d+\.\d+\S*)/m))==null?void 0:d[1])??null,o=((y=(f=t.match(/^\/\/ name: (.+)$/m))==null?void 0:f[1])==null?void 0:y.trim())??null,l=new Set((((k=t.match(/^\/\/ plugins: (.+)$/m))==null?void 0:k[1])??"").split(";").map(A=>A.trim()).filter(Boolean));return{sdk:s,name:o,preloads:n,gl:i,is2d:t.includes("// 2d: enable"),plugins:l,bootFonts:Kn(t)}},Xn=1,Jn=2,Qn=3,Zn=4,er=5,tr=6,St=7,nr=8,_t=9,rr=10,or=11,sr=12,ir=13,ar=14,lr=15,cr=16,kt=17,ur=18,fr=19,Oe=20,ai=21,li=22,ci=23,ui=24,fi=25,dr=26,pr=27,hr=28,mr=29,Tt=30,Lt=31,gr=32,br=33,xt=34,Rt=35,Ct=36,Pe=1,yr=2,vr=4,de=8,Er=16,Ye=32,wr=64,qe=128,Sr=256,_r=512,kr=1024,Tr=2048,Lr=4096,At=8192,di=16384,xr=32768,Rr=1<<18,Cr=255,$t=256,Ot=512,Ar=1024,Q=0,$r=1,Or=2,Ur=3,De=4,be=255,un=e=>{const t=e.clientWidth;if(!t)return 1;const n=e.getBoundingClientRect().width;return n>0?n/t:1},Ut={text:{type:"text"},password:{type:"password"},search:{type:"search"},tel:{type:"tel"},email:{type:"email"},url:{type:"url"},number:{type:"text",mode:"numeric"},decimal:{type:"text",mode:"decimal"},date:{type:"date"},time:{type:"time"}},Nt=(e,t)=>e==null?t:e===!0||e==="true"||e==="1"||typeof e=="number"&&e>.5,oe=(e,t,n)=>{n===null?e.removeAttribute(t):e.getAttribute(t)!==n&&e.setAttribute(t,n)},Nr=(e,t)=>{if(e.type!=="input"&&e.type!=="textarea")return;const n=e.el,r=t("placeholder");if(oe(n,"placeholder",r==null?null:String(r)),e.type==="input"){const l=Ut[String(t("type")??"text")]??Ut.text,d=n;d.type!==l.type&&(d.type=l.type),oe(n,"inputmode",l.mode??null);const f=t("enterKey");oe(n,"enterkeyhint",f==null?null:String(f))}const i=t("autocapitalize");oe(n,"autocapitalize",i==null?null:String(i));const s=t("autocorrect");s==null?(oe(n,"autocorrect",null),oe(n,"spellcheck",null)):(oe(n,"autocorrect",Nt(s,!0)?"on":"off"),oe(n,"spellcheck",Nt(s,!0)?"true":"false"));const o=Number(t("maxLength")??0);oe(n,"maxlength",o>0?String(Math.round(o)):null)},Be={width:"layout",height:"layout",minWidth:"layout",minHeight:"layout",maxWidth:"layout",maxHeight:"layout",flexBasis:"layout",flexGrow:"layout",flexShrink:"layout",aspectRatio:"layout",gap:"layout",rowGap:"layout",columnGap:"layout",padding:"layout",paddingTop:"layout",paddingRight:"layout",paddingBottom:"layout",paddingLeft:"layout",paddingHorizontal:"layout",paddingVertical:"layout",paddingStart:"layout",paddingEnd:"layout",margin:"layout",marginTop:"layout",marginRight:"layout",marginBottom:"layout",marginLeft:"layout",marginHorizontal:"layout",marginVertical:"layout",marginStart:"layout",marginEnd:"layout",left:"layout",right:"layout",top:"layout",bottom:"layout",start:"layout",end:"layout",inset:"layout",flexDirection:"enum",justifyContent:"enum",alignItems:"enum",alignSelf:"enum",position:"enum",flexWrap:"enum",boxSizing:"enum",display:"enum",overflow:"enum",scrollDirection:"enum",borderWidth:"len",borderTopWidth:"len",borderRightWidth:"len",borderBottomWidth:"len",borderLeftWidth:"len",borderHorizontalWidth:"len",borderVerticalWidth:"len",borderStartWidth:"len",borderEndWidth:"len",color:"color",backgroundColor:"color",borderColor:"color",borderTopColor:"color",borderRightColor:"color",borderBottomColor:"color",borderLeftColor:"color",placeholderColor:"color",refreshControlColor:"color",tintColor:"color",rippleColor:"color",overlayColor:"color",backgroundImage:"str",backgroundSize:"enum",backgroundGradient:"str",borderRadius:"len",borderTopLeftRadius:"len",borderTopRightRadius:"len",borderBottomRightRadius:"len",borderBottomLeftRadius:"len",opacity:"num",transform:"str",transformOrigin:"str",pointerEvents:"enum",fontSize:"len",fontWeight:"num",fontStyle:"enum",fontFamily:"str",lineHeight:"len",letterSpacing:"len",textAlign:"enum",textDecoration:"enum",lineClamp:"num",textOverflow:"enum",objectFit:"enum",placeholder:"str",type:"enum",enterKey:"enum",autocapitalize:"enum",autocorrect:"num",maxLength:"num",keyboardShrink:"num",keyboardDismiss:"num",showScrollbar:"num",overscrollMode:"enum",keyboardDismissMode:"enum",snap:"enum",sheetDetents:"str",sheetDetent:"num",sheetDismissible:"num",dim:"num"},Mr={p:"padding",pt:"paddingTop",pr:"paddingRight",pb:"paddingBottom",pl:"paddingLeft",px:"paddingHorizontal",py:"paddingVertical",m:"margin",mt:"marginTop",mr:"marginRight",mb:"marginBottom",ml:"marginLeft",mx:"marginHorizontal",my:"marginVertical",bgColor:"backgroundColor",bgImage:"backgroundImage",bgSize:"backgroundSize",bgGradient:"backgroundGradient"},Fr=["placeholderColor","refreshControlColor","tintColor","rippleColor","overlayColor","backgroundGradient"],Pr={display:"flex",overflow:"visible",scrollDirection:"vertical",borderWidth:0,borderTopWidth:0,borderRightWidth:0,borderBottomWidth:0,borderLeftWidth:0,borderHorizontalWidth:0,borderVerticalWidth:0,borderStartWidth:0,borderEndWidth:0,backgroundColor:0,borderColor:0,borderTopColor:0,borderRightColor:0,borderBottomColor:0,borderLeftColor:0,backgroundImage:"",backgroundSize:"cover",borderRadius:0,borderTopLeftRadius:0,borderTopRightRadius:0,borderBottomRightRadius:0,borderBottomLeftRadius:0,opacity:1,transform:"identity",transformOrigin:"50% 50%",pointerEvents:"all",fontSize:14,fontWeight:400,fontStyle:"normal",lineHeight:"normal",letterSpacing:0,textAlign:"start",textDecoration:"none",lineClamp:0,textOverflow:"ellipsis",objectFit:"cover",placeholder:"",type:"text",enterKey:"done",autocapitalize:"sentences",autocorrect:1,maxLength:0,keyboardShrink:1,keyboardDismiss:1,showScrollbar:1,overscrollMode:"default",keyboardDismissMode:"interactive",snap:"none",sheetDetents:"",sheetDetent:0,sheetDismissible:1,dim:0},Ir=["duration","delay","layer","commit","loop","loopMode","name"];function fn(e){let t=e.trim();/\sxmlns\s*=/.test(t)||(t=t.replace(/<svg\b/,'<svg xmlns="http://www.w3.org/2000/svg"'));const n=new TextEncoder().encode(t);let r="";for(const i of n)r+=String.fromCharCode(i);return`data:image/svg+xml;base64,${btoa(r)}`}function Dr(e){const t=e.match(/viewBox\s*=\s*["']([^"']+)["']/);return t?t[1]:null}const Br=/\b(?:calc|min|max|clamp)\(/,Vr=e=>{if(!Br.test(e))return e;let t="";for(let n=0;n<e.length;n++){if(e.startsWith("var(",n)){let i=0;const s=n;for(;n<e.length;n++)if(e[n]==="(")i++;else if(e[n]===")"&&--i===0)break;t+=e.slice(s,Math.min(n+1,e.length));continue}const r=e[n];if(r==="+"||r==="-"){const i=t[t.length-1]??"";if(!(r==="-"&&(i==="e"&&"btlr".includes(e[n+1]??"")||e.slice(Math.max(0,n-7),n)==="comfort"))&&/[\w%.)]/.test(i)){for(t=t.trimEnd()+" "+r+" ";e[n+1]===" ";)n++;continue}}t+=r}return t.replace(/\d*\.\d+|\d+/g,(n,r,i)=>{const s=i.slice(0,r).trimEnd(),o=i.slice(r+n.length).trimStart();return/[\w.%]$/.test(s)||/^[\w.%]/.test(o)||/[*/]$/.test(s)||/^[*/]/.test(o)?n:n+"px"})},zr={top:"12px",bottom:"4px",left:"16px",right:"16px"},dn=e=>`max(var(--safe-${e}, 0px), calc(var(--safe-${e}-bar, 0px) + var(--comfort-${e}, ${zr[e]})))`,Wr={vw:"cqw",vh:"cqh",vmin:"cqmin",vmax:"cqmax"},Re=e=>Vr(e).replace(new RegExp("(?<![-\\w])comfort-(top|bottom|left|right)\\b","g"),(t,n)=>dn(n)).replace(new RegExp("(?<![-\\w])safe-(top|bottom|left|right)\\b","g"),(t,n)=>`var(--safe-${n}, 0px)`).replace(/(\d)(vmin|vmax|vw|vh)\b/g,(t,n,r)=>n+Wr[r]),pt=/^-?\d*\.?\d+$/,se=e=>{if(typeof e=="number")return e+"px";const t=String(e).trim();return pt.test(t)?t+"px":Re(t)},Ce=e=>{if(typeof e=="number")return"#"+(e>>>0).toString(16).padStart(e<=16777215?6:8,"0");const t=String(e);return t==="clear"?"transparent":t},Gr=/^(rotate|skew)/,Hr=/^translate/,jr=e=>{const t=String(e).trim();return t===""||t==="identity"?"none":t.replace(/([a-zA-Z0-9]+)\s*\(([^)]*)\)/g,(n,r,i)=>{const s=Hr.test(r)?"px":Gr.test(r)?"deg":null;return s===null?n:`${r}(${i.split(",").map(o=>{const l=o.trim();return pt.test(l)?l+s:Re(l)}).join(", ")})`})},pn=e=>typeof e=="number"?e+"px":String(e).trim().split(/\s+/).map(t=>pt.test(t)?t+"px":Re(t)).join(" "),ne=0,re=1,_e=2,Ie=["top","right","bottom","left"],Kr=(e,t)=>e==="safe-all"?`var(--safe-${t}, 0px)`:e==="comfort-all"||e==="comfort-x"||e==="comfort-y"?dn(t):se(e),at=(e,t)=>{const n=(i,s)=>({rank:s,to:o=>i.map(l=>[t(l),Kr(o,l)])}),r=i=>({rank:re,to:s=>[[t(i),se(s)]]});return{[e]:n(Ie,ne),[`${e}Horizontal`]:n(["left","right"],re),[`${e}Vertical`]:n(["top","bottom"],re),[`${e}Start`]:r("inline-start"),[`${e}End`]:r("inline-end"),[`${e}Top`]:n(["top"],_e),[`${e}Right`]:n(["right"],_e),[`${e}Bottom`]:n(["bottom"],_e),[`${e}Left`]:n(["left"],_e)}},Yr=()=>{const e=at("border",n=>`border-${n}-width`),t={};for(const n of Object.keys(e))t[`${n}Width`]=e[n];return t},ht=e=>{const t=[];let n=0,r="";for(const i of e.trim()){if(i==="("&&n++,i===")"&&n--,/\s/.test(i)&&n===0){r&&t.push(r),r="";continue}r+=i}return r&&t.push(r),t},qr=new Set(["none","solid","dashed","dotted","double","hidden"]),Xr=/^-?\d*\.?\d+([a-z%]*)$|^(calc|min|max|clamp)\(/,we=(e,t)=>({rank:t,to:n=>{let r=null,i=null,s=null;if(typeof n=="number")r=n+"px";else for(const l of ht(String(n)))qr.has(l)?s=l:Xr.test(l)?r=se(l):i=Ce(l);const o=[];for(const l of e)o.push([`border-${l}-width`,r??(i?"1px":"0px")]),o.push([`border-${l}-color`,i??"transparent"]),s&&o.push([`border-${l}-style`,s]);return o}}),Jr=e=>{const t=String(e).trim(),n=(s,o,l)=>[["flex-grow",s],["flex-shrink",o],["flex-basis",l]];if(t==="none")return n("0","0","auto");if(t==="auto")return n("1","1","auto");if(t==="initial")return n("0","1","auto");const r=ht(t),i=s=>s!==void 0&&/^-?\d*\.?\d+$/.test(s);return i(r[0])?r.length===1?n(r[0],"1","0%"):i(r[1])?n(r[0],r[1],r[2]!==void 0?se(r[2]):"0%"):n(r[0],"1",se(r[1])):n("1","1",se(r[0]??"auto"))},hn=e=>{var t;return typeof e=="object"&&e!==null&&"_h"in e&&typeof((t=e._h)==null?void 0:t.id)=="number"},Qr=e=>e.replace(/<svg\b[^>]*>/,t=>t.replace(/\spreserveAspectRatio\s*=\s*("[^"]*"|'[^']*')/,"").replace(/^<svg\b/,'<svg preserveAspectRatio="none"')),mn=(e,t)=>{if(hn(e))return`url("${t.bufferUrl(e._h.id)}")`;const n=String(e??"");return n===""?"none":n.startsWith("id:")?`url("${t.bufferUrl(Number(n.slice(3)))}")`:n.startsWith("svg:")?`url("${fn(Qr(n.slice(4)))}")`:`url("${n.replace(/["\\\n]/g,r=>"\\"+r)}")`},Zr=/^\s*(repeating-)?(linear|radial|conic)-gradient\(/,gn=e=>{const t=String(e??"").trim();return t===""?"none":Re(t)},eo=(e,t)=>{const n=typeof e=="string"&&Zr.test(e),r=hn(e)||typeof e=="string"&&/^(id:|https?:|data:|blob:|\.{0,2}\/)/.test(e);return[["--lc-bg-grad",n?gn(e):"none"],["--lc-bg-img",r?mn(e,t):"none"],["background-color",n||r?"transparent":Ce(e)]]},to=e=>{const t=typeof e=="number"?[e+"px"]:ht(String(e)).map(o=>pn(o)),[n,r=n,i=n,s=r]=t;return[["border-top-left-radius",n??"0px"],["border-top-right-radius",r??"0px"],["border-bottom-right-radius",i??"0px"],["border-bottom-left-radius",s??"0px"]]},no=new Set(["serif","sans-serif","monospace","cursive","fantasy","system-ui","ui-monospace","ui-sans-serif","ui-serif","inherit"]),ro=e=>{const t=String(e).trim();return t==="monospaced"?"monospace":t.startsWith("var(")||t.includes(",")||/^["']/.test(t)||no.has(t)?t:`"${t.replace(/"/g,'\\"')}"`},oo=e=>e===!0||e==="true"||typeof e=="number"&&e>.5||e==="1",so=e=>t=>e[String(t)]??String(t),Ae={width:{css:"width",as:"len"},height:{css:"height",as:"len"},minWidth:{css:"min-width",as:"len"},minHeight:{css:"min-height",as:"len"},maxWidth:{css:"max-width",as:"len"},maxHeight:{css:"max-height",as:"len"},flexBasis:{css:"flex-basis",as:"len"},flexGrow:{css:"flex-grow",as:"num"},flexShrink:{css:"flex-shrink",as:"num"},aspectRatio:{css:"aspect-ratio",as:"num"},gap:{css:["row-gap","column-gap"],as:"len",rank:ne},rowGap:{css:"row-gap",as:"len"},columnGap:{css:"column-gap",as:"len"},...at("padding",e=>`padding-${e}`),...at("margin",e=>`margin-${e}`),inset:{css:Ie,as:"len",rank:ne},top:{css:"top",as:"len"},right:{css:"right",as:"len"},bottom:{css:"bottom",as:"len"},left:{css:"left",as:"len"},start:{css:"inset-inline-start",as:"len",rank:re},end:{css:"inset-inline-end",as:"len",rank:re},flexDirection:{css:"flex-direction",as:"raw"},justifyContent:{css:"justify-content",as:"raw"},alignItems:{css:"align-items",as:"raw"},alignSelf:{css:"align-self",as:"raw"},position:{css:"position",as:"raw"},flexWrap:{css:"flex-wrap",as:"raw"},boxSizing:{css:"box-sizing",as:"raw"},display:{css:"display",as:"raw"},overflow:{rank:ne,to:e=>{const t=e==="scroll"?"auto":String(e);return[["overflow-x",t],["overflow-y",t]]}},scrollDirection:{to:e=>e==="horizontal"?[["overflow-x","auto"],["overflow-y","hidden"],["flex-direction","row"]]:e==="all"?[["overflow-x","auto"],["overflow-y","auto"]]:[["overflow-x","hidden"],["overflow-y","auto"],["flex-direction","column"]],rank:re},...Yr(),color:{css:"color"},backgroundColor:{css:"background-color"},borderColor:{css:Ie.map(e=>`border-${e}-color`),rank:ne},borderTopColor:{css:"border-top-color"},borderRightColor:{css:"border-right-color"},borderBottomColor:{css:"border-bottom-color"},borderLeftColor:{css:"border-left-color"},placeholderColor:{css:"--lc-placeholder"},refreshControlColor:{css:"--lc-refresh"},tintColor:{css:"--lc-tint"},overlayColor:{css:"--lc-overlay"},rippleColor:{none:"a browser has no ripple: the press feedback of a page is `$pressed`"},backgroundImage:{to:(e,t)=>[["--lc-bg-img",mn(e,t)]]},backgroundGradient:{to:e=>[["--lc-bg-grad",gn(e)]]},backgroundSize:{to:e=>e==="tile"?[["background-size","auto"],["background-repeat","repeat"],["background-position","0 0"]]:[["background-size",e==="fill"?"100% 100%":String(e)],["background-repeat","no-repeat"],["background-position","center"]]},borderRadius:{to,rank:ne},borderTopLeftRadius:{css:"border-top-left-radius"},borderTopRightRadius:{css:"border-top-right-radius"},borderBottomRightRadius:{css:"border-bottom-right-radius"},borderBottomLeftRadius:{css:"border-bottom-left-radius"},opacity:{css:"opacity"},transform:{to:e=>[["transform",jr(e)]]},transformOrigin:{css:"transform-origin",as:"lens"},pointerEvents:{to:e=>[["pointer-events",e==="none"?"none":"auto"]]},dim:{css:"--lc-dim"},fontSize:{css:"font-size"},fontWeight:{css:"font-weight",as:"raw"},fontStyle:{to:e=>[["font-style",e===2?"italic":e===0?"normal":String(e)]]},fontFamily:{to:e=>[["font-family",ro(e)]]},lineHeight:{to:e=>[["line-height",e==="normal"?"1.2":se(e)]]},letterSpacing:{css:"letter-spacing"},textAlign:{css:"text-align"},textDecoration:{css:"text-decoration-line"},lineClamp:{rank:ne,to:e=>Number(e)>0?[["display","-webkit-box"],["-webkit-box-orient","vertical"],["-webkit-line-clamp",String(Math.round(Number(e)))],["overflow-x","hidden"],["overflow-y","hidden"]]:[["-webkit-line-clamp","none"]]},textOverflow:{css:"text-overflow"},objectFit:{to:e=>[["object-fit",String(e)],["--lc-fit",e==="fill"?"100% 100%":String(e)]]},placeholder:{attr:!0},type:{attr:!0},enterKey:{attr:!0},autocapitalize:{attr:!0},autocorrect:{attr:!0},maxLength:{attr:!0},keyboardShrink:{attr:!0},keyboardDismiss:{attr:!0},showScrollbar:{to:e=>[["scrollbar-width",oo(e)?"thin":"none"]]},overscrollMode:{to:e=>[["overscroll-behavior",so({default:"auto",none:"none",absorb:"contain"})(e)]]},keyboardDismissMode:{attr:!0},snap:{to:e=>e==="none"?[["scroll-snap-type","none"],["--lc-snap","none"]]:[["scroll-snap-type","both mandatory"],["--lc-snap",String(e)]]},sheetDetents:{attr:!0},sheetDetent:{attr:!0},sheetDismissible:{attr:!0},background:{to:eo,rank:ne},border:we(Ie,ne),borderTop:we(["top"],re),borderRight:we(["right"],re),borderBottom:we(["bottom"],re),borderLeft:we(["left"],re),flex:{to:Jr,rank:ne}},Xe=e=>Mr[e]??e,io=e=>e in Ae,bn=e=>{const t=Ae[e];return!!t&&"attr"in t},ao={overflow:"overflow-x",pointerEvents:"pointer-events",fontFamily:"font-family",fontStyle:"font-style",lineHeight:"line-height",objectFit:"object-fit",transform:"transform",borderRadius:"border-top-left-radius",borderWidth:"border-top-width",borderColor:"border-top-color",backgroundImage:"--lc-bg-img",backgroundGradient:"--lc-bg-grad",backgroundSize:"background-size",showScrollbar:"scrollbar-width",overscrollMode:"overscroll-behavior",snap:"--lc-snap",lineClamp:"-webkit-line-clamp",borderTopWidth:"border-top-width",borderRightWidth:"border-right-width",borderBottomWidth:"border-bottom-width",borderLeftWidth:"border-left-width",borderHorizontalWidth:"border-left-width",borderVerticalWidth:"border-top-width",borderStartWidth:"border-inline-start-width",borderEndWidth:"border-inline-end-width"},lo=e=>{const t=Ae[e];return t&&"css"in t&&typeof t.css=="string"?t.css:ao[e]??null},co={len:se,lens:pn,num:e=>String(e),raw:e=>String(e),color:Ce},uo={color:"color",len:"len",enum:"raw",num:"num",str:"raw",layout:"raw"},Mt=e=>{const t=Ae[e];return t&&"rank"in t&&t.rank!==void 0?t.rank:_e},Je=(e,t,n)=>{const r=[...e].filter(([,s])=>s!=null);r.sort((s,o)=>Mt(s[0])-Mt(o[0]));const i=new Map;for(const[s,o]of r){const l=Ae[s];if(!l){n==null||n(s);continue}if("attr"in l||"none"in l)continue;if("to"in l){for(const[y,k]of l.to(o,t))i.set(y,k);continue}const d=co[l.as??uo[Be[s]??"layout"]],f=d(o);if(typeof l.css=="string")i.set(l.css,f);else for(const y of l.css)i.set(y,f)}return i},Ft=e=>[...e].sort((t,n)=>t[0]<n[0]?-1:t[0]>n[0]?1:0).map(([t,n])=>`${t}:${n}`).join(";"),yn=0,vn=1,Le=2,xe=3,En=4,me=65,Ve=1.70158,Ue=Ve*1.525,Pt=Ve+1,It=2*Math.PI/3,Dt=2*Math.PI/4.5,Ne=e=>e<1/2.75?7.5625*e*e:e<2/2.75?7.5625*(e-=1.5/2.75)*e+.75:e<2.5/2.75?7.5625*(e-=2.25/2.75)*e+.9375:7.5625*(e-=2.625/2.75)*e+.984375,lt=[["linear",e=>e],["inSine",e=>1-Math.cos(e*Math.PI/2)],["outSine",e=>Math.sin(e*Math.PI/2)],["inOutSine",e=>-(Math.cos(Math.PI*e)-1)/2],["inQuad",e=>e*e],["outQuad",e=>1-(1-e)*(1-e)],["inOutQuad",e=>e<.5?2*e*e:1-(-2*e+2)**2/2],["inCubic",e=>e*e*e],["outCubic",e=>1-(1-e)**3],["inOutCubic",e=>e<.5?4*e*e*e:1-(-2*e+2)**3/2],["inQuart",e=>e**4],["outQuart",e=>1-(1-e)**4],["inOutQuart",e=>e<.5?8*e**4:1-(-2*e+2)**4/2],["inExpo",e=>e===0?0:2**(10*e-10)],["outExpo",e=>e===1?1:1-2**(-10*e)],["inOutExpo",e=>e===0?0:e===1?1:e<.5?2**(20*e-10)/2:(2-2**(-20*e+10))/2],["inCirc",e=>1-Math.sqrt(1-e*e)],["outCirc",e=>Math.sqrt(1-(e-1)**2)],["inOutCirc",e=>e<.5?(1-Math.sqrt(1-(2*e)**2))/2:(Math.sqrt(1-(-2*e+2)**2)+1)/2],["inBack",e=>Pt*e*e*e-Ve*e*e],["outBack",e=>1+Pt*(e-1)**3+Ve*(e-1)**2],["inOutBack",e=>e<.5?(2*e)**2*((Ue+1)*2*e-Ue)/2:((2*e-2)**2*((Ue+1)*(e*2-2)+Ue)+2)/2],["inElastic",e=>e===0?0:e===1?1:-(2**(10*e-10))*Math.sin((e*10-10.75)*It)],["outElastic",e=>e===0?0:e===1?1:2**(-10*e)*Math.sin((e*10-.75)*It)+1],["inOutElastic",e=>e===0?0:e===1?1:e<.5?-(2**(20*e-10)*Math.sin((20*e-11.125)*Dt))/2:2**(-20*e+10)*Math.sin((20*e-11.125)*Dt)/2+1],["inBounce",e=>1-Ne(1-e)],["outBounce",Ne],["inOutBounce",e=>e<.5?(1-Ne(1-2*e))/2:(1+Ne(2*e-1))/2],["smoothstep",e=>e*e*(3-2*e)]],fo={in:"inQuad",out:"outQuad",inOut:"inOutQuad",easeIn:"inQuad",easeOut:"outQuad",easeInOut:"inOutQuad","ease-in":"inQuad","ease-out":"outQuad","ease-in-out":"inOutQuad"},po=e=>{const t=fo[e]??e;for(let n=0;n<lt.length;n++)if(lt[n][0]===t)return n;return-1},ho=[.25,.1,.25,1],Me={kind:yn,p:[0,0,0,0]},mo=e=>{const t=new Float32Array(me);for(let n=0;n<me;n++)t[n]=e(n/(me-1));return t[0]=0,t[me-1]=1,t},go=e=>{if(e===void 0)return Me;if(typeof e=="function")return{kind:En,p:[0,0,0,0],table:mo(e)};if(Array.isArray(e)){const[i,s,o,l]=e;return{kind:Le,p:[i,s,o,l]}}const t=e.trim();if(t==="linear")return Me;if(t==="ease")return{kind:Le,p:ho};if(t==="steps")return{kind:xe,p:[1,0,0,0]};let n=/^steps\(\s*(\d+)\s*(?:,\s*(start|end|jump-start|jump-end)\s*)?\)$/.exec(t);if(n)return{kind:xe,p:[Math.max(1,parseInt(n[1],10)),n[2]==="start"||n[2]==="jump-start"?1:0,0,0]};if(n=/^cubic-bezier\(\s*([-\d.e]+)\s*,\s*([-\d.e]+)\s*,\s*([-\d.e]+)\s*,\s*([-\d.e]+)\s*\)$/.exec(t),n)return{kind:Le,p:[parseFloat(n[1]),parseFloat(n[2]),parseFloat(n[3]),parseFloat(n[4])]};const r=po(t);return r===0?Me:r>0?{kind:vn,p:[r,0,0,0]}:(console.warn(`[tween] unknown easing "${t}" — using linear`),Me)},bo=(e,t,n,r,i)=>{const s=1-3*r+3*t,o=3*r-6*t,l=3*t,d=1-3*i+3*n,f=3*i-6*n,y=3*n;let k=e;for(let A=0;A<8;A++){const _=((s*k+o)*k+l)*k-e,p=(3*s*k+2*o)*k+l;if(Math.abs(_)<1e-6||Math.abs(p)<1e-6)break;k-=_/p}return k=Math.min(1,Math.max(0,k)),((d*k+f)*k+y)*k},yo=(e,t)=>{if(t<=0)return e.kind===xe&&e.p[1]===1?1/e.p[0]:0;if(t>=1)return 1;switch(e.kind){case vn:return lt[e.p[0]][1](t);case Le:return bo(t,e.p[0],e.p[1],e.p[2],e.p[3]);case xe:{const n=e.p[0];return e.p[1]===1?Math.min(1,Math.ceil(t*n)/n):Math.floor(t*n)/n}case En:{const n=e.table,r=t*(me-1),i=Math.floor(r),s=n[i],o=n[Math.min(i+1,me-1)];return s+(o-s)*(r-i)}default:return t}},Bt=24,Vt=e=>String(Math.round(e*1e4)/1e4),vo="cubic-bezier(0.3333, 0, 0.6667, 1)",Eo=e=>{switch(e.kind){case yn:return"linear";case Le:return`cubic-bezier(${e.p.map(Vt).join(", ")})`;case xe:return`steps(${e.p[0]}, ${e.p[1]===1?"jump-start":"jump-end"})`}const t=[];for(let n=0;n<=Bt;n++)t.push(Vt(yo(e,n/Bt)));return`linear(${t.join(", ")})`},wo=e=>e===void 0||e==="smoothstep"?vo:Eo(go(e)),wn=["lc-base","lc-bag","lc-own","lc-orient","lc-class","lc-hover","lc-press","lc-focus"],So=["--lc-bg-grad","--lc-bg-img","--lc-tint","--lc-overlay","--lc-refresh","--lc-fit","--lc-src","--lc-src-tint","--lc-iw","--lc-ih","--lc-ir","--lc-crop-size","--lc-crop-pos"],Sn="lc-app",_o=`
2
+ ${e.stack}`:String(e);try{return JSON.stringify(e)}catch{return String(e)}},zn=(e,t)=>{const n=o=>{fetch(`${e}/log`,{method:"POST",body:JSON.stringify(o),headers:{"content-type":"application/json"},keepalive:!0}).catch(()=>{})};let r=-1;const i=async()=>{try{const{v:o}=await(await fetch(`${e}/version`,{cache:"no-store"})).json();typeof o=="number"&&t(o)}catch{}setTimeout(i,2500)};setTimeout(i,2500);const s=()=>{let o;try{o=new WebSocket(e.replace(/^http/,"ws")+"/ws")}catch{return}o.onopen=()=>o.send(JSON.stringify({type:"hello",v:r})),o.onmessage=l=>{let d={};try{d=JSON.parse(String(l.data))}catch{return}d.type==="reload"&&typeof d.v=="number"&&t(d.v)},o.onclose=()=>{setTimeout(s,2e3)}};return s(),{bundleUrl:o=>`${e}/bundle.js?t=web&v=${o}`,versionOf(o,l){const d=Number(o.headers.get("x-lecodes-v"));return o.headers.has("x-lecodes-v")&&Number.isFinite(d)?d:l},ran(o){r=o},log:(o,l)=>n({type:"log",level:o,text:l.map(Vn).join(" ")}),error:o=>n({type:"error",text:o})}},Wn=(e,t)=>{try{const n=new URL(e,t);return n.origin===new URL(t).origin&&(n.protocol==="http:"||n.protocol==="https:")?n.href:null}catch{return null}},Gn="modulepreload",Hn=function(e,t){return new URL(e,t).href},Et={},ce=function(t,n,r){let i=Promise.resolve();if(n&&n.length>0){let o=function(y){return Promise.all(y.map(k=>Promise.resolve(k).then(A=>({status:"fulfilled",value:A}),A=>({status:"rejected",reason:A}))))};const l=document.getElementsByTagName("link"),d=document.querySelector("meta[property=csp-nonce]"),f=(d==null?void 0:d.nonce)||(d==null?void 0:d.getAttribute("nonce"));i=o(n.map(y=>{if(y=Hn(y,r),y in Et)return;Et[y]=!0;const k=y.endsWith(".css"),A=k?'[rel="stylesheet"]':"";if(!!r)for(let E=l.length-1;E>=0;E--){const b=l[E];if(b.href===y&&(!k||b.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${y}"]${A}`))return;const p=document.createElement("link");if(p.rel=k?"stylesheet":Gn,k||(p.as="script"),p.crossOrigin="",p.href=y,f&&p.setAttribute("nonce",f),document.head.appendChild(p),k)return new Promise((E,b)=>{p.addEventListener("load",E),p.addEventListener("error",()=>b(new Error(`Unable to preload CSS for ${y}`)))})}))}function s(o){const l=new Event("vite:preloadError",{cancelable:!0});if(l.payload=o,window.dispatchEvent(l),!l.defaultPrevented)throw o}return i.then(o=>{for(const l of o||[])l.status==="rejected"&&s(l.reason);return t().catch(s)})},wt="2.1.0",Ke=e=>{const t=e==null?void 0:e.match(/^(\d+)\.\d+\.\d+/);return t?Number(t[1]):null},jn=(e,t)=>Ke(e)!==null&&Ke(e)===Ke(t),Kn=(e,t="woff2")=>{const n=/^\/\/ fonts: (.+)$/m.exec(e);if(!n)return[];const r=[];for(const i of n[1].split(";")){const s=i.indexOf("=");if(s===-1)continue;const o=/^(.+)@(\d+)(i?)$/.exec(i.slice(0,s));o&&r.push({family:o[1],weight:parseInt(o[2],10),italic:o[3]==="i",url:i.slice(s+1).replace("{fmt}",t)})}return r},Yn=async(e,t,n=2e3)=>{const r=t??(typeof document>"u"?void 0:document);if(!(r!=null&&r.fonts)||e.length===0)return;const i=[];for(const s of e)try{const o=new FontFace(s.family,`url("${s.url}")`,{weight:String(s.weight),style:s.italic?"italic":"normal"});r.fonts.add(o),i.push(o.load().catch(()=>{}))}catch{}await Promise.race([Promise.all(i),new Promise(s=>setTimeout(s,n))])},qn=e=>{var d,f,y,k;const t=e.slice(0,2e3),n=new Set,r=t.match(/\/\/ preload: (.+)/);if(r)for(const A of r[1].split(";"))n.add(A);const i=t.includes("// gl: disable")?"off":t.includes("// gl: defer")?"defer":"on",s=((d=t.match(/^\/\/ sdk: (\d+\.\d+\.\d+\S*)/m))==null?void 0:d[1])??null,o=((y=(f=t.match(/^\/\/ name: (.+)$/m))==null?void 0:f[1])==null?void 0:y.trim())??null,l=new Set((((k=t.match(/^\/\/ plugins: (.+)$/m))==null?void 0:k[1])??"").split(";").map(A=>A.trim()).filter(Boolean));return{sdk:s,name:o,preloads:n,gl:i,is2d:t.includes("// 2d: enable"),plugins:l,bootFonts:Kn(t)}},Xn=1,Jn=2,Qn=3,Zn=4,er=5,tr=6,St=7,nr=8,_t=9,rr=10,or=11,sr=12,ir=13,ar=14,lr=15,cr=16,kt=17,ur=18,fr=19,Oe=20,ai=21,li=22,ci=23,ui=24,fi=25,dr=26,pr=27,hr=28,mr=29,Tt=30,Lt=31,gr=32,br=33,xt=34,Rt=35,Ct=36,Pe=1,yr=2,vr=4,de=8,Er=16,Ye=32,wr=64,qe=128,Sr=256,_r=512,kr=1024,Tr=2048,Lr=4096,At=8192,di=16384,xr=32768,Rr=1<<18,Cr=255,$t=256,Ot=512,Ar=1024,Q=0,$r=1,Or=2,Ur=3,De=4,be=255,un=e=>{const t=e.clientWidth;if(!t)return 1;const n=e.getBoundingClientRect().width;return n>0?n/t:1},Ut={text:{type:"text"},password:{type:"password"},search:{type:"search"},tel:{type:"tel"},email:{type:"email"},url:{type:"url"},number:{type:"text",mode:"numeric"},decimal:{type:"text",mode:"decimal"},date:{type:"date"},time:{type:"time"}},Nt=(e,t)=>e==null?t:e===!0||e==="true"||e==="1"||typeof e=="number"&&e>.5,oe=(e,t,n)=>{n===null?e.removeAttribute(t):e.getAttribute(t)!==n&&e.setAttribute(t,n)},Nr=(e,t)=>{if(e.type!=="input"&&e.type!=="textarea")return;const n=e.el,r=t("placeholder");if(oe(n,"placeholder",r==null?null:String(r)),e.type==="input"){const l=Ut[String(t("type")??"text")]??Ut.text,d=n;d.type!==l.type&&(d.type=l.type),oe(n,"inputmode",l.mode??null);const f=t("enterKey");oe(n,"enterkeyhint",f==null?null:String(f))}const i=t("autocapitalize");oe(n,"autocapitalize",i==null?null:String(i));const s=t("autocorrect");s==null?(oe(n,"autocorrect",null),oe(n,"spellcheck",null)):(oe(n,"autocorrect",Nt(s,!0)?"on":"off"),oe(n,"spellcheck",Nt(s,!0)?"true":"false"));const o=Number(t("maxLength")??0);oe(n,"maxlength",o>0?String(Math.round(o)):null)},Be={width:"layout",height:"layout",minWidth:"layout",minHeight:"layout",maxWidth:"layout",maxHeight:"layout",flexBasis:"layout",flexGrow:"layout",flexShrink:"layout",aspectRatio:"layout",gap:"layout",rowGap:"layout",columnGap:"layout",padding:"layout",paddingTop:"layout",paddingRight:"layout",paddingBottom:"layout",paddingLeft:"layout",paddingHorizontal:"layout",paddingVertical:"layout",paddingStart:"layout",paddingEnd:"layout",margin:"layout",marginTop:"layout",marginRight:"layout",marginBottom:"layout",marginLeft:"layout",marginHorizontal:"layout",marginVertical:"layout",marginStart:"layout",marginEnd:"layout",left:"layout",right:"layout",top:"layout",bottom:"layout",start:"layout",end:"layout",inset:"layout",flexDirection:"enum",justifyContent:"enum",alignItems:"enum",alignSelf:"enum",position:"enum",flexWrap:"enum",boxSizing:"enum",display:"enum",overflow:"enum",scrollDirection:"enum",borderWidth:"len",borderTopWidth:"len",borderRightWidth:"len",borderBottomWidth:"len",borderLeftWidth:"len",borderHorizontalWidth:"len",borderVerticalWidth:"len",borderStartWidth:"len",borderEndWidth:"len",color:"color",backgroundColor:"color",borderColor:"color",borderTopColor:"color",borderRightColor:"color",borderBottomColor:"color",borderLeftColor:"color",placeholderColor:"color",refreshControlColor:"color",tintColor:"color",rippleColor:"color",overlayColor:"color",backgroundImage:"str",backgroundSize:"enum",backgroundGradient:"str",borderRadius:"len",borderTopLeftRadius:"len",borderTopRightRadius:"len",borderBottomRightRadius:"len",borderBottomLeftRadius:"len",opacity:"num",transform:"str",transformOrigin:"str",pointerEvents:"enum",fontSize:"len",fontWeight:"num",fontStyle:"enum",fontFamily:"str",lineHeight:"len",letterSpacing:"len",textAlign:"enum",textDecoration:"enum",lineClamp:"num",textOverflow:"enum",objectFit:"enum",placeholder:"str",type:"enum",enterKey:"enum",autocapitalize:"enum",autocorrect:"num",maxLength:"num",keyboardShrink:"num",keyboardDismiss:"num",showScrollbar:"num",overscrollMode:"enum",keyboardDismissMode:"enum",snap:"enum",sheetDetents:"str",sheetDetent:"num",sheetDismissible:"num",dim:"num"},Mr={p:"padding",pt:"paddingTop",pr:"paddingRight",pb:"paddingBottom",pl:"paddingLeft",px:"paddingHorizontal",py:"paddingVertical",m:"margin",mt:"marginTop",mr:"marginRight",mb:"marginBottom",ml:"marginLeft",mx:"marginHorizontal",my:"marginVertical",bgColor:"backgroundColor",bgImage:"backgroundImage",bgSize:"backgroundSize",bgGradient:"backgroundGradient"},Fr=["placeholderColor","refreshControlColor","tintColor","rippleColor","overlayColor","backgroundGradient"],Pr={display:"flex",overflow:"visible",scrollDirection:"vertical",borderWidth:0,borderTopWidth:0,borderRightWidth:0,borderBottomWidth:0,borderLeftWidth:0,borderHorizontalWidth:0,borderVerticalWidth:0,borderStartWidth:0,borderEndWidth:0,backgroundColor:0,borderColor:0,borderTopColor:0,borderRightColor:0,borderBottomColor:0,borderLeftColor:0,backgroundImage:"",backgroundSize:"cover",borderRadius:0,borderTopLeftRadius:0,borderTopRightRadius:0,borderBottomRightRadius:0,borderBottomLeftRadius:0,opacity:1,transform:"identity",transformOrigin:"50% 50%",pointerEvents:"all",fontSize:14,fontWeight:400,fontStyle:"normal",lineHeight:"normal",letterSpacing:0,textAlign:"start",textDecoration:"none",lineClamp:0,textOverflow:"ellipsis",objectFit:"cover",placeholder:"",type:"text",enterKey:"done",autocapitalize:"sentences",autocorrect:1,maxLength:0,keyboardShrink:1,keyboardDismiss:1,showScrollbar:1,overscrollMode:"default",keyboardDismissMode:"interactive",snap:"none",sheetDetents:"",sheetDetent:0,sheetDismissible:1,dim:0},Ir=["duration","delay","layer","commit","loop","loopMode","name"];function fn(e){let t=e.trim();/\sxmlns\s*=/.test(t)||(t=t.replace(/<svg\b/,'<svg xmlns="http://www.w3.org/2000/svg"'));const n=new TextEncoder().encode(t);let r="";for(const i of n)r+=String.fromCharCode(i);return`data:image/svg+xml;base64,${btoa(r)}`}function Dr(e){const t=e.match(/viewBox\s*=\s*["']([^"']+)["']/);return t?t[1]:null}const Br=/\b(?:calc|min|max|clamp)\(/,Vr=e=>{if(!Br.test(e))return e;let t="";for(let n=0;n<e.length;n++){if(e.startsWith("var(",n)){let i=0;const s=n;for(;n<e.length;n++)if(e[n]==="(")i++;else if(e[n]===")"&&--i===0)break;t+=e.slice(s,Math.min(n+1,e.length));continue}const r=e[n];if(r==="+"||r==="-"){const i=t[t.length-1]??"";if(!(r==="-"&&(i==="e"&&"btlr".includes(e[n+1]??"")||e.slice(Math.max(0,n-7),n)==="comfort"))&&/[\w%.)]/.test(i)){for(t=t.trimEnd()+" "+r+" ";e[n+1]===" ";)n++;continue}}t+=r}return t.replace(/\d*\.\d+|\d+/g,(n,r,i)=>{const s=i.slice(0,r).trimEnd(),o=i.slice(r+n.length).trimStart();return/[\w.%]$/.test(s)||/^[\w.%]/.test(o)||/[*/]$/.test(s)||/^[*/]/.test(o)?n:n+"px"})},zr={top:"12px",bottom:"4px",left:"16px",right:"16px"},dn=e=>`max(var(--safe-${e}, 0px), calc(var(--safe-${e}-bar, 0px) + var(--comfort-${e}, ${zr[e]})))`,Wr={vw:"cqw",vh:"cqh",vmin:"cqmin",vmax:"cqmax"},Re=e=>Vr(e).replace(new RegExp("(?<![-\\w])comfort-(top|bottom|left|right)\\b","g"),(t,n)=>dn(n)).replace(new RegExp("(?<![-\\w])safe-(top|bottom|left|right)\\b","g"),(t,n)=>`var(--safe-${n}, 0px)`).replace(/(\d)(vmin|vmax|vw|vh)\b/g,(t,n,r)=>n+Wr[r]),pt=/^-?\d*\.?\d+$/,se=e=>{if(typeof e=="number")return e+"px";const t=String(e).trim();return pt.test(t)?t+"px":Re(t)},Ce=e=>{if(typeof e=="number")return"#"+(e>>>0).toString(16).padStart(e<=16777215?6:8,"0");const t=String(e);return t==="clear"?"transparent":t},Gr=/^(rotate|skew)/,Hr=/^translate/,jr=e=>{const t=String(e).trim();return t===""||t==="identity"?"none":t.replace(/([a-zA-Z0-9]+)\s*\(([^)]*)\)/g,(n,r,i)=>{const s=Hr.test(r)?"px":Gr.test(r)?"deg":null;return s===null?n:`${r}(${i.split(",").map(o=>{const l=o.trim();return pt.test(l)?l+s:Re(l)}).join(", ")})`})},pn=e=>typeof e=="number"?e+"px":String(e).trim().split(/\s+/).map(t=>pt.test(t)?t+"px":Re(t)).join(" "),ne=0,re=1,_e=2,Ie=["top","right","bottom","left"],Kr=(e,t)=>e==="safe-all"?`var(--safe-${t}, 0px)`:e==="comfort-all"||e==="comfort-x"||e==="comfort-y"?dn(t):se(e),at=(e,t)=>{const n=(i,s)=>({rank:s,to:o=>i.map(l=>[t(l),Kr(o,l)])}),r=i=>({rank:re,to:s=>[[t(i),se(s)]]});return{[e]:n(Ie,ne),[`${e}Horizontal`]:n(["left","right"],re),[`${e}Vertical`]:n(["top","bottom"],re),[`${e}Start`]:r("inline-start"),[`${e}End`]:r("inline-end"),[`${e}Top`]:n(["top"],_e),[`${e}Right`]:n(["right"],_e),[`${e}Bottom`]:n(["bottom"],_e),[`${e}Left`]:n(["left"],_e)}},Yr=()=>{const e=at("border",n=>`border-${n}-width`),t={};for(const n of Object.keys(e))t[`${n}Width`]=e[n];return t},ht=e=>{const t=[];let n=0,r="";for(const i of e.trim()){if(i==="("&&n++,i===")"&&n--,/\s/.test(i)&&n===0){r&&t.push(r),r="";continue}r+=i}return r&&t.push(r),t},qr=new Set(["none","solid","dashed","dotted","double","hidden"]),Xr=/^-?\d*\.?\d+([a-z%]*)$|^(calc|min|max|clamp)\(/,we=(e,t)=>({rank:t,to:n=>{let r=null,i=null,s=null;if(typeof n=="number")r=n+"px";else for(const l of ht(String(n)))qr.has(l)?s=l:Xr.test(l)?r=se(l):i=Ce(l);const o=[];for(const l of e)o.push([`border-${l}-width`,r??(i?"1px":"0px")]),o.push([`border-${l}-color`,i??"transparent"]),s&&o.push([`border-${l}-style`,s]);return o}}),Jr=e=>{const t=String(e).trim(),n=(s,o,l)=>[["flex-grow",s],["flex-shrink",o],["flex-basis",l]];if(t==="none")return n("0","0","auto");if(t==="auto")return n("1","1","auto");if(t==="initial")return n("0","1","auto");const r=ht(t),i=s=>s!==void 0&&/^-?\d*\.?\d+$/.test(s);return i(r[0])?r.length===1?n(r[0],"1","0%"):i(r[1])?n(r[0],r[1],r[2]!==void 0?se(r[2]):"0%"):n(r[0],"1",se(r[1])):n("1","1",se(r[0]??"auto"))},hn=e=>{var t;return typeof e=="object"&&e!==null&&"_h"in e&&typeof((t=e._h)==null?void 0:t.id)=="number"},Qr=e=>e.replace(/<svg\b[^>]*>/,t=>t.replace(/\spreserveAspectRatio\s*=\s*("[^"]*"|'[^']*')/,"").replace(/^<svg\b/,'<svg preserveAspectRatio="none"')),mn=(e,t)=>{if(hn(e))return`url("${t.bufferUrl(e._h.id)}")`;const n=String(e??"");return n===""?"none":n.startsWith("id:")?`url("${t.bufferUrl(Number(n.slice(3)))}")`:n.startsWith("svg:")?`url("${fn(Qr(n.slice(4)))}")`:`url("${n.replace(/["\\\n]/g,r=>"\\"+r)}")`},Zr=/^\s*(repeating-)?(linear|radial|conic)-gradient\(/,gn=e=>{const t=String(e??"").trim();return t===""?"none":Re(t)},eo=(e,t)=>{const n=typeof e=="string"&&Zr.test(e),r=hn(e)||typeof e=="string"&&/^(id:|https?:|data:|blob:|\.{0,2}\/)/.test(e);return[["--lc-bg-grad",n?gn(e):"none"],["--lc-bg-img",r?mn(e,t):"none"],["background-color",n||r?"transparent":Ce(e)]]},to=e=>{const t=typeof e=="number"?[e+"px"]:ht(String(e)).map(o=>pn(o)),[n,r=n,i=n,s=r]=t;return[["border-top-left-radius",n??"0px"],["border-top-right-radius",r??"0px"],["border-bottom-right-radius",i??"0px"],["border-bottom-left-radius",s??"0px"]]},no=new Set(["serif","sans-serif","monospace","cursive","fantasy","system-ui","ui-monospace","ui-sans-serif","ui-serif","inherit"]),ro=e=>{const t=String(e).trim();return t==="monospaced"?"monospace":t.startsWith("var(")||t.includes(",")||/^["']/.test(t)||no.has(t)?t:`"${t.replace(/"/g,'\\"')}"`},oo=e=>e===!0||e==="true"||typeof e=="number"&&e>.5||e==="1",so=e=>t=>e[String(t)]??String(t),Ae={width:{css:"width",as:"len"},height:{css:"height",as:"len"},minWidth:{css:"min-width",as:"len"},minHeight:{css:"min-height",as:"len"},maxWidth:{css:"max-width",as:"len"},maxHeight:{css:"max-height",as:"len"},flexBasis:{css:"flex-basis",as:"len"},flexGrow:{css:"flex-grow",as:"num"},flexShrink:{css:"flex-shrink",as:"num"},aspectRatio:{css:"aspect-ratio",as:"num"},gap:{css:["row-gap","column-gap"],as:"len",rank:ne},rowGap:{css:"row-gap",as:"len"},columnGap:{css:"column-gap",as:"len"},...at("padding",e=>`padding-${e}`),...at("margin",e=>`margin-${e}`),inset:{css:Ie,as:"len",rank:ne},top:{css:"top",as:"len"},right:{css:"right",as:"len"},bottom:{css:"bottom",as:"len"},left:{css:"left",as:"len"},start:{css:"inset-inline-start",as:"len",rank:re},end:{css:"inset-inline-end",as:"len",rank:re},flexDirection:{css:"flex-direction",as:"raw"},justifyContent:{css:"justify-content",as:"raw"},alignItems:{css:"align-items",as:"raw"},alignSelf:{css:"align-self",as:"raw"},position:{css:"position",as:"raw"},flexWrap:{css:"flex-wrap",as:"raw"},boxSizing:{css:"box-sizing",as:"raw"},display:{css:"display",as:"raw"},overflow:{rank:ne,to:e=>{const t=e==="scroll"?"auto":String(e);return[["overflow-x",t],["overflow-y",t]]}},scrollDirection:{to:e=>e==="horizontal"?[["overflow-x","auto"],["overflow-y","hidden"],["flex-direction","row"]]:e==="all"?[["overflow-x","auto"],["overflow-y","auto"]]:[["overflow-x","hidden"],["overflow-y","auto"],["flex-direction","column"]],rank:re},...Yr(),color:{css:"color"},backgroundColor:{css:"background-color"},borderColor:{css:Ie.map(e=>`border-${e}-color`),rank:ne},borderTopColor:{css:"border-top-color"},borderRightColor:{css:"border-right-color"},borderBottomColor:{css:"border-bottom-color"},borderLeftColor:{css:"border-left-color"},placeholderColor:{css:"--lc-placeholder"},refreshControlColor:{css:"--lc-refresh"},tintColor:{css:"--lc-tint"},overlayColor:{css:"--lc-overlay"},rippleColor:{none:"a browser has no ripple: the press feedback of a page is `$pressed`"},backgroundImage:{to:(e,t)=>[["--lc-bg-img",mn(e,t)]]},backgroundGradient:{to:e=>[["--lc-bg-grad",gn(e)]]},backgroundSize:{to:e=>e==="tile"?[["background-size","auto"],["background-repeat","repeat"],["background-position","0 0"]]:[["background-size",e==="fill"?"100% 100%":String(e)],["background-repeat","no-repeat"],["background-position","center"]]},borderRadius:{to,rank:ne},borderTopLeftRadius:{css:"border-top-left-radius"},borderTopRightRadius:{css:"border-top-right-radius"},borderBottomRightRadius:{css:"border-bottom-right-radius"},borderBottomLeftRadius:{css:"border-bottom-left-radius"},opacity:{css:"opacity"},transform:{to:e=>[["transform",jr(e)]]},transformOrigin:{css:"transform-origin",as:"lens"},pointerEvents:{to:e=>[["pointer-events",e==="none"?"none":"auto"]]},dim:{css:"--lc-dim"},fontSize:{css:"font-size"},fontWeight:{css:"font-weight",as:"raw"},fontStyle:{to:e=>[["font-style",e===2?"italic":e===0?"normal":String(e)]]},fontFamily:{to:e=>[["font-family",ro(e)]]},lineHeight:{to:e=>[["line-height",e==="normal"?"1.2":se(e)]]},letterSpacing:{css:"letter-spacing"},textAlign:{css:"text-align"},textDecoration:{css:"text-decoration-line"},lineClamp:{rank:ne,to:e=>Number(e)>0?[["display","-webkit-box"],["-webkit-box-orient","vertical"],["-webkit-line-clamp",String(Math.round(Number(e)))],["overflow-x","hidden"],["overflow-y","hidden"]]:[["-webkit-line-clamp","none"]]},textOverflow:{css:"text-overflow"},objectFit:{to:e=>[["object-fit",String(e)],["--lc-fit",e==="fill"?"100% 100%":String(e)]]},placeholder:{attr:!0},type:{attr:!0},enterKey:{attr:!0},autocapitalize:{attr:!0},autocorrect:{attr:!0},maxLength:{attr:!0},keyboardShrink:{attr:!0},keyboardDismiss:{attr:!0},showScrollbar:{to:e=>[["scrollbar-width",oo(e)?"thin":"none"]]},overscrollMode:{to:e=>[["overscroll-behavior",so({default:"auto",none:"none",absorb:"contain"})(e)]]},keyboardDismissMode:{attr:!0},snap:{to:e=>e==="none"?[["scroll-snap-type","none"],["--lc-snap","none"]]:[["scroll-snap-type","both mandatory"],["--lc-snap",String(e)]]},sheetDetents:{attr:!0},sheetDetent:{attr:!0},sheetDismissible:{attr:!0},background:{to:eo,rank:ne},border:we(Ie,ne),borderTop:we(["top"],re),borderRight:we(["right"],re),borderBottom:we(["bottom"],re),borderLeft:we(["left"],re),flex:{to:Jr,rank:ne}},Xe=e=>Mr[e]??e,io=e=>e in Ae,bn=e=>{const t=Ae[e];return!!t&&"attr"in t},ao={overflow:"overflow-x",pointerEvents:"pointer-events",fontFamily:"font-family",fontStyle:"font-style",lineHeight:"line-height",objectFit:"object-fit",transform:"transform",borderRadius:"border-top-left-radius",borderWidth:"border-top-width",borderColor:"border-top-color",backgroundImage:"--lc-bg-img",backgroundGradient:"--lc-bg-grad",backgroundSize:"background-size",showScrollbar:"scrollbar-width",overscrollMode:"overscroll-behavior",snap:"--lc-snap",lineClamp:"-webkit-line-clamp",borderTopWidth:"border-top-width",borderRightWidth:"border-right-width",borderBottomWidth:"border-bottom-width",borderLeftWidth:"border-left-width",borderHorizontalWidth:"border-left-width",borderVerticalWidth:"border-top-width",borderStartWidth:"border-inline-start-width",borderEndWidth:"border-inline-end-width"},lo=e=>{const t=Ae[e];return t&&"css"in t&&typeof t.css=="string"?t.css:ao[e]??null},co={len:se,lens:pn,num:e=>String(e),raw:e=>String(e),color:Ce},uo={color:"color",len:"len",enum:"raw",num:"num",str:"raw",layout:"raw"},Mt=e=>{const t=Ae[e];return t&&"rank"in t&&t.rank!==void 0?t.rank:_e},Je=(e,t,n)=>{const r=[...e].filter(([,s])=>s!=null);r.sort((s,o)=>Mt(s[0])-Mt(o[0]));const i=new Map;for(const[s,o]of r){const l=Ae[s];if(!l){n==null||n(s);continue}if("attr"in l||"none"in l)continue;if("to"in l){for(const[y,k]of l.to(o,t))i.set(y,k);continue}const d=co[l.as??uo[Be[s]??"layout"]],f=d(o);if(typeof l.css=="string")i.set(l.css,f);else for(const y of l.css)i.set(y,f)}return i},Ft=e=>[...e].sort((t,n)=>t[0]<n[0]?-1:t[0]>n[0]?1:0).map(([t,n])=>`${t}:${n}`).join(";"),yn=0,vn=1,Le=2,xe=3,En=4,me=65,Ve=1.70158,Ue=Ve*1.525,Pt=Ve+1,It=2*Math.PI/3,Dt=2*Math.PI/4.5,Ne=e=>e<1/2.75?7.5625*e*e:e<2/2.75?7.5625*(e-=1.5/2.75)*e+.75:e<2.5/2.75?7.5625*(e-=2.25/2.75)*e+.9375:7.5625*(e-=2.625/2.75)*e+.984375,lt=[["linear",e=>e],["inSine",e=>1-Math.cos(e*Math.PI/2)],["outSine",e=>Math.sin(e*Math.PI/2)],["inOutSine",e=>-(Math.cos(Math.PI*e)-1)/2],["inQuad",e=>e*e],["outQuad",e=>1-(1-e)*(1-e)],["inOutQuad",e=>e<.5?2*e*e:1-(-2*e+2)**2/2],["inCubic",e=>e*e*e],["outCubic",e=>1-(1-e)**3],["inOutCubic",e=>e<.5?4*e*e*e:1-(-2*e+2)**3/2],["inQuart",e=>e**4],["outQuart",e=>1-(1-e)**4],["inOutQuart",e=>e<.5?8*e**4:1-(-2*e+2)**4/2],["inExpo",e=>e===0?0:2**(10*e-10)],["outExpo",e=>e===1?1:1-2**(-10*e)],["inOutExpo",e=>e===0?0:e===1?1:e<.5?2**(20*e-10)/2:(2-2**(-20*e+10))/2],["inCirc",e=>1-Math.sqrt(1-e*e)],["outCirc",e=>Math.sqrt(1-(e-1)**2)],["inOutCirc",e=>e<.5?(1-Math.sqrt(1-(2*e)**2))/2:(Math.sqrt(1-(-2*e+2)**2)+1)/2],["inBack",e=>Pt*e*e*e-Ve*e*e],["outBack",e=>1+Pt*(e-1)**3+Ve*(e-1)**2],["inOutBack",e=>e<.5?(2*e)**2*((Ue+1)*2*e-Ue)/2:((2*e-2)**2*((Ue+1)*(e*2-2)+Ue)+2)/2],["inElastic",e=>e===0?0:e===1?1:-(2**(10*e-10))*Math.sin((e*10-10.75)*It)],["outElastic",e=>e===0?0:e===1?1:2**(-10*e)*Math.sin((e*10-.75)*It)+1],["inOutElastic",e=>e===0?0:e===1?1:e<.5?-(2**(20*e-10)*Math.sin((20*e-11.125)*Dt))/2:2**(-20*e+10)*Math.sin((20*e-11.125)*Dt)/2+1],["inBounce",e=>1-Ne(1-e)],["outBounce",Ne],["inOutBounce",e=>e<.5?(1-Ne(1-2*e))/2:(1+Ne(2*e-1))/2],["smoothstep",e=>e*e*(3-2*e)]],fo={in:"inQuad",out:"outQuad",inOut:"inOutQuad",easeIn:"inQuad",easeOut:"outQuad",easeInOut:"inOutQuad","ease-in":"inQuad","ease-out":"outQuad","ease-in-out":"inOutQuad"},po=e=>{const t=fo[e]??e;for(let n=0;n<lt.length;n++)if(lt[n][0]===t)return n;return-1},ho=[.25,.1,.25,1],Me={kind:yn,p:[0,0,0,0]},mo=e=>{const t=new Float32Array(me);for(let n=0;n<me;n++)t[n]=e(n/(me-1));return t[0]=0,t[me-1]=1,t},go=e=>{if(e===void 0)return Me;if(typeof e=="function")return{kind:En,p:[0,0,0,0],table:mo(e)};if(Array.isArray(e)){const[i,s,o,l]=e;return{kind:Le,p:[i,s,o,l]}}const t=e.trim();if(t==="linear")return Me;if(t==="ease")return{kind:Le,p:ho};if(t==="steps")return{kind:xe,p:[1,0,0,0]};let n=/^steps\(\s*(\d+)\s*(?:,\s*(start|end|jump-start|jump-end)\s*)?\)$/.exec(t);if(n)return{kind:xe,p:[Math.max(1,parseInt(n[1],10)),n[2]==="start"||n[2]==="jump-start"?1:0,0,0]};if(n=/^cubic-bezier\(\s*([-\d.e]+)\s*,\s*([-\d.e]+)\s*,\s*([-\d.e]+)\s*,\s*([-\d.e]+)\s*\)$/.exec(t),n)return{kind:Le,p:[parseFloat(n[1]),parseFloat(n[2]),parseFloat(n[3]),parseFloat(n[4])]};const r=po(t);return r===0?Me:r>0?{kind:vn,p:[r,0,0,0]}:(console.warn(`[tween] unknown easing "${t}" — using linear`),Me)},bo=(e,t,n,r,i)=>{const s=1-3*r+3*t,o=3*r-6*t,l=3*t,d=1-3*i+3*n,f=3*i-6*n,y=3*n;let k=e;for(let A=0;A<8;A++){const _=((s*k+o)*k+l)*k-e,p=(3*s*k+2*o)*k+l;if(Math.abs(_)<1e-6||Math.abs(p)<1e-6)break;k-=_/p}return k=Math.min(1,Math.max(0,k)),((d*k+f)*k+y)*k},yo=(e,t)=>{if(t<=0)return e.kind===xe&&e.p[1]===1?1/e.p[0]:0;if(t>=1)return 1;switch(e.kind){case vn:return lt[e.p[0]][1](t);case Le:return bo(t,e.p[0],e.p[1],e.p[2],e.p[3]);case xe:{const n=e.p[0];return e.p[1]===1?Math.min(1,Math.ceil(t*n)/n):Math.floor(t*n)/n}case En:{const n=e.table,r=t*(me-1),i=Math.floor(r),s=n[i],o=n[Math.min(i+1,me-1)];return s+(o-s)*(r-i)}default:return t}},Bt=24,Vt=e=>String(Math.round(e*1e4)/1e4),vo="cubic-bezier(0.3333, 0, 0.6667, 1)",Eo=e=>{switch(e.kind){case yn:return"linear";case Le:return`cubic-bezier(${e.p.map(Vt).join(", ")})`;case xe:return`steps(${e.p[0]}, ${e.p[1]===1?"jump-start":"jump-end"})`}const t=[];for(let n=0;n<=Bt;n++)t.push(Vt(yo(e,n/Bt)));return`linear(${t.join(", ")})`},wo=e=>e===void 0||e==="smoothstep"?vo:Eo(go(e)),wn=["lc-base","lc-bag","lc-own","lc-orient","lc-class","lc-hover","lc-press","lc-focus"],So=["--lc-bg-grad","--lc-bg-img","--lc-tint","--lc-overlay","--lc-refresh","--lc-fit","--lc-src","--lc-src-tint","--lc-iw","--lc-ih","--lc-ir","--lc-crop-size","--lc-crop-pos"],Sn="lc-app",_o=`
3
3
  @layer ${wn.join(", ")};
4
4
 
5
5
  @layer lc-base {
@@ -10,7 +10,7 @@ import { upsertDotEnv } from "../../project/env"
10
10
  import {
11
11
  type AppConfig, SDK_VARIANTS, VERSION_RE, childEnv, cmpVersion, ensureShellIgnored, fetchLatestSdkVersion,
12
12
  projectRoot, readJson, resolvePlugin, resolveSdkVersion, resolveVariant, runTool, sdkSatisfies, slugify,
13
- writeJson, type LockEntry,
13
+ writeJson, type LockEntry, resolveUpdateUrl,
14
14
  } from "./shared"
15
15
  import { loadIconMaster, restampManifestIcon, writeAndroidIcons } from "./icon"
16
16
  import {
@@ -19,7 +19,7 @@ import {
19
19
  appManifestTemplate, appViewModelTemplate, gradleWrapperProperties, mainActivityTemplate,
20
20
  pluginBuildGradleTemplate, rootBuildGradleTemplate, runtimeBuildGradleTemplate,
21
21
  runtimeKotlinTemplate, settingsGradleTemplate, stringsXmlTemplate, themesXmlTemplate,
22
- type AndroidRuntimePlugin, type AndroidShellIdentity,
22
+ type AndroidRuntimePlugin, type AndroidShellIdentity, runtimeManifestTemplate,
23
23
  } from "./templates/android"
24
24
  import { GRADLEW_BAT, GRADLEW_SH } from "./templates/gradlew"
25
25
  import { type SdkVariant } from "./templates/ios"
@@ -33,10 +33,12 @@ import { type SdkVariant } from "./templates/ios"
33
33
 
34
34
  export const ANDROID_SDK_REPO = "letary/lecodes-android-sdk"
35
35
 
36
- /** Oldest lecodes-android-sdk a generated shell can use — 1.2.0 moved system-back wiring into
37
- * engine.attach() and the template dropped its own dispatcher block, so an older SDK under
38
- * this template would leave the back button dead. */
39
- export const ANDROID_SDK_MIN_VERSION = "1.2.0"
36
+ /** Oldest lecodes-android-sdk a generated shell can use — 2.0.13 = the first 2.x release (the fat
37
+ * AAR of hosts/android's :lecodes over runtime/native: the tree of truth, the host tables,
38
+ * :lecodes-ui). A 2.x bundle is refused by a 1.x runtime (`_creatorApp.run()` gates on the major),
39
+ * so a shell pinned below this would install and show nothing; `lecodes app update` moves a 1.x
40
+ * shell here. (1.2.0 was the 1.x floor: system back moved into engine.attach().) */
41
+ export const ANDROID_SDK_MIN_VERSION = "2.0.13"
40
42
 
41
43
  /** First lecodes-android-sdk release that publishes the variant matrix (lecodes-sdk-3d / -2d /
42
44
  * -core next to the full lecodes-sdk asset). Older pins always link the full artifact. */
@@ -376,6 +378,11 @@ export const syncAndroid = async (root: string, config: AppConfig, prior: SyncAr
376
378
  const runtimeKt = join(androidDir, "lecodes-runtime", "src", "main", "kotlin", "io", "letary", "lecodes", "runtime", "LeCodesRuntime.kt")
377
379
  mkdirSync(dirname(runtimeKt), { recursive: true })
378
380
  writeFileSync(runtimeKt, runtimeKotlinTemplate(runtimePlugins, shellUuid))
381
+ // ---- OTA: the update URL rides the runtime module's manifest (merged into the app's by AGP) and
382
+ // LeCodesShell.boot reads it — app.json "update" resolved like the iOS plist key ----
383
+ const updateUrl = resolveUpdateUrl(config.update, root, sdkVersion)
384
+ writeFileSync(join(androidDir, "lecodes-runtime", "src", "main", "AndroidManifest.xml"), runtimeManifestTemplate(updateUrl))
385
+ if (updateUrl) log(`${c.green("update")} OTA source ${updateUrl} — checked natively at launch (LeCodesShell.boot), new code applies next launch`)
379
386
 
380
387
  if (pluginSpecs.length > 0 || existsSync(lockPath)) writeJson(lockPath, { plugins: newLock })
381
388
 
@@ -390,11 +397,6 @@ export const syncAndroid = async (root: string, config: AppConfig, prior: SyncAr
390
397
  restampManifestIcon(androidDir)
391
398
  }
392
399
 
393
- // ---- OTA: not on Android yet ----
394
- if (config.update) {
395
- note(`app.json "update" is iOS-only for now — the Android runtime has no native updater, so it's ignored for this shell.`)
396
- }
397
-
398
400
  log("")
399
401
  success(`Android shell synced: app.js + ${runtimePlugins.length} plugin(s), SDK ${sdkVersion}, v${config.version ?? "1.0.0"} (${config.android?.versionCode ?? 1}).`)
400
402
  note("Build & run from Android Studio (`lecodes app open android`) — user files in android/app/ were not touched.")
@@ -991,6 +993,20 @@ export const doctorAndroidChecks = async (
991
993
  } catch { /* offline — not a preflight failure */ }
992
994
  }
993
995
 
996
+ if (config.update) {
997
+ // The manifest meta-data is necessary but not sufficient: the user-owned AppViewModel must boot
998
+ // through LeCodesShell.boot (the updater's check + the newest local bundle). A shell generated
999
+ // before Android OTA existed reads assets/app.js directly — downloads would land but never run,
1000
+ // and sync can't rewrite app/. Doctor is the only place that can catch it.
1001
+ const manifest = join(androidDir, "lecodes-runtime", "src", "main", "AndroidManifest.xml")
1002
+ check(existsSync(manifest) && readFileSync(manifest, "utf8").includes("io.letary.lecodes.UPDATE_URL"), "OTA update URL in the runtime manifest", "run `lecodes app sync`", false)
1003
+ const appSrc = join(androidDir, "app", "src", "main", "java")
1004
+ const kt = existsSync(appSrc) ? readdirSync(appSrc, { recursive: true }).map(String).filter((f) => f.endsWith(".kt")) : []
1005
+ const boots = kt.some((f) => readFileSync(join(appSrc, f), "utf8").includes("LeCodesShell.boot"))
1006
+ check(boots, "App code launches via the updater (LeCodesShell.boot)",
1007
+ "no app/src/main/java/**/*.kt calls LeCodesShell.boot(engine, context) — downloaded updates would never run; see \"Over-the-air updates\" in android/README.md for the AppViewModel snippet", false)
1008
+ }
1009
+
994
1010
  const signing = resolveAndroidSigning(androidDir)
995
1011
  check(!!signing, "Release signing key", "`lecodes app keystore create` or `keystore use <file>` (release builds are refused without one)", false)
996
1012
  if (signing) {
@@ -2,8 +2,7 @@ import { execFileSync } from "node:child_process"
2
2
  import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs"
3
3
  import { homedir } from "node:os"
4
4
  import { basename, dirname, join, resolve } from "node:path"
5
- import { readManifest, type Manifest } from "../../project/manifest"
6
- import { loadConfig, requireApiUrl } from "../../platform/config"
5
+ import { readManifest } from "../../project/manifest"
7
6
  import { compileShell, type BundleResource } from "../compile"
8
7
  import { CliError, UsageError, bool, c, defineCommand, log, note, str, success, warn } from "../../cli"
9
8
  import {
@@ -15,7 +14,7 @@ import {
15
14
  import {
16
15
  FIRST_PARTY_PLUGINS, SDK_VARIANTS, cmpVersion, ensureShellIgnored,
17
16
  fetchLatestSdkVersion, isLocalPluginSpec, latestPluginTag, projectRoot, readJson, resolvePlugin, resolveSdkVersion, sdkSatisfies,
18
- resolveVariant, runTool, slugify, writeJson, VERSION_RE, type AppConfig, type LockEntry,
17
+ resolveVariant, runTool, slugify, writeJson, VERSION_RE, type AppConfig, type LockEntry, resolveUpdateUrl,
19
18
  } from "./shared"
20
19
  import {
21
20
  ANDROID_SDK_MIN_VERSION, ANDROID_SDK_REPO, androidBuild, doctorAndroidChecks, initAndroid,
@@ -212,27 +211,6 @@ const init = defineCommand({
212
211
 
213
212
  /* --------------------------------------- sync --------------------------------------- */
214
213
 
215
- /** Resolve app.json "update" to the URL the shell's native updater checks (LeCodesUpdater):
216
- * absent or `true` = the project's live bundle on the platform — `/code/<uuid>.js?sdk=<version>`,
217
- * the launcher's own URL, so a pushed project updates its shells with no server of its own (a
218
- * local-only project has none: unconfigured); `false` = no OTA; a string / { url } = custom
219
- * hosting, passed through. Warns on http: ATS blocks it on device. */
220
- const resolveUpdateUrl = (update: AppConfig["update"], root: string, sdkVersion: string): string | undefined => {
221
- if (update === false) return undefined
222
- let url = typeof update === "string" ? update : typeof update === "object" ? update.url : undefined
223
- if (!url) {
224
- let manifest: Manifest | null = null
225
- try { manifest = readManifest(root) } catch { /* not a project dir */ }
226
- if (!manifest) {
227
- if (update) warn(`app.json "update": true needs a server-backed project — use "update": {"url": "https://…/app.js"} for custom hosting. OTA left unconfigured.`)
228
- return undefined
229
- }
230
- url = `${requireApiUrl(loadConfig())}/code/${manifest.uuid}.js?sdk=${sdkVersion}`
231
- }
232
- if (!url.startsWith("https://")) warn(`update URL is not https — iOS blocks it on device (ATS): ${url}`)
233
- return url
234
- }
235
-
236
214
  /** `lecodes app sync [ios|android]` — no platform syncs every shell dir that exists. A
237
215
  * dual-shell project compiles the bundle ONCE (the iOS pass) and the Android pass reuses
238
216
  * the byte-identical output. `app update` and `app build ios` run it too. */
@@ -461,7 +439,7 @@ const sync = defineCommand({
461
439
  name: "sync",
462
440
  summary: "Make every existing shell match the project: bundle + assets + SDK variant + plugins + CLI-owned files",
463
441
  usage: "[ios|android]",
464
- description: "Compiles the bundle and embeds the assets it references (ios/App/Resources/ resp. the android assets/; offline by default, also for server projects — app.json \"resources\": {\"remote\": [globs]} keeps heavy assets streaming), picks the SDK variant from the bundle's engine use, vendors the app.json plugins (pinned in plugins.lock.json) and regenerates the CLI-owned shell files. User files (ios/App/*.swift, android/app/) are never touched.\n\nNative OTA JS updates at launch (iOS): on by default for a pushed project (its bundle on the platform, the launcher's own URL), app.json \"update\": {\"url\": …} for custom hosting, \"update\": false turns it off. app.json \"icon\" (a square ≥1024px image) renders the app icons every sync (\"iconBackground\": the adaptive / flatten backdrop). No platform = every shell that exists; a dual-shell project compiles once.",
442
+ description: "Compiles the bundle and embeds the assets it references (ios/App/Resources/ resp. the android assets/; offline by default, also for server projects — app.json \"resources\": {\"remote\": [globs]} keeps heavy assets streaming), picks the SDK variant from the bundle's engine use, vendors the app.json plugins (pinned in plugins.lock.json) and regenerates the CLI-owned shell files. User files (ios/App/*.swift, android/app/) are never touched.\n\nNative OTA JS updates at launch (both shells; the current launch runs the newest LOCAL bundle, a download applies on the next): on by default for a pushed project (its bundle on the platform, the launcher's own URL), app.json \"update\": {\"url\": …} for custom hosting, \"update\": false turns it off. A download runs only when it is of the shell's SDK major and not older than the bundle running. app.json \"icon\" (a square ≥1024px image) renders the app icons every sync (\"iconBackground\": the adaptive / flatten backdrop). No platform = every shell that exists; a dual-shell project compiles once.",
465
443
  examples: ["lecodes app sync", "lecodes app sync android"],
466
444
  run: ({ args }) => runSync(args[0]),
467
445
  })
@@ -746,14 +724,25 @@ const doctorIosChecks = async (root: string, config: AppConfig, check: Check) =>
746
724
  const plist = readFileSync(join(root, "ios", "App", "Info.plist"), "utf8")
747
725
  check(plist.includes("<key>LeCodesUpdateURL</key>"), "OTA update URL in Info.plist", "run `lecodes app sync`", false)
748
726
  // The plist key is necessary but not sufficient: the user-owned ViewController must route
749
- // the launch through LeCodesUpdater. Shells generated before OTA existed read Bundle.main
750
- // directly — downloads land but never run, and sync can't rewrite App/*.swift. Doctor is
751
- // the only place that can catch it.
727
+ // the launch through the updater — a 2.x shell by subclassing LeCodesAppViewController (its
728
+ // boot takes the newest local bundle), a 1.x one by calling LeCodesUpdater.activeBundle()
729
+ // itself. Shells generated before OTA existed read Bundle.main directly — downloads land but
730
+ // never run, and sync can't rewrite App/*.swift. Doctor is the only place that can catch it.
752
731
  const appDir = join(root, "ios", "App")
753
- const callsUpdater = readdirSync(appDir).some(f =>
754
- f.endsWith(".swift") && readFileSync(join(appDir, f), "utf8").includes("LeCodesUpdater.activeBundle"))
755
- check(callsUpdater, "App code launches via LeCodesUpdater",
756
- `no App/*.swift calls LeCodesUpdater.activeBundle() — downloaded updates would never run; see "Over-the-air updates" in ios/README.md for the ViewController snippet`, false)
732
+ const swift = readdirSync(appDir).filter(f => f.endsWith(".swift")).map(f => readFileSync(join(appDir, f), "utf8"))
733
+ const launchesViaUpdater = swift.some(t => t.includes("LeCodesAppViewController") || t.includes("LeCodesUpdater.activeBundle"))
734
+ check(launchesViaUpdater, "App code launches via the updater",
735
+ `no App/*.swift subclasses LeCodesAppViewController or calls LeCodesUpdater.activeBundle() — downloaded updates would never run; see "Over-the-air updates" in ios/README.md`, false)
736
+ }
737
+ // A 1.x shell moved to a 2.x SDK by \`lecodes app update\`: its CLI-owned files are regenerated, but
738
+ // App/*.swift are the user's and keep the 1.x shape — a ViewController over LeCodesViewController
739
+ // whose init needs an engine, so SceneDelegate's \`ViewController()\` no longer compiles. The 2.x
740
+ // shape is LeCodesAppViewController (LeCodesRuntime); the three files are the init templates.
741
+ {
742
+ const vc = join(root, "ios", "App", "ViewController.swift")
743
+ const is2x = !existsSync(vc) || readFileSync(vc, "utf8").includes("LeCodesAppViewController")
744
+ check(is2x, "App/ViewController.swift is the 2.x shell (LeCodesAppViewController)",
745
+ "a 1.x shell's App/*.swift: replace AppDelegate.swift, SceneDelegate.swift and ViewController.swift with the ones `lecodes app init ios` writes today (your own code goes into ViewController.configure(_:))", false)
757
746
  }
758
747
 
759
748
  const asc = readJson<{ keyPath?: string }>(join(homedir(), ".lecodes", "asc.json"))
@@ -2,7 +2,8 @@ import { execFileSync } from "node:child_process"
2
2
  import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs"
3
3
  import { homedir } from "node:os"
4
4
  import { dirname, join, resolve } from "node:path"
5
- import { findProjectRoot } from "../../project/manifest"
5
+ import { findProjectRoot, readManifest, type Manifest } from "../../project/manifest"
6
+ import { loadConfig, requireApiUrl } from "../../platform/config"
6
7
  import { DEFAULT_IGNORE_FILE } from "../../project/ignore"
7
8
  import { CliError, c, log, note, warn } from "../../cli"
8
9
  import { pascal, readManifest as readPluginManifest, type IosPackage, type PluginManifest as PluginManifest2 } from "../../plugin/manifest"
@@ -49,7 +50,9 @@ export interface AppConfig {
49
50
  * (natively, before the JS engine — offline is a no-op, new code applies next launch).
50
51
  * `true` = this project's live bundle on the platform (server-backed only); or an https
51
52
  * URL to a compiled bundle on any hosting (string or `{ "url": … }`). Absent = no OTA.
52
- * iOS-only for now — the Android runtime has no native updater yet. */
53
+ * Both shells: iOS reads it from Info.plist (LeCodesUpdater), Android from the manifest
54
+ * meta-data the CLI-owned lecodes-runtime module carries (LeCodesShell.boot). A download runs
55
+ * only when it is of the shell's SDK major and not older than the bundle running. */
53
56
  update?: boolean | string | { url?: string }
54
57
  /** `variant`: SDK product — "auto" (default: sync picks the smallest one covering the engines
55
58
  * the compiled bundle actually uses) or core|2d|3d|full to force one.
@@ -248,6 +251,28 @@ export interface LockEntry { repo: string, tag: string, commit: string, version:
248
251
  export const isLocalPluginSpec = (spec: string): boolean =>
249
252
  spec.startsWith("./") || spec.startsWith("../") || spec.startsWith("/") || spec.startsWith("file:")
250
253
 
254
+ /** Resolve app.json "update" to the URL a shell's native updater checks (LeCodesUpdater on both
255
+ * platforms): absent or `true` = the project's live bundle on the platform —
256
+ * `/code/<uuid>.js?sdk=<version>`, the launcher's own URL, so a pushed project updates its shells
257
+ * with no server of its own (a local-only project has none: unconfigured); `false` = no OTA; a
258
+ * string / { url } = custom hosting, passed through (one file: the updater refuses a bundle of
259
+ * another SDK major). Warns on http: iOS blocks it on device (ATS), Android's cleartext policy too. */
260
+ export const resolveUpdateUrl = (update: AppConfig["update"], root: string, sdkVersion: string): string | undefined => {
261
+ if (update === false) return undefined
262
+ let url = typeof update === "string" ? update : typeof update === "object" ? update.url : undefined
263
+ if (!url) {
264
+ let manifest: Manifest | null = null
265
+ try { manifest = readManifest(root) } catch { /* not a project dir */ }
266
+ if (!manifest) {
267
+ if (update) warn(`app.json "update": true needs a server-backed project — use "update": {"url": "https://…/app.js"} for custom hosting. OTA left unconfigured.`)
268
+ return undefined
269
+ }
270
+ url = `${requireApiUrl(loadConfig())}/code/${manifest.uuid}.js?sdk=${sdkVersion}`
271
+ }
272
+ if (!url.startsWith("https://")) warn(`update URL is not https — the phones block it on device (ATS / cleartext policy): ${url}`)
273
+ return url
274
+ }
275
+
251
276
  /** Does the SDK `version` a shell links satisfy a plugin manifest's `sdk` range (`^2.0.0`: the
252
277
  * same major, not older; `~2.1.0`: the same major.minor, not older; `2.0.0`: exactly)? */
253
278
  export const sdkSatisfies = (version: string, range: string): boolean => {
@@ -121,14 +121,14 @@ android {
121
121
  minSdk = ${MIN_SDK}
122
122
  }
123
123
  compileOptions {
124
- sourceCompatibility = JavaVersion.VERSION_11
125
- targetCompatibility = JavaVersion.VERSION_11
124
+ sourceCompatibility = JavaVersion.VERSION_17
125
+ targetCompatibility = JavaVersion.VERSION_17
126
126
  }
127
127
  }
128
128
 
129
129
  kotlin {
130
130
  compilerOptions {
131
- jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11)
131
+ jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
132
132
  }
133
133
  }
134
134
 
@@ -142,6 +142,19 @@ dependencies {
142
142
  `
143
143
  }
144
144
 
145
+ /** lecodes-runtime/src/main/AndroidManifest.xml — CLI-owned: the OTA source as application
146
+ * meta-data, merged into the app's manifest by AGP and read by LeCodesShell.boot (the Android twin
147
+ * of Info.plist's LeCodesUpdateURL). No url = no meta-data = every check is a no-op. */
148
+ export const runtimeManifestTemplate = (updateUrl: string | undefined): string => `<?xml version="1.0" encoding="utf-8"?>
149
+ <!-- Generated by \`lecodes app sync\` — regenerated on every sync, do not edit. -->
150
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
151
+ <application>${updateUrl ? `
152
+ <!-- lecodes:update — native OTA source (app.json "update"); LeCodesShell.boot checks it at launch -->
153
+ <meta-data android:name="io.letary.lecodes.UPDATE_URL" android:value="${updateUrl.replace(/&/g, "&amp;")}" />` : ""}
154
+ </application>
155
+ </manifest>
156
+ `
157
+
145
158
  /** lecodes-runtime/src/main/kotlin/io/letary/lecodes/runtime/LeCodesRuntime.kt — the generated
146
159
  * registration + host-hook surface. Plugins are called by FQN (no import collisions); the
147
160
  * activity hooks forward to the SDK's LeCodesAppHooks, which NAMES NO PLUGIN — a plugin that
@@ -209,14 +222,14 @@ android {
209
222
  minSdk = ${MIN_SDK}${consumer}
210
223
  }
211
224
  compileOptions {
212
- sourceCompatibility = JavaVersion.VERSION_11
213
- targetCompatibility = JavaVersion.VERSION_11
225
+ sourceCompatibility = JavaVersion.VERSION_17
226
+ targetCompatibility = JavaVersion.VERSION_17
214
227
  }
215
228
  }
216
229
 
217
230
  kotlin {
218
231
  compilerOptions {
219
- jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11)
232
+ jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
220
233
  }
221
234
  }
222
235
 
@@ -331,7 +344,22 @@ the init-time placeholder.
331
344
 
332
345
  ## Notes
333
346
 
334
- - No native OTA on Android yet — app.json \`"update"\` is ignored for this shell (iOS-only).
347
+
348
+ ## Over-the-air updates
349
+
350
+ Native, before the JS engine: \`LeCodesShell.boot(engine, context)\` in \`AppViewModel\` asks the
351
+ source for newer code (a conditional GET; offline is a no-op), stores it and runs it on the NEXT
352
+ launch — the current launch runs the newest LOCAL bundle. Configured in \`../app.json\`:
353
+ \`"update": true\` = the project's live bundle on the platform (a pushed project), \`"update":
354
+ { "url": "https://…/app.js" }\` = your own hosting, \`false\` = off. Sync writes the URL into
355
+ \`lecodes-runtime/src/main/AndroidManifest.xml\` (meta-data \`io.letary.lecodes.UPDATE_URL\`). A
356
+ download runs only when it is of this shell's SDK major and not older than the bundle running; a
357
+ store update always wins over a downloaded bundle. A shell generated before OTA existed reads
358
+ \`assets/app.js\` itself — replace that with:
359
+
360
+ \`\`\`kotlin
361
+ if (!LeCodesShell.boot(engine, getApplication<Application>())) Log.e("LeCodes", "bundled app.js is missing — run \`lecodes app sync\`")
362
+ \`\`\`
335
363
  - Push: the notification service needs \`app/google-services.json\` from the le.codes Firebase
336
364
  project; \`lecodes app sync\` applies the google-services Gradle plugin only when that file
337
365
  exists (and warns otherwise).
@@ -394,14 +422,14 @@ android {
394
422
  }
395
423
  }
396
424
  compileOptions {
397
- sourceCompatibility = JavaVersion.VERSION_11
398
- targetCompatibility = JavaVersion.VERSION_11
425
+ sourceCompatibility = JavaVersion.VERSION_17
426
+ targetCompatibility = JavaVersion.VERSION_17
399
427
  }
400
428
  }
401
429
 
402
430
  kotlin {
403
431
  compilerOptions {
404
- jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11)
432
+ jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
405
433
  }
406
434
  }
407
435
 
@@ -548,6 +576,7 @@ package ${pkg}
548
576
  import android.app.Activity
549
577
  import android.app.Application
550
578
  import androidx.lifecycle.AndroidViewModel
579
+ import io.letary.lecodes.LeCodesShell
551
580
  import io.letary.lecodes.LecodesEngine
552
581
 
553
582
  class AppViewModel(application: Application) : AndroidViewModel(application) {
@@ -571,8 +600,14 @@ class AppViewModel(application: Application) : AndroidViewModel(application) {
571
600
  fun onActivityCreate(activity: Activity) {
572
601
  engine.attach(activity)
573
602
  if (!isCreated) {
574
- val code = getApplication<Application>().assets.open("app.js").bufferedReader().use { it.readText() }
575
- engine.run(code)
603
+ // OTA (native, no JS involved): LeCodesShell.boot starts the check FIRST — even a bundle
604
+ // that crashes on boot can never block its own fix — then runs the newest local bundle
605
+ // (a downloaded update, or the embedded assets/app.js). New code applies on the next
606
+ // launch. Configured via app.json "update" (→ the runtime module's manifest meta-data);
607
+ // offline: no-op.
608
+ if (!LeCodesShell.boot(engine, getApplication<Application>())) {
609
+ android.util.Log.e("LeCodes", "bundled app.js is missing — run \`lecodes app sync\`")
610
+ }
576
611
  isCreated = true
577
612
  }
578
613
  }