scad-gltf 0.3.3 → 0.4.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.
- package/README.md +31 -4
- package/bin/gen.js +1 -1
- package/editor/dist/assets/{OutputPass-B6MrFnGE.js → OutputPass-C3DZe46t.js} +2 -2
- package/editor/dist/assets/{index-CgVih1C4.js → index-D-FSzMhd.js} +76 -76
- package/editor/dist/assets/{index-B1KAb97r.css → index-D8In1wnN.css} +1 -1
- package/editor/dist/assets/{openscad-CdBCY4mx.wasm → openscad-DiJCzDqJ.wasm} +0 -0
- package/editor/dist/assets/preview-Br3_yFrj.js +1 -0
- package/editor/dist/assets/preview-Cx3FHqKG.css +1 -0
- package/editor/dist/assets/{prompt-ui-B9QCNLfJ.js → prompt-ui-BjZlMzjn.js} +1 -0
- package/editor/dist/content.js +1 -1
- package/editor/dist/index.html +7 -6
- package/editor/dist/preview.html +20 -15
- package/editor/dist/sw.js +1 -1
- package/package.json +1 -1
- package/src/cli-utils.js +2 -2
- package/src/ext/openscad.js +1 -1
- package/src/ext/openscad.wasm +0 -0
- package/src/project-prompts.js +65 -32
- package/src/prompt.js +1 -0
- package/editor/dist/assets/preview-A6M04CFK.css +0 -1
- package/editor/dist/assets/preview-BbUpKLF4.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
*{box-sizing:border-box}body{color:#ddd;background-color:#222;margin:0;font-family:system-ui,sans-serif;overflow:hidden}#app-container{flex-direction:row;width:100vw;height:100vh;display:flex}@media (width<=800px),(orientation:portrait){#app-container{flex-direction:column}}#left-panel{background:#1e1e1e;border-right:1px solid #444;flex-direction:column;flex:1;gap:15px;min-width:0;min-height:50vh;padding:15px;display:flex;overflow-y:auto}@media (width<=800px),(orientation:portrait){#left-panel{border-bottom:1px solid #444;border-right:none}}#right-panel{background:#111;flex:1;min-width:0;min-height:50vh;position:relative}.panel-section{flex-direction:column;gap:8px;display:flex}.flex-grow{flex-grow:1}h2{color:#fff;text-transform:uppercase;letter-spacing:.5px;margin:0;font-size:14px}.help-text{color:#aaa;margin:0 0 5px;font-size:11px}textarea{color:#d4d4d4;resize:vertical;background:#2a2a2a;border:1px solid #444;border-radius:4px;outline:none;width:100%;padding:10px;font-family:monospace;font-size:12px}textarea:focus{border-color:#666}input[type=text],input[type=number],select{color:#d4d4d4;background:#2a2a2a;border:1px solid #444;border-radius:4px;outline:none;align-items:center;height:34px;padding:0 8px;font-size:13px;display:flex}input[type=text]:focus,input[type=number]:focus,select:focus{border-color:#666}#editor{white-space:pre;flex-grow:1;min-height:150px;overflow:auto}button{color:#fff;cursor:pointer;background:#444;border:1px solid #555;border-radius:4px;justify-content:center;align-items:center;height:34px;padding:0 12px;font-size:13px;font-weight:500;transition:background .2s;display:flex}button:hover{background:#555}button.primary{background:#06c;border-color:#05a}button.primary:hover{background:#07e}button.danger{background:#c00;border-color:#a00}button.danger:hover{background:#e00}.controls-row{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.toggles{-webkit-user-select:none;user-select:none;font-size:13px}.scad-prompt-toggles{-webkit-user-select:none;user-select:none;flex-wrap:wrap;gap:8px 12px;margin-bottom:8px;font-size:13px;display:flex}.scad-prompt-toggles label{cursor:pointer;align-items:center;gap:4px;display:flex}.scad-prompt-group{background:#2a2a2a;border:1px solid #444;border-radius:4px;flex-direction:column;gap:8px;margin-bottom:8px;padding:8px;display:flex}.scad-prompt-group-header{color:#eee;border-bottom:1px solid #444;justify-content:space-between;align-items:center;padding-bottom:6px;font-size:13px;font-weight:700;display:flex}.scad-prompt-group-header label{cursor:pointer;align-items:center;gap:4px;font-size:12px;font-weight:400;display:flex}.scad-prompt-toggles.nested{margin-bottom:0}.toggles label{cursor:pointer;align-items:center;gap:4px;display:flex}.toggles label input[type=number]{color:#d4d4d4;background:#2a2a2a;border:1px solid #444;border-radius:4px;outline:none;width:45px;height:24px;padding:2px 4px;font-family:monospace;font-size:12px}input[type=range]{-webkit-appearance:none;background:0 0;flex:1;width:100%;margin:8px 0}input[type=range]:focus{outline:none}input[type=range]::-webkit-slider-runnable-track{cursor:pointer;background:#444;border-radius:3px;width:100%;height:6px}input[type=range]::-webkit-slider-thumb{cursor:pointer;-webkit-appearance:none;background:#06c;border-radius:50%;width:16px;height:16px;margin-top:-5px}#backend-url{flex:1;min-width:0}#backend-ui{flex-direction:column;gap:10px;margin-top:10px;display:none}#backend-ui.active{display:flex}#backend-select{flex:1;min-width:0}#backend-single-save-btn,#backend-delete-btn{flex-shrink:0}#viewer{width:100%;height:100%;overflow:hidden}#viewer canvas{display:block}#viewer-controls{z-index:10;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background:#0009;border-radius:6px;align-items:center;gap:12px;padding:6px 12px;display:flex;position:absolute;top:15px;right:15px}#viewer-controls label{color:#fff;cursor:pointer;-webkit-user-select:none;user-select:none;align-items:center;gap:4px;margin:0;font-size:13px;display:flex}#fullscreen-btn,#screenshot-btn{color:#fff;cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;height:auto;font-size:16px;display:flex}#fullscreen-btn{padding:0 4px}#screenshot-btn{padding:0 4px 5px}#fullscreen-btn:hover,#screenshot-btn:hover{color:#0af;background:0 0}#drag-overlay{color:#fff;z-index:9999;letter-spacing:1px;background:#0066ccd9;justify-content:center;align-items:center;width:100vw;height:100vh;font-size:2rem;font-weight:700;display:none;position:fixed;top:0;left:0}#drag-overlay.active{display:flex}.footer-section{align-items:center;margin-top:auto;padding-top:5px}.source-link{color:#888;font-size:12px;text-decoration:none;transition:color .2s}.source-link:hover{color:#fff}
|
|
1
|
+
*{box-sizing:border-box}body{color:#ddd;background-color:#222;margin:0;font-family:system-ui,sans-serif;overflow:hidden}#app-container{flex-direction:row;width:100vw;height:100vh;display:flex}@media (width<=800px),(orientation:portrait){#app-container{flex-direction:column}}#left-panel{background:#1e1e1e;border-right:1px solid #444;flex-direction:column;flex:1;gap:15px;min-width:0;min-height:50vh;padding:15px;display:flex;overflow-y:auto}@media (width<=800px),(orientation:portrait){#left-panel{border-bottom:1px solid #444;border-right:none}}#right-panel{background:#111;flex:1;min-width:0;min-height:50vh;position:relative}.panel-section{flex-direction:column;gap:8px;display:flex}.flex-grow{flex-grow:1}h2{color:#fff;text-transform:uppercase;letter-spacing:.5px;margin:0;font-size:14px}.help-text{color:#aaa;margin:0 0 5px;font-size:11px}textarea{color:#d4d4d4;resize:vertical;background:#2a2a2a;border:1px solid #444;border-radius:4px;outline:none;width:100%;padding:10px;font-family:monospace;font-size:12px}textarea:focus{border-color:#666}input[type=text],input[type=number],select{color:#d4d4d4;background:#2a2a2a;border:1px solid #444;border-radius:4px;outline:none;align-items:center;height:34px;padding:0 8px;font-size:13px;display:flex}input[type=text]:focus,input[type=number]:focus,select:focus{border-color:#666}#editor{white-space:pre;flex-grow:1;min-height:150px;overflow:auto}button{color:#fff;cursor:pointer;background:#444;border:1px solid #555;border-radius:4px;justify-content:center;align-items:center;height:34px;padding:0 12px;font-size:13px;font-weight:500;transition:background .2s;display:flex}button:hover{background:#555}button.primary{background:#06c;border-color:#05a}button.primary:hover{background:#07e}button.danger{background:#c00;border-color:#a00}button.danger:hover{background:#e00}.controls-row{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.toggles{-webkit-user-select:none;user-select:none;font-size:13px}.scad-prompt-toggles{-webkit-user-select:none;user-select:none;flex-wrap:wrap;gap:8px 12px;margin-bottom:8px;font-size:13px;display:flex}.scad-prompt-toggles label{cursor:pointer;align-items:center;gap:4px;display:flex}.scad-prompt-group{background:#2a2a2a;border:1px solid #444;border-radius:4px;flex-direction:column;gap:8px;margin-bottom:8px;padding:8px;display:flex}.scad-prompt-group-header{color:#eee;border-bottom:1px solid #444;justify-content:space-between;align-items:center;padding-bottom:6px;font-size:13px;font-weight:700;display:flex}.scad-prompt-group-header label{cursor:pointer;align-items:center;gap:4px;font-size:12px;font-weight:400;display:flex}.scad-prompt-toggles.nested{margin-bottom:0}.toggles label{cursor:pointer;align-items:center;gap:4px;display:flex}.toggles label input[type=number]{color:#d4d4d4;background:#2a2a2a;border:1px solid #444;border-radius:4px;outline:none;width:45px;height:24px;padding:2px 4px;font-family:monospace;font-size:12px}input[type=range]{-webkit-appearance:none;background:0 0;flex:1;width:100%;margin:8px 0}input[type=range]:focus{outline:none}input[type=range]::-webkit-slider-runnable-track{cursor:pointer;background:#444;border-radius:3px;width:100%;height:6px}input[type=range]::-webkit-slider-thumb{cursor:pointer;-webkit-appearance:none;background:#06c;border-radius:50%;width:16px;height:16px;margin-top:-5px}#backend-url{flex:1;min-width:0}#backend-ui{flex-direction:column;gap:10px;margin-top:10px;display:none}#backend-ui.active{display:flex}#backend-select{flex:1;min-width:0}#backend-single-save-btn,#backend-delete-btn{flex-shrink:0}#viewer{width:100%;height:100%;overflow:hidden}#viewer canvas{display:block}#viewer-controls{z-index:10;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background:#0009;border-radius:6px;flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:12px;max-width:calc(100% - 30px);padding:6px 12px;display:flex;position:absolute;top:15px;right:15px}#viewer-controls select{color:#fff;cursor:pointer;background:#333;border:1px solid #555;border-radius:4px;outline:none;padding:2px 6px;font-size:13px}#viewer-controls label{color:#fff;cursor:pointer;-webkit-user-select:none;user-select:none;align-items:center;gap:4px;margin:0;font-size:13px;display:flex}#fullscreen-btn,#screenshot-btn{color:#fff;cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;height:auto;font-size:16px;display:flex}#fullscreen-btn{padding:0 4px}#screenshot-btn{padding:0 4px 5px}#fullscreen-btn:hover,#screenshot-btn:hover{color:#0af;background:0 0}#drag-overlay{color:#fff;z-index:9999;letter-spacing:1px;background:#0066ccd9;justify-content:center;align-items:center;width:100vw;height:100vh;font-size:2rem;font-weight:700;display:none;position:fixed;top:0;left:0}#drag-overlay.active{display:flex}.footer-section{align-items:center;margin-top:auto;padding-top:5px}.source-link{color:#888;font-size:12px;text-decoration:none;transition:color .2s}.source-link:hover{color:#fff}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{A as e,B as t,D as n,E as r,H as i,O as a,R as o,W as s,a as ee,d as c,et as te,f as ne,ht as re,i as ie,l as ae,lt as oe,mt as se,n as ce,o as le,p as l,r as ue,s as de,t as fe,u as pe,ut as u,w as me,y as he}from"./OutputPass-C3DZe46t.js";var d=document.getElementById(`viewer-container`),f=document.getElementById(`viewer`),p=document.getElementById(`filename-input`),m=document.getElementById(`save-btn`),h=document.getElementById(`open-editor-btn`),g=document.getElementById(`show-grid-cb`),_=document.getElementById(`wireframe-cb`),v=document.getElementById(`fullscreen-btn`),y=document.getElementById(`screenshot-btn`),b=document.getElementById(`camera-select`),x=document.getElementById(`anim-controls`),S=document.getElementById(`anim-play-btn`),C=document.getElementById(`anim-select`),w=document.getElementById(`anim-slider`),T=null,E=null,D=!0,O=!1,k=[],A=``,j=!1,M=null,N=null,P=!1,F=[],I=null;function L(e,t,n){if(e){if(e.isPerspectiveCamera)e.aspect=t/n,e.updateProjectionMatrix();else if(e.isOrthographicCamera){let r=t/n;e.userData.ymag||(e.userData.ymag=e.top),e.left=-e.userData.ymag*r,e.right=e.userData.ymag*r,e.top=e.userData.ymag,e.bottom=-e.userData.ymag,e.updateProjectionMatrix()}}}var R=new te;R.background=new he(2236962);var z=new i(60,window.innerWidth/window.innerHeight,.1,2e3);z.position.set(50,50,-50),I=z;var B=new pe({antialias:!0});B.setSize(window.innerWidth,window.innerHeight),B.setPixelRatio(Math.min(window.devicePixelRatio,2)),B.shadowMap.enabled=!0,B.shadowMap.type=2,B.toneMapping=4,B.toneMappingExposure=1,f.appendChild(B.domElement);var V=new ie(B),H=new ue(R,I);V.addPass(H);var ge=new ce(new oe(window.innerWidth,window.innerHeight),.1,.1,2);V.addPass(ge);var _e=new fe;V.addPass(_e);var U=new de(z,B.domElement);U.enableDamping=!0,U.dampingFactor=.1,R.environment=new ae(B).fromScene(new ee,.04).texture,R.environmentIntensity=.8,b&&b.addEventListener(`change`,e=>{let t=e.target.value;t===``?(I=z,U.enabled=!0):(I=F[parseInt(t)],U.enabled=!1);let n=d?d.clientWidth:window.innerWidth,r=d?d.clientHeight:window.innerHeight;L(I,n,r),H.camera=I});var W=new a;R.add(W);var ve=new s(2e3,2e3),ye=new t({color:2236962,roughness:.8,metalness:.1}),G=new o(ve,ye);G.rotation.x=-Math.PI/2,G.receiveShadow=!0,R.add(G);var K=new n(2e3,100,5592405,3355443);K.material.transparent=!0,K.material.opacity=.5,R.add(K);var q=new ne(100);R.add(q);var J=new me(16777215,1.5);J.castShadow=!0,J.shadow.mapSize.width=2048,J.shadow.mapSize.height=2048,J.shadow.bias=-5e-4,W.add(J),W.add(J.target);var Y=new e(16777215,4473924,.6);Y.position.set(0,20,0),W.add(Y),_&&_.addEventListener(`change`,()=>{let e=_.checked;T&&T.traverse(t=>{t.isMesh&&t.material&&(Array.isArray(t.material)?t.material.forEach(t=>{t.wireframe=e}):t.material.wireframe=e)})}),v&&d&&(v.addEventListener(`click`,()=>{document.fullscreenElement?document.exitFullscreen().catch(e=>{console.warn(`Error attempting to exit fullscreen: ${e.message}`)}):d.requestFullscreen().catch(e=>{console.warn(`Error attempting to enable fullscreen: ${e.message}`)})}),document.addEventListener(`fullscreenchange`,()=>{setTimeout(()=>window.dispatchEvent(new Event(`resize`)),100)})),y&&y.addEventListener(`click`,()=>{P=!0;let e=y.innerText;y.innerText=`✅`,setTimeout(()=>{y.innerText=e},1e3)});function X(){E&&(D=!1,E.paused=!0,S&&(S.innerText=`▶ Play`))}function Z(e){if(!N||!k[e])return;E&&E.stop();let t=k[e];E=N.clipAction(t),E.play(),E.time=0,N.update(0),D=!0,E.paused=!1,S&&(S.innerText=`⏸ Pause`),w&&(w.value=0)}C&&C.addEventListener(`change`,e=>{Z(parseInt(e.target.value))}),S&&S.addEventListener(`click`,()=>{E&&(D=!D,E.paused=!D,S.innerText=D?`⏸ Pause`:`▶ Play`)}),w&&(w.addEventListener(`mousedown`,()=>{O=!0,X()}),w.addEventListener(`mouseup`,()=>{O=!1}),w.addEventListener(`touchstart`,()=>{O=!0,X()},{passive:!0}),w.addEventListener(`touchend`,()=>{O=!1},{passive:!0}),w.addEventListener(`input`,e=>{if(X(),E){let t=E.getClip().duration;E.time=parseFloat(e.target.value)*t,N&&N.update(0)}}));var Q=performance.now();function $(){requestAnimationFrame($);let e=performance.now(),t=(e-Q)/1e3;if(Q=e,N&&(N.update(t),E&&D&&!O)){let e=E.getClip().duration;if(e>0){let t=E.time%e;t<0&&(t+=e),w&&(w.value=t/e)}}U.update();let n=!g||g.checked;if(K.visible=n,q.visible=n,typeof pathTracer<`u`&&E&&D&&pathTracer.updateCamera(),V.render(),P){P=!1;let e=B.domElement.toDataURL(`image/png`);window.parent.postMessage({type:`ADD_IMAGE_TO_CHAT`,dataUrl:e},`*`)}}$(),window.addEventListener(`resize`,()=>{let e=d?d.clientWidth:window.innerWidth,t=d?d.clientHeight:window.innerHeight;L(I,e,t),B.setSize(e,t),V.setSize(e,t)});function be(e,t){if(!e)return new l;e.updateMatrixWorld(!0);let n=new l().setFromObject(e);if(!t||t.length===0)return n;let r=new Map;e.traverse(e=>{r.set(e,{position:e.position.clone(),quaternion:e.quaternion.clone(),scale:e.scale.clone()})});let i=new c(e);for(let r of t){let t=i.clipAction(r);t.play();let a=new Set([0,r.duration]);if(r.tracks){for(let e of r.tracks)if(e.times)for(let t=0;t<e.times.length;t++)a.add(e.times[t])}if(r.duration>0)for(let e=0;e<=20;e++)a.add(e/20*r.duration);let o=Array.from(a).sort((e,t)=>e-t);if(o.length>60){let e=[],t=(o.length-1)/59;for(let n=0;n<60;n++)e.push(o[Math.round(n*t)]);o=e}for(let r of o)t.time=r,i.update(0),e.updateMatrixWorld(!0),n.expandByObject(e);t.stop()}return i.stopAllAction(),i.uncacheRoot(e),r.forEach((e,t)=>{t.position.copy(e.position),t.quaternion.copy(e.quaternion),t.scale.copy(e.scale)}),e.updateMatrixWorld(!0),n}function xe(e){return new Promise((t,n)=>{T&&=(N&&=(N.stopAllAction(),N.uncacheRoot(N.getRoot()),null),E=null,k=[],R.remove(T),T.traverse(e=>{e.isMesh&&(e.geometry.dispose(),Array.isArray(e.material)?e.material.forEach(e=>e.dispose()):e.material.dispose())}),null);let r=e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength);new le().parse(r,``,e=>{T=e.scene,k=e.animations||[];let n=_?_.checked:!1;F=[],T.traverse(e=>{e.isCamera&&F.push(e)});let r=!1;b?F.length>0?(b.style.display=`inline-block`,b.innerHTML=`<option value="">Free Camera</option>`,F.forEach((e,t)=>{let n=document.createElement(`option`);n.value=t,n.innerText=e.name||`Camera ${t+1}`,b.appendChild(n)}),b.value=`0`,I=F[0],U.enabled=!1,r=!0):(b.style.display=`none`,b.value=``,I=z,U.enabled=!0):(I=z,U.enabled=!0);let i=f?f.clientWidth:window.innerWidth,a=f?f.clientHeight:window.innerHeight;L(I,i,a),H.camera=I,k.length>0?(N=new c(T),r=!0,x?(S&&(S.style.display=``),C&&(C.style.display=``),w&&(w.style.display=``),C.innerHTML=``,k.forEach((e,t)=>{let n=document.createElement(`option`);n.value=t,n.innerText=e.name||`Animation ${t+1}`,C.appendChild(n)}),Z(0)):(k.forEach(e=>{let t=N.clipAction(e);t.play(),t.time=0}),N.update(0))):(x&&(S&&(S.style.display=`none`),C&&(C.style.display=`none`),w&&(w.style.display=`none`)),E=null),x&&(x.style.display=r?`flex`:`none`),T.traverse(e=>{e.isMesh&&(e.castShadow=!0,e.receiveShadow=!0,e.frustumCulled=!1,e.material&&(Array.isArray(e.material)?e.material.forEach(e=>{e.wireframe=n}):e.material.wireframe=n))}),R.add(T),Se(),t()},e=>{console.error(e),n(e)})})}function Se(){if(!T)return;let e=be(T,k);if(e.isEmpty())return;let t=e.getCenter(new u),n=e.getSize(new u),i=Math.max(n.x,n.y,n.z)||10;G.position.y=e.min.y-.01,K.position.y=G.position.y+.001,q.position.y=G.position.y+.002;let a=z.fov*(Math.PI/180),o=i/(2*Math.tan(a/2));z.aspect<1&&(o/=z.aspect),o*=1.5,z.near=Math.max(.01,i*.01),z.far=Math.max(2e3,o*10),z.updateProjectionMatrix(),U.maxDistance=z.far,z.position.set(t.x+o*.8,t.y+o*.8,t.z-o*.8),z.lookAt(t),U.target.copy(t),U.update(),J.position.set(t.x+i,t.y+i*1.5,t.z-i),J.target.position.copy(t),J.target.updateMatrixWorld();let s=i*1.5;J.shadow.camera.left=-s,J.shadow.camera.right=s,J.shadow.camera.top=s,J.shadow.camera.bottom=-s,J.shadow.camera.near=.1,J.shadow.camera.far=i*5,J.shadow.camera.updateProjectionMatrix(),R.fog=new r(2236962,o*1.5,o*5)}async function Ce(e){let t={},n=`http://localhost:3000`,r=[];try{let e=await fetch(`${n}/api/scads`);e.ok&&(r=(await e.json()).files||[])}catch{return t}let i=new Set;async function a(e){let o=/(?:include|use)\s*([<"])([^>"]+)([>"])/g,s;for(;(s=o.exec(e))!==null;){let e=s[2],o=e.split(/[/\\]/).pop();if(!i.has(e)&&r.includes(o)){i.add(e);try{let r=await fetch(`${n}/api/scads/${encodeURIComponent(o)}`);if(r.ok){let n=await r.json();t[e]=n.content,await a(n.content)}}catch{console.warn(`Failed to load dependency: ${e}`)}}}}return await a(e),t}async function we(e){if(j){M=e;return}j=!0;try{let t=await Ce(e);await xe(await se(e,{wasmUrl:re,additionalFiles:t}))}catch(e){console.error(e)}finally{if(j=!1,M!==null){let e=M;M=null,we(e)}}}window.addEventListener(`message`,async e=>{if(e.data.type===`RENDER_SCAD`){A=e.data.code;let t=A.match(/\/\*\s*Model Name:\s*(.*?)\s*\*\//i);if(t&&t[1]){let e=t[1].trim().replace(/\s+/g,`_`).replace(/[^a-zA-Z0-9_-]/g,``);e&&(p.value=e)}await we(e.data.code)}}),m.addEventListener(`click`,async()=>{let e=p.value.trim();if(!e){alert(`Please enter a filename.`),p.focus();return}let t=`http://localhost:3000`;try{m.innerText=`Checking...`,m.disabled=!0;let n=await fetch(`${t}/api/scads`);if(!n.ok)throw Error("Could not reach `scad-gltf serve`.");let r=await n.json(),i=e.toLowerCase().endsWith(`.scad`)?e:`${e}.scad`;if(r.files&&r.files.includes(i)&&!confirm(`File "${i}" already exists. Overwrite?`)){m.innerText=`Save`,m.disabled=!1;return}m.innerText=`Saving...`;let a=await fetch(`${t}/api/scads`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({filename:e,content:A||``})});if(!a.ok){let e=await a.json();throw Error(e.error||`Save failed.`)}m.innerText=`✅ Saved!`,setTimeout(()=>{m.innerText=`Save`,m.disabled=!1},2e3)}catch(t){console.warn(`Backend not connected or save failed, falling back to download`,t);let n=new Blob([A||``],{type:`text/plain`}),r=URL.createObjectURL(n),i=document.createElement(`a`);i.href=r,i.download=e.toLowerCase().endsWith(`.scad`)?e:`${e}.scad`,document.body.appendChild(i),i.click(),document.body.removeChild(i),URL.revokeObjectURL(r),m.innerText=`✅ Downloaded!`,setTimeout(()=>{m.innerText=`Save`,m.disabled=!1},2e3)}});async function Te(e){try{if(typeof CompressionStream<`u`){let t=new Blob([e]).stream().pipeThrough(new CompressionStream(`deflate-raw`)),n=await new Response(t).arrayBuffer(),r=new Uint8Array(n),i=``;for(let e=0;e<r.length;e++)i+=String.fromCharCode(r[e]);return`c`+btoa(i).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``)}}catch(e){console.warn(`CompressionStream failed, falling back`,e)}return`u`+btoa(unescape(encodeURIComponent(e))).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``)}h.addEventListener(`click`,async()=>{if(!A){alert(`No model loaded to open.`);return}try{h.disabled=!0,h.innerText=`Preparing...`;let e=`https://iliagrigorevdev.github.io/scad-gltf/#${await Te(A)}`;window.open(e,`_blank`),window.parent.postMessage({type:`CLOSE_PREVIEW`},`*`),h.innerText=`✅ Opened!`,setTimeout(()=>{h.innerText=`Edit`,h.disabled=!1},2e3)}catch(e){console.error(`Error creating editor link`,e),alert(`Error creating editor link: `+e.message),h.innerText=`Edit`,h.disabled=!1}});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
body{background-color:#333;width:100vw;height:100vh;margin:0;font-family:sans-serif;display:flex;overflow:hidden}#viewer-container{background:#1e1e1e;flex-grow:1;width:100%;height:100vh;position:relative;overflow:hidden}#viewer{width:100%;height:100%}#top-ui{z-index:10;pointer-events:none;flex-wrap:wrap;justify-content:space-between;align-items:flex-start;gap:10px;display:flex;position:absolute;top:15px;left:15px;right:40px}#top-ui>*{pointer-events:auto}#save-ui,#viewer-controls{-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background:#0009;border-radius:6px;flex-wrap:wrap;align-items:center;gap:8px;padding:6px;display:flex}#viewer-controls select{color:#fff;cursor:pointer;background:#333;border:1px solid #555;border-radius:4px;outline:none;padding:2px 6px;font-size:13px}#viewer-controls label{color:#fff;cursor:pointer;-webkit-user-select:none;user-select:none;align-items:center;gap:4px;margin:0;font-size:13px;display:flex}#fullscreen-btn,#screenshot-btn{color:#fff;cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;height:auto;font-size:16px;display:flex}#fullscreen-btn{padding:0 4px}#screenshot-btn{padding:0 4px 6px}#fullscreen-btn:hover,#screenshot-btn:hover{color:#0af;background:0 0}#filename-input{color:#fff;background:#333;border:1px solid #555;border-radius:4px;outline:none;width:160px;padding:4px 8px;font-family:monospace}#filename-input:focus{border-color:#0af}#save-btn{color:#fff;cursor:pointer;background:#0af;border:none;border-radius:4px;padding:4px 12px;font-weight:700}#save-btn:hover:not(:disabled){background:#08c}#save-btn:disabled{opacity:.7;cursor:not-allowed}#open-editor-btn{color:#fff;cursor:pointer;background:#6f42c1;border:none;border-radius:4px;padding:4px 12px;font-weight:700}#open-editor-btn:hover:not(:disabled){background:#5a32a3}#open-editor-btn:disabled{opacity:.7;cursor:not-allowed}#anim-controls{z-index:10;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background:#0009;border-radius:8px;flex-wrap:wrap;justify-content:center;align-items:center;gap:10px;width:90%;max-width:500px;padding:8px 16px;display:flex;position:absolute;bottom:20px;left:50%;transform:translate(-50%)}#anim-controls select{color:#fff;text-overflow:ellipsis;background:#333;border:1px solid #555;border-radius:4px;outline:none;max-width:150px;padding:4px;font-size:13px;overflow:hidden}#anim-controls button{color:#fff;cursor:pointer;white-space:nowrap;background:#444;border:none;border-radius:4px;padding:4px 12px;font-size:13px}#anim-controls button:hover{background:#555}#anim-slider{cursor:pointer;-webkit-appearance:none;background:0 0;flex-grow:1;min-width:80px;margin:0}#anim-slider:focus{outline:none}#anim-slider::-webkit-slider-runnable-track{cursor:pointer;background:#444;border-radius:3px;width:100%;height:6px}#anim-slider::-webkit-slider-thumb{cursor:pointer;-webkit-appearance:none;background:#0af;border-radius:50%;width:14px;height:14px;margin-top:-4px}
|
|
@@ -27,6 +27,7 @@ Important Geometry rules:
|
|
|
27
27
|
Important Animation rules:
|
|
28
28
|
- Wrapping: Use the 'armature(animations=...)' module at the root to wrap all animated components.
|
|
29
29
|
- Hierarchies: Use the 'bone(name="BoneName", t=[x,y,z], r=[x,y,z])' module to define hierarchical animated parts.
|
|
30
|
+
- Camera Animation: You can create an animated glTF camera by naming a bone "Camera" or "camera". You can append numbers or separators to the name (e.g., "Camera1", "Camera_fov60" for Perspective 60 deg FOV, or "Camera_ortho_mag10" for Orthographic, scale 10). Physical parts like "CameraMount" are safely ignored. It will animate just like any other bone! Note: The camera looks forward along the +Y axis, with +Z as its UP direction.
|
|
30
31
|
- Auto-Unioning: Any child meshes (e.g., cube, cylinder, imported objects) placed directly inside an 'armature()' or 'bone()' node are automatically unioned together by the engine and rigidly attached to that bone's transform node. Child bones remain separate nodes in the hierarchy.
|
|
31
32
|
- Animation Data: The 'animations' property is an array of named animation sequences. Each sequence contains an array of tracks defining keyframes for each bone. Format:
|
|
32
33
|
animations = [
|
package/editor/dist/content.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{i as e,n as t,r as n,t as r}from"./assets/prompt-ui-
|
|
1
|
+
import{i as e,n as t,r as n,t as r}from"./assets/prompt-ui-BjZlMzjn.js";console.log(`🚀 SCAD Preview Extension loaded!`),window.addEventListener(`message`,e=>{if(e.data){if(e.data.type===`CLOSE_PREVIEW`){let e=document.getElementById(`scad-preview-iframe`);e&&e.remove()}else e.data.type===`ADD_IMAGE_TO_CHAT`&&i(e.data.dataUrl)}});async function i(e){try{let t=await(await fetch(e)).blob(),n=new File([t],`preview.png`,{type:`image/png`}),r=document.querySelector(`ms-prompt-box textarea[formcontrolname="promptText"]`)||document.querySelector(`ms-prompt-box textarea`)||document.querySelector(`textarea`);if(r){r.focus();let e=new DataTransfer;e.items.add(n);let t=new ClipboardEvent(`paste`,{clipboardData:e,bubbles:!0,cancelable:!0});r.dispatchEvent(t)}else console.warn(`Could not find chat input to paste the image.`)}catch(e){console.error(`Error adding image to chat:`,e)}}new MutationObserver(()=>{a(),o(),document.querySelectorAll(`ms-code-block[data-test-language="openscad" i], ms-code-block[data-test-language="scad" i]`).forEach(e=>{if(e.querySelector(`.scad-preview-btn`))return;let t=document.createElement(`button`);t.className=`scad-preview-btn`,t.title=`Preview 3D`,t.setAttribute(`aria-label`,`Preview 3D`),t.innerHTML=`
|
|
2
2
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
3
3
|
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path>
|
|
4
4
|
<polyline points="3.27 6.96 12 12.01 20.73 6.96"></polyline>
|
package/editor/dist/index.html
CHANGED
|
@@ -44,10 +44,10 @@
|
|
|
44
44
|
content="https://iliagrigorevdev.github.io/scad-gltf/icon.png"
|
|
45
45
|
/>
|
|
46
46
|
|
|
47
|
-
<script type="module" crossorigin src="./assets/index-
|
|
48
|
-
<link rel="modulepreload" crossorigin href="./assets/OutputPass-
|
|
49
|
-
<link rel="modulepreload" crossorigin href="./assets/prompt-ui-
|
|
50
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
47
|
+
<script type="module" crossorigin src="./assets/index-D-FSzMhd.js"></script>
|
|
48
|
+
<link rel="modulepreload" crossorigin href="./assets/OutputPass-C3DZe46t.js">
|
|
49
|
+
<link rel="modulepreload" crossorigin href="./assets/prompt-ui-BjZlMzjn.js">
|
|
50
|
+
<link rel="stylesheet" crossorigin href="./assets/index-D8In1wnN.css">
|
|
51
51
|
<link rel="manifest" href="./manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="./registerSW.js"></script></head>
|
|
52
52
|
<body>
|
|
53
53
|
<div id="app-container">
|
|
@@ -103,14 +103,15 @@
|
|
|
103
103
|
id="anim-controls-section"
|
|
104
104
|
style="display: none"
|
|
105
105
|
>
|
|
106
|
-
<h2>3. Animation
|
|
106
|
+
<h2>3. Animation & Camera</h2>
|
|
107
107
|
<div class="controls-row">
|
|
108
108
|
<button id="anim-play-btn" class="primary" style="width: 90px">
|
|
109
109
|
⏸ Pause
|
|
110
110
|
</button>
|
|
111
111
|
<select id="anim-select"></select>
|
|
112
|
+
<select id="camera-select" style="display: none"></select>
|
|
112
113
|
</div>
|
|
113
|
-
<div class="controls-row">
|
|
114
|
+
<div class="controls-row" id="anim-slider-row">
|
|
114
115
|
<input
|
|
115
116
|
type="range"
|
|
116
117
|
id="anim-slider"
|
package/editor/dist/preview.html
CHANGED
|
@@ -2,28 +2,33 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
|
-
<script type="module" crossorigin src="./assets/preview-
|
|
6
|
-
<link rel="modulepreload" crossorigin href="./assets/OutputPass-
|
|
7
|
-
<link rel="stylesheet" crossorigin href="./assets/preview-
|
|
5
|
+
<script type="module" crossorigin src="./assets/preview-Br3_yFrj.js"></script>
|
|
6
|
+
<link rel="modulepreload" crossorigin href="./assets/OutputPass-C3DZe46t.js">
|
|
7
|
+
<link rel="stylesheet" crossorigin href="./assets/preview-Cx3FHqKG.css">
|
|
8
8
|
<link rel="manifest" href="./manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="./registerSW.js"></script></head>
|
|
9
9
|
<body>
|
|
10
10
|
<div id="viewer-container">
|
|
11
|
-
<div id="
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
</
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
11
|
+
<div id="top-ui">
|
|
12
|
+
<div id="save-ui">
|
|
13
|
+
<input type="text" id="filename-input" placeholder="Name" />
|
|
14
|
+
<button id="save-btn">Save</button>
|
|
15
|
+
<button id="open-editor-btn">Edit</button>
|
|
16
|
+
</div>
|
|
17
|
+
<div id="viewer-controls">
|
|
18
|
+
<label
|
|
19
|
+
><input type="checkbox" id="show-grid-cb" checked /> Grid</label
|
|
20
|
+
>
|
|
21
|
+
<label><input type="checkbox" id="wireframe-cb" /> Wireframe</label>
|
|
22
|
+
<button id="screenshot-btn" title="Take Screenshot & Add to Chat">
|
|
23
|
+
📷
|
|
24
|
+
</button>
|
|
25
|
+
<button id="fullscreen-btn" title="Toggle Full Screen">⛶</button>
|
|
26
|
+
</div>
|
|
23
27
|
</div>
|
|
24
28
|
<div id="anim-controls" style="display: none">
|
|
25
29
|
<button id="anim-play-btn">⏸ Pause</button>
|
|
26
30
|
<select id="anim-select"></select>
|
|
31
|
+
<select id="camera-select" style="display: none"></select>
|
|
27
32
|
<input
|
|
28
33
|
type="range"
|
|
29
34
|
id="anim-slider"
|
package/editor/dist/sw.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
if(!self.define){let e,s={};const i=(i,r)=>(i=new URL(i+".js",r).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(r,n)=>{const t=e||("document"in self?document.currentScript.src:"")||location.href;if(s[t])return;let l={};const o=e=>i(e,t),c={module:{uri:t},exports:l,require:o};s[t]=Promise.all(r.map(e=>c[e]||o(e))).then(e=>(n(...e),l))}}define(["./workbox-9c191d2f"],function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"registerSW.js",revision:"402b66900e731ca748771b6fc5e7a068"},{url:"preview.html",revision:"
|
|
1
|
+
if(!self.define){let e,s={};const i=(i,r)=>(i=new URL(i+".js",r).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(r,n)=>{const t=e||("document"in self?document.currentScript.src:"")||location.href;if(s[t])return;let l={};const o=e=>i(e,t),c={module:{uri:t},exports:l,require:o};s[t]=Promise.all(r.map(e=>c[e]||o(e))).then(e=>(n(...e),l))}}define(["./workbox-9c191d2f"],function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"registerSW.js",revision:"402b66900e731ca748771b6fc5e7a068"},{url:"preview.html",revision:"b427de9da7f99ca8b90ef8a999fe1c50"},{url:"index.html",revision:"6a6a36f54aef626681e37ecfa3d55535"},{url:"icon.png",revision:"a22007c1fb99682b77960b0d210e273e"},{url:"content.js",revision:"50c6b501e23043613af896c59d6ced31"},{url:"content.css",revision:"4ea93a2d03164b49c80a48138305e564"},{url:"content-loader.js",revision:"d94f1755552d49c1fe3a6da123e8a346"},{url:"aristea_wreck_puresky_2k.hdr",revision:"e764c66f871ab0987f3fac422edc841d"},{url:"assets/prompt-ui-BjZlMzjn.js",revision:null},{url:"assets/preview-Cx3FHqKG.css",revision:null},{url:"assets/preview-Br3_yFrj.js",revision:null},{url:"assets/openscad-DiJCzDqJ.wasm",revision:null},{url:"assets/index-D8In1wnN.css",revision:null},{url:"assets/index-D-FSzMhd.js",revision:null},{url:"assets/OutputPass-C3DZe46t.js",revision:null},{url:"icon.png",revision:"a22007c1fb99682b77960b0d210e273e"},{url:"manifest.webmanifest",revision:"705049c691faa532cc20c736d56d54df"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("index.html")))});
|
package/package.json
CHANGED
package/src/cli-utils.js
CHANGED
|
@@ -670,11 +670,11 @@ export async function runCliApp({
|
|
|
670
670
|
console.error("Examples with JSON options (Automated AI flow):");
|
|
671
671
|
if (isRawScad) {
|
|
672
672
|
console.error(
|
|
673
|
-
` scad-gltf gen ${projectType} "A modular sci-fi corridor piece" '{"openaiApiKey": "sk-...", "openaiModel": "gpt-4o"}'`,
|
|
673
|
+
` scad-gltf gen ${projectType} "A modular sci-fi corridor piece" '{"openaiApiKey": "sk-...", "openaiModel": "gpt-4o", "scadFiles": ["./base.scad"]}'`,
|
|
674
674
|
);
|
|
675
675
|
} else {
|
|
676
676
|
console.error(
|
|
677
|
-
` scad-gltf gen ${projectType} "A simple 3D simulation" '{"openaiApiKey": "sk-...", "openaiModel": "gpt-4o"}'`,
|
|
677
|
+
` scad-gltf gen ${projectType} "A simple 3D simulation" '{"openaiApiKey": "sk-...", "openaiModel": "gpt-4o", "scadFiles": ["./character.scad"]}'`,
|
|
678
678
|
);
|
|
679
679
|
}
|
|
680
680
|
process.exit(1);
|