@json-to-office/jto 0.26.0 → 0.28.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.
Files changed (30) hide show
  1. package/dist/cli.js +134 -77
  2. package/dist/cli.js.map +1 -1
  3. package/dist/client/assets/{HomePage-CcmtpSuV.js → HomePage-BX2wX21e.js} +3 -3
  4. package/dist/client/assets/HomePage-BX2wX21e.js.map +1 -0
  5. package/dist/client/assets/{JsonEditorPage-RDa8GWDd.js → JsonEditorPage-DseCjMok.js} +3 -3
  6. package/dist/client/assets/{JsonEditorPage-RDa8GWDd.js.map → JsonEditorPage-DseCjMok.js.map} +1 -1
  7. package/dist/client/assets/{MonacoPluginProvider-DM63WAJx.js → MonacoPluginProvider-DvO7h20V.js} +3 -3
  8. package/dist/client/assets/{MonacoPluginProvider-DM63WAJx.js.map → MonacoPluginProvider-DvO7h20V.js.map} +1 -1
  9. package/dist/client/assets/{button-CyTe9rTl.js → button-DzgfrDEj.js} +2 -2
  10. package/dist/client/assets/{button-CyTe9rTl.js.map → button-DzgfrDEj.js.map} +1 -1
  11. package/dist/client/assets/{editor-DCtGYNz2.js → editor-BDi6QWgh.js} +2 -2
  12. package/dist/client/assets/{editor-DCtGYNz2.js.map → editor-BDi6QWgh.js.map} +1 -1
  13. package/dist/client/assets/{editor-monaco-json-CU9ciwPv.js → editor-monaco-json-CsQqBG2A.js} +2 -2
  14. package/dist/client/assets/{editor-monaco-json-CU9ciwPv.js.map → editor-monaco-json-CsQqBG2A.js.map} +1 -1
  15. package/dist/client/assets/{index-CHAIoQtG.js → index-Cxjik2Rg.js} +3 -3
  16. package/dist/client/assets/{index-CHAIoQtG.js.map → index-Cxjik2Rg.js.map} +1 -1
  17. package/dist/client/assets/preview-BOF6SWhK.js +3 -0
  18. package/dist/client/assets/preview-BOF6SWhK.js.map +1 -0
  19. package/dist/client/index.html +1 -1
  20. package/dist/client/templates/management-plan.pptx.json +18 -18
  21. package/dist/client/templates/minimalist-pitch-deck.pptx.json +19 -19
  22. package/dist/client/templates/modern-annual-report-2.docx.json +12 -12
  23. package/dist/client/templates/modern-annual-report-3.docx.json +14 -14
  24. package/dist/client/templates/standard-annual-report.docx.json +2 -2
  25. package/dist/render-server.js +62 -48
  26. package/dist/render-server.js.map +1 -1
  27. package/package.json +7 -8
  28. package/dist/client/assets/HomePage-CcmtpSuV.js.map +0 -1
  29. package/dist/client/assets/preview-oxnxMi4x.js +0 -3
  30. package/dist/client/assets/preview-oxnxMi4x.js.map +0 -1
