@popmelt.com/core 0.10.3 → 0.11.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@popmelt.com/core",
3
- "version": "0.10.3",
3
+ "version": "0.11.1",
4
4
  "description": "The design collaboration layer for AI coding agents",
5
5
  "license": "PolyForm-Shield-1.0.0",
6
6
  "author": "Popmelt <reb@popmelt.com> (https://popmelt.com)",
@@ -70,10 +70,6 @@
70
70
  "types": "./dist/memory-store.d.mts",
71
71
  "import": "./dist/memory-store.mjs"
72
72
  },
73
- "./canvas": {
74
- "source": "./src/canvas/index.ts",
75
- "import": "./dist/canvas.mjs"
76
- },
77
73
  "./next": {
78
74
  "source": "./src/plugins/next.ts",
79
75
  "import": "./dist/plugin-next.mjs",
package/dist/canvas.mjs DELETED
@@ -1 +0,0 @@
1
- import{createRoot as ge}from"react-dom/client";import{createElement as me}from"react";import{useEffect as K,useMemo as le,useReducer as de,useRef as ce,useState as pe}from"react";var z="popmelt-canvas";function M(e){return Math.round(e/24)*24}var k={viewport:{offsetX:0,offsetY:0,scale:1},components:[],selectedId:null,highlightedName:null,dragState:null};function A(){try{let e=localStorage.getItem(z);if(!e)return{};let t=JSON.parse(e);return{components:t.components??[],viewport:t.viewport??k.viewport}}catch{return{}}}function Y(e){try{let t={components:e.components,viewport:e.viewport};localStorage.setItem(z,JSON.stringify(t))}catch{}}function W(e,t){switch(t.type){case"PLACE_COMPONENT":{let o=t.component,r={...o,x:M(o.x),y:M(o.y),width:M(o.width),height:M(o.height)};return{...e,components:[...e.components,r],selectedId:r.id}}case"REMOVE_COMPONENT":return{...e,components:e.components.filter(o=>o.id!==t.id),selectedId:e.selectedId===t.id?null:e.selectedId};case"MOVE_COMPONENT":return{...e,components:e.components.map(o=>o.id===t.id?{...o,x:M(t.x),y:M(t.y)}:o)};case"RESIZE_COMPONENT":return{...e,components:e.components.map(o=>o.id===t.id?{...o,width:Math.max(200,t.width),height:Math.max(150,t.height)}:o)};case"SELECT":return{...e,selectedId:t.id};case"SET_VIEWPORT":return{...e,viewport:t.viewport};case"PAN":return{...e,viewport:{...e.viewport,offsetX:e.viewport.offsetX+t.deltaX,offsetY:e.viewport.offsetY+t.deltaY}};case"START_DRAG":return{...e,dragState:t.dragState};case"END_DRAG":return{...e,dragState:null};case"HIGHLIGHT":return{...e,highlightedName:t.name};default:return e}}function X(e,t,o){return{x:(e-o.offsetX)/o.scale,y:(t-o.offsetY)/o.scale}}import{useCallback as te,useEffect as N,useRef as L}from"react";import{useCallback as R,useRef as G,useState as O}from"react";var ee="data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMicgaGVpZ2h0PScxMic+PGRlZnM+PHBhdHRlcm4gaWQ9J2QnIHdpZHRoPSc0JyBoZWlnaHQ9JzQnIHBhdHRlcm5Vbml0cz0ndXNlclNwYWNlT25Vc2UnPjxwYXRoIGQ9J00tMSwxIGwyLC0yIE0wLDQgbDQsLTQgTTMsNSBsMiwtMicgc3Ryb2tlPSdibGFjaycgc3Ryb2tlLXdpZHRoPScuNScvPjwvcGF0dGVybj48L2RlZnM+PHJlY3Qgd2lkdGg9JzEyJyBoZWlnaHQ9JzEyJyBmaWxsPSd1cmwoI2QpJy8+PC9zdmc+",_={borderWidth:3,borderStyle:"solid",borderImage:`url("${ee}") 4 / 1.9 / 0 round`};import{jsx as x,jsxs as C}from"react/jsx-runtime";function V({component:e,selected:t,highlighted:o,isDragging:r,viewport:b,dispatch:u}){let y=G(null),l=G(null),[i,w]=O(null),[g,d]=O(!1),[n,c]=O(!1),s=e.entry.routes&&e.entry.routes.length>0,a=R(()=>{d(!1),c(!1);try{let h=l.current?.contentWindow?.location.pathname;if(!h)return;let P=new URL(e.iframeUrl).pathname;h!==P&&w(h)}catch{}setTimeout(()=>{try{l.current?.contentDocument?.querySelector("nextjs-portal")&&d(!0)}catch{}},2e3)},[e.iframeUrl]),f=R(h=>{h.stopPropagation(),u({type:"SELECT",id:e.id});let P={type:"move",componentId:e.id,startX:h.clientX,startY:h.clientY,originX:e.x,originY:e.y};u({type:"START_DRAG",dragState:P})},[e.id,e.x,e.y,u]),m=R(h=>{h.stopPropagation(),h.preventDefault(),u({type:"SELECT",id:e.id});let P={type:"resize",componentId:e.id,startX:h.clientX,startY:h.clientY,originW:e.width,originH:e.height};u({type:"START_DRAG",dragState:P})},[e.id,e.width,e.height,u]),v=R(h=>{h.stopPropagation(),u({type:"REMOVE_COMPONENT",id:e.id})},[e.id,u]),E=R(h=>{h.stopPropagation(),u({type:"SELECT",id:e.id})},[e.id,u]),p=1/b.scale,I=t||o,D=3*p,q=4*p;return C("div",{onMouseDown:E,style:{position:"absolute",left:e.x,top:e.y,width:e.width,height:e.height,background:"#ffffff",display:"flex",flexDirection:"column",boxShadow:I?`0 ${2*p}px ${12*p}px rgba(0,0,0,0.12)`:`0 ${p}px ${4*p}px rgba(0,0,0,0.06)`,borderWidth:D,borderStyle:"solid",borderImage:_.borderImage,opacity:I?1:void 0},children:[x("div",{onMouseDown:E,style:{position:"absolute",inset:-(D+q),pointerEvents:"auto"}}),!I&&x("div",{style:{position:"absolute",inset:-D,pointerEvents:"none",background:"rgba(255,255,255,0.75)"}}),C("div",{ref:y,onMouseDown:f,style:{height:28*p,background:g?"#fef2f2":"#f9fafb",display:"flex",alignItems:"center",padding:`0 ${8*p}px`,cursor:"grab",userSelect:"none",borderBottom:`${p}px solid rgba(0,0,0,0.06)`,flexShrink:0,position:"relative"},children:[C("span",{style:{flex:1,fontSize:11*p,fontWeight:600,color:"#1f2937",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",fontFamily:"inherit",display:"flex",alignItems:"center",gap:4*p},children:[e.entry.name,!s&&x("span",{style:{fontSize:9*p,color:"#d1d5db",fontWeight:400},children:"(no route)"}),g&&x("span",{style:{fontSize:9*p,color:"#ef4444",fontWeight:600},children:"build error"}),i&&C("span",{style:{fontSize:9*p,color:"#f59e0b",fontWeight:400},children:["\u2192 ",i]})]}),C("span",{style:{fontSize:10*p,color:"#9ca3af",marginRight:6*p,whiteSpace:"nowrap",fontFamily:"inherit"},children:[Math.round(e.width),"\xD7",Math.round(e.height)]}),x("button",{onClick:v,style:{background:"none",border:"none",color:"#9ca3af",cursor:"pointer",fontSize:12*p,lineHeight:1,padding:`${2*p}px ${4*p}px`,fontFamily:"inherit"},children:"\u2715"})]}),C("div",{style:{flex:1,position:"relative",overflow:"hidden"},children:[x("iframe",{ref:l,src:e.iframeUrl,onLoad:a,sandbox:"allow-scripts allow-same-origin allow-forms allow-popups allow-modals",style:{width:"100%",height:"100%",border:"none",pointerEvents:r?"none":"auto",background:"#fff"},title:e.entry.name}),g&&!n&&C("div",{style:{position:"absolute",inset:0,background:"#fff",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",padding:24,zIndex:2},children:[x("div",{style:{fontSize:13,color:"#991b1b",fontWeight:600},children:"Build error"}),C("div",{style:{fontSize:11,color:"#6b7280",marginTop:6,textAlign:"center"},children:["This component has unresolvable imports.",x("br",{}),"Other components are not affected."]}),x("button",{onClick:()=>c(!0),style:{marginTop:14,fontSize:11,color:"#6b7280",background:"none",border:"1px solid #e5e7eb",padding:"4px 12px",cursor:"pointer",fontFamily:"inherit"},children:"Show details"})]})]}),x("div",{onMouseDown:m,style:{position:"absolute",right:0,bottom:0,width:14*p,height:14*p,cursor:"nwse-resize",background:"linear-gradient(135deg, transparent 50%, rgba(0,0,0,0.15) 50%)",zIndex:1}})]})}import{jsx as B,jsxs as H}from"react/jsx-runtime";var oe=.1,ne=3,re=.004;function $({state:e,dispatch:t}){let o=L(null),{viewport:r,components:b,selectedId:u,highlightedName:y,dragState:l}=e,i=L({x:0,y:0}),w=L({viewport:r,dispatch:t});w.current={viewport:r,dispatch:t};function g(n,c,s,a,f,m){let v=Math.min(ne,Math.max(oe,a)),E=v/c.scale;s({type:"SET_VIEWPORT",viewport:{scale:v,offsetX:f-(f-c.offsetX)*E,offsetY:m-(m-c.offsetY)*E}})}N(()=>{let n=o.current;if(!n)return;let c=a=>{a.preventDefault();let{viewport:f,dispatch:m}=w.current,v=n.getBoundingClientRect();if(i.current={x:a.clientX-v.left,y:a.clientY-v.top},a.ctrlKey||a.metaKey){let E=1-a.deltaY*re;g(n,f,m,f.scale*E,i.current.x,i.current.y)}else m({type:"PAN",deltaX:-a.deltaX,deltaY:-a.deltaY})},s=a=>{let f=n.getBoundingClientRect();i.current={x:a.clientX-f.left,y:a.clientY-f.top}};return n.addEventListener("wheel",c,{passive:!1}),n.addEventListener("mousemove",s),()=>{n.removeEventListener("wheel",c),n.removeEventListener("mousemove",s)}},[]),N(()=>{let n=o.current;if(!n)return;let c=s=>{if(!s.metaKey&&!s.ctrlKey)return;let{viewport:a,dispatch:f}=w.current,{x:m,y:v}=i.current;s.key==="0"?(s.preventDefault(),g(n,a,f,1,m,v)):s.key==="="||s.key==="+"?(s.preventDefault(),g(n,a,f,a.scale+.25,m,v)):s.key==="-"&&(s.preventDefault(),g(n,a,f,a.scale-.25,m,v))};return window.addEventListener("keydown",c,{capture:!0}),()=>window.removeEventListener("keydown",c,{capture:!0})},[]);let d=te(n=>{n.target!==n.currentTarget&&n.target!==o.current?.firstChild||(t({type:"SELECT",id:null}),t({type:"START_DRAG",dragState:{type:"pan",startX:n.clientX,startY:n.clientY,originOffsetX:r.offsetX,originOffsetY:r.offsetY}}))},[r.offsetX,r.offsetY,t]);return N(()=>{if(!l)return;let n=s=>{let a=s.clientX-l.startX,f=s.clientY-l.startY;switch(l.type){case"pan":t({type:"SET_VIEWPORT",viewport:{...r,offsetX:l.originOffsetX+a,offsetY:l.originOffsetY+f}});break;case"move":t({type:"MOVE_COMPONENT",id:l.componentId,x:l.originX+a/r.scale,y:l.originY+f/r.scale});break;case"resize":{let m=a/r.scale,v=f/r.scale;s.shiftKey&&(Math.abs(m)>Math.abs(v)?v=0:m=0),t({type:"RESIZE_COMPONENT",id:l.componentId,width:l.originW+m,height:l.originH+v});break}}},c=()=>{t({type:"END_DRAG"})};return window.addEventListener("mousemove",n),window.addEventListener("mouseup",c),()=>{window.removeEventListener("mousemove",n),window.removeEventListener("mouseup",c)}},[l,r,t]),H("div",{ref:o,onMouseDown:d,style:{flex:1,overflow:"hidden",position:"relative",background:"#f9fafb",cursor:l?.type==="pan"?"grabbing":"default"},children:[H("div",{style:{transform:`translate(${r.offsetX}px, ${r.offsetY}px) scale(${r.scale})`,transformOrigin:"0 0",position:"absolute",top:0,left:0},children:[B("div",{onMouseDown:d,style:{position:"fixed",top:-1e4,left:-1e4,width:2e4,height:2e4,backgroundImage:"radial-gradient(circle, rgba(0,0,0,0.1) 1px, transparent 1px)",backgroundSize:"24px 24px",pointerEvents:"auto"}}),b.map(n=>B(V,{component:n,selected:n.id===u,highlighted:n.entry.name===y,isDragging:!!l,viewport:r,dispatch:t},n.id))]}),H("div",{style:{position:"absolute",bottom:12,right:12,fontSize:10,color:"#6b7280",background:"#ffffff",padding:"3px 8px",border:"1px solid rgba(0,0,0,0.08)",userSelect:"none",fontFamily:"inherit"},children:[Math.round(r.scale*100),"%"]})]})}import{useCallback as Z,useMemo as ie,useState as J}from"react";function F(e,t){let o=`${e}::${t}`,r=5381;for(let b=0;b<o.length;b++)r=(r<<5)+r+o.charCodeAt(b)&4294967295;return(r>>>0).toString(16).padStart(8,"0")}import{jsx as S,jsxs as T}from"react/jsx-runtime";var ae=Date.now();function Q({manifest:e,viewport:t,dispatch:o,devOrigin:r,selectedName:b,highlightedName:u}){let[y,l]=J(""),i=ie(()=>{if(!e)return new Map;let d=new Map,n=y.toLowerCase();for(let c of e.components){if(n&&!c.name.toLowerCase().includes(n))continue;let s=c.category||"root",a=d.get(s)??[];a.push(c),d.set(s,a)}return d},[e,y]),w=Z(d=>{let n={x:(window.innerWidth-260)/2+260,y:window.innerHeight/2},c=X(n.x,n.y,t),s=400,a=300,f=F(d.filePath,d.name),m=`${r}/popmelt/render/${f}`,v={id:`placed-${++ae}`,entry:d,x:c.x-s/2,y:c.y-a/2,width:s,height:a,iframeUrl:m};o({type:"PLACE_COMPONENT",component:v})},[t,o,r]),g=Z(d=>{o({type:"HIGHLIGHT",name:d})},[o]);return T("div",{style:{width:260,height:"100%",background:"#ffffff",borderRight:"1px solid rgba(0,0,0,0.08)",display:"flex",flexDirection:"column",flexShrink:0,overflow:"hidden"},children:[T("div",{style:{padding:"12px 12px 8px",borderBottom:"1px solid rgba(0,0,0,0.08)"},children:[S("div",{style:{fontSize:11,fontWeight:700,color:"#1f2937",marginBottom:8,letterSpacing:"0.5px",textTransform:"uppercase"},children:"Components"}),S("input",{type:"text",placeholder:"search...",value:y,onChange:d=>l(d.target.value),style:{width:"100%",padding:"5px 8px",fontSize:12,background:"#f9fafb",border:"1px solid rgba(0,0,0,0.1)",borderRadius:0,color:"#1f2937",outline:"none",boxSizing:"border-box",fontFamily:"inherit"}})]}),T("div",{style:{flex:1,overflowY:"auto",overflowX:"hidden",padding:"4px 0"},children:[!e&&S("div",{style:{padding:12,fontSize:11,color:"#9ca3af"},children:"loading..."}),e&&i.size===0&&S("div",{style:{padding:12,fontSize:11,color:"#9ca3af"},children:"no components found"}),[...i.entries()].map(([d,n])=>S(se,{category:d,entries:n,onPlace:w,onHover:g,selectedName:b,highlightedName:u},d))]})]})}function se({category:e,entries:t,onPlace:o,onHover:r,selectedName:b,highlightedName:u}){let[y,l]=J(!1);return T("div",{children:[T("div",{onClick:()=>l(i=>!i),style:{padding:"6px 12px",fontSize:10,fontWeight:700,color:y?"#9ca3af":"#1f2937",textTransform:"uppercase",letterSpacing:"0.5px",cursor:"pointer",userSelect:"none",display:"flex",alignItems:"center",gap:4,transition:"color 0.15s"},children:[S("span",{style:{color:"#9ca3af",fontWeight:400},children:y?"[+]":"[-]"}),e,S("span",{style:{color:"#d1d5db",marginLeft:"auto",fontWeight:400},children:t.length})]}),!y&&t.map(i=>{let w=i.name===b,g=i.name===u,d=w||g;return T("div",{onClick:()=>o(i),onMouseEnter:()=>r(i.name),onMouseLeave:()=>r(null),style:{padding:"5px 12px 5px 24px",cursor:"pointer",borderLeft:d?"2px solid #1f2937":"2px solid transparent",background:d?"#f9fafb":"transparent",transition:"background 0.1s, border-color 0.1s"},children:[S("div",{style:{fontSize:12,color:"#1f2937",fontWeight:600},children:i.name}),S("div",{style:{fontSize:10,color:"#9ca3af",marginTop:1,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:i.filePath}),i.routes&&i.routes.length>0?S("div",{style:{display:"flex",gap:3,marginTop:3,flexWrap:"wrap"},children:i.routes.map(n=>S("span",{style:{fontSize:9,padding:"1px 4px",background:"#f3f4f6",color:"#6b7280",border:"1px solid rgba(0,0,0,0.06)"},children:n},n))}):S("div",{style:{fontSize:9,color:"#d1d5db",marginTop:3},children:"(no route)"})]},i.name+i.filePath)})]})}import{jsx as U,jsxs as ue}from"react/jsx-runtime";var fe='"JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace';function j({devOrigin:e,bridgeOrigin:t}){let[o,r]=de(W,k,i=>({...i,...A()})),[b,u]=pe(null),y=ce(void 0);K(()=>(clearTimeout(y.current),y.current=setTimeout(()=>Y(o),300),()=>clearTimeout(y.current)),[o.components,o.viewport]);let l=le(()=>o.selectedId?o.components.find(i=>i.id===o.selectedId)?.entry.name??null:null,[o.selectedId,o.components]);return K(()=>{let i=!1;async function w(){try{let g=await fetch(`${t}/canvas/manifest`);if(!g.ok)throw new Error(`HTTP ${g.status}`);let d=await g.json();i||u(d)}catch(g){console.error("[Popmelt Canvas] Failed to load manifest:",g)}}return w(),()=>{i=!0}},[t]),ue("div",{style:{display:"flex",flexDirection:"row",width:"100vw",height:"100dvh",fontFamily:fe,color:"#1f2937",background:"#ffffff"},children:[U(Q,{manifest:b,viewport:o.viewport,dispatch:r,devOrigin:e,selectedName:l,highlightedName:o.highlightedName}),U($,{state:o,dispatch:r})]})}function _e(e,t){ge(e).render(me(j,t))}export{_e as mountCanvas};
@@ -1 +0,0 @@
1
- import{a as c}from"./chunk-NOHXTT24.mjs";import{request as d}from"http";function u(){let e=process.env.POPMELT_BRIDGE_URL;if(!e)return null;try{let r=new URL(e);return{port:r.port?Number.parseInt(r.port,10):null,projectId:process.env.POPMELT_PROJECT_ID||null,leaseEligible:!1}}catch{return null}}async function h(e,r,n){let t=u();if(e?.bridge===!1||t)return t??{port:null,projectId:null,leaseEligible:!1};try{let{startPopmelt:i}=await import("./server-NGFDH5K4.mjs"),o=r.config?.server?.host,l=o===!0?"0.0.0.0":o||"localhost",s=r.config?.server?.port??n,a=`http://${l==="0.0.0.0"?"localhost":l}:${s}`,p=await i({port:e?.port,projectRoot:e?.projectRoot,devOrigin:a,force:!0,detached:!0,manageLease:!1});return console.log(`[popmelt] bridge ready at http://localhost:${p.port}`),{port:p.port,projectId:p.projectId,leaseEligible:!0}}catch(i){let o=i instanceof Error?i.message:String(i);return console.warn("[popmelt] bridge failed to start:",o),{port:null,projectId:null,leaseEligible:!1}}}async function v(e,r,n=fetch){if(!e.leaseEligible||e.port===null)return null;let t=await c({port:e.port,projectId:e.projectId},n);if(!t)return null;let i=()=>{o()},o=async()=>{process.removeListener("beforeExit",i),await t.release()};return process.once("beforeExit",i),r.httpServer?.once("close",()=>{o()}),{id:t.id,release:o}}function g(e,r,n){if(n.port===null)return null;let t=new URL(r,"http://popmelt.local");if(t.pathname!==e&&!t.pathname.startsWith(`${e}/`))return null;let i=t.pathname.slice(e.length),o=i==="/status"?"/status":`/canvas${i}`,l=new URL(o,`http://localhost:${n.port}`);return t.searchParams.forEach((s,a)=>l.searchParams.append(a,s)),n.projectId&&l.searchParams.set("projectId",n.projectId),l}function b(e,r){return(n,t,i)=>{let o=g(e,n.url||"",r());if(!o)return i();let l=d(o,{method:n.method,headers:{...n.headers,host:o.host}},s=>{t.writeHead(s.statusCode||502,s.headers),s.pipe(t)});l.on("error",()=>{t.writeHead(502,{"Content-Type":"text/plain"}),t.end(`Popmelt bridge not running on port ${r().port}`)}),n.pipe(l)}}function P(e){let r=e.port===null?null:`http://localhost:${e.port}`;return`window.__POPMELT_BRIDGE_URL__=${JSON.stringify(r)};window.__POPMELT_PROJECT_ID__=${JSON.stringify(e.projectId)};`}export{h as a,v as b,b as c,P as d};
@@ -1 +0,0 @@
1
- import{readdir as h,readFile as d,stat as Z}from"fs/promises";import{basename as F,dirname as A,join as m,relative as y}from"path";var w=new Set(["node_modules",".next","dist","__popmelt",".git",".turbo"]),R=[/\.test\.tsx?$/,/\.spec\.tsx?$/,/\.stories\.tsx?$/],x=/^[A-Z][a-zA-Z0-9]+$/,D=[{regex:/export\s+function\s+([A-Z][a-zA-Z0-9]+)/g,type:"named"},{regex:/export\s+default\s+function\s+([A-Z][a-zA-Z0-9]+)/g,type:"default"},{regex:/export\s+const\s+([A-Z][a-zA-Z0-9]+)\s*[=:]/g,type:"named"},{regex:/export\s+default\s+([A-Z][a-zA-Z0-9]+)\s*;/g,type:"default"}];async function E(e,n){let s=[],o=await h(e,{withFileTypes:!0});for(let t of o)if(t.isDirectory()){if(w.has(t.name))continue;let r=await E(m(e,t.name),n);s.push(...r)}else if(t.isFile()&&t.name.endsWith(".tsx")){let r=m(e,t.name),p=y(n,r);if(R.some(i=>i.test(p)))continue;s.push(r)}return s}function M(e,n,s){let o=y(s,n),t=[],r=new Set;for(let{regex:p,type:i}of D){p.lastIndex=0;let a;for(;(a=p.exec(e))!==null;){let c=a[1];if(r.has(c)||!x.test(c))continue;r.add(c);let f,u=A(o);u!=="."&&(f=u.replace(/^app\//,"").replace(/^src\//,"")),t.push({name:c,filePath:o,exportType:i,category:f})}}return t}async function T(e){let n=new Map,s=m(e,"app"),o;try{await Z(s),o=await P(s,e)}catch{return n}for(let t of o){let r=await d(t,"utf-8"),i="/"+A(y(e,t)).replace(/^app\/?/,"").replace(/\([^)]+\)\/?/g,"").replace(/\/$/,""),a=/import\s+.*?from\s+['"](\.\.?\/[^'"]+)['"]/g,c;for(;(c=a.exec(r))!==null;){let f=c[1],u=F(f).replace(/\.(tsx?|jsx?)$/,""),C=c[0].match(/\b([A-Z][a-zA-Z0-9]+)\b/g)||[];for(let l of C){if(!x.test(l))continue;let g=n.get(l)??[];g.includes(i)||g.push(i),n.set(l,g)}if(x.test(u)){let l=n.get(u)??[];l.includes(i)||l.push(i),n.set(u,l)}}}return n}async function P(e,n){let s=[],o=await h(e,{withFileTypes:!0});for(let t of o)if(t.isDirectory()){if(w.has(t.name))continue;s.push(...await P(m(e,t.name),n))}else t.isFile()&&t.name==="page.tsx"&&s.push(m(e,t.name));return s}async function b(e){let n=await E(e,e),s=await T(e),o=[];for(let t of n){let r=await d(t,"utf-8"),p=M(r,t,e);for(let i of p){let a=s.get(i.name);a&&a.length>0&&(i.routes=a),o.push(i)}}return o.sort((t,r)=>t.name.localeCompare(r.name)),{version:1,scannedAt:Date.now(),components:o}}export{b as scanForComponents};
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- import{readdir as h,readFile as d,stat as Z}from"fs/promises";import{basename as F,dirname as A,join as m,relative as y}from"path";var w=new Set(["node_modules",".next","dist","__popmelt",".git",".turbo"]),R=[/\.test\.tsx?$/,/\.spec\.tsx?$/,/\.stories\.tsx?$/],x=/^[A-Z][a-zA-Z0-9]+$/,D=[{regex:/export\s+function\s+([A-Z][a-zA-Z0-9]+)/g,type:"named"},{regex:/export\s+default\s+function\s+([A-Z][a-zA-Z0-9]+)/g,type:"default"},{regex:/export\s+const\s+([A-Z][a-zA-Z0-9]+)\s*[=:]/g,type:"named"},{regex:/export\s+default\s+([A-Z][a-zA-Z0-9]+)\s*;/g,type:"default"}];async function E(e,n){let s=[],o=await h(e,{withFileTypes:!0});for(let t of o)if(t.isDirectory()){if(w.has(t.name))continue;let r=await E(m(e,t.name),n);s.push(...r)}else if(t.isFile()&&t.name.endsWith(".tsx")){let r=m(e,t.name),p=y(n,r);if(R.some(i=>i.test(p)))continue;s.push(r)}return s}function M(e,n,s){let o=y(s,n),t=[],r=new Set;for(let{regex:p,type:i}of D){p.lastIndex=0;let a;for(;(a=p.exec(e))!==null;){let c=a[1];if(r.has(c)||!x.test(c))continue;r.add(c);let f,u=A(o);u!=="."&&(f=u.replace(/^app\//,"").replace(/^src\//,"")),t.push({name:c,filePath:o,exportType:i,category:f})}}return t}async function T(e){let n=new Map,s=m(e,"app"),o;try{await Z(s),o=await P(s,e)}catch{return n}for(let t of o){let r=await d(t,"utf-8"),i="/"+A(y(e,t)).replace(/^app\/?/,"").replace(/\([^)]+\)\/?/g,"").replace(/\/$/,""),a=/import\s+.*?from\s+['"](\.\.?\/[^'"]+)['"]/g,c;for(;(c=a.exec(r))!==null;){let f=c[1],u=F(f).replace(/\.(tsx?|jsx?)$/,""),C=c[0].match(/\b([A-Z][a-zA-Z0-9]+)\b/g)||[];for(let l of C){if(!x.test(l))continue;let g=n.get(l)??[];g.includes(i)||g.push(i),n.set(l,g)}if(x.test(u)){let l=n.get(u)??[];l.includes(i)||l.push(i),n.set(u,l)}}}return n}async function P(e,n){let s=[],o=await h(e,{withFileTypes:!0});for(let t of o)if(t.isDirectory()){if(w.has(t.name))continue;s.push(...await P(m(e,t.name),n))}else t.isFile()&&t.name==="page.tsx"&&s.push(m(e,t.name));return s}async function b(e){let n=await E(e,e),s=await T(e),o=[];for(let t of n){let r=await d(t,"utf-8"),p=M(r,t,e);for(let i of p){let a=s.get(i.name);a&&a.length>0&&(i.routes=a),o.push(i)}}return o.sort((t,r)=>t.name.localeCompare(r.name)),{version:1,scannedAt:Date.now(),components:o}}export{b as scanForComponents};
@@ -1,107 +0,0 @@
1
- #!/usr/bin/env node
2
- import{createHash as x}from"crypto";import{mkdir as m,readFile as p,rm as S,stat as P,writeFile as f}from"fs/promises";import{dirname as w,join as a,relative as b,resolve as v}from"path";function g(t,r){let n=`${t}::${r}`,s=5381;for(let e=0;e<n.length;e++)s=(s<<5)+s+n.charCodeAt(e)&4294967295;return(s>>>0).toString(16).padStart(8,"0")}var h=/\/\/.*$|\/\*[\s\S]*?\*\//gm;async function k(t){let r={root:t,entries:[]};try{let n=await p(a(t,"tsconfig.json"),"utf-8"),s=JSON.parse(n.replace(h,"")),e=s.compilerOptions?.paths,o=s.compilerOptions?.baseUrl??".";if(!e&&s.extends)try{let i=a(t,s.extends),l=await p(i,"utf-8"),u=JSON.parse(l.replace(h,""));e=u.compilerOptions?.paths,o=u.compilerOptions?.baseUrl??o}catch{}return e?{root:a(t,o),entries:Object.entries(e)}:r}catch{return r}}function R(t,r){for(let[n,s]of r.entries)if(n.endsWith("/*")){let e=n.slice(0,-1);if(t.startsWith(e)){let o=t.slice(e.length),i=s[0],l=i.endsWith("/*")?i.slice(0,-1):i;return a(r.root,l,o)}}else if(n===t&&s.length>0)return a(r.root,s[0]);return null}var T=[".tsx",".ts",".jsx",".js","/index.tsx","/index.ts"];async function y(t){for(let r of["",...T])try{if((await P(t+r)).isFile())return!0}catch{}return!1}async function E(t,r,n){try{let s=a(r,t),e=await p(s,"utf-8"),o=[],i=/from\s+['"]([^'"]+)['"]/g,l;for(;(l=i.exec(e))!==null;){let c=l[1];if(c.startsWith("."))o.push(y(v(w(s),c)));else{let d=R(c,n);d&&o.push(y(d))}}return(await Promise.all(o)).every(Boolean)}catch{return!1}}async function O(t,r){let n=await k(r);return(await Promise.all(t.components.map(async e=>await E(e.filePath,r,n)?{key:`${e.filePath}::${e.name}`,slug:g(e.filePath,e.name),filePath:e.filePath,exportName:e.name,exportType:e.exportType}:(console.warn(`[Popmelt] Skipping ${e.filePath}::${e.name} \u2014 unresolvable imports`),null)))).filter(e=>e!==null)}var C=12;function A(t){let r=t.map(n=>n.key).sort().join(`
3
- `);return x("sha256").update(C+`
4
- `+r).digest("hex").slice(0,16)}async function $(t){for(let r of["app","src/app"]){let n=a(t,r);try{if((await P(n)).isDirectory())return n}catch{}}return null}function N(t){return t.split("\\").join("/")}function M(t,r,n){let s=a(n,t.filePath.replace(/\.(tsx?|jsx?)$/,"")),e=JSON.stringify(N(b(r,s)));return t.exportType==="default"?`'use client';
5
- import dynamic from 'next/dynamic';
6
- export default dynamic(() => import(${e}), { ssr: false });
7
- `:`'use client';
8
- import dynamic from 'next/dynamic';
9
- export default dynamic(
10
- () => import(${e}).then(mod => ({ default: mod.${t.exportName} })),
11
- { ssr: false },
12
- );
13
- `}var W=`// Auto-generated by Popmelt scanner \u2014 do not edit
14
- 'use client';
15
-
16
- import { useEffect, type ReactNode } from 'react';
17
-
18
- export default function PopmeltRenderLayout({ children }: { children: ReactNode }) {
19
- useEffect(() => {
20
- const origPush = history.pushState.bind(history);
21
- const origReplace = history.replaceState.bind(history);
22
-
23
- history.pushState = function (...args: Parameters<typeof history.pushState>) {
24
- const url = args[2];
25
- if (url && String(url).indexOf('/popmelt/render') !== 0) {
26
- console.warn('[Popmelt] Blocked navigation to:', url);
27
- return;
28
- }
29
- return origPush(...args);
30
- };
31
-
32
- history.replaceState = function (...args: Parameters<typeof history.replaceState>) {
33
- const url = args[2];
34
- if (url && String(url).indexOf('/popmelt/render') !== 0) {
35
- console.warn('[Popmelt] Blocked navigation to:', url);
36
- return;
37
- }
38
- return origReplace(...args);
39
- };
40
-
41
- const handleClick = (e: MouseEvent) => {
42
- const anchor = (e.target as Element).closest?.('a');
43
- if (!anchor) return;
44
- const href = anchor.getAttribute('href');
45
- if (href && !href.startsWith('/popmelt/render') && !href.startsWith('#')) {
46
- e.preventDefault();
47
- e.stopPropagation();
48
- console.warn('[Popmelt] Blocked link navigation to:', href);
49
- }
50
- };
51
-
52
- document.addEventListener('click', handleClick, true);
53
-
54
- return () => {
55
- history.pushState = origPush;
56
- history.replaceState = origReplace;
57
- document.removeEventListener('click', handleClick, true);
58
- };
59
- }, []);
60
-
61
- return children;
62
- }
63
- `,B=`// Auto-generated by Popmelt scanner \u2014 do not edit
64
- 'use client';
65
-
66
- export default function PopmeltRenderError({
67
- error,
68
- reset,
69
- }: {
70
- error: Error & { digest?: string };
71
- reset: () => void;
72
- }) {
73
- return (
74
- <div style={{ padding: 40, fontFamily: 'system-ui' }}>
75
- <h2 style={{ color: '#ef4444', fontSize: 16, fontWeight: 600, margin: 0 }}>
76
- Component render error
77
- </h2>
78
- <pre style={{
79
- marginTop: 12,
80
- padding: 12,
81
- background: '#fef2f2',
82
- border: '1px solid #fecaca',
83
- fontSize: 12,
84
- color: '#991b1b',
85
- whiteSpace: 'pre-wrap',
86
- wordBreak: 'break-word',
87
- }}>
88
- {error.message}
89
- </pre>
90
- <button
91
- onClick={reset}
92
- style={{
93
- marginTop: 12,
94
- padding: '6px 16px',
95
- fontSize: 12,
96
- background: '#1f2937',
97
- color: '#fff',
98
- border: 'none',
99
- cursor: 'pointer',
100
- }}
101
- >
102
- Retry
103
- </button>
104
- </div>
105
- );
106
- }
107
- `;async function z(t,r,n){let s=await O(t,r);if(s.length===0)return n??"";let e=A(s);if(e===n)return e;let o=await $(r);if(!o)return n??"";let i=a(o,"popmelt","render");await S(i,{recursive:!0,force:!0}),await m(i,{recursive:!0});let l=[f(a(i,"layout.tsx"),W),f(a(i,"error.tsx"),B)];for(let u of s){let c=a(i,u.slug);l.push(m(c,{recursive:!0}).then(()=>f(a(c,"page.tsx"),M(u,c,r))))}return await Promise.all(l),e}export{z as generateRenderFiles};
@@ -1,106 +0,0 @@
1
- import{createHash as x}from"crypto";import{mkdir as m,readFile as p,rm as S,stat as P,writeFile as f}from"fs/promises";import{dirname as w,join as a,relative as b,resolve as v}from"path";function g(t,r){let n=`${t}::${r}`,s=5381;for(let e=0;e<n.length;e++)s=(s<<5)+s+n.charCodeAt(e)&4294967295;return(s>>>0).toString(16).padStart(8,"0")}var h=/\/\/.*$|\/\*[\s\S]*?\*\//gm;async function k(t){let r={root:t,entries:[]};try{let n=await p(a(t,"tsconfig.json"),"utf-8"),s=JSON.parse(n.replace(h,"")),e=s.compilerOptions?.paths,o=s.compilerOptions?.baseUrl??".";if(!e&&s.extends)try{let i=a(t,s.extends),l=await p(i,"utf-8"),u=JSON.parse(l.replace(h,""));e=u.compilerOptions?.paths,o=u.compilerOptions?.baseUrl??o}catch{}return e?{root:a(t,o),entries:Object.entries(e)}:r}catch{return r}}function R(t,r){for(let[n,s]of r.entries)if(n.endsWith("/*")){let e=n.slice(0,-1);if(t.startsWith(e)){let o=t.slice(e.length),i=s[0],l=i.endsWith("/*")?i.slice(0,-1):i;return a(r.root,l,o)}}else if(n===t&&s.length>0)return a(r.root,s[0]);return null}var T=[".tsx",".ts",".jsx",".js","/index.tsx","/index.ts"];async function y(t){for(let r of["",...T])try{if((await P(t+r)).isFile())return!0}catch{}return!1}async function E(t,r,n){try{let s=a(r,t),e=await p(s,"utf-8"),o=[],i=/from\s+['"]([^'"]+)['"]/g,l;for(;(l=i.exec(e))!==null;){let c=l[1];if(c.startsWith("."))o.push(y(v(w(s),c)));else{let d=R(c,n);d&&o.push(y(d))}}return(await Promise.all(o)).every(Boolean)}catch{return!1}}async function O(t,r){let n=await k(r);return(await Promise.all(t.components.map(async e=>await E(e.filePath,r,n)?{key:`${e.filePath}::${e.name}`,slug:g(e.filePath,e.name),filePath:e.filePath,exportName:e.name,exportType:e.exportType}:(console.warn(`[Popmelt] Skipping ${e.filePath}::${e.name} \u2014 unresolvable imports`),null)))).filter(e=>e!==null)}var C=12;function A(t){let r=t.map(n=>n.key).sort().join(`
2
- `);return x("sha256").update(C+`
3
- `+r).digest("hex").slice(0,16)}async function $(t){for(let r of["app","src/app"]){let n=a(t,r);try{if((await P(n)).isDirectory())return n}catch{}}return null}function N(t){return t.split("\\").join("/")}function M(t,r,n){let s=a(n,t.filePath.replace(/\.(tsx?|jsx?)$/,"")),e=JSON.stringify(N(b(r,s)));return t.exportType==="default"?`'use client';
4
- import dynamic from 'next/dynamic';
5
- export default dynamic(() => import(${e}), { ssr: false });
6
- `:`'use client';
7
- import dynamic from 'next/dynamic';
8
- export default dynamic(
9
- () => import(${e}).then(mod => ({ default: mod.${t.exportName} })),
10
- { ssr: false },
11
- );
12
- `}var W=`// Auto-generated by Popmelt scanner \u2014 do not edit
13
- 'use client';
14
-
15
- import { useEffect, type ReactNode } from 'react';
16
-
17
- export default function PopmeltRenderLayout({ children }: { children: ReactNode }) {
18
- useEffect(() => {
19
- const origPush = history.pushState.bind(history);
20
- const origReplace = history.replaceState.bind(history);
21
-
22
- history.pushState = function (...args: Parameters<typeof history.pushState>) {
23
- const url = args[2];
24
- if (url && String(url).indexOf('/popmelt/render') !== 0) {
25
- console.warn('[Popmelt] Blocked navigation to:', url);
26
- return;
27
- }
28
- return origPush(...args);
29
- };
30
-
31
- history.replaceState = function (...args: Parameters<typeof history.replaceState>) {
32
- const url = args[2];
33
- if (url && String(url).indexOf('/popmelt/render') !== 0) {
34
- console.warn('[Popmelt] Blocked navigation to:', url);
35
- return;
36
- }
37
- return origReplace(...args);
38
- };
39
-
40
- const handleClick = (e: MouseEvent) => {
41
- const anchor = (e.target as Element).closest?.('a');
42
- if (!anchor) return;
43
- const href = anchor.getAttribute('href');
44
- if (href && !href.startsWith('/popmelt/render') && !href.startsWith('#')) {
45
- e.preventDefault();
46
- e.stopPropagation();
47
- console.warn('[Popmelt] Blocked link navigation to:', href);
48
- }
49
- };
50
-
51
- document.addEventListener('click', handleClick, true);
52
-
53
- return () => {
54
- history.pushState = origPush;
55
- history.replaceState = origReplace;
56
- document.removeEventListener('click', handleClick, true);
57
- };
58
- }, []);
59
-
60
- return children;
61
- }
62
- `,B=`// Auto-generated by Popmelt scanner \u2014 do not edit
63
- 'use client';
64
-
65
- export default function PopmeltRenderError({
66
- error,
67
- reset,
68
- }: {
69
- error: Error & { digest?: string };
70
- reset: () => void;
71
- }) {
72
- return (
73
- <div style={{ padding: 40, fontFamily: 'system-ui' }}>
74
- <h2 style={{ color: '#ef4444', fontSize: 16, fontWeight: 600, margin: 0 }}>
75
- Component render error
76
- </h2>
77
- <pre style={{
78
- marginTop: 12,
79
- padding: 12,
80
- background: '#fef2f2',
81
- border: '1px solid #fecaca',
82
- fontSize: 12,
83
- color: '#991b1b',
84
- whiteSpace: 'pre-wrap',
85
- wordBreak: 'break-word',
86
- }}>
87
- {error.message}
88
- </pre>
89
- <button
90
- onClick={reset}
91
- style={{
92
- marginTop: 12,
93
- padding: '6px 16px',
94
- fontSize: 12,
95
- background: '#1f2937',
96
- color: '#fff',
97
- border: 'none',
98
- cursor: 'pointer',
99
- }}
100
- >
101
- Retry
102
- </button>
103
- </div>
104
- );
105
- }
106
- `;async function z(t,r,n){let s=await O(t,r);if(s.length===0)return n??"";let e=A(s);if(e===n)return e;let o=await $(r);if(!o)return n??"";let i=a(o,"popmelt","render");await S(i,{recursive:!0,force:!0}),await m(i,{recursive:!0});let l=[f(a(i,"layout.tsx"),W),f(a(i,"error.tsx"),B)];for(let u of s){let c=a(i,u.slug);l.push(m(c,{recursive:!0}).then(()=>f(a(c,"page.tsx"),M(u,c,r))))}return await Promise.all(l),e}export{z as generateRenderFiles};