lecodes-cli 2.4.3 → 2.4.5
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.
- package/dist/index.js +10892 -18223
- package/package.json +8 -8
- package/runtime/sdk-types.json +1 -1
- package/runtime/web/VERSION.json +3 -3
- package/runtime/web/player.js +17 -17
- package/runtime/web-lite/assets/{index-oPsDD47E.js → index-Bd7pdF7u.js} +1 -1
- package/runtime/web-lite/assets/scene3d-DXpVYhGy.js +1105 -0
- package/runtime/web-lite/features.json +2 -2
- package/runtime/web-lite/lite.js +3 -3
- package/runtime/web-page/page.js +6 -6
- package/src/commands/app/index.ts +1 -1
- package/src/commands/app/shared.ts +4 -2
- package/src/commands/app/templates/android.ts +17 -7
- package/src/commands/init/index.ts +195 -195
- package/src/commands/shared.ts +79 -79
- package/src/commands/thumbs.ts +185 -185
- package/src/compile/screenEntry.ts +123 -123
- package/src/project/ignore.ts +164 -164
- package/runtime/web-lite/assets/scene3d-BAl_QQyi.js +0 -1105
package/runtime/web-page/page.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var ft=Object.defineProperty;var mt=(
|
|
1
|
+
var ft=Object.defineProperty;var mt=(s,n,t)=>n in s?ft(s,n,{enumerable:!0,configurable:!0,writable:!0,value:t}):s[n]=t;var p=(s,n,t)=>mt(s,typeof n!="symbol"?n+"":n,t);(function(){const n=document.createElement("link").relList;if(n&&n.supports&&n.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))e(i);new MutationObserver(i=>{for(const r of i)if(r.type==="childList")for(const a of r.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&e(a)}).observe(document,{childList:!0,subtree:!0});function t(i){const r={};return i.integrity&&(r.integrity=i.integrity),i.referrerPolicy&&(r.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?r.credentials="include":i.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function e(i){if(i.ep)return;i.ep=!0;const r=t(i);fetch(i.href,r)}})();const Y=[{id:"iphone",name:"iPhone",width:393,height:852,radius:55,bezel:10,keyboard:336,keyboardLook:"ios",insets:{portrait:{top:59,right:0,bottom:34,left:0},landscape:{top:0,right:59,bottom:21,left:59}},marks:{island:{width:126,height:37,inset:11},homeIndicator:{width:139,height:5,inset:8},status:{height:59}}},{id:"android",name:"Android",width:412,height:915,radius:28,bezel:8,keyboard:300,keyboardLook:"android",insets:{portrait:{top:24,right:0,bottom:48,left:0,bars:["top","bottom"]},landscape:{top:24,right:0,bottom:48,left:0,bars:["top","bottom"]}},marks:{camera:{size:12,inset:6},navigation:{height:48},status:{height:24}}},{id:"tablet",name:"Tablet",width:820,height:1180,radius:18,bezel:14,keyboard:0,keyboardLook:"ios",insets:{portrait:{top:24,right:0,bottom:20,left:0},landscape:{top:24,right:0,bottom:20,left:0}},marks:{homeIndicator:{width:260,height:5,inset:7},status:{height:24}}}],ot=s=>Y.find(n=>n.id===s)??null,Q=(s,n)=>n==="landscape"?{width:s.height,height:s.width}:{width:s.width,height:s.height},bt=(s,n,t)=>{const e=Q(s,n),i=e.width+s.bezel*2,r=e.height+s.bezel*2;return t.width<=0||t.height<=0?1:Math.min(1,t.width/i,t.height/r)},kt=(s,n,t)=>{if(s>=1)return 1;const e=Math.floor(n*s),i=r=>Math.abs(r-Math.round(r))<1e-6;for(let r=e;r>0&&r>e-8;r--)if(i(r*t))return r/n;return e>0?e/n:s},yt=`
|
|
2
2
|
.marks { position: absolute; inset: 0; pointer-events: none; color: var(--lecodes-frame-marks, #ffffff); font: 600 15px/1 system-ui, sans-serif; }
|
|
3
3
|
/* what the system draws OVER the app is seen on any app: light on a dark one, dark on a light one */
|
|
4
4
|
.marks > * { position: absolute; mix-blend-mode: difference; }
|
|
@@ -12,7 +12,7 @@ var ft=Object.defineProperty;var mt=(e,n,t)=>n in e?ft(e,n,{enumerable:!0,config
|
|
|
12
12
|
.navigation .back { border: 0; width: 0; height: 0; border-style: solid; border-width: 8px 13px 8px 0; border-color: transparent currentColor transparent transparent; }
|
|
13
13
|
.navigation .homeButton { border-radius: 50%; }
|
|
14
14
|
.navigation .recent { border-radius: 3px; }
|
|
15
|
-
`,y=
|
|
15
|
+
`,y=s=>`${s}px`,I=(s,n,t)=>{const e=s.createElement("div");return e.className=n,Object.assign(e.style,t),e},wt=(s,n,t)=>{const e=s.ownerDocument,{marks:i}=n,r=t==="landscape",a=[];if(i.island){const{width:h,height:b,inset:w}=i.island;a.push(r?I(e,"island solid",{left:y(w),top:"50%",width:y(b),height:y(h),transform:"translateY(-50%)"}):I(e,"island solid",{top:y(w),left:"50%",width:y(h),height:y(b),transform:"translateX(-50%)"}))}if(i.camera){const{size:h,inset:b}=i.camera;a.push(r?I(e,"camera solid",{left:y(b),top:"50%",width:y(h),height:y(h),transform:"translateY(-50%)"}):I(e,"camera solid",{top:y(b),left:"50%",width:y(h),height:y(h),transform:"translateX(-50%)"}))}if(i.homeIndicator){const{width:h,height:b,inset:w}=i.homeIndicator;a.push(I(e,"home",{bottom:y(w),left:"50%",width:y(h),height:y(b),transform:"translateX(-50%)"}))}const g=n.insets[t].top;if(i.status&&g>0){const h=I(e,"clock",{top:"0",left:y(i.island?34:18),height:y(Math.min(g,i.status.height))});h.textContent="9:41",i.island||(h.style.fontSize="13px"),a.push(h)}if(i.navigation){const h=I(e,"navigation",{height:y(i.navigation.height)});for(const b of["back","homeButton","recent"]){const w=e.createElement("i");w.className=b,h.appendChild(w)}a.push(h)}s.replaceChildren(...a)},vt=[.38,.7,.125,1],xt=`cubic-bezier(${vt.join(", ")})`,Et=`
|
|
16
16
|
.keys { position: absolute; left: 0; right: 0; bottom: 0; overflow: hidden; box-sizing: border-box; font: 400 22px/1 -apple-system, "SF Pro Text", Roboto, system-ui, sans-serif; color: #000; transform: translateY(100%); will-change: transform; }
|
|
17
17
|
.keys.shown { transform: translateY(0); }
|
|
18
18
|
.keys.ios { background: #d1d3d9; }
|
|
@@ -29,13 +29,13 @@ var ft=Object.defineProperty;var mt=(e,n,t)=>n in e?ft(e,n,{enumerable:!0,config
|
|
|
29
29
|
.keys .k.shift::before { content: ""; display: block; width: 0; height: 0; border-style: solid; border-width: 0 9px 10px 9px; border-color: transparent transparent #000 transparent; }
|
|
30
30
|
.keys .k.del::before { content: "⌫"; font-size: 20px; }
|
|
31
31
|
.keys .k.emoji::before { content: "☺"; font-size: 20px; }
|
|
32
|
-
|
|
32
|
+
`,St={en:[["q","w","e","r","t","y","u","i","o","p"],["a","s","d","f","g","h","j","k","l"],["z","x","c","v","b","n","m"]],ru:[["й","ц","у","к","е","н","г","ш","щ","з","х"],["ф","ы","в","а","п","р","о","л","д","ж","э"],["я","ч","с","м","и","т","ь","б","ю"]]},$t={en:{space:"space",enter:"return"},ru:{space:"Пробел",enter:"Ввод"}},N=s=>`${Math.round(s*2)/2}px`,Lt=(s,n)=>{const{look:t,width:e,height:i,inset:r}=n,a=n.language.slice(0,2)==="ru"?"ru":"en",g=St[a],h=$t[a],b=t==="ios",w=[],f=(k,z,F,V,G,gt)=>{const q=s.createElement("div");q.className=`k${z?` ${z}`:""}`,q.textContent=k,Object.assign(q.style,{left:N(F),top:N(V),width:N(G),height:N(gt)}),w.push(q)},B=b?44:40,j=s.createElement("div");j.className="bar",Object.assign(j.style,{top:"0",height:N(B)});for(let k=0;k<(b?2:3);k++)j.append(s.createElement("i"));w.push(j);const m=3,x=6,M=b?11:9,P=g[0].length,L=(e-m*2-x*(P-1))/P,o=i-B-r,c=Math.min(b?42:46,(o-M*5)/4),d=B+Math.max(M,(o-(c*4+M*3))/2-2),l=k=>d+k*(c+M),E=k=>(e-k*L-(k-1)*x)/2;g.forEach((k,z)=>{const F=E(k.length);k.forEach((V,G)=>f(V,"",F+G*(L+x),l(z),L,c))});const C=g[2],$=E(C.length)-x-m;f("","s shift",m,l(2),$,c),f("","s del",e-m-$,l(2),$,c);const v=l(3),A=L*2+x;if(b){f("123","s",m,v,A,c),f("","s emoji",m+A+x,v,L,c);const k=m+A+x+L+x;f(h.space,"space",k,v,e-k-m-A-x,c),f(h.enter,"s",e-m-A,v,A,c)}else{f("?123","s",m,v,A,c),f(",","",m+A+x,v,L,c);const k=m+A+x+L+x,z=e-m-A-x-L;f("","space",k,v,z-x-k,c),f(".","",z,v,L,c),f("↵","s",e-m-A,v,A,c)}return w},rt=(s,n)=>{s.style.transition=n>0?`transform ${n}ms ${xt}`:""},At=(s,n,t=0)=>{const e=s.classList.contains("shown");rt(s,t),s.className=`keys ${n.look}${e?" shown":""}`,s.style.height=N(n.height),s.replaceChildren(...Lt(s.ownerDocument,n)),e||(t>0&&s.offsetHeight,s.classList.add("shown"))},Ct=(s,n=0)=>{const t=()=>{s.classList.contains("shown")||(s.replaceChildren(),s.style.height="0")};if(!s.classList.contains("shown")){t();return}rt(s,n),s.classList.remove("shown"),n>0?s.addEventListener("transitionend",t,{once:!0}):t()},Ot=s=>{if(s.dataset.pointer)return;s.dataset.pointer="taken",s.style.pointerEvents="auto";const n=t=>{t.preventDefault(),t.stopPropagation()};for(const t of["pointerdown","mousedown","touchstart","click"])s.addEventListener(t,n,{passive:!1})},Rt=`
|
|
33
33
|
:host { display: block; position: relative; overflow: hidden; min-width: 0; min-height: 0; }
|
|
34
34
|
.stage { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
|
|
35
35
|
.device { position: relative; flex: none; box-sizing: content-box; background: var(--lecodes-frame-body, #0b0b0d); box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.10), 0 24px 60px rgba(0, 0, 0, 0.35); }
|
|
36
36
|
.screen { position: relative; overflow: hidden; background: #000; width: 100%; height: 100%; }
|
|
37
37
|
iframe { display: block; width: 100%; height: 100%; border: 0; background: #000; }
|
|
38
|
-
.over { position: absolute;
|
|
38
|
+
.over { position: absolute; left: 0; top: 0; width: 100%; height: 100%; pointer-events: none; }
|
|
39
39
|
:host([bare]) .device { box-shadow: none; background: none; }
|
|
40
40
|
.pick { position: absolute; inset: 0; pointer-events: none; }
|
|
41
41
|
:host([picking]) .pick { pointer-events: auto; cursor: crosshair; }
|
|
@@ -45,5 +45,5 @@ iframe { display: block; width: 100%; height: 100%; border: 0; background: #000;
|
|
|
45
45
|
.found.low > b { bottom: auto; top: 100%; margin: 3px 0 0; }
|
|
46
46
|
${yt}
|
|
47
47
|
${Et}
|
|
48
|
-
`,jt=600,Bt=["player","src","device","orientation","language","bare","inspect"],zt=3e3,H=class H extends HTMLElement{constructor(){super();p(this,"stage");p(this,"body");p(this,"screen");p(this,"over");p(this,"marks");p(this,"keys");p(this,"pick");p(this,"found");p(this,"asked",new Map);p(this,"requests",0);p(this,"pointing",null);p(this,"pointed",!1);p(this,"iframe",null);p(this,"observer");p(this,"loaded",!1);p(this,"mounting",!1);p(this,"turned",null);p(this,"locked",!1);p(this,"shownScale",1);p(this,"onWindow",()=>{this.layout(),this.tellSafeArea()});p(this,"onMessage",t=>{var i;if(!this.iframe||t.source!==this.iframe.contentWindow)return;const s=t.data;if(!(!s||typeof s!="object"))switch(s.type){case"lecodes:ready":this.highlight(null),this.emit("app-ready",{src:String(s.src)});break;case"lecodes:error":this.emit("app-error",s.error);break;case"lecodes:log":this.emit("app-log",{level:s.level,text:String(s.text)});break;case"lecodes:inspected":(i=this.asked.get(s.request))==null||i(s.answer);break;case"lecodes:keyboard":this.drawKeyboard(s.height,s.duration);break;case"lecodes:orientation":{s.mode==="landscape"||s.mode==="portrait"?(this.turned=s.mode,this.locked=!0):this.locked=!1,this.layout(),this.tellSafeArea(),this.emit("app-orientation",{orientation:this.orientation,locked:this.locked});break}}});const t=this.attachShadow({mode:"open"}),s=this.ownerDocument,i=s.createElement("style");i.textContent=Ot;const r=a=>{const g=s.createElement("div");return g.className=a,g};this.stage=r("stage"),this.body=r("device"),this.screen=r("screen"),this.over=r("over"),this.marks=r("marks"),this.keys=r("keys"),Rt(this.keys),this.pick=r("pick"),this.found=r("found"),this.found.append(s.createElement("i"),s.createElement("b")),this.over.append(this.found,this.keys,this.marks,this.pick),this.pick.addEventListener("pointermove",a=>this.point(a)),this.pick.addEventListener("pointerleave",()=>{this.pointing=null,this.picking&&(this.highlight(null),this.emit("app-point",null))}),this.pick.addEventListener("click",a=>{this.at(a).then(g=>{g&&(this.highlight(g),this.emit("app-pick",g))})}),this.screen.appendChild(this.over),this.body.appendChild(this.screen),this.stage.appendChild(this.body),t.append(i,this.stage),this.observer=new ResizeObserver(()=>this.layout())}static get noRoom(){return window.innerWidth<jt}get player(){return this.getAttribute("player")??""}set player(t){this.setAttribute("player",t)}get src(){return this.getAttribute("src")??""}set src(t){this.setAttribute("src",t)}get device(){return this.getAttribute("device")??Y[0].id}set device(t){this.setAttribute("device",t)}get language(){return this.getAttribute("language")??""}set language(t){this.setAttribute("language",t)}get orientation(){return this.turned??(this.getAttribute("orientation")==="landscape"?"landscape":"portrait")}set orientation(t){this.turned=null,this.locked=!1,this.setAttribute("orientation",t)}get orientationLocked(){return this.locked}get bare(){return this.hasAttribute("bare")||this.current===null}get inspect(){return this.hasAttribute("inspect")}set inspect(t){this.toggleAttribute("inspect",t)}get picking(){return this.hasAttribute("picking")}set picking(t){this.toggleAttribute("picking",t),t||this.highlight(null)}get scale(){return this.shownScale}get current(){return this.hasAttribute("bare")||H.noRoom?null:ot(this.device)}get playerUrl(){return this.urlFor(null)}rotate(){this.locked||(this.orientation=this.orientation==="portrait"?"landscape":"portrait",this.emit("app-orientation",{orientation:this.orientation,locked:!1}))}ask(t){return new Promise(s=>{const i=++this.requests,r=a=>{this.asked.delete(i)&&s(a&&a.ask===t.ask?a:null)};this.asked.set(i,r),window.setTimeout(()=>r(null),zt),this.post({type:"lecodes:inspect",request:i,question:t})})}highlight(t){const s=this.found;if(!t){s.style.display="none";return}const{rect:i,padding:r}=t;Object.assign(s.style,{display:"block",left:`${i.x}px`,top:`${i.y}px`,width:`${i.width}px`,height:`${i.height}px`}),Object.assign(s.firstElementChild.style,{left:`${r.left}px`,top:`${r.top}px`,right:`${r.right}px`,bottom:`${r.bottom}px`}),s.lastElementChild.textContent=`${t.type}${t.name?` "${t.name}"`:""} ${Math.round(i.width)} × ${Math.round(i.height)}`,s.classList.toggle("low",i.y<24)}reload(){this.post({type:"lecodes:reload"})}connectedCallback(){this.observer.observe(this),window.addEventListener("message",this.onMessage),window.addEventListener("resize",this.onWindow),this.remount()}disconnectedCallback(){var t;this.observer.disconnect(),window.removeEventListener("message",this.onMessage),window.removeEventListener("resize",this.onWindow),(t=this.iframe)==null||t.remove(),this.iframe=null}attributeChangedCallback(t,s,i){if(!(!this.isConnected||s===i))switch(t){case"player":this.remount();break;case"src":this.loaded?i&&this.post({type:"lecodes:run",src:i}):this.remount();break;case"language":this.loaded?i&&this.post({type:"lecodes:language",language:i}):this.remount();break;case"inspect":this.remount();break;default:this.layout(),this.tellSafeArea()}}toApp(t){const s=this.screen.getBoundingClientRect();return{x:(t.clientX-s.left)/this.shownScale,y:(t.clientY-s.top)/this.shownScale}}async at(t){var s;return((s=await this.ask({ask:"at",...this.toApp(t)}))==null?void 0:s.node)??null}point(t){this.pointing=this.toApp(t),!this.pointed&&(this.pointed=!0,(async()=>{var s;for(;this.pointing&&this.picking;){const{x:i,y:r}=this.pointing;this.pointing=null;const a=((s=await this.ask({ask:"at",x:i,y:r}))==null?void 0:s.node)??null;if(!this.picking)break;this.highlight(a),this.emit("app-point",a)}this.pointed=!1})())}remount(){this.mounting||(this.mounting=!0,queueMicrotask(()=>{this.mounting=!1,this.isConnected&&this.mount()}))}mount(){var s;if((s=this.iframe)==null||s.remove(),this.iframe=null,this.loaded=!1,!this.player)return;const t=this.ownerDocument.createElement("iframe");t.addEventListener("load",()=>{this.iframe===t&&(this.loaded=!0,this.tellSafeArea())}),t.setAttribute("allow","autoplay; fullscreen; camera; microphone; geolocation; clipboard-read; clipboard-write; gamepad"),t.title="LeCodes app",t.src=this.urlFor(this.current),this.screen.prepend(t),this.iframe=t,this.layout()}urlFor(t){var i;const s=new URL(this.player,this.ownerDocument.baseURI);if(this.src&&s.searchParams.set("src",this.src),this.language&&s.searchParams.set("lang",this.language),this.inspect&&s.searchParams.set("inspect",""),t){const r=t.insets[this.orientation];s.searchParams.set("safe-area",[r.top,r.right,r.bottom,r.left].join(",")),(i=r.bars)!=null&&i.length&&s.searchParams.set("bars",r.bars.join(","));const a=this.keyboardBand(t);a>0&&s.searchParams.set("keyboard-band",String(a))}return s.href}layout(){const t=this.current;this.toggleAttribute("framed",t!==null);const s=this.body.style;if(!t){this.shownScale=1,Object.assign(s,{width:"100%",height:"100%",padding:"0",borderRadius:"0",zoom:""}),this.screen.style.borderRadius="0",this.marks.replaceChildren();return}const i=this.orientation,r=Q(t,i);this.shownScale=kt(bt(t,i,{width:this.clientWidth,height:this.clientHeight}),r.width,window.devicePixelRatio||1),Object.assign(s,{width:`${r.width}px`,height:`${r.height}px`,padding:`${t.bezel}px`,borderRadius:`${t.radius+t.bezel}px`,zoom:String(this.shownScale)}),this.screen.style.borderRadius=`${t.radius}px`,wt(this.marks,t,i)}post(t){var i;const s=(i=this.iframe)==null?void 0:i.contentWindow;!s||!this.player||s.postMessage(t,new URL(this.player,this.ownerDocument.baseURI).origin)}tellSafeArea(){const t=this.current;this.post({type:"lecodes:safe-area",insets:t?{...t.insets[this.orientation]}:null});const s=t?this.keyboardBand(t):0;this.post({type:"lecodes:keyboard-band",height:s>0?s:null}),s===0&&this.drawKeyboard(0)}keyboardBand(t){return this.orientation==="portrait"?t.keyboard:0}drawKeyboard(t,s=0){const i=this.current;if(!i||t<=0){Ct(this.keys,s);return}const r=Q(i,this.orientation);At(this.keys,{look:i.keyboardLook,language:this.language,width:r.width,height:t,inset:i.insets[this.orientation].bottom},s)}emit(t,s){this.dispatchEvent(new CustomEvent(t,{detail:s}))}};p(H,"observedAttributes",Bt);let D=H;const It=(e="lecodes-frame")=>{customElements.get(e)||customElements.define(e,D)};typeof customElements<"u"&&It();const $=(e,n,t)=>{const s=document.createElement(e);return n&&(s.className=n),t!==void 0&&(s.textContent=t),s},st=e=>e.type+(e.name?` "${e.name}"`:""),it=/^(width|height|minWidth|minHeight|maxWidth|maxHeight|aspectRatio|flex|flexGrow|flexShrink|flexBasis|flexDirection|flexWrap|alignItems|alignSelf|alignContent|justifyContent|gap|rowGap|columnGap|position|left|top|right|bottom|inset|display|overflow|zIndex|m|mx|my|mt|mr|mb|ml|margin\w*|p|px|py|pt|pr|pb|pl|padding\w*)$/,R=e=>typeof e=="string"?e:typeof e=="number"?String(Math.round(e*100)/100):Array.isArray(e)?e.map(R).join(" "):typeof e=="object"&&e!==null?Object.entries(e).map(([n,t])=>`${n}: ${R(t)}`).join(", "):String(e),Mt=(e,n,t)=>{const s=n.querySelector("#pick"),i=n.querySelector("#tree"),r=n.querySelector("#details"),a=new Set;let g=0,h=!1;const b=(o,c,d)=>{const l=$("div","node");l.dataset.id=String(o.id),l.style.paddingLeft=`${8+c*14}px`,l.classList.toggle("chosen",o.id===g);const E=$("span","fold",o.children.length===0?"":a.has(o.id)?"▸":"▾");if(E.addEventListener("click",C=>{C.stopPropagation(),a.has(o.id)?a.delete(o.id):a.add(o.id),w()}),l.append(E,$("span","type",o.type)),o.name&&l.append($("span","name",` "${o.name}"`)),o.text&&l.append($("span","text",` ${o.text.replace(/\s+/g," ")}`)),l.addEventListener("mouseenter",()=>e.highlight({...o,padding:{top:0,right:0,bottom:0,left:0}})),l.addEventListener("click",()=>void x(o.id)),d.append(l),!a.has(o.id))for(const C of o.children)b(C,c+1,d)},w=async()=>{if(!h)return;const o=await e.ask({ask:"tree"}),c=$("div");o||c.append($("div","empty","No app is running."));for(const d of(o==null?void 0:o.layers)??[])d.kind==="widget"&&c.append($("div","layer","widget")),b(d.tree,0,c);i.replaceChildren(c)};i.addEventListener("mouseleave",()=>{M()});const f=(o,c)=>{if(c.length===0)return null;const d=$("div","section");d.append($("div","title",o));for(const[l,E]of c){const C=$("div","pair");C.append($("span","key",l),$("span","value",E)),d.append(C)}return d},B=o=>typeof o=="object"&&o!==null&&!Array.isArray(o),j=(o,c)=>Object.entries(o??{}).filter(([d,l])=>c(d,l)).map(([d,l])=>[d,R(l)]),m=o=>{var _;if(!o){r.replaceChildren($("div","empty","Pick an element: in the app, or in the outline."));return}const c=$("div","head",st(o)),d=$("div","path",o.path.slice(0,-1).map(st).join(" › ")),{rect:l,padding:E}=o,C=[f("Box",[["size",`${R(l.width)} × ${R(l.height)}`],["position",`${R(l.x)}, ${R(l.y)}`],...E.top||E.right||E.bottom||E.left?[["padding",`${E.top} ${E.right} ${E.bottom} ${E.left}`]]:[],...o.scroll?[["scroll",`${o.scroll.axis}: ${R(o.scroll.offset)} of ${R(o.scroll.content)}`]]:[],...(_=o.state)!=null&&_.length?[["state",o.state.join(", ")]]:[]]),o.text!==void 0?f("Text",[["",o.text]]):null,f("Layout",j(o.set,(L,v)=>it.test(L)&&!B(v))),f("Set by the app",j(o.set,(L,v)=>!it.test(L)&&!B(v))),...Object.entries(o.set??{}).filter(([,L])=>B(L)).map(([L,v])=>f(L,j(v,()=>!0))),f("Painted",Object.entries(o.style).map(([L,v])=>[L,R(v)])),o.source?f("Made at",[["",`${o.source.file}:${o.source.line}:${o.source.column}`]]):null];r.replaceChildren(c,d,...C.filter(L=>L!==null))},x=async o=>{var d;g=o;const c=((d=await e.ask({ask:"node",node:o}))==null?void 0:d.node)??null;e.highlight(c),m(c);for(const l of i.querySelectorAll(".node"))l.classList.toggle("chosen",l.dataset.id===String(o))},M=async()=>{var o;e.picking||e.highlight(g?((o=await e.ask({ask:"node",node:g}))==null?void 0:o.node)??null:null)},P=o=>{e.picking=o,s.setAttribute("aria-pressed",String(o))};s.addEventListener("click",()=>P(!e.picking)),e.addEventListener("app-pick",o=>{const c=o.detail;P(!1),e.highlight(c),g=c.id;for(const d of c.path)a.delete(d.id);m(c),w().then(()=>{var d;return(d=i.querySelector(".node.chosen"))==null?void 0:d.scrollIntoView({block:"nearest"})})}),e.addEventListener("app-ready",()=>{g=0,a.clear(),m(null),w()});const S=o=>{h=o,n.hidden=!o,t.setAttribute("aria-pressed",String(o)),o?w():(P(!1),e.highlight(null))};return t.addEventListener("click",()=>S(!h)),n.querySelector("#refresh").addEventListener("click",()=>void w()),m(null),{setOpen:S,get open(){return h}}},W=new URLSearchParams(location.search),u=document.getElementById("frame");u.player=W.get("player")??"player/?dev=../..";W.get("src")&&(u.src=W.get("src"));u.inspect=!0;D.noRoom&&location.replace(u.playerUrl);const K=e=>{try{return localStorage.getItem(`lecodes-dev:${e}`)}catch{return null}},U=(e,n)=>{try{localStorage.setItem(`lecodes-dev:${e}`,n)}catch{}},at="none",J=W.get("device")??K("device")??Y[0].id;u.device=J===at||ot(J)?J:Y[0].id;u.orientation=(W.get("orientation")??K("orientation"))==="landscape"?"landscape":"portrait";const ct=document.getElementById("devices"),Z=document.getElementById("rotate"),Pt=document.getElementById("scale"),X=()=>{for(const n of ct.querySelectorAll("button"))n.setAttribute("aria-pressed",String(n.dataset.device===u.device));const e=!u.bare;Z.disabled=!e||u.orientationLocked,Z.title=u.orientationLocked?"The app holds the orientation (app.setOrientation)":"Turn the device",Pt.textContent=e&&u.scale<1?`${Math.round(u.scale*100)}%`:""};for(const{id:e,name:n}of[...Y,{id:at,name:"No frame"}]){const t=document.createElement("button");t.type="button",t.dataset.device=e,t.textContent=n,t.addEventListener("click",()=>{u.device=e,U("device",e),X()}),ct.appendChild(t)}Z.addEventListener("click",()=>{u.rotate(),U("orientation",u.orientation)});document.getElementById("reload").addEventListener("click",()=>u.reload());u.addEventListener("app-orientation",X);new ResizeObserver(X).observe(u);X();const lt=document.getElementById("console"),O=document.getElementById("console-lines"),dt=document.getElementById("console-toggle"),nt=document.getElementById("console-count"),Nt=500;let T=0;const tt=e=>{lt.hidden=!e,dt.setAttribute("aria-pressed",String(e)),U("console",e?"1":"0")},et=()=>{nt.textContent=T>0?String(T):"",nt.classList.toggle("errors",T>0)},ht=(e,n,t)=>{const s=O.scrollTop+O.clientHeight>=O.scrollHeight-4,i=document.createElement("div");if(i.className=`line ${e}`,i.textContent=n,t){const r=document.createElement("span");r.className="where",r.textContent=`
|
|
49
|
-
at ${t}`,i.appendChild(r)}for(
|
|
48
|
+
`,jt=600,Bt=["player","src","device","orientation","language","bare","inspect"],zt=3e3,H=class H extends HTMLElement{constructor(){super();p(this,"stage");p(this,"body");p(this,"screen");p(this,"over");p(this,"marks");p(this,"keys");p(this,"pick");p(this,"found");p(this,"asked",new Map);p(this,"requests",0);p(this,"pointing",null);p(this,"pointed",!1);p(this,"iframe",null);p(this,"observer");p(this,"loaded",!1);p(this,"mounting",!1);p(this,"turned",null);p(this,"locked",!1);p(this,"shownScale",1);p(this,"toldScale",1);p(this,"onWindow",()=>{this.layout(),this.tellSafeArea()});p(this,"onMessage",t=>{var i;if(!this.iframe||t.source!==this.iframe.contentWindow)return;const e=t.data;if(!(!e||typeof e!="object"))switch(e.type){case"lecodes:ready":this.highlight(null),this.emit("app-ready",{src:String(e.src)});break;case"lecodes:error":this.emit("app-error",e.error);break;case"lecodes:log":this.emit("app-log",{level:e.level,text:String(e.text)});break;case"lecodes:inspected":(i=this.asked.get(e.request))==null||i(e.answer);break;case"lecodes:keyboard":this.drawKeyboard(e.height,e.duration);break;case"lecodes:orientation":{e.mode==="landscape"||e.mode==="portrait"?(this.turned=e.mode,this.locked=!0):this.locked=!1,this.layout(),this.tellSafeArea(),this.emit("app-orientation",{orientation:this.orientation,locked:this.locked});break}}});const t=this.attachShadow({mode:"open"}),e=this.ownerDocument,i=e.createElement("style");i.textContent=Rt;const r=a=>{const g=e.createElement("div");return g.className=a,g};this.stage=r("stage"),this.body=r("device"),this.screen=r("screen"),this.over=r("over"),this.marks=r("marks"),this.keys=r("keys"),Ot(this.keys),this.pick=r("pick"),this.found=r("found"),this.found.append(e.createElement("i"),e.createElement("b")),this.over.append(this.found,this.keys,this.marks,this.pick),this.pick.addEventListener("pointermove",a=>this.point(a)),this.pick.addEventListener("pointerleave",()=>{this.pointing=null,this.picking&&(this.highlight(null),this.emit("app-point",null))}),this.pick.addEventListener("click",a=>{this.at(a).then(g=>{g&&(this.highlight(g),this.emit("app-pick",g))})}),this.screen.appendChild(this.over),this.body.appendChild(this.screen),this.stage.appendChild(this.body),t.append(i,this.stage),this.observer=new ResizeObserver(()=>this.layout())}static get noRoom(){return window.innerWidth<jt}get player(){return this.getAttribute("player")??""}set player(t){this.setAttribute("player",t)}get src(){return this.getAttribute("src")??""}set src(t){this.setAttribute("src",t)}get device(){return this.getAttribute("device")??Y[0].id}set device(t){this.setAttribute("device",t)}get language(){return this.getAttribute("language")??""}set language(t){this.setAttribute("language",t)}get orientation(){return this.turned??(this.getAttribute("orientation")==="landscape"?"landscape":"portrait")}set orientation(t){this.turned=null,this.locked=!1,this.setAttribute("orientation",t)}get orientationLocked(){return this.locked}get bare(){return this.hasAttribute("bare")||this.current===null}get inspect(){return this.hasAttribute("inspect")}set inspect(t){this.toggleAttribute("inspect",t)}get picking(){return this.hasAttribute("picking")}set picking(t){this.toggleAttribute("picking",t),t||this.highlight(null)}get scale(){return this.shownScale}get current(){return this.hasAttribute("bare")||H.noRoom?null:ot(this.device)}get playerUrl(){return this.urlFor(null)}rotate(){this.locked||(this.orientation=this.orientation==="portrait"?"landscape":"portrait",this.emit("app-orientation",{orientation:this.orientation,locked:!1}))}ask(t){return new Promise(e=>{const i=++this.requests,r=a=>{this.asked.delete(i)&&e(a&&a.ask===t.ask?a:null)};this.asked.set(i,r),window.setTimeout(()=>r(null),zt),this.post({type:"lecodes:inspect",request:i,question:t})})}highlight(t){const e=this.found;if(!t){e.style.display="none";return}const{rect:i,padding:r}=t;Object.assign(e.style,{display:"block",left:`${i.x}px`,top:`${i.y}px`,width:`${i.width}px`,height:`${i.height}px`}),Object.assign(e.firstElementChild.style,{left:`${r.left}px`,top:`${r.top}px`,right:`${r.right}px`,bottom:`${r.bottom}px`}),e.lastElementChild.textContent=`${t.type}${t.name?` "${t.name}"`:""} ${Math.round(i.width)} × ${Math.round(i.height)}`,e.classList.toggle("low",i.y<24)}reload(){this.post({type:"lecodes:reload"})}connectedCallback(){this.observer.observe(this),window.addEventListener("message",this.onMessage),window.addEventListener("resize",this.onWindow),this.remount()}disconnectedCallback(){var t;this.observer.disconnect(),window.removeEventListener("message",this.onMessage),window.removeEventListener("resize",this.onWindow),(t=this.iframe)==null||t.remove(),this.iframe=null}attributeChangedCallback(t,e,i){if(!(!this.isConnected||e===i))switch(t){case"player":this.remount();break;case"src":this.loaded?i&&this.post({type:"lecodes:run",src:i}):this.remount();break;case"language":this.loaded?i&&this.post({type:"lecodes:language",language:i}):this.remount();break;case"inspect":this.remount();break;default:this.layout(),this.tellSafeArea()}}toApp(t){const e=this.screen.getBoundingClientRect();return{x:(t.clientX-e.left)/this.shownScale,y:(t.clientY-e.top)/this.shownScale}}async at(t){var e;return((e=await this.ask({ask:"at",...this.toApp(t)}))==null?void 0:e.node)??null}point(t){this.pointing=this.toApp(t),!this.pointed&&(this.pointed=!0,(async()=>{var e;for(;this.pointing&&this.picking;){const{x:i,y:r}=this.pointing;this.pointing=null;const a=((e=await this.ask({ask:"at",x:i,y:r}))==null?void 0:e.node)??null;if(!this.picking)break;this.highlight(a),this.emit("app-point",a)}this.pointed=!1})())}remount(){this.mounting||(this.mounting=!0,queueMicrotask(()=>{this.mounting=!1,this.isConnected&&this.mount()}))}mount(){var e;if((e=this.iframe)==null||e.remove(),this.iframe=null,this.loaded=!1,!this.player)return;this.layout();const t=this.ownerDocument.createElement("iframe");t.addEventListener("load",()=>{this.iframe===t&&(this.loaded=!0,this.tellSafeArea(),this.tellScale())}),t.setAttribute("allow","autoplay; fullscreen; camera; microphone; geolocation; clipboard-read; clipboard-write; gamepad"),t.title="LeCodes app",t.src=this.urlFor(this.current),this.screen.prepend(t),this.iframe=t}urlFor(t){var i;const e=new URL(this.player,this.ownerDocument.baseURI);if(this.src&&e.searchParams.set("src",this.src),this.language&&e.searchParams.set("lang",this.language),this.inspect&&e.searchParams.set("inspect",""),t&&this.shownScale!==1&&(e.searchParams.set("scale",String(this.shownScale)),this.toldScale=this.shownScale),t){const r=t.insets[this.orientation];e.searchParams.set("safe-area",[r.top,r.right,r.bottom,r.left].join(",")),(i=r.bars)!=null&&i.length&&e.searchParams.set("bars",r.bars.join(","));const a=this.keyboardBand(t);a>0&&e.searchParams.set("keyboard-band",String(a))}return e.href}layout(){const t=this.current;this.toggleAttribute("framed",t!==null);const e=this.body.style;if(!t){this.shownScale=1,Object.assign(e,{width:"100%",height:"100%",padding:"0",borderRadius:"0"}),this.screen.style.borderRadius="0",Object.assign(this.over.style,{width:"",height:"",zoom:""}),this.marks.replaceChildren(),this.tellScale();return}const i=this.orientation,r=Q(t,i),a=this.shownScale=kt(bt(t,i,{width:this.clientWidth,height:this.clientHeight}),r.width,window.devicePixelRatio||1);Object.assign(e,{width:`${r.width*a}px`,height:`${r.height*a}px`,padding:`${t.bezel*a}px`,borderRadius:`${(t.radius+t.bezel)*a}px`}),this.screen.style.borderRadius=`${t.radius*a}px`,Object.assign(this.over.style,{width:`${r.width}px`,height:`${r.height}px`,zoom:a===1?"":String(a)}),wt(this.marks,t,i),this.tellScale()}post(t){var i;const e=(i=this.iframe)==null?void 0:i.contentWindow;!e||!this.player||e.postMessage(t,new URL(this.player,this.ownerDocument.baseURI).origin)}tellSafeArea(){const t=this.current;this.post({type:"lecodes:safe-area",insets:t?{...t.insets[this.orientation]}:null});const e=t?this.keyboardBand(t):0;this.post({type:"lecodes:keyboard-band",height:e>0?e:null}),e===0&&this.drawKeyboard(0)}tellScale(){!this.loaded||this.toldScale===this.shownScale||(this.toldScale=this.shownScale,this.post({type:"lecodes:scale",scale:this.shownScale}))}keyboardBand(t){return this.orientation==="portrait"?t.keyboard:0}drawKeyboard(t,e=0){const i=this.current;if(!i||t<=0){Ct(this.keys,e);return}const r=Q(i,this.orientation);At(this.keys,{look:i.keyboardLook,language:this.language,width:r.width,height:t,inset:i.insets[this.orientation].bottom},e)}emit(t,e){this.dispatchEvent(new CustomEvent(t,{detail:e}))}};p(H,"observedAttributes",Bt);let D=H;const It=(s="lecodes-frame")=>{customElements.get(s)||customElements.define(s,D)};typeof customElements<"u"&&It();const S=(s,n,t)=>{const e=document.createElement(s);return n&&(e.className=n),t!==void 0&&(e.textContent=t),e},st=s=>s.type+(s.name?` "${s.name}"`:""),it=/^(width|height|minWidth|minHeight|maxWidth|maxHeight|aspectRatio|flex|flexGrow|flexShrink|flexBasis|flexDirection|flexWrap|alignItems|alignSelf|alignContent|justifyContent|gap|rowGap|columnGap|position|left|top|right|bottom|inset|display|overflow|zIndex|m|mx|my|mt|mr|mb|ml|margin\w*|p|px|py|pt|pr|pb|pl|padding\w*)$/,O=s=>typeof s=="string"?s:typeof s=="number"?String(Math.round(s*100)/100):Array.isArray(s)?s.map(O).join(" "):typeof s=="object"&&s!==null?Object.entries(s).map(([n,t])=>`${n}: ${O(t)}`).join(", "):String(s),Mt=(s,n,t)=>{const e=n.querySelector("#pick"),i=n.querySelector("#tree"),r=n.querySelector("#details"),a=new Set;let g=0,h=!1;const b=(o,c,d)=>{const l=S("div","node");l.dataset.id=String(o.id),l.style.paddingLeft=`${8+c*14}px`,l.classList.toggle("chosen",o.id===g);const E=S("span","fold",o.children.length===0?"":a.has(o.id)?"▸":"▾");if(E.addEventListener("click",C=>{C.stopPropagation(),a.has(o.id)?a.delete(o.id):a.add(o.id),w()}),l.append(E,S("span","type",o.type)),o.name&&l.append(S("span","name",` "${o.name}"`)),o.text&&l.append(S("span","text",` ${o.text.replace(/\s+/g," ")}`)),l.addEventListener("mouseenter",()=>s.highlight({...o,padding:{top:0,right:0,bottom:0,left:0}})),l.addEventListener("click",()=>void x(o.id)),d.append(l),!a.has(o.id))for(const C of o.children)b(C,c+1,d)},w=async()=>{if(!h)return;const o=await s.ask({ask:"tree"}),c=S("div");o||c.append(S("div","empty","No app is running."));for(const d of(o==null?void 0:o.layers)??[])d.kind==="widget"&&c.append(S("div","layer","widget")),b(d.tree,0,c);i.replaceChildren(c)};i.addEventListener("mouseleave",()=>{M()});const f=(o,c)=>{if(c.length===0)return null;const d=S("div","section");d.append(S("div","title",o));for(const[l,E]of c){const C=S("div","pair");C.append(S("span","key",l),S("span","value",E)),d.append(C)}return d},B=o=>typeof o=="object"&&o!==null&&!Array.isArray(o),j=(o,c)=>Object.entries(o??{}).filter(([d,l])=>c(d,l)).map(([d,l])=>[d,O(l)]),m=o=>{var _;if(!o){r.replaceChildren(S("div","empty","Pick an element: in the app, or in the outline."));return}const c=S("div","head",st(o)),d=S("div","path",o.path.slice(0,-1).map(st).join(" › ")),{rect:l,padding:E}=o,C=[f("Box",[["size",`${O(l.width)} × ${O(l.height)}`],["position",`${O(l.x)}, ${O(l.y)}`],...E.top||E.right||E.bottom||E.left?[["padding",`${E.top} ${E.right} ${E.bottom} ${E.left}`]]:[],...o.scroll?[["scroll",`${o.scroll.axis}: ${O(o.scroll.offset)} of ${O(o.scroll.content)}`]]:[],...(_=o.state)!=null&&_.length?[["state",o.state.join(", ")]]:[]]),o.text!==void 0?f("Text",[["",o.text]]):null,f("Layout",j(o.set,($,v)=>it.test($)&&!B(v))),f("Set by the app",j(o.set,($,v)=>!it.test($)&&!B(v))),...Object.entries(o.set??{}).filter(([,$])=>B($)).map(([$,v])=>f($,j(v,()=>!0))),f("Painted",Object.entries(o.style).map(([$,v])=>[$,O(v)])),o.source?f("Made at",[["",`${o.source.file}:${o.source.line}:${o.source.column}`]]):null];r.replaceChildren(c,d,...C.filter($=>$!==null))},x=async o=>{var d;g=o;const c=((d=await s.ask({ask:"node",node:o}))==null?void 0:d.node)??null;s.highlight(c),m(c);for(const l of i.querySelectorAll(".node"))l.classList.toggle("chosen",l.dataset.id===String(o))},M=async()=>{var o;s.picking||s.highlight(g?((o=await s.ask({ask:"node",node:g}))==null?void 0:o.node)??null:null)},P=o=>{s.picking=o,e.setAttribute("aria-pressed",String(o))};e.addEventListener("click",()=>P(!s.picking)),s.addEventListener("app-pick",o=>{const c=o.detail;P(!1),s.highlight(c),g=c.id;for(const d of c.path)a.delete(d.id);m(c),w().then(()=>{var d;return(d=i.querySelector(".node.chosen"))==null?void 0:d.scrollIntoView({block:"nearest"})})}),s.addEventListener("app-ready",()=>{g=0,a.clear(),m(null),w()});const L=o=>{h=o,n.hidden=!o,t.setAttribute("aria-pressed",String(o)),o?w():(P(!1),s.highlight(null))};return t.addEventListener("click",()=>L(!h)),n.querySelector("#refresh").addEventListener("click",()=>void w()),m(null),{setOpen:L,get open(){return h}}},W=new URLSearchParams(location.search),u=document.getElementById("frame");u.player=W.get("player")??"player/?dev=../..";W.get("src")&&(u.src=W.get("src"));u.inspect=!0;D.noRoom&&location.replace(u.playerUrl);const K=s=>{try{return localStorage.getItem(`lecodes-dev:${s}`)}catch{return null}},U=(s,n)=>{try{localStorage.setItem(`lecodes-dev:${s}`,n)}catch{}},at="none",J=W.get("device")??K("device")??Y[0].id;u.device=J===at||ot(J)?J:Y[0].id;u.orientation=(W.get("orientation")??K("orientation"))==="landscape"?"landscape":"portrait";const ct=document.getElementById("devices"),Z=document.getElementById("rotate"),Pt=document.getElementById("scale"),X=()=>{for(const n of ct.querySelectorAll("button"))n.setAttribute("aria-pressed",String(n.dataset.device===u.device));const s=!u.bare;Z.disabled=!s||u.orientationLocked,Z.title=u.orientationLocked?"The app holds the orientation (app.setOrientation)":"Turn the device",Pt.textContent=s&&u.scale<1?`${Math.round(u.scale*100)}%`:""};for(const{id:s,name:n}of[...Y,{id:at,name:"No frame"}]){const t=document.createElement("button");t.type="button",t.dataset.device=s,t.textContent=n,t.addEventListener("click",()=>{u.device=s,U("device",s),X()}),ct.appendChild(t)}Z.addEventListener("click",()=>{u.rotate(),U("orientation",u.orientation)});document.getElementById("reload").addEventListener("click",()=>u.reload());u.addEventListener("app-orientation",X);new ResizeObserver(X).observe(u);X();const lt=document.getElementById("console"),R=document.getElementById("console-lines"),dt=document.getElementById("console-toggle"),nt=document.getElementById("console-count"),Nt=500;let T=0;const tt=s=>{lt.hidden=!s,dt.setAttribute("aria-pressed",String(s)),U("console",s?"1":"0")},et=()=>{nt.textContent=T>0?String(T):"",nt.classList.toggle("errors",T>0)},ht=(s,n,t)=>{const e=R.scrollTop+R.clientHeight>=R.scrollHeight-4,i=document.createElement("div");if(i.className=`line ${s}`,i.textContent=n,t){const r=document.createElement("span");r.className="where",r.textContent=`
|
|
49
|
+
at ${t}`,i.appendChild(r)}for(R.appendChild(i);R.childElementCount>Nt;)R.firstElementChild.remove();e&&(R.scrollTop=R.scrollHeight)};u.addEventListener("app-log",s=>{var i;const{level:n,text:t}=s.detail,e=(i=/^\[(ERROR|WARN)\]/.exec(t))==null?void 0:i[1];ht(e==="ERROR"?"error":e==="WARN"?"warn":n,t)});u.addEventListener("app-error",s=>{var e,i;const n=s.detail,t=(e=n.frames)==null?void 0:e[0];T++,et(),ht("error",n.detail?`${n.message}: ${n.detail}`:n.message,t?`${t.source}:${t.line}:${t.column}${t.fn?` (${t.fn})`:""}`:(i=n.nativeFrames)==null?void 0:i[0]),tt(!0)});u.addEventListener("app-ready",()=>{T=0,et()});const pt=document.getElementById("inspect-toggle"),ut=Mt(u,document.getElementById("inspector"),pt);pt.addEventListener("click",()=>U("inspect",ut.open?"1":"0"));ut.setOpen(K("inspect")==="1");dt.addEventListener("click",()=>tt(lt.hidden));document.getElementById("console-clear").addEventListener("click",()=>{R.replaceChildren(),T=0,et()});tt(K("console")==="1");
|
|
@@ -484,7 +484,7 @@ const sync = defineCommand({
|
|
|
484
484
|
name: "sync",
|
|
485
485
|
summary: "Make every existing shell match the project: bundle + assets + SDK variant + plugins + CLI-owned files",
|
|
486
486
|
usage: "[ios|android]",
|
|
487
|
-
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 first launch of an installed build waits (≤ 5 s, the splash held) for the check and runs the newest bundle; every later launch runs the newest LOCAL bundle and 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; a store update keeps a downloaded bundle only when it is newer code than the one it ships. app.json \"icon\" (a square ≥1024px image) renders the app icons every sync (\"iconBackground\": the adaptive / flatten backdrop). app.json \"splash\" ({\"background\": \"#RRGGBB\", \"image\": \"./splash.png\", \"size\": 160}, all optional) renders the launch screen of both shells every sync — the colour with the image fitted centred into a size × size pt / dp square, 160 by default (iOS LaunchScreen.storyboard, Android the system splash of Theme.LeCodesRuntime, which the app's theme inherits). app.json \"urlScheme\" (\"myshop\") is the scheme the shells open on: iOS CFBundleURLTypes, Android a BROWSABLE intent-filter — a link arrives as app.launchUrl / the \"url\" event (the return of Browser.open with a returnUrl). app.json \"appLinks\" ([\"https://shop.example/pay/\"]) are the verified https links they open on — iOS universal links (associated-domains in App.entitlements), Android App Links (autoVerify intent-filters in the manifest); sync prints the apple-app-site-association / assetlinks.json each site must serve. No platform = every shell that exists; a dual-shell project compiles once.",
|
|
487
|
+
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 first launch of an installed build waits (≤ 5 s, the splash held) for the check and runs the newest bundle (a source not newer than the build answers 304 to the build's compile date: nothing downloaded, the embedded bundle runs); every later launch runs the newest LOCAL bundle and 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; a store update keeps a downloaded bundle only when it is newer code than the one it ships. app.json \"icon\" (a square ≥1024px image) renders the app icons every sync (\"iconBackground\": the adaptive / flatten backdrop). app.json \"splash\" ({\"background\": \"#RRGGBB\", \"image\": \"./splash.png\", \"size\": 160}, all optional) renders the launch screen of both shells every sync — the colour with the image fitted centred into a size × size pt / dp square, 160 by default (iOS LaunchScreen.storyboard, Android the system splash of Theme.LeCodesRuntime, which the app's theme inherits). app.json \"urlScheme\" (\"myshop\") is the scheme the shells open on: iOS CFBundleURLTypes, Android a BROWSABLE intent-filter — a link arrives as app.launchUrl / the \"url\" event (the return of Browser.open with a returnUrl). app.json \"appLinks\" ([\"https://shop.example/pay/\"]) are the verified https links they open on — iOS universal links (associated-domains in App.entitlements), Android App Links (autoVerify intent-filters in the manifest); sync prints the apple-app-site-association / assetlinks.json each site must serve. No platform = every shell that exists; a dual-shell project compiles once.",
|
|
488
488
|
examples: ["lecodes app sync", "lecodes app sync android"],
|
|
489
489
|
run: ({ args }) => runSync(args[0]),
|
|
490
490
|
})
|
|
@@ -86,8 +86,10 @@ export interface AppConfig {
|
|
|
86
86
|
resources?: { remote?: string[] }
|
|
87
87
|
/** Over-the-air JS updates: where the shell checks for newer compiled code at launch
|
|
88
88
|
* (natively, before the JS engine — offline is a no-op). The first launch of an installed
|
|
89
|
-
* build waits for the check (≤ 5 s, the splash held) and runs the newest bundle
|
|
90
|
-
*
|
|
89
|
+
* build waits for the check (≤ 5 s, the splash held) and runs the newest bundle — a 304 when
|
|
90
|
+
* the source is not newer than the build (If-Modified-Since from the embedded bundle's compile
|
|
91
|
+
* date: nothing downloaded, the embedded bundle runs); every later launch runs the local
|
|
92
|
+
* bundle at once and new code applies next launch.
|
|
91
93
|
* `true` = this project's live bundle on the platform (server-backed only); or an https
|
|
92
94
|
* URL to a compiled bundle on any hosting (string or `{ "url": … }`). Absent = no OTA.
|
|
93
95
|
* Both shells: iOS reads it from Info.plist (LeCodesUpdater), Android from the manifest
|
|
@@ -193,9 +193,9 @@ export const runtimeThemeTemplate = (splash?: AndroidSplashResources): string =>
|
|
|
193
193
|
|
|
194
194
|
/** lecodes-runtime/src/main/res/values-v31/lecodes_splash.xml — only with app.json "splash": the
|
|
195
195
|
* theme's Android 12+ twin (a v31 style replaces the base one whole, hence every item again) adding
|
|
196
|
-
* the system splash — the colour, and as the icon the image inset (288 − size) / 2
|
|
197
|
-
*
|
|
198
|
-
* icon otherwise). */
|
|
196
|
+
* the system splash — the colour, and as the icon the image inset (288 − size) / 2 of the 288 dp
|
|
197
|
+
* icon canvas on each side (a fraction: splashIconTemplate says why) or nothing (transparent: the
|
|
198
|
+
* system would draw the launcher icon otherwise). */
|
|
199
199
|
export const runtimeThemeV31Template = (splash: AndroidSplashResources): string => `<?xml version="1.0" encoding="utf-8"?>
|
|
200
200
|
<!-- Generated by \`lecodes app sync\` from app.json "splash" — regenerated on every sync, do not edit. -->
|
|
201
201
|
<resources>
|
|
@@ -218,14 +218,23 @@ export const splashWindowTemplate = (splash: AndroidSplashResources): string =>
|
|
|
218
218
|
|
|
219
219
|
/** lecodes-runtime/src/main/res/drawable/lecodes_splash_icon.xml — Android 12+'s splash icon: the
|
|
220
220
|
* size dp image inset to the 288 dp canvas the system draws (and masks to a 192 dp circle). The
|
|
221
|
-
* inset is
|
|
221
|
+
* inset is a FRACTION of the canvas, never dp: the system pre-draws the icon into a bitmap of its
|
|
222
|
+
* default icon size (108 dp, SplashscreenIconDrawableFactory.ImmobileIconDrawable) and scales that
|
|
223
|
+
* bitmap up to the splash's 192 dp — a dp inset is taken off the small canvas as it is (the image
|
|
224
|
+
* gets what is left of the 1.5 × 108 = 162 dp layer: 34 dp at the default inset of 64, 4 dp — a dot
|
|
225
|
+
* — at size 130), a fraction scales with it. */
|
|
222
226
|
export const splashIconTemplate = (size: number): string => `<?xml version="1.0" encoding="utf-8"?>
|
|
223
227
|
<!-- Generated by \`lecodes app sync\` from app.json "splash" — regenerated on every sync, do not edit. -->
|
|
224
228
|
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
|
225
229
|
android:drawable="@drawable/lecodes_splash"
|
|
226
|
-
android:inset="${
|
|
230
|
+
android:inset="${splashIconInsetPercent(size)}%" />
|
|
227
231
|
`
|
|
228
232
|
|
|
233
|
+
/** The inset of the splash icon as a percentage of its 288 dp canvas, (288 − size) / 2 of 288, at
|
|
234
|
+
* most four decimals (0.0001 % of 288 dp is nothing). */
|
|
235
|
+
export const splashIconInsetPercent = (size: number): string =>
|
|
236
|
+
(((SPLASH_ICON_CANVAS_DP - size) / 2 / SPLASH_ICON_CANVAS_DP) * 100).toFixed(4).replace(/\.?0+$/, "")
|
|
237
|
+
|
|
229
238
|
/** lecodes-runtime/src/main/kotlin/io/letary/lecodes/runtime/LeCodesRuntime.kt — the generated
|
|
230
239
|
* registration + host-hook surface. Plugins are called by FQN (no import collisions); the
|
|
231
240
|
* activity hooks forward to the SDK's LeCodesAppHooks, which NAMES NO PLUGIN — a plugin that
|
|
@@ -426,8 +435,9 @@ colour (\`background\`, default \`"iconBackground"\`, default white) with the im
|
|
|
426
435
|
a \`size\`×\`size\` dp square (160 by default) — never at its pixel size — from
|
|
427
436
|
\`drawable-mdpi…xxxhdpi/lecodes_splash.png\` (size × 1/1.5/2/3/4 px: 160…640 at the default) of the one
|
|
428
437
|
master. Android 12+ shows it as the system splash (\`windowSplashScreenBackground\` +
|
|
429
|
-
\`windowSplashScreenAnimatedIcon\`: the image on the 288 dp icon canvas, inset (288 − size) / 2
|
|
430
|
-
|
|
438
|
+
\`windowSplashScreenAnimatedIcon\`: the image on the 288 dp icon canvas, inset (288 − size) / 2 on
|
|
439
|
+
each side as a fraction of the canvas — the system draws the icon small and scales it up, so a dp
|
|
440
|
+
inset would leave a dot — of which the system shows only the inner 192 dp circle — keep the mark inside one; a \`size\` above
|
|
431
441
|
192 is cut there, sync warns, and 288 is the most it takes), API 23–30 as the window background. Use
|
|
432
442
|
a square mark of at least 3 × size px (a non-square one is fitted into the square, a smaller one is
|
|
433
443
|
upscaled). No image = the colour
|