@@ -0,0 +1,3 @@
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/docx-preview-DQa8jhzH.js","assets/react-vendor-CTFDOf2G.js"])))=>i.map(i=>d[i]);
2
+ import{j as e,c as O,r as u}from"./react-vendor-CTFDOf2G.js";import{S as z,c as R,h as K,F as w,_ as ue,p as xe,A as F}from"./index-Cxjik2Rg.js";import{S as E,B as fe,P as he,b as pe,D as ge,c as je,d as we,e as ve}from"./HomePage-BX2wX21e.js";import{e as be,T as A,I as X}from"./ui-vendor-Nd4Lg2re.js";import{u as L,a as Q,d as q,b as Ne}from"./MonacoPluginProvider-DvO7h20V.js";import{ag as Y,ah as ye}from"./radix-ui-Bq-7y_Sq.js";import"./query-vendor-Df8-oete.js";import"./monaco-editor-BcgxPCqT.js";import"./state-vendor-ByFYl9bq.js";import"./button-DzgfrDEj.js";function Se({className:n,currentStage:s="parsing",message:i}){const o=["parsing","building","rendering","finalizing"],t={parsing:"Validating JSON",building:"Building structure",rendering:"Rendering content",finalizing:"Finalizing document"},c=o.indexOf(s);return e.jsxs("div",{className:R("p-6 space-y-4",n),children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx(z,{size:"md"}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("p",{className:"text-sm font-medium",children:"Generating Document"}),e.jsx("p",{className:"text-xs text-muted-foreground",children:i||`${t[s]}...`})]})]}),e.jsx("div",{className:"space-y-3",children:o.map((a,r)=>{const l=r===c,d=r<c;return e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"flex justify-between text-xs",children:[e.jsx("span",{className:R(l?"text-primary font-medium":d?"text-muted-foreground":"text-muted-foreground/60"),children:t[a]}),e.jsx("span",{className:"text-muted-foreground",children:d?"✓":l?"...":"○"})]}),e.jsx("div",{className:"w-full bg-secondary rounded-full h-1.5",children:e.jsx("div",{className:R("h-1.5 rounded-full transition-all duration-300",d?"bg-success":l?"bg-primary animate-pulse":"bg-transparent"),style:{width:d||l?"100%":"0%"}})})]},a)})})]})}function Ee({renderingLibrary:n,className:s}){const i=c=>{switch(c){case"LibreOffice":return{name:"LibreOffice",description:"Converting document to PDF locally..."};case"Office":return{name:"Microsoft Office",description:"Uploading file and loading Office viewer..."};case"Docs":return{name:"Google Docs",description:"Uploading file and loading Docs viewer..."};default:return{name:"Default",description:"Processing document..."}}},{name:o,description:t}=i(n);return e.jsxs("div",{className:R("flex flex-col items-center justify-center h-full p-8 space-y-4",s),children:[e.jsx(z,{size:"lg"}),e.jsxs("div",{className:"text-center space-y-2",children:[e.jsx("p",{className:"text-sm font-medium",children:"Rendering Preview"}),e.jsxs("p",{className:"text-xs text-muted-foreground",children:["Using ",o," renderer"]}),e.jsx("p",{className:"text-xs text-muted-foreground/80",children:t})]}),e.jsxs("div",{className:"w-full max-w-md space-y-3",children:[e.jsx(E,{className:"h-16 w-full"}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(E,{className:"h-4 w-full"}),e.jsx(E,{className:"h-4 w-3/4"}),e.jsx(E,{className:"h-4 w-1/2"})]}),e.jsx(E,{className:"h-12 w-full"})]})]})}const Z=O.forwardRef(({isLoading:n,iframeSrc:s,iframeSrcDoc:i,isGenerating:o,generationProgress:t},c)=>{const[a,r]=u.useState(!1);u.useEffect(()=>{r(!1)},[s,i]);const l=()=>{r(!0)},d=()=>{console.warn("Iframe failed to load content"),r(!1)},h=i?"allow-scripts allow-popups allow-forms":void 0,p=!!(s||i);return o&&!p?e.jsx("div",{className:"grow",children:e.jsx(Se,{currentStage:t==null?void 0:t.stage,message:t==null?void 0:t.message})}):n&&!p?e.jsx("div",{className:"grow",children:e.jsx(Ee,{renderingLibrary:"LibreOffice"})}):p?e.jsxs("div",{className:"h-full w-full relative bg-white",children:[!a&&e.jsx("div",{className:"absolute inset-0 bg-muted/80 flex items-center justify-center z-10",children:e.jsxs("div",{className:"text-center",children:[e.jsx("div",{className:"animate-spin rounded-full h-8 w-8 border-b-2 border-foreground mx-auto mb-2"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Loading preview..."})]})}),(o||n)&&e.jsx("div",{className:"absolute inset-0 bg-background/60 backdrop-blur-[2px] flex items-center justify-center z-20 transition-opacity duration-200",children:e.jsxs("div",{className:"flex flex-col items-center gap-2",children:[e.jsx(z,{size:"lg"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:o?"Generating...":"Rendering..."})]})}),e.jsx("iframe",{ref:c,className:"w-full h-full border-0",style:{backgroundColor:"white",opacity:a?1:0,transition:"opacity 0.2s ease-in-out"},src:s,srcDoc:i,onLoad:l,onError:d,sandbox:h,title:"Document preview","aria-label":`Preview of rendered ${K.toLowerCase()}`},i?"srcdoc":"src")]}):null});Z.displayName="PreviewFrame";const Re=O.memo(Z);function Le({warnings:n,className:s}){const[i,o]=O.useState(!1);return!n||n.length===0?null:e.jsxs("div",{className:s,children:[e.jsxs("button",{type:"button",onClick:()=>o(!i),className:"flex w-full items-center gap-2 rounded-sm border border-transparent bg-warning/10 px-3 py-1.5 text-left transition-colors hover:bg-warning/20",children:[e.jsx(be,{className:`h-3.5 w-3.5 text-warning transition-transform flex-shrink-0 ${i?"rotate-90":""}`}),e.jsx(A,{className:"h-3.5 w-3.5 text-warning flex-shrink-0"}),e.jsxs("span",{className:"text-xs font-medium text-warning",children:[n.length," warning",n.length!==1?"s":""]})]}),i&&e.jsx("div",{className:"mt-1.5 space-y-1.5",children:n.map((t,c)=>e.jsxs("div",{className:"flex items-start gap-2 rounded-sm border bg-card px-3 py-2",children:[t.severity==="info"?e.jsx(X,{className:"h-3.5 w-3.5 mt-0.5 text-data-blue flex-shrink-0"}):e.jsx(A,{className:"h-3.5 w-3.5 mt-0.5 text-warning flex-shrink-0"}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsx("code",{className:"text-[11px] font-medium text-warning bg-warning/15 px-1 py-0.5 rounded-sm",children:t.component}),t.severity==="info"&&e.jsx("span",{className:"text-[10px] uppercase tracking-wide text-data-blue font-medium",children:"info"})]}),e.jsx("p",{className:"mt-0.5 text-xs text-muted-foreground leading-relaxed",children:t.message}),t.context&&Object.keys(t.context).length>0&&e.jsxs("details",{className:"mt-1.5",children:[e.jsx("summary",{className:"text-[11px] text-muted-foreground/70 cursor-pointer hover:text-foreground",children:"Context"}),e.jsx("pre",{className:"mt-1 text-[11px] bg-header-bg p-1.5 rounded-sm overflow-x-auto text-muted-foreground",children:JSON.stringify(t.context,null,2)})]})]})]},c))})]})}const V={pptx:["default","dark","minimal"],docx:["minimal","corporate","modern"]};function Ce({className:n}){var a;const s=L(r=>r.activeTab),i=L(r=>r.documentTypes),o=L(r=>r.documents),t=Q(r=>r.customThemes),c=u.useMemo(()=>{var d,h,p;if(!s||i[s]==="application/json+theme")return null;const r=o.find(f=>f.name===s);if(!(r!=null&&r.text))return null;let l;try{l=(h=(d=JSON.parse(r.text))==null?void 0:d.props)==null?void 0:h.theme}catch{return null}if(typeof l!="string"||!l||(p=V[w])!=null&&p.includes(l))return null;for(const f of Object.values(t))if(f.valid&&f.name===l)return null;return l},[s,i,o,t]);return c?e.jsxs("div",{className:`flex items-start gap-2 rounded-sm border border-transparent bg-warning/10 px-3 py-2 ${n??""}`,children:[e.jsx(A,{className:"h-3.5 w-3.5 mt-0.5 text-warning flex-shrink-0"}),e.jsxs("div",{className:"flex-1 min-w-0 text-xs leading-relaxed",children:[e.jsxs("span",{className:"font-medium text-warning",children:["Theme"," ",e.jsx("code",{className:"bg-warning/20 px-1 py-0.5 rounded-sm text-warning",children:c})," ","is not available."]})," ",e.jsxs("span",{className:"text-warning/80",children:["Add it as a custom theme or use a built-in theme (",(a=V[w])==null?void 0:a.join(", "),"). The renderer will fall back to a default."]})]})]}):null}async function ke(n){const s=await ue(()=>import("./docx-preview-DQa8jhzH.js"),__vite__mapDeps([0,1])),i=document.createElement("body"),o=document.createElement("head"),t=s.renderAsync(n,i,o,{inWrapper:!0}),c=new Promise((l,d)=>{setTimeout(()=>{d(new Error("DocxJS renderAsync timed out after 10 seconds"))},1e4)});await Promise.race([t,c]);const a=document.createElement("link");a.rel="stylesheet",a.href=`${xe.basePath}/css/preview/docxjs.css`,o.appendChild(a);const r=document.createElement("html");return r.appendChild(o),r.appendChild(i),{iframeSrcDoc:r.outerHTML}}async function De(n,s,i,o){const t=typeof i=="string"&&i.length>0;let c;if(t){let l;try{l=JSON.parse(i)}catch(h){throw new Error(`Invalid JSON document: ${h.message}`)}const d={jsonDefinition:l,customThemes:o??{},options:{sourceName:n}};c=await fetch(F.preview.libreofficeFromJson,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(d)})}else{const l=new FormData,d=w==="pptx"?".pptx":".docx";l.append("file",s,`${n}${d}`),c=await fetch(F.preview.libreoffice,{method:"POST",body:l})}if(!c.ok){let l=`LibreOffice preview failed (${c.status})`;try{const d=await c.json();d&&typeof d=="object"&&typeof d.error=="string"&&(l=d.error)}catch{}throw new Error(l)}const a=await c.blob();if(!a.size)throw new Error("LibreOffice preview returned an empty PDF");const r=URL.createObjectURL(a);return{iframeSrc:r,cleanup:()=>URL.revokeObjectURL(r)}}async function Oe(n,s,i=w==="docx"?"docxjs":"LibreOffice",o,t,c){try{if(!s||s.size===0)throw new Error("Invalid or empty document blob");let a;return w==="docx"&&i==="docxjs"?a=await ke(s):a=await De(n,s,t,c),{status:"success",name:n,payload:a}}catch(a){const r=a instanceof Error?a.message:String(a);return{status:"error",name:n,payload:new Error(r)}}}const U=u.forwardRef(({className:n,value:s,...i},o)=>e.jsx(Y,{ref:o,className:R("relative h-4 w-full overflow-hidden rounded-full bg-secondary",n),...i,children:e.jsx(ye,{className:"h-full w-full flex-1 bg-primary transition-all",style:{transform:`translateX(-${100-(s||0)}%)`}})}));U.displayName=Y.displayName;function ee(n){if(n===0)return"0 B";const s=1024,i=["B","KB","MB","GB"],o=Math.floor(Math.log(n)/Math.log(s));return parseFloat((n/Math.pow(s,o)).toFixed(2))+" "+i[o]}function Te(){const[n,s]=u.useState(null),[i,o]=u.useState(!0),[t,c]=u.useState(null);if(u.useEffect(()=>{const l=new AbortController,d=async()=>{try{const f=await fetch(F.cacheStats,{signal:l.signal});if(!f.ok)throw new Error("Failed to fetch cache stats");const b=await f.json();l.signal.aborted||(s(b.data),c(null))}catch(f){l.signal.aborted||(f instanceof Error&&f.name!=="AbortError"?c(f.message):(!(f instanceof Error)||f.name!=="AbortError")&&c("Failed to load cache stats"))}finally{l.signal.aborted||o(!1)}};d();const h=setInterval(d,5e3),p=()=>d();return window.addEventListener("cache:cleared",p),()=>{l.abort(),clearInterval(h),window.removeEventListener("cache:cleared",p)}},[]),i)return e.jsx("div",{className:"flex items-center justify-center p-8",children:e.jsx("div",{className:"animate-spin rounded-full h-8 w-8 border-b-2 border-primary"})});if(t)return e.jsxs("div",{className:"text-destructive p-4",children:["Error loading cache metrics: ",t]});if(!n)return null;const{document:a}=n,r=a.hitRate*100;return e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"flex items-center gap-6 text-sm",children:[e.jsxs("div",{children:[e.jsx("span",{className:"text-muted-foreground",children:"Hit Rate"})," ",e.jsxs("span",{className:"font-semibold text-base",children:[r.toFixed(1),"%"]}),e.jsxs("span",{className:"text-muted-foreground ml-1",children:["(",a.hits," hits / ",a.misses," misses)"]})]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-muted-foreground",children:"Size"})," ",e.jsx("span",{className:"font-semibold",children:ee(a.size)}),e.jsxs("span",{className:"text-muted-foreground ml-1",children:["(",a.itemCount," items)"]})]}),e.jsx(fe,{variant:a.enabled?"default":"secondary",children:a.enabled?"Enabled":"Disabled"})]}),e.jsx(U,{value:r,className:"h-2","aria-label":"Cache hit rate"}),w==="docx"&&n.components&&e.jsx(Me,{data:n.components})]})}function Me({data:n}){return n.componentStats.length===0?null:e.jsxs("div",{className:"space-y-2",children:[e.jsx("h4",{className:"text-sm font-medium",children:"Module Breakdown"}),e.jsx("div",{className:"space-y-1",children:n.componentStats.map(s=>{const i=(s.hitRate||0)*100,o=s.totalRequests||s.hits+s.misses,t=s.memoryUsage||s.entries*s.avgSize||0;return e.jsxs("div",{className:"px-3 py-1.5 text-sm",children:[e.jsxs("div",{className:"flex items-center justify-between mb-1",children:[e.jsx("span",{className:"font-medium",children:s.type}),e.jsxs("div",{className:"flex items-center gap-2 text-xs text-muted-foreground",children:[e.jsxs("span",{children:[i.toFixed(0),"% hit"]}),e.jsxs("span",{children:[o," req"]}),e.jsx("span",{children:ee(t)})]})]}),e.jsx(U,{value:i,className:"h-1.5"})]},s.type)})})]})}const Pe=u.memo(function({editTimestamp:s,debounceMs:i}){const[o,t]=u.useState(()=>Math.max(0,s+i-Date.now()));if(u.useEffect(()=>{let r;const l=()=>{const d=Math.max(0,s+i-Date.now());t(d),d<=0&&r!=null&&clearInterval(r)};return l(),r=setInterval(l,100),()=>{r!=null&&clearInterval(r)}},[s,i]),o<=0)return null;const c=(o/1e3).toFixed(1),a=Math.min(100,o/i*100);return e.jsxs("span",{className:"inline-flex items-center gap-1.5 text-xs text-warning tabular-nums",children:[e.jsx("span",{className:"h-1 rounded-full bg-warning/60 transition-[width] duration-100",style:{width:`${Math.round(a*.4)}px`}}),c,"s"]})});function We(){var D,G;const{autoReload:n,renderingLibrary:s,saveDocumentDebounceWait:i,setSettings:o}=q(m=>m),{name:t,text:c,blob:a,isGenerating:r,generationProgress:l,globalError:d,cacheStatus:h,cacheHitRate:p,warnings:f,isRendering:b,isPreviewStale:se,editSequence:te,lastBuiltSequence:ne,editTimestamp:_,hasValidationErrors:re,setOutput:v}=Ne(m=>m),j=L(m=>m.activeTab),$=L(m=>m.documentTypes),J=Q(m=>m.customThemes),T=u.useMemo(()=>{const m={};for(const x of Object.values(J))x.valid&&x.name&&x.parsed&&(m[x.name]=x.parsed);return m},[J]),[N,ae]=u.useState(void 0),[ie,oe]=u.useState(void 0),[ce,M]=u.useState(!1),H=O.useRef(null),C=u.useRef(null),P=u.useRef(!1),k=u.useCallback(()=>{C.current&&(C.current(),C.current=null)},[]),y=u.useCallback(async(m,x,de,me)=>{v({isRendering:!0,isPreviewStale:!1,globalError:void 0});try{const{status:S,payload:g}=await Oe(m,x,s,void 0,de,me);if(S!=="success")throw g instanceof Error?g:new Error(String(g));k(),ae(g.iframeSrc||void 0),oe(g.iframeSrcDoc||void 0),C.current=g.cleanup||null}catch(S){const g=S instanceof Error?S.message:String(S);let B=g;g.includes("Failed to fetch")?B="Network error: Unable to load required resources. Check if the server is running properly.":g.includes("timeout")&&(B="Render timeout: The rendering process took too long."),v({globalError:B})}finally{v({isRendering:!1})}},[v,k,s]),le=u.useRef({name:t,blob:a,text:c,doRender:y});u.useEffect(()=>{le.current={name:t,blob:a,text:c,doRender:y}});const I=u.useCallback(async()=>{P.current=!0,window.dispatchEvent(new CustomEvent("preview:flushAndBuild"))},[]);u.useEffect(()=>{a&&t&&n&&s==="docxjs"?y(t,a,c,T):a&&t&&P.current?(P.current=!1,y(t,a,c,T)):a&&t&&v({isPreviewStale:!0})},[a,t,c,T,n,s,y,v]),u.useEffect(()=>()=>{k()},[k]),u.useEffect(()=>{const m=()=>I(),x=()=>M(!0);return window.addEventListener("preview:manualRender",m),window.addEventListener("preview:showCacheMetrics",x),()=>{window.removeEventListener("preview:manualRender",m),window.removeEventListener("preview:showCacheMetrics",x)}},[I]);const W=((G=(D=q)==null?void 0:D.getState)==null?void 0:G.call(D).useGlobalPreviewHeader)===!1;return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex h-full flex-col",children:[W&&e.jsx(he,{iframeRef:H,displayReloadButton:!!N&&!((N==null?void 0:N.startsWith("blob:"))??!1),name:(t==null?void 0:t.trim())||"Preview",blob:a,autoReload:n,onToggleAutoReload:()=>o({autoReload:!n}),onManualRender:I,isGenerating:r,isRendering:b,onShowCacheMetrics:()=>M(!0),documentText:c,warnings:f,renderingLibrary:s,setRenderingLibrary:m=>o({renderingLibrary:m})}),!W&&e.jsx(pe,{}),w==="docx"&&s==="docxjs"&&e.jsxs("div",{className:"px-3 py-1.5 flex items-center gap-2 border-b bg-data-blue/10 text-xs text-data-blue",children:[e.jsx(X,{className:"h-3.5 w-3.5 shrink-0"}),e.jsx("span",{children:"The docxjs renderer is not 100% representative of the actual .docx. Use LibreOffice for higher fidelity."})]}),(()=>{const m=(te??0)>(ne??0),x=(se||m)&&!r&&!b;return(h&&h!=="UNKNOWN"||x)&&e.jsxs("div",{className:`px-3 py-1.5 flex items-center justify-between border-b overflow-hidden ${x?"bg-warning/10":"bg-header-bg"}`,children:[e.jsx("div",{className:"flex items-center gap-2 min-w-0 truncate",children:h==="HIT"&&!x?e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"h-1.5 w-1.5 rounded-full bg-success flex-shrink-0"}),e.jsx("span",{className:"text-xs text-muted-foreground truncate",children:"Cached"})]}):h==="MISS"&&!x?e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"h-1.5 w-1.5 rounded-full bg-accent2 flex-shrink-0"}),e.jsxs("span",{className:"text-xs text-muted-foreground truncate",children:["Fresh ",K.toLowerCase()]})]}):x?e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"h-1.5 w-1.5 rounded-full bg-warning flex-shrink-0"}),e.jsx("span",{className:"text-xs text-warning truncate",children:"Outdated — click Run"}),_&&m&&!r&&!re&&n&&s==="docxjs"&&e.jsx(Pe,{editTimestamp:_,debounceMs:i+200})]}):null}),!x&&p&&p!=="0.0%"&&e.jsxs("span",{className:"text-xs text-muted-foreground flex-shrink-0 ml-2",children:[p," hit rate"]})]})})(),e.jsx(Le,{warnings:f,className:"mx-3 my-2"}),e.jsx(Ce,{className:"mx-3 my-2"}),d&&!r?e.jsx("div",{className:"flex-1 flex items-center justify-center p-6",children:e.jsxs("div",{className:"max-w-md rounded-sm border-l-4 border-l-destructive bg-destructive/10 px-4 py-3 text-sm text-destructive",children:[e.jsx("p",{className:"font-medium mb-1",children:"Generation failed"}),e.jsx("p",{className:"text-xs text-destructive/80 break-words",children:d})]})}):e.jsx(Re,{ref:H,isLoading:!!b,iframeSrc:N,iframeSrcDoc:ie,isGenerating:(()=>{const m=n&&s==="docxjs",x=j&&t&&j!==t&&$[j]!=="application/json+theme";return!!r||!!(m&&x)})(),generationProgress:(()=>{const m=n&&s==="docxjs",x=j&&t&&j!==t&&$[j]!=="application/json+theme";return m&&x&&!r?{stage:"parsing",message:`Building preview for ${j}...`}:l})()})]}),e.jsx(ge,{open:ce,onOpenChange:M,children:e.jsxs(je,{className:"max-w-2xl",children:[e.jsx(we,{children:e.jsx(ve,{children:"Cache Performance"})}),e.jsx(Te,{})]})})]})}export{We as Preview};
3
+ //# sourceMappingURL=preview-BOF6SWhK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"mappings":"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","names":["DocumentGenerationLoader","className","currentStage","message","stages","stageLabels","currentStageIndex","cn","jsxs","jsx","Spinner","stage","index","isActive","isCompleted","PreviewLoading","renderingLibrary","getLibraryInfo","library","name","description","Skeleton","PreviewFrame","React","isLoading","iframeSrc","iframeSrcDoc","isGenerating","generationProgress","ref","iframeLoaded","setIframeLoaded","useState","useEffect","handleIframeLoad","handleIframeError","iframeSandbox","hasContent","FORMAT_LABEL","PreviewFrameMemoized","WarningsPanel","warnings","isExpanded","setIsExpanded","ChevronRight","AlertTriangle","warning","Info","BUILTIN_THEMES","UnavailableThemeWarning","activeTab","useDocumentsStore","s","documentTypes","documents","customThemes","useThemesStore","missingThemeName","useMemo","doc","d","themeName","_b","_a","_c","FORMAT","ct","renderDocxWithDocxJS","blob","docx_preview","__vitePreload","bodyEl","headEl","renderPromise","timeoutPromise","_","reject","overrideStyleEl","env","htmlEl","renderWithLibreOffice","jsonText","useJsonPath","response","jsonDefinition","err","body","API_ENDPOINTS","formData","ext","json","pdfBlob","objectUrl","renderDocument","_baseUrl","payload","error","errorMessage","Progress","React.forwardRef","value","props","ProgressPrimitive.Root","ProgressPrimitive.Indicator","formatBytes","bytes","k","sizes","i","CacheMetrics","stats","setStats","loading","setLoading","setError","controller","fetchData","data","interval","handleCacheCleared","hitRatePct","Badge","ComponentBreakdown","stat","hitRate","requests","memory","DebounceCountdown","memo","editTimestamp","debounceMs","remaining","setRemaining","id","tick","r","secs","pct","Preview","autoReload","saveDocumentDebounceWait","setSettings","useSettingsStore","state","text","globalError","cacheStatus","cacheHitRate","isRendering","isPreviewStale","editSequence","lastBuiltSequence","hasValidationErrors","setOutput","useOutputStore","customThemesMap","themesForServer","out","setIframeSrc","setIframeSrcDoc","showCacheMetrics","setShowCacheMetrics","iframeRef","renderCleanupRef","useRef","pendingManualRenderRef","cleanupRenderedPreview","useCallback","doRender","docName","docBlob","docText","themes","status","displayMessage","manualRenderRef","handleManualRender","onManual","onShowCache","localHeaderVisible","Fragment","PreviewHeaderMemoized","lib","Separator","InfoIcon","hasUnsyncedEdits","isStale","willAutoBuild","isSwitchingDoc","Dialog","DialogContent","DialogHeader","DialogTitle"],"ignoreList":[],"sources":["../../../src/client/components/ui/loading-states.tsx","../../../src/client/components/playground/preview-frame.tsx","../../../src/client/components/playground/warnings-panel.tsx","../../../src/client/components/playground/unavailable-theme-warning.tsx","../../../src/client/lib/render.ts","../../../src/client/components/ui/progress.tsx","../../../src/client/components/cache-metrics.tsx","../../../src/client/components/playground/preview.tsx"],"sourcesContent":["import { cn } from '@/lib/utils';\nimport { Spinner } from './spinner';\nimport { Skeleton } from './skeleton';\n\ninterface LoadingOverlayProps {\n isLoading: boolean;\n children: React.ReactNode;\n message?: string;\n className?: string;\n}\n\nexport function LoadingOverlay({\n isLoading,\n children,\n message = 'Loading...',\n className,\n}: LoadingOverlayProps) {\n return (\n <div className={cn('relative', className)}>\n {children}\n {isLoading && (\n <div className=\"absolute inset-0 bg-background/80 backdrop-blur-sm flex items-center justify-center z-50\">\n <div className=\"flex flex-col items-center gap-2\">\n <Spinner size=\"lg\" />\n <p className=\"text-sm text-muted-foreground\">{message}</p>\n </div>\n </div>\n )}\n </div>\n );\n}\n\ninterface DocumentGenerationLoaderProps {\n className?: string;\n currentStage?: 'parsing' | 'building' | 'rendering' | 'finalizing';\n message?: string;\n}\n\nexport function DocumentGenerationLoader({\n className,\n currentStage = 'parsing',\n message,\n}: DocumentGenerationLoaderProps) {\n const stages = ['parsing', 'building', 'rendering', 'finalizing'];\n const stageLabels = {\n parsing: 'Validating JSON',\n building: 'Building structure',\n rendering: 'Rendering content',\n finalizing: 'Finalizing document',\n };\n\n const currentStageIndex = stages.indexOf(currentStage);\n\n return (\n <div className={cn('p-6 space-y-4', className)}>\n <div className=\"flex items-center gap-3\">\n <Spinner size=\"md\" />\n <div className=\"space-y-1\">\n <p className=\"text-sm font-medium\">Generating Document</p>\n <p className=\"text-xs text-muted-foreground\">\n {message || `${stageLabels[currentStage]}...`}\n </p>\n </div>\n </div>\n\n <div className=\"space-y-3\">\n {stages.map((stage, index) => {\n const isActive = index === currentStageIndex;\n const isCompleted = index < currentStageIndex;\n return (\n <div key={stage} className=\"space-y-2\">\n <div className=\"flex justify-between text-xs\">\n <span\n className={cn(\n isActive\n ? 'text-primary font-medium'\n : isCompleted\n ? 'text-muted-foreground'\n : 'text-muted-foreground/60'\n )}\n >\n {stageLabels[stage as keyof typeof stageLabels]}\n </span>\n <span className=\"text-muted-foreground\">\n {isCompleted ? '✓' : isActive ? '...' : '○'}\n </span>\n </div>\n <div className=\"w-full bg-secondary rounded-full h-1.5\">\n <div\n className={cn(\n 'h-1.5 rounded-full transition-all duration-300',\n isCompleted\n ? 'bg-success'\n : isActive\n ? 'bg-primary animate-pulse'\n : 'bg-transparent'\n )}\n style={{\n width: isCompleted ? '100%' : isActive ? '100%' : '0%',\n }}\n />\n </div>\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n\ninterface PreviewLoadingProps {\n renderingLibrary?: string;\n className?: string;\n}\n\nexport function PreviewLoading({\n renderingLibrary,\n className,\n}: PreviewLoadingProps) {\n const getLibraryInfo = (library?: string) => {\n switch (library) {\n case 'LibreOffice':\n return {\n name: 'LibreOffice',\n description: 'Converting document to PDF locally...',\n };\n case 'Office':\n return {\n name: 'Microsoft Office',\n description: 'Uploading file and loading Office viewer...',\n };\n case 'Docs':\n return {\n name: 'Google Docs',\n description: 'Uploading file and loading Docs viewer...',\n };\n default:\n return { name: 'Default', description: 'Processing document...' };\n }\n };\n\n const { name, description } = getLibraryInfo(renderingLibrary);\n\n return (\n <div\n className={cn(\n 'flex flex-col items-center justify-center h-full p-8 space-y-4',\n className\n )}\n >\n <Spinner size=\"lg\" />\n <div className=\"text-center space-y-2\">\n <p className=\"text-sm font-medium\">Rendering Preview</p>\n <p className=\"text-xs text-muted-foreground\">Using {name} renderer</p>\n <p className=\"text-xs text-muted-foreground/80\">{description}</p>\n </div>\n\n <div className=\"w-full max-w-md space-y-3\">\n <Skeleton className=\"h-16 w-full\" />\n <div className=\"space-y-2\">\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-4 w-1/2\" />\n </div>\n <Skeleton className=\"h-12 w-full\" />\n </div>\n </div>\n );\n}\n\ninterface InlineLoaderProps {\n message?: string;\n size?: 'sm' | 'md';\n className?: string;\n}\n\nexport function InlineLoader({\n message = 'Loading...',\n size = 'sm',\n className,\n}: InlineLoaderProps) {\n return (\n <div className={cn('flex items-center gap-2', className)}>\n <Spinner size={size} />\n <span className=\"text-sm text-muted-foreground\">{message}</span>\n </div>\n );\n}\n\ninterface FileOperationLoaderProps {\n operation: 'upload' | 'download' | 'save' | 'delete';\n fileName?: string;\n progress?: number;\n className?: string;\n}\n\nexport function FileOperationLoader({\n operation,\n fileName,\n progress,\n className,\n}: FileOperationLoaderProps) {\n const operationLabels = {\n upload: 'Uploading',\n download: 'Downloading',\n save: 'Saving',\n delete: 'Deleting',\n };\n\n return (\n <div\n className={cn(\n 'flex items-center gap-3 p-3 border rounded-sm bg-header-bg',\n className\n )}\n >\n <Spinner size=\"sm\" />\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium truncate\">\n {operationLabels[operation]} {fileName && `\"${fileName}\"`}\n </p>\n {progress !== undefined && (\n <div className=\"mt-1\">\n <div className=\"flex justify-between text-xs mb-1\">\n <span>{progress}%</span>\n <span className=\"text-muted-foreground\">\n {progress < 100 ? 'In progress...' : 'Complete'}\n </span>\n </div>\n <div className=\"w-full bg-secondary rounded-full h-1.5\">\n <div\n className=\"bg-primary h-1.5 rounded-full transition-all duration-300\"\n style={{ width: `${progress}%` }}\n />\n </div>\n </div>\n )}\n </div>\n </div>\n );\n}\n","import React, { useEffect, useState } from 'react';\nimport { DocumentGenerationLoader, PreviewLoading } from '../ui/loading-states';\nimport { Spinner } from '../ui/spinner';\nimport { FORMAT_LABEL } from '../../lib/env';\n\nconst PreviewFrame = React.forwardRef<\n HTMLIFrameElement,\n {\n isLoading: boolean;\n iframeSrc?: string;\n iframeSrcDoc?: string;\n isGenerating?: boolean;\n generationProgress?: {\n stage: 'parsing' | 'building' | 'rendering' | 'finalizing';\n message?: string;\n };\n }\n>(\n (\n {\n isLoading,\n iframeSrc,\n iframeSrcDoc,\n isGenerating,\n generationProgress,\n },\n ref\n ) => {\n const [iframeLoaded, setIframeLoaded] = useState(false);\n\n // Reset iframe loaded state when content changes\n useEffect(() => {\n setIframeLoaded(false);\n }, [iframeSrc, iframeSrcDoc]);\n\n // Handle iframe load event\n const handleIframeLoad = () => {\n setIframeLoaded(true);\n };\n\n // Handle iframe errors\n const handleIframeError = () => {\n console.warn('Iframe failed to load content');\n setIframeLoaded(false);\n };\n\n // PDF blob URLs and remote viewers can be blocked by Chromium-based browsers\n // when loaded in a sandboxed iframe. Keep sandbox only for srcDoc-based preview.\n const iframeSandbox = iframeSrcDoc\n ? 'allow-scripts allow-popups allow-forms'\n : undefined;\n\n const hasContent = Boolean(iframeSrc || iframeSrcDoc);\n\n // Full-screen loaders only when no existing content\n if (isGenerating && !hasContent) {\n return (\n <div className=\"grow\">\n <DocumentGenerationLoader\n currentStage={generationProgress?.stage}\n message={generationProgress?.message}\n />\n </div>\n );\n }\n\n if (isLoading && !hasContent) {\n return (\n <div className=\"grow\">\n <PreviewLoading renderingLibrary=\"LibreOffice\" />\n </div>\n );\n }\n\n if (hasContent) {\n return (\n <div className=\"h-full w-full relative bg-white\">\n {/* Loading overlay */}\n {!iframeLoaded && (\n <div className=\"absolute inset-0 bg-muted/80 flex items-center justify-center z-10\">\n <div className=\"text-center\">\n <div className=\"animate-spin rounded-full h-8 w-8 border-b-2 border-foreground mx-auto mb-2\"></div>\n <p className=\"text-sm text-muted-foreground\">\n Loading preview...\n </p>\n </div>\n </div>\n )}\n {/* Generating/Rendering overlay on top of existing content */}\n {(isGenerating || isLoading) && (\n <div className=\"absolute inset-0 bg-background/60 backdrop-blur-[2px] flex items-center justify-center z-20 transition-opacity duration-200\">\n <div className=\"flex flex-col items-center gap-2\">\n <Spinner size=\"lg\" />\n <p className=\"text-sm text-muted-foreground\">\n {isGenerating ? 'Generating...' : 'Rendering...'}\n </p>\n </div>\n </div>\n )}\n {/* Key forces remount when switching between srcDoc (sandboxed) and\n src (blob URL, no sandbox) to avoid stale sandbox race condition */}\n <iframe\n key={iframeSrcDoc ? 'srcdoc' : 'src'}\n ref={ref}\n className=\"w-full h-full border-0\"\n style={{\n backgroundColor: 'white',\n // Fade in after load to prevent flash\n opacity: iframeLoaded ? 1 : 0,\n transition: 'opacity 0.2s ease-in-out',\n }}\n src={iframeSrc}\n srcDoc={iframeSrcDoc}\n onLoad={handleIframeLoad}\n onError={handleIframeError}\n // Security: sandbox srcDoc to prevent XSS from rendered HTML.\n // Blob URLs (src) must NOT be sandboxed without allow-same-origin.\n sandbox={iframeSandbox}\n // Accessibility\n title=\"Document preview\"\n aria-label={`Preview of rendered ${FORMAT_LABEL.toLowerCase()}`}\n />\n </div>\n );\n }\n return null;\n }\n);\n\nPreviewFrame.displayName = 'PreviewFrame';\nexport const PreviewFrameMemoized = React.memo(PreviewFrame);\n","import React from 'react';\nimport { AlertTriangle, Info, ChevronRight } from 'lucide-react';\nimport type { GenerationWarning } from '../../store/output-store';\n\ninterface WarningsPanelProps {\n warnings: GenerationWarning[] | null | undefined;\n className?: string;\n}\n\nexport function WarningsPanel({ warnings, className }: WarningsPanelProps) {\n const [isExpanded, setIsExpanded] = React.useState(false);\n\n if (!warnings || warnings.length === 0) {\n return null;\n }\n\n return (\n <div className={className}>\n <button\n type=\"button\"\n onClick={() => setIsExpanded(!isExpanded)}\n className=\"flex w-full items-center gap-2 rounded-sm border border-transparent bg-warning/10 px-3 py-1.5 text-left transition-colors hover:bg-warning/20\"\n >\n <ChevronRight\n className={`h-3.5 w-3.5 text-warning transition-transform flex-shrink-0 ${isExpanded ? 'rotate-90' : ''}`}\n />\n <AlertTriangle className=\"h-3.5 w-3.5 text-warning flex-shrink-0\" />\n <span className=\"text-xs font-medium text-warning\">\n {warnings.length} warning{warnings.length !== 1 ? 's' : ''}\n </span>\n </button>\n\n {isExpanded && (\n <div className=\"mt-1.5 space-y-1.5\">\n {warnings.map((warning, index) => (\n <div\n key={index}\n className=\"flex items-start gap-2 rounded-sm border bg-card px-3 py-2\"\n >\n {warning.severity === 'info' ? (\n <Info className=\"h-3.5 w-3.5 mt-0.5 text-data-blue flex-shrink-0\" />\n ) : (\n <AlertTriangle className=\"h-3.5 w-3.5 mt-0.5 text-warning flex-shrink-0\" />\n )}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-1.5\">\n <code className=\"text-[11px] font-medium text-warning bg-warning/15 px-1 py-0.5 rounded-sm\">\n {warning.component}\n </code>\n {warning.severity === 'info' && (\n <span className=\"text-[10px] uppercase tracking-wide text-data-blue font-medium\">\n info\n </span>\n )}\n </div>\n <p className=\"mt-0.5 text-xs text-muted-foreground leading-relaxed\">\n {warning.message}\n </p>\n {warning.context && Object.keys(warning.context).length > 0 && (\n <details className=\"mt-1.5\">\n <summary className=\"text-[11px] text-muted-foreground/70 cursor-pointer hover:text-foreground\">\n Context\n </summary>\n <pre className=\"mt-1 text-[11px] bg-header-bg p-1.5 rounded-sm overflow-x-auto text-muted-foreground\">\n {JSON.stringify(warning.context, null, 2)}\n </pre>\n </details>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n );\n}\n","import { useMemo } from 'react';\nimport { AlertTriangle } from 'lucide-react';\nimport { useDocumentsStore } from '../../store/documents-store-provider';\nimport { useThemesStore } from '../../store/themes-store-provider';\nimport { FORMAT } from '../../lib/env';\n\n// Theme names registered in the server-side core packages. Documents may\n// reference these even without a matching customThemes entry.\nconst BUILTIN_THEMES: Record<string, readonly string[]> = {\n pptx: ['default', 'dark', 'minimal'],\n docx: ['minimal', 'corporate', 'modern'],\n};\n\nexport function UnavailableThemeWarning({ className }: { className?: string }) {\n const activeTab = useDocumentsStore((s) => s.activeTab);\n const documentTypes = useDocumentsStore((s) => s.documentTypes);\n const documents = useDocumentsStore((s) => s.documents);\n const customThemes = useThemesStore((s) => s.customThemes);\n\n const missingThemeName = useMemo(() => {\n if (!activeTab) return null;\n if (documentTypes[activeTab] === 'application/json+theme') return null;\n\n const doc = documents.find((d) => d.name === activeTab);\n if (!doc?.text) return null;\n\n let themeName: unknown;\n try {\n themeName = JSON.parse(doc.text)?.props?.theme;\n } catch {\n return null;\n }\n if (typeof themeName !== 'string' || !themeName) return null;\n\n if (BUILTIN_THEMES[FORMAT]?.includes(themeName)) return null;\n\n for (const ct of Object.values(customThemes)) {\n if (ct.valid && ct.name === themeName) return null;\n }\n\n return themeName;\n }, [activeTab, documentTypes, documents, customThemes]);\n\n if (!missingThemeName) return null;\n\n return (\n <div\n className={`flex items-start gap-2 rounded-sm border border-transparent bg-warning/10 px-3 py-2 ${className ?? ''}`}\n >\n <AlertTriangle className=\"h-3.5 w-3.5 mt-0.5 text-warning flex-shrink-0\" />\n <div className=\"flex-1 min-w-0 text-xs leading-relaxed\">\n <span className=\"font-medium text-warning\">\n Theme{' '}\n <code className=\"bg-warning/20 px-1 py-0.5 rounded-sm text-warning\">\n {missingThemeName}\n </code>{' '}\n is not available.\n </span>{' '}\n <span className=\"text-warning/80\">\n Add it as a custom theme or use a built-in theme (\n {BUILTIN_THEMES[FORMAT]?.join(', ')}). The renderer will fall back to\n a default.\n </span>\n </div>\n </div>\n );\n}\n","import { FORMAT } from './env';\nimport { API_ENDPOINTS } from '../config/api';\nimport { env } from './env';\n\nexport type RenderPayload = {\n iframeSrc?: string;\n iframeSrcDoc?: string;\n cleanup?: () => void;\n};\n\nasync function renderDocxWithDocxJS(blob: Blob): Promise<RenderPayload> {\n const docx_preview = await import('docx-preview');\n\n const bodyEl = document.createElement('body');\n const headEl = document.createElement('head');\n\n const renderPromise = docx_preview.renderAsync(blob, bodyEl, headEl, {\n inWrapper: true,\n });\n\n const timeoutPromise = new Promise((_, reject) => {\n setTimeout(() => {\n reject(new Error('DocxJS renderAsync timed out after 10 seconds'));\n }, 10000);\n });\n\n await Promise.race([renderPromise, timeoutPromise]);\n\n const overrideStyleEl = document.createElement('link');\n overrideStyleEl.rel = 'stylesheet';\n overrideStyleEl.href = `${env.basePath}/css/preview/docxjs.css`;\n headEl.appendChild(overrideStyleEl);\n\n const htmlEl = document.createElement('html');\n htmlEl.appendChild(headEl);\n htmlEl.appendChild(bodyEl);\n\n return { iframeSrcDoc: htmlEl.outerHTML };\n}\n\nasync function renderWithLibreOffice(\n name: string,\n blob: Blob,\n jsonText?: string,\n customThemes?: Record<string, unknown>\n): Promise<RenderPayload> {\n // When we know the source JSON, hit the from-json endpoint. The server\n // generates + converts in one step and passes resolved fonts directly to\n // the LibreOffice font stager — embedded fonts render correctly in the PDF.\n // Falling back to the upload path is still useful if the JSON is lost or\n // we're previewing an externally-supplied file.\n const useJsonPath = typeof jsonText === 'string' && jsonText.length > 0;\n\n let response: Response;\n if (useJsonPath) {\n let jsonDefinition: unknown;\n try {\n jsonDefinition = JSON.parse(jsonText!);\n } catch (err) {\n throw new Error(`Invalid JSON document: ${(err as Error).message}`);\n }\n const body: Record<string, unknown> = {\n jsonDefinition,\n customThemes: customThemes ?? {},\n // sourceName lets the server resolve relative asset paths against the\n // discovered document's own directory (#142). Names only — the server\n // maps them to paths itself.\n options: { sourceName: name },\n };\n response = await fetch(API_ENDPOINTS.preview.libreofficeFromJson, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n } else {\n const formData = new FormData();\n const ext = FORMAT === 'pptx' ? '.pptx' : '.docx';\n formData.append('file', blob, `${name}${ext}`);\n response = await fetch(API_ENDPOINTS.preview.libreoffice, {\n method: 'POST',\n body: formData,\n });\n }\n\n if (!response.ok) {\n let message = `LibreOffice preview failed (${response.status})`;\n try {\n const json = await response.json();\n if (json && typeof json === 'object' && typeof json.error === 'string') {\n message = json.error;\n }\n } catch {}\n throw new Error(message);\n }\n\n const pdfBlob = await response.blob();\n if (!pdfBlob.size) {\n throw new Error('LibreOffice preview returned an empty PDF');\n }\n\n const objectUrl = URL.createObjectURL(pdfBlob);\n return {\n iframeSrc: objectUrl,\n cleanup: () => URL.revokeObjectURL(objectUrl),\n };\n}\n\nexport function cleanupIframe(iframe: HTMLIFrameElement) {\n try {\n if (iframe.contentWindow) {\n iframe.contentWindow.location.replace('about:blank');\n }\n iframe.src = 'about:blank';\n iframe.srcdoc = '';\n if (iframe.parentNode) {\n iframe.parentNode.removeChild(iframe);\n }\n } catch (error) {\n console.warn('Error cleaning up iframe:', error);\n }\n}\n\nexport type RenderLibrary = 'docxjs' | 'LibreOffice';\n\nexport async function renderDocument(\n name: string,\n blob: Blob,\n library: RenderLibrary = FORMAT === 'docx' ? 'docxjs' : 'LibreOffice',\n _baseUrl?: string,\n jsonText?: string,\n customThemes?: Record<string, unknown>\n) {\n try {\n if (!blob || blob.size === 0) {\n throw new Error('Invalid or empty document blob');\n }\n\n let payload: RenderPayload;\n\n if (FORMAT === 'docx' && library === 'docxjs') {\n payload = await renderDocxWithDocxJS(blob);\n } else {\n payload = await renderWithLibreOffice(name, blob, jsonText, customThemes);\n }\n\n return { status: 'success' as const, name, payload };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return { status: 'error' as const, name, payload: new Error(errorMessage) };\n }\n}\n\n// Keep backward compat exports for pptx code\nexport const renderPptx = (name: string, blob: Blob, baseUrl: string) =>\n renderDocument(name, blob, 'LibreOffice', baseUrl);\n","import * as React from 'react';\nimport * as ProgressPrimitive from '@radix-ui/react-progress';\nimport { cn } from '../../lib/utils';\n\nconst Progress = React.forwardRef<\n React.ElementRef<typeof ProgressPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>\n>(({ className, value, ...props }, ref) => (\n <ProgressPrimitive.Root\n ref={ref}\n className={cn(\n 'relative h-4 w-full overflow-hidden rounded-full bg-secondary',\n className\n )}\n {...props}\n >\n <ProgressPrimitive.Indicator\n className=\"h-full w-full flex-1 bg-primary transition-all\"\n style={{ transform: `translateX(-${100 - (value || 0)}%)` }}\n />\n </ProgressPrimitive.Root>\n));\nProgress.displayName = ProgressPrimitive.Root.displayName;\n\nexport { Progress };\n","import { useEffect, useState } from 'react';\nimport { Badge } from './ui/badge';\nimport { Progress } from './ui/progress';\nimport { API_ENDPOINTS } from '../config/api';\nimport { FORMAT } from '../lib/env';\n\ninterface CacheStats {\n document: {\n hits: number;\n misses: number;\n hitRate: number;\n size: number;\n itemCount: number;\n enabled: boolean;\n };\n components?: ComponentCacheData;\n}\n\ninterface ComponentStatistics {\n type: string;\n hits: number;\n misses: number;\n avgProcessTime: number;\n avgSize: number;\n entries: number;\n hitRate?: number;\n totalRequests?: number;\n memoryUsage?: number;\n}\n\ninterface ComponentCacheData {\n entries: number;\n totalSize: number;\n hitRate: number;\n totalHits: number;\n totalMisses: number;\n avgResponseTime: number;\n evictions: number;\n componentStats: ComponentStatistics[];\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes === 0) return '0 B';\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];\n}\n\nexport function CacheMetrics() {\n const [stats, setStats] = useState<CacheStats | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n const controller = new AbortController();\n\n const fetchData = async () => {\n try {\n const response = await fetch(API_ENDPOINTS.cacheStats, {\n signal: controller.signal,\n });\n if (!response.ok) throw new Error('Failed to fetch cache stats');\n const data = await response.json();\n\n if (!controller.signal.aborted) {\n setStats(data.data);\n setError(null);\n }\n } catch (err) {\n if (!controller.signal.aborted) {\n if (err instanceof Error && err.name !== 'AbortError') {\n setError(err.message);\n } else if (!(err instanceof Error) || err.name !== 'AbortError') {\n setError('Failed to load cache stats');\n }\n }\n } finally {\n if (!controller.signal.aborted) setLoading(false);\n }\n };\n\n fetchData();\n const interval = setInterval(fetchData, 5000);\n\n const handleCacheCleared = () => fetchData();\n window.addEventListener('cache:cleared', handleCacheCleared);\n\n return () => {\n controller.abort();\n clearInterval(interval);\n window.removeEventListener('cache:cleared', handleCacheCleared);\n };\n }, []);\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center p-8\">\n <div className=\"animate-spin rounded-full h-8 w-8 border-b-2 border-primary\" />\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"text-destructive p-4\">\n Error loading cache metrics: {error}\n </div>\n );\n }\n\n if (!stats) return null;\n\n const { document: doc } = stats;\n const hitRatePct = doc.hitRate * 100;\n\n return (\n <div className=\"space-y-4\">\n {/* Summary row */}\n <div className=\"flex items-center gap-6 text-sm\">\n <div>\n <span className=\"text-muted-foreground\">Hit Rate</span>{' '}\n <span className=\"font-semibold text-base\">{hitRatePct.toFixed(1)}%</span>\n <span className=\"text-muted-foreground ml-1\">\n ({doc.hits} hits / {doc.misses} misses)\n </span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">Size</span>{' '}\n <span className=\"font-semibold\">{formatBytes(doc.size)}</span>\n <span className=\"text-muted-foreground ml-1\">\n ({doc.itemCount} items)\n </span>\n </div>\n <Badge variant={doc.enabled ? 'default' : 'secondary'}>\n {doc.enabled ? 'Enabled' : 'Disabled'}\n </Badge>\n </div>\n\n <Progress value={hitRatePct} className=\"h-2\" aria-label=\"Cache hit rate\" />\n\n {/* Module-level breakdown (docx only) */}\n {FORMAT === 'docx' && stats.components && (\n <ComponentBreakdown data={stats.components} />\n )}\n </div>\n );\n}\n\nfunction ComponentBreakdown({ data }: { data: ComponentCacheData }) {\n if (data.componentStats.length === 0) return null;\n\n return (\n <div className=\"space-y-2\">\n <h4 className=\"text-sm font-medium\">Module Breakdown</h4>\n <div className=\"space-y-1\">\n {data.componentStats.map((stat) => {\n const hitRate = (stat.hitRate || 0) * 100;\n const requests = stat.totalRequests || stat.hits + stat.misses;\n const memory = stat.memoryUsage || stat.entries * stat.avgSize || 0;\n\n return (\n <div key={stat.type} className=\"px-3 py-1.5 text-sm\">\n <div className=\"flex items-center justify-between mb-1\">\n <span className=\"font-medium\">{stat.type}</span>\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <span>{hitRate.toFixed(0)}% hit</span>\n <span>{requests} req</span>\n <span>{formatBytes(memory)}</span>\n </div>\n </div>\n <Progress value={hitRate} className=\"h-1.5\" />\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n","import React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n memo,\n} from 'react';\nimport { InfoIcon } from 'lucide-react';\nimport { PreviewFrameMemoized } from './preview-frame';\nimport { PreviewHeaderMemoized } from './preview-header';\nimport { WarningsPanel } from './warnings-panel';\nimport { UnavailableThemeWarning } from './unavailable-theme-warning';\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from '../ui/dialog';\nimport { Separator } from '../ui/separator';\nimport { useOutputStore } from '../../store/output-store-provider';\nimport { useSettingsStore } from '../../store/settings-store-provider';\nimport { renderDocument } from '../../lib/render';\nimport { useDocumentsStore } from '../../store/documents-store-provider';\nimport { useThemesStore } from '../../store/themes-store-provider';\nimport { CacheMetrics } from '../cache-metrics';\nimport { FORMAT, FORMAT_LABEL } from '../../lib/env';\n\n/**\n * Tiny live countdown that ticks every 100ms and shows the\n * remaining debounce time before generation starts.\n * Renders nothing once the deadline is reached.\n */\nconst DebounceCountdown = memo(function DebounceCountdown({\n editTimestamp,\n debounceMs,\n}: {\n editTimestamp: number;\n debounceMs: number;\n}) {\n const [remaining, setRemaining] = useState(() =>\n Math.max(0, editTimestamp + debounceMs - Date.now())\n );\n\n useEffect(() => {\n let id: ReturnType<typeof setInterval> | undefined;\n const tick = () => {\n const r = Math.max(0, editTimestamp + debounceMs - Date.now());\n setRemaining(r);\n if (r <= 0 && id != null) clearInterval(id);\n };\n tick();\n id = setInterval(tick, 100);\n return () => {\n if (id != null) clearInterval(id);\n };\n }, [editTimestamp, debounceMs]);\n\n if (remaining <= 0) return null;\n\n const secs = (remaining / 1000).toFixed(1);\n const pct = Math.min(100, (remaining / debounceMs) * 100);\n\n return (\n <span className=\"inline-flex items-center gap-1.5 text-xs text-warning tabular-nums\">\n <span\n className=\"h-1 rounded-full bg-warning/60 transition-[width] duration-100\"\n style={{ width: `${Math.round(pct * 0.4)}px` }}\n />\n {secs}s\n </span>\n );\n});\n\nexport function Preview() {\n const {\n autoReload,\n renderingLibrary,\n saveDocumentDebounceWait,\n setSettings,\n } = useSettingsStore((state) => state);\n const {\n name,\n text,\n blob,\n isGenerating,\n generationProgress,\n globalError,\n cacheStatus,\n cacheHitRate,\n warnings,\n isRendering,\n isPreviewStale,\n editSequence,\n lastBuiltSequence,\n editTimestamp,\n hasValidationErrors,\n setOutput,\n } = useOutputStore((state) => state);\n const activeTab = useDocumentsStore((state) => state.activeTab);\n const documentTypes = useDocumentsStore((state) => state.documentTypes);\n // Select the raw map (stable reference until themes mutate), then memoize\n // the transform. A selector that returns a freshly-constructed object on\n // every call would burn through Zustand's Object.is equality and retrigger\n // renders indefinitely.\n const customThemesMap = useThemesStore((state) => state.customThemes);\n const themesForServer = useMemo(() => {\n const out: Record<string, unknown> = {};\n for (const ct of Object.values(customThemesMap)) {\n if (ct.valid && ct.name && ct.parsed) out[ct.name] = ct.parsed;\n }\n return out;\n }, [customThemesMap]);\n\n const [iframeSrc, setIframeSrc] = useState<string | undefined>(undefined);\n const [iframeSrcDoc, setIframeSrcDoc] = useState<string | undefined>(\n undefined\n );\n const [showCacheMetrics, setShowCacheMetrics] = useState<boolean>(false);\n const iframeRef = React.useRef<HTMLIFrameElement>(null);\n\n const renderCleanupRef = useRef<(() => void) | null>(null);\n const pendingManualRenderRef = useRef(false);\n\n const cleanupRenderedPreview = useCallback(() => {\n if (renderCleanupRef.current) {\n renderCleanupRef.current();\n renderCleanupRef.current = null;\n }\n }, []);\n\n // Core render function\n const doRender = useCallback(\n async (\n docName: string,\n docBlob: Blob,\n docText?: string,\n themes?: Record<string, unknown>\n ) => {\n setOutput({\n isRendering: true,\n isPreviewStale: false,\n globalError: undefined,\n });\n\n try {\n const { status, payload } = await renderDocument(\n docName,\n docBlob,\n renderingLibrary,\n undefined,\n docText,\n themes\n );\n\n if (status !== 'success') {\n throw payload instanceof Error ? payload : new Error(String(payload));\n }\n\n cleanupRenderedPreview();\n setIframeSrc(payload.iframeSrc || undefined);\n setIframeSrcDoc(payload.iframeSrcDoc || undefined);\n renderCleanupRef.current = payload.cleanup || null;\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n\n let displayMessage = errorMessage;\n if (errorMessage.includes('Failed to fetch')) {\n displayMessage =\n 'Network error: Unable to load required resources. Check if the server is running properly.';\n } else if (errorMessage.includes('timeout')) {\n displayMessage =\n 'Render timeout: The rendering process took too long.';\n }\n\n setOutput({ globalError: displayMessage });\n } finally {\n setOutput({ isRendering: false });\n }\n },\n [setOutput, cleanupRenderedPreview, renderingLibrary]\n );\n\n // Ref to always hold latest manual-render deps so the event listener never goes stale\n const manualRenderRef = useRef({ name, blob, text, doRender });\n useEffect(() => {\n manualRenderRef.current = { name, blob, text, doRender };\n });\n\n const handleManualRender = useCallback(async () => {\n // Set flag so blob-change effect renders the new blob when it arrives\n pendingManualRenderRef.current = true;\n // Flush debounces + trigger build via editor.tsx\n window.dispatchEvent(new CustomEvent('preview:flushAndBuild'));\n }, []); // stable — reads from ref\n\n // Auto-render when blob changes and docxjs + autoReload are active\n // Mark preview stale when blob changes but auto-render is OFF\n // Also render if a manual Run triggered the build (pendingManualRenderRef)\n useEffect(() => {\n if (blob && name && autoReload && renderingLibrary === 'docxjs') {\n doRender(name, blob, text, themesForServer);\n } else if (blob && name && pendingManualRenderRef.current) {\n pendingManualRenderRef.current = false;\n doRender(name, blob, text, themesForServer);\n } else if (blob && name) {\n setOutput({ isPreviewStale: true });\n }\n }, [\n blob,\n name,\n text,\n themesForServer,\n autoReload,\n renderingLibrary,\n doRender,\n setOutput,\n ]);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n cleanupRenderedPreview();\n };\n }, [cleanupRenderedPreview]);\n\n // Listen for global header events (stable — handleManualRender never changes)\n useEffect(() => {\n const onManual = () => handleManualRender();\n const onShowCache = () => setShowCacheMetrics(true);\n\n window.addEventListener('preview:manualRender', onManual);\n window.addEventListener('preview:showCacheMetrics', onShowCache);\n return () => {\n window.removeEventListener('preview:manualRender', onManual);\n window.removeEventListener('preview:showCacheMetrics', onShowCache);\n };\n }, [handleManualRender]);\n\n const localHeaderVisible =\n (useSettingsStore as any)?.getState?.().useGlobalPreviewHeader === false;\n\n return (\n <>\n <div className=\"flex h-full flex-col\">\n {localHeaderVisible && (\n <PreviewHeaderMemoized\n iframeRef={iframeRef}\n displayReloadButton={\n Boolean(iframeSrc) && !(iframeSrc?.startsWith('blob:') ?? false)\n }\n name={name?.trim() || 'Preview'}\n blob={blob}\n autoReload={autoReload}\n onToggleAutoReload={() => setSettings({ autoReload: !autoReload })}\n onManualRender={handleManualRender}\n isGenerating={isGenerating}\n isRendering={isRendering}\n onShowCacheMetrics={() => setShowCacheMetrics(true)}\n documentText={text}\n warnings={warnings}\n renderingLibrary={renderingLibrary}\n setRenderingLibrary={(lib) =>\n setSettings({ renderingLibrary: lib } as any)\n }\n />\n )}\n {/* The local header draws its own `border-b`; a standalone separator\n underneath it stacked two hairlines. */}\n {!localHeaderVisible && <Separator />}\n {FORMAT === 'docx' && renderingLibrary === 'docxjs' && (\n <div className=\"px-3 py-1.5 flex items-center gap-2 border-b bg-data-blue/10 text-xs text-data-blue\">\n <InfoIcon className=\"h-3.5 w-3.5 shrink-0\" />\n <span>\n The docxjs renderer is not 100% representative of the actual\n .docx. Use LibreOffice for higher fidelity.\n </span>\n </div>\n )}\n {/* Status Bar: cache + stale combined */}\n {(() => {\n const hasUnsyncedEdits =\n (editSequence ?? 0) > (lastBuiltSequence ?? 0);\n const isStale =\n (isPreviewStale || hasUnsyncedEdits) &&\n !isGenerating &&\n !isRendering;\n return (\n ((cacheStatus && cacheStatus !== 'UNKNOWN') || isStale) && (\n <div\n className={`px-3 py-1.5 flex items-center justify-between border-b overflow-hidden ${\n isStale ? 'bg-warning/10' : 'bg-header-bg'\n }`}\n >\n <div className=\"flex items-center gap-2 min-w-0 truncate\">\n {cacheStatus === 'HIT' && !isStale ? (\n <>\n <div className=\"h-1.5 w-1.5 rounded-full bg-success flex-shrink-0\" />\n <span className=\"text-xs text-muted-foreground truncate\">\n Cached\n </span>\n </>\n ) : cacheStatus === 'MISS' && !isStale ? (\n <>\n <div className=\"h-1.5 w-1.5 rounded-full bg-accent2 flex-shrink-0\" />\n <span className=\"text-xs text-muted-foreground truncate\">\n Fresh {FORMAT_LABEL.toLowerCase()}\n </span>\n </>\n ) : isStale ? (\n <>\n <div className=\"h-1.5 w-1.5 rounded-full bg-warning flex-shrink-0\" />\n <span className=\"text-xs text-warning truncate\">\n Outdated — click Run\n </span>\n {editTimestamp &&\n hasUnsyncedEdits &&\n !isGenerating &&\n !hasValidationErrors &&\n autoReload &&\n renderingLibrary === 'docxjs' && (\n <DebounceCountdown\n editTimestamp={editTimestamp}\n debounceMs={saveDocumentDebounceWait + 200}\n />\n )}\n </>\n ) : null}\n </div>\n {!isStale && cacheHitRate && cacheHitRate !== '0.0%' && (\n <span className=\"text-xs text-muted-foreground flex-shrink-0 ml-2\">\n {cacheHitRate} hit rate\n </span>\n )}\n </div>\n )\n );\n })()}\n {/* Warnings Panel */}\n <WarningsPanel warnings={warnings} className=\"mx-3 my-2\" />\n <UnavailableThemeWarning className=\"mx-3 my-2\" />\n {/* Generation Error — centered in preview area */}\n {globalError && !isGenerating ? (\n <div className=\"flex-1 flex items-center justify-center p-6\">\n <div className=\"max-w-md rounded-sm border-l-4 border-l-destructive bg-destructive/10 px-4 py-3 text-sm text-destructive\">\n <p className=\"font-medium mb-1\">Generation failed</p>\n <p className=\"text-xs text-destructive/80 break-words\">\n {globalError}\n </p>\n </div>\n </div>\n ) : (\n <PreviewFrameMemoized\n ref={iframeRef}\n isLoading={Boolean(isRendering)}\n iframeSrc={iframeSrc}\n iframeSrcDoc={iframeSrcDoc}\n isGenerating={(() => {\n const willAutoBuild = autoReload && renderingLibrary === 'docxjs';\n const isSwitchingDoc =\n activeTab &&\n name &&\n activeTab !== name &&\n documentTypes[activeTab] !== 'application/json+theme';\n return (\n Boolean(isGenerating) ||\n Boolean(willAutoBuild && isSwitchingDoc)\n );\n })()}\n generationProgress={(() => {\n const willAutoBuild = autoReload && renderingLibrary === 'docxjs';\n const isSwitchingDoc =\n activeTab &&\n name &&\n activeTab !== name &&\n documentTypes[activeTab] !== 'application/json+theme';\n return willAutoBuild && isSwitchingDoc && !isGenerating\n ? {\n stage: 'parsing' as const,\n message: `Building preview for ${activeTab}...`,\n }\n : generationProgress;\n })()}\n />\n )}\n </div>\n\n {/* Cache Metrics Dialog */}\n <Dialog open={showCacheMetrics} onOpenChange={setShowCacheMetrics}>\n <DialogContent className=\"max-w-2xl\">\n <DialogHeader>\n <DialogTitle>Cache Performance</DialogTitle>\n </DialogHeader>\n <CacheMetrics />\n </DialogContent>\n </Dialog>\n </>\n );\n}\n"],"file":"assets/preview-BOF6SWhK.js"}
@@ -9,7 +9,7 @@
9
9
  <link rel="preconnect" href="https://fonts.googleapis.com" />
10
10
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
11
11
  <title>JSON to Office</title>
12
- <script type="module" crossorigin src="/assets/index-CHAIoQtG.js"></script>
12
+ <script type="module" crossorigin src="/assets/index-Cxjik2Rg.js"></script>
13
13
  <link rel="modulepreload" crossorigin href="/assets/react-vendor-CTFDOf2G.js">
14
14
  <link rel="modulepreload" crossorigin href="/assets/query-vendor-Df8-oete.js">
15
15
  <link rel="modulepreload" crossorigin href="/assets/radix-ui-Bq-7y_Sq.js">
@@ -198,7 +198,7 @@
198
198
  {
199
199
  "name": "image",
200
200
  "props": {
201
- "path": "packages/jto/src/client/public/templates/media/management-plan/crop_s1.jpg",
201
+ "path": "media/management-plan/crop_s1.jpg",
202
202
  "x": 6.138,
203
203
  "y": 0,
204
204
  "w": 3.862,
@@ -799,7 +799,7 @@
799
799
  {
800
800
  "name": "image",
801
801
  "props": {
802
- "path": "packages/jto/src/client/public/templates/media/management-plan/crop_s3.jpg",
802
+ "path": "media/management-plan/crop_s3.jpg",
803
803
  "x": 5.611,
804
804
  "y": 0,
805
805
  "w": 4.389,
@@ -1360,7 +1360,7 @@
1360
1360
  {
1361
1361
  "name": "image",
1362
1362
  "props": {
1363
- "path": "packages/jto/src/client/public/templates/media/management-plan/crop_s5.jpg",
1363
+ "path": "media/management-plan/crop_s5.jpg",
1364
1364
  "x": 5.0,
1365
1365
  "y": 3.055,
1366
1366
  "w": 1.892,
@@ -2314,7 +2314,7 @@
2314
2314
  {
2315
2315
  "name": "image",
2316
2316
  "props": {
2317
- "path": "packages/jto/src/client/public/templates/media/management-plan/crop_s9.jpg",
2317
+ "path": "media/management-plan/crop_s9.jpg",
2318
2318
  "x": 5.0,
2319
2319
  "y": 0.532,
2320
2320
  "w": 4.5,
@@ -2662,7 +2662,7 @@
2662
2662
  {
2663
2663
  "name": "image",
2664
2664
  "props": {
2665
- "path": "packages/jto/src/client/public/templates/media/management-plan/crop_s10.jpg",
2665
+ "path": "media/management-plan/crop_s10.jpg",
2666
2666
  "x": 1.595,
2667
2667
  "y": 0.565,
2668
2668
  "w": 3.767,
@@ -2672,7 +2672,7 @@
2672
2672
  {
2673
2673
  "name": "image",
2674
2674
  "props": {
2675
- "path": "packages/jto/src/client/public/templates/media/management-plan/crop_s10.jpg",
2675
+ "path": "media/management-plan/crop_s10.jpg",
2676
2676
  "x": 1.595,
2677
2677
  "y": 3.051,
2678
2678
  "w": 3.767,
@@ -2933,7 +2933,7 @@
2933
2933
  {
2934
2934
  "name": "image",
2935
2935
  "props": {
2936
- "path": "packages/jto/src/client/public/templates/media/management-plan/crop_s11.jpg",
2936
+ "path": "media/management-plan/crop_s11.jpg",
2937
2937
  "x": 6.123,
2938
2938
  "y": 0,
2939
2939
  "w": 3.877,
@@ -3227,7 +3227,7 @@
3227
3227
  {
3228
3228
  "name": "image",
3229
3229
  "props": {
3230
- "path": "packages/jto/src/client/public/templates/media/management-plan/crop_s12.jpg",
3230
+ "path": "media/management-plan/crop_s12.jpg",
3231
3231
  "x": 5.0,
3232
3232
  "y": 0.565,
3233
3233
  "w": 2.282,
@@ -3609,7 +3609,7 @@
3609
3609
  {
3610
3610
  "name": "image",
3611
3611
  "props": {
3612
- "path": "packages/jto/src/client/public/templates/media/management-plan/crop_s13.jpg",
3612
+ "path": "media/management-plan/crop_s13.jpg",
3613
3613
  "x": 0.6,
3614
3614
  "y": 1.877,
3615
3615
  "w": 2.039,
@@ -3619,7 +3619,7 @@
3619
3619
  {
3620
3620
  "name": "image",
3621
3621
  "props": {
3622
- "path": "packages/jto/src/client/public/templates/media/management-plan/crop_s13.jpg",
3622
+ "path": "media/management-plan/crop_s13.jpg",
3623
3623
  "x": 2.85,
3624
3624
  "y": 1.877,
3625
3625
  "w": 2.039,
@@ -3629,7 +3629,7 @@
3629
3629
  {
3630
3630
  "name": "image",
3631
3631
  "props": {
3632
- "path": "packages/jto/src/client/public/templates/media/management-plan/crop_s13.jpg",
3632
+ "path": "media/management-plan/crop_s13.jpg",
3633
3633
  "x": 5.1,
3634
3634
  "y": 1.877,
3635
3635
  "w": 2.039,
@@ -3639,7 +3639,7 @@
3639
3639
  {
3640
3640
  "name": "image",
3641
3641
  "props": {
3642
- "path": "packages/jto/src/client/public/templates/media/management-plan/crop_s13.jpg",
3642
+ "path": "media/management-plan/crop_s13.jpg",
3643
3643
  "x": 7.361,
3644
3644
  "y": 1.877,
3645
3645
  "w": 2.039,
@@ -3827,7 +3827,7 @@
3827
3827
  {
3828
3828
  "name": "image",
3829
3829
  "props": {
3830
- "path": "packages/jto/src/client/public/templates/media/management-plan/crop_s14.jpg",
3830
+ "path": "media/management-plan/crop_s14.jpg",
3831
3831
  "x": 5.43,
3832
3832
  "y": 0.55,
3833
3833
  "w": 4.07,
@@ -4056,7 +4056,7 @@
4056
4056
  {
4057
4057
  "name": "image",
4058
4058
  "props": {
4059
- "path": "packages/jto/src/client/public/templates/media/management-plan/crop_s15.jpg",
4059
+ "path": "media/management-plan/crop_s15.jpg",
4060
4060
  "x": 0,
4061
4061
  "y": 2.812,
4062
4062
  "w": 6.142,
@@ -4500,7 +4500,7 @@
4500
4500
  {
4501
4501
  "name": "image",
4502
4502
  "props": {
4503
- "path": "packages/jto/src/client/public/templates/media/management-plan/crop_s16.jpg",
4503
+ "path": "media/management-plan/crop_s16.jpg",
4504
4504
  "x": 0,
4505
4505
  "y": 0,
4506
4506
  "w": 5.0,
@@ -5040,7 +5040,7 @@
5040
5040
  {
5041
5041
  "name": "image",
5042
5042
  "props": {
5043
- "path": "packages/jto/src/client/public/templates/media/management-plan/crop_s18a.jpg",
5043
+ "path": "media/management-plan/crop_s18a.jpg",
5044
5044
  "x": 0.499,
5045
5045
  "y": 1.125,
5046
5046
  "w": 2.007,
@@ -5050,7 +5050,7 @@
5050
5050
  {
5051
5051
  "name": "image",
5052
5052
  "props": {
5053
- "path": "packages/jto/src/client/public/templates/media/management-plan/crop_s18b.jpg",
5053
+ "path": "media/management-plan/crop_s18b.jpg",
5054
5054
  "x": 3.09,
5055
5055
  "y": 1.125,
5056
5056
  "w": 2.087,
@@ -5633,7 +5633,7 @@
5633
5633
  {
5634
5634
  "name": "image",
5635
5635
  "props": {
5636
- "path": "packages/jto/src/client/public/templates/media/management-plan/crop_s19.jpg",
5636
+ "path": "media/management-plan/crop_s19.jpg",
5637
5637
  "x": 5.0,
5638
5638
  "y": 3.414,
5639
5639
  "w": 5.0,
@@ -500,7 +500,7 @@
500
500
  {
501
501
  "name": "image",
502
502
  "props": {
503
- "path": "packages/jto/src/client/public/templates/media/minimalist-pitch-deck/image1.jpg",
503
+ "path": "media/minimalist-pitch-deck/image1.jpg",
504
504
  "x": 0.5,
505
505
  "y": 3.212,
506
506
  "w": 3.399,
@@ -657,7 +657,7 @@
657
657
  {
658
658
  "name": "image",
659
659
  "props": {
660
- "path": "packages/jto/src/client/public/templates/media/minimalist-pitch-deck/image1.jpg",
660
+ "path": "media/minimalist-pitch-deck/image1.jpg",
661
661
  "x": 6.047,
662
662
  "y": 0.761,
663
663
  "w": 1.525,
@@ -672,7 +672,7 @@
672
672
  {
673
673
  "name": "image",
674
674
  "props": {
675
- "path": "packages/jto/src/client/public/templates/media/minimalist-pitch-deck/image1.jpg",
675
+ "path": "media/minimalist-pitch-deck/image1.jpg",
676
676
  "x": 7.975,
677
677
  "y": 0.761,
678
678
  "w": 1.525,
@@ -1297,7 +1297,7 @@
1297
1297
  {
1298
1298
  "name": "image",
1299
1299
  "props": {
1300
- "path": "packages/jto/src/client/public/templates/media/minimalist-pitch-deck/image1.jpg",
1300
+ "path": "media/minimalist-pitch-deck/image1.jpg",
1301
1301
  "x": 0.833,
1302
1302
  "y": 0.741,
1303
1303
  "w": 1.992,
@@ -1312,7 +1312,7 @@
1312
1312
  {
1313
1313
  "name": "image",
1314
1314
  "props": {
1315
- "path": "packages/jto/src/client/public/templates/media/minimalist-pitch-deck/image3.png",
1315
+ "path": "media/minimalist-pitch-deck/image3.png",
1316
1316
  "x": 0.683,
1317
1317
  "y": 0.65,
1318
1318
  "w": 2.317,
@@ -1433,7 +1433,7 @@
1433
1433
  {
1434
1434
  "name": "image",
1435
1435
  "props": {
1436
- "path": "packages/jto/src/client/public/templates/media/minimalist-pitch-deck/image1.jpg",
1436
+ "path": "media/minimalist-pitch-deck/image1.jpg",
1437
1437
  "x": 6.987,
1438
1438
  "y": 0.0,
1439
1439
  "w": 3.013,
@@ -1566,7 +1566,7 @@
1566
1566
  {
1567
1567
  "name": "image",
1568
1568
  "props": {
1569
- "path": "packages/jto/src/client/public/templates/media/minimalist-pitch-deck/image1.jpg",
1569
+ "path": "media/minimalist-pitch-deck/image1.jpg",
1570
1570
  "x": 0.5,
1571
1571
  "y": 2.812,
1572
1572
  "w": 2.671,
@@ -1581,7 +1581,7 @@
1581
1581
  {
1582
1582
  "name": "image",
1583
1583
  "props": {
1584
- "path": "packages/jto/src/client/public/templates/media/minimalist-pitch-deck/image1.jpg",
1584
+ "path": "media/minimalist-pitch-deck/image1.jpg",
1585
1585
  "x": 3.665,
1586
1586
  "y": 2.812,
1587
1587
  "w": 2.671,
@@ -1596,7 +1596,7 @@
1596
1596
  {
1597
1597
  "name": "image",
1598
1598
  "props": {
1599
- "path": "packages/jto/src/client/public/templates/media/minimalist-pitch-deck/image1.jpg",
1599
+ "path": "media/minimalist-pitch-deck/image1.jpg",
1600
1600
  "x": 6.829,
1601
1601
  "y": 2.812,
1602
1602
  "w": 2.671,
@@ -2324,7 +2324,7 @@
2324
2324
  {
2325
2325
  "name": "image",
2326
2326
  "props": {
2327
- "path": "packages/jto/src/client/public/templates/media/minimalist-pitch-deck/image1.jpg",
2327
+ "path": "media/minimalist-pitch-deck/image1.jpg",
2328
2328
  "x": 0.0,
2329
2329
  "y": 0.0,
2330
2330
  "w": 2.5,
@@ -2339,7 +2339,7 @@
2339
2339
  {
2340
2340
  "name": "image",
2341
2341
  "props": {
2342
- "path": "packages/jto/src/client/public/templates/media/minimalist-pitch-deck/image1.jpg",
2342
+ "path": "media/minimalist-pitch-deck/image1.jpg",
2343
2343
  "x": 2.5,
2344
2344
  "y": 0.0,
2345
2345
  "w": 2.5,
@@ -2510,7 +2510,7 @@
2510
2510
  {
2511
2511
  "name": "image",
2512
2512
  "props": {
2513
- "path": "packages/jto/src/client/public/templates/media/minimalist-pitch-deck/image1.jpg",
2513
+ "path": "media/minimalist-pitch-deck/image1.jpg",
2514
2514
  "x": 0.5,
2515
2515
  "y": 0.741,
2516
2516
  "w": 2.5,
@@ -2525,7 +2525,7 @@
2525
2525
  {
2526
2526
  "name": "image",
2527
2527
  "props": {
2528
- "path": "packages/jto/src/client/public/templates/media/minimalist-pitch-deck/image1.jpg",
2528
+ "path": "media/minimalist-pitch-deck/image1.jpg",
2529
2529
  "x": 3.388,
2530
2530
  "y": 0.741,
2531
2531
  "w": 1.612,
@@ -2703,7 +2703,7 @@
2703
2703
  {
2704
2704
  "name": "image",
2705
2705
  "props": {
2706
- "path": "packages/jto/src/client/public/templates/media/minimalist-pitch-deck/image1.jpg",
2706
+ "path": "media/minimalist-pitch-deck/image1.jpg",
2707
2707
  "x": 5.559,
2708
2708
  "y": 2.867,
2709
2709
  "w": 3.895,
@@ -2822,7 +2822,7 @@
2822
2822
  {
2823
2823
  "name": "image",
2824
2824
  "props": {
2825
- "path": "packages/jto/src/client/public/templates/media/minimalist-pitch-deck/image1.jpg",
2825
+ "path": "media/minimalist-pitch-deck/image1.jpg",
2826
2826
  "x": 0.5,
2827
2827
  "y": 0.869,
2828
2828
  "w": 2.46,
@@ -3391,7 +3391,7 @@
3391
3391
  {
3392
3392
  "name": "image",
3393
3393
  "props": {
3394
- "path": "packages/jto/src/client/public/templates/media/minimalist-pitch-deck/image1.jpg",
3394
+ "path": "media/minimalist-pitch-deck/image1.jpg",
3395
3395
  "x": 2.939,
3396
3396
  "y": 0.869,
3397
3397
  "w": 2.483,
@@ -3406,7 +3406,7 @@
3406
3406
  {
3407
3407
  "name": "image",
3408
3408
  "props": {
3409
- "path": "packages/jto/src/client/public/templates/media/minimalist-pitch-deck/image1.jpg",
3409
+ "path": "media/minimalist-pitch-deck/image1.jpg",
3410
3410
  "x": 5.992,
3411
3411
  "y": 0.869,
3412
3412
  "w": 1.433,
@@ -3421,7 +3421,7 @@
3421
3421
  {
3422
3422
  "name": "image",
3423
3423
  "props": {
3424
- "path": "packages/jto/src/client/public/templates/media/minimalist-pitch-deck/image1.jpg",
3424
+ "path": "media/minimalist-pitch-deck/image1.jpg",
3425
3425
  "x": 7.825,
3426
3426
  "y": 0.869,
3427
3427
  "w": 1.433,
@@ -3584,7 +3584,7 @@
3584
3584
  {
3585
3585
  "name": "image",
3586
3586
  "props": {
3587
- "path": "packages/jto/src/client/public/templates/media/minimalist-pitch-deck/image1.jpg",
3587
+ "path": "media/minimalist-pitch-deck/image1.jpg",
3588
3588
  "x": 0.5,
3589
3589
  "y": 3.253,
3590
3590
  "w": 3.275,