@json-to-office/jto 0.2.1 → 0.3.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 (27) hide show
  1. package/dist/cli.js +54 -17
  2. package/dist/cli.js.map +1 -1
  3. package/dist/client/assets/{HomePage-DX_EbLqY.js → HomePage-muPAe_M2.js} +3 -3
  4. package/dist/client/assets/{HomePage-DX_EbLqY.js.map → HomePage-muPAe_M2.js.map} +1 -1
  5. package/dist/client/assets/{JsonEditorPage-C8a43kG2.js → JsonEditorPage-DKor0I6f.js} +3 -3
  6. package/dist/client/assets/{JsonEditorPage-C8a43kG2.js.map → JsonEditorPage-DKor0I6f.js.map} +1 -1
  7. package/dist/client/assets/{MonacoPluginProvider-C_k1zpkx.js → MonacoPluginProvider-BGYshg9R.js} +3 -3
  8. package/dist/client/assets/MonacoPluginProvider-BGYshg9R.js.map +1 -0
  9. package/dist/client/assets/{button-DLt0WPRa.js → button-BzUKH2aN.js} +2 -2
  10. package/dist/client/assets/{button-DLt0WPRa.js.map → button-BzUKH2aN.js.map} +1 -1
  11. package/dist/client/assets/{editor-LsSIrw3S.js → editor-BS2qlF-X.js} +8 -8
  12. package/dist/client/assets/editor-BS2qlF-X.js.map +1 -0
  13. package/dist/client/assets/editor-monaco-json-DKrX3IiN.js +6 -0
  14. package/dist/client/assets/{editor-monaco-json-CTzuM8w2.js.map → editor-monaco-json-DKrX3IiN.js.map} +1 -1
  15. package/dist/client/assets/index-D_aKAAFg.js +3 -0
  16. package/dist/client/assets/index-D_aKAAFg.js.map +1 -0
  17. package/dist/client/assets/preview-AJZwEEO9.js +3 -0
  18. package/dist/client/assets/preview-AJZwEEO9.js.map +1 -0
  19. package/dist/client/index.html +1 -1
  20. package/package.json +5 -5
  21. package/dist/client/assets/MonacoPluginProvider-C_k1zpkx.js.map +0 -1
  22. package/dist/client/assets/editor-LsSIrw3S.js.map +0 -1
  23. package/dist/client/assets/editor-monaco-json-CTzuM8w2.js +0 -6
  24. package/dist/client/assets/index-CslfgN2g.js +0 -3
  25. package/dist/client/assets/index-CslfgN2g.js.map +0 -1
  26. package/dist/client/assets/preview-CeZ8u4Ec.js +0 -3
  27. package/dist/client/assets/preview-CeZ8u4Ec.js.map +0 -1
@@ -0,0 +1,3 @@
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/docx-preview-DkKCgwb-.js","assets/react-vendor-B_QXlk-T.js","assets/radix-ui-DkKm4m5M.js","assets/query-vendor-CT0SoqJh.js"])))=>i.map(i=>d[i]);
2
+ import{j as e}from"./query-vendor-CT0SoqJh.js";import{a as L,r as d,ar as H,as as ae}from"./react-vendor-B_QXlk-T.js";import{S as F,c as S,g as W,F as C,_ as ne,j as ie,A as q}from"./index-D_aKAAFg.js";import{S as E,B as oe,P as le,b as ce,D as de,c as me,d as ue,e as xe}from"./HomePage-muPAe_M2.js";import{e as fe,T as $,I as he}from"./ui-vendor-ChWVh3T-.js";import{d as B,b as pe,u as G}from"./MonacoPluginProvider-BGYshg9R.js";import"./radix-ui-DkKm4m5M.js";import"./monaco-editor-B-NlkJ5A.js";import"./button-BzUKH2aN.js";import"./state-vendor-Az8redj0.js";function ge({className:r,currentStage:t="parsing",message:a}){const n=["parsing","building","rendering","finalizing"],s={parsing:"Validating JSON",building:"Building structure",rendering:"Rendering content",finalizing:"Finalizing document"},l=n.indexOf(t);return e.jsxs("div",{className:S("p-6 space-y-4",r),children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx(F,{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:a||`${s[t]}...`})]})]}),e.jsx("div",{className:"space-y-3",children:n.map((i,o)=>{const c=o===l,u=o<l;return e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"flex justify-between text-xs",children:[e.jsx("span",{className:S(c?"text-primary font-medium":u?"text-muted-foreground":"text-muted-foreground/60"),children:s[i]}),e.jsx("span",{className:"text-muted-foreground",children:u?"✓":c?"...":"○"})]}),e.jsx("div",{className:"w-full bg-secondary rounded-full h-1.5",children:e.jsx("div",{className:S("h-1.5 rounded-full transition-all duration-300",u?"bg-green-500":c?"bg-primary animate-pulse":"bg-transparent"),style:{width:u||c?"100%":"0%"}})})]},i)})})]})}function be({renderingLibrary:r,className:t}){const a=l=>{switch(l){case"LibreOffice":return{name:"LibreOffice",description:"Converting PPTX 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:n,description:s}=a(r);return e.jsxs("div",{className:S("flex flex-col items-center justify-center h-full p-8 space-y-4",t),children:[e.jsx(F,{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 ",n," renderer"]}),e.jsx("p",{className:"text-xs text-muted-foreground/80",children:s})]}),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 J=L.forwardRef(({isLoading:r,iframeSrc:t,iframeSrcDoc:a,isGenerating:n,generationProgress:s},l)=>{const[i,o]=d.useState(!1);d.useEffect(()=>{o(!1)},[t,a]);const c=()=>{o(!0)},u=()=>{console.warn("Iframe failed to load content"),o(!1)},g=a?"allow-scripts allow-popups allow-forms":void 0,h=!!(t||a);return n&&!h?e.jsx("div",{className:"grow",children:e.jsx(ge,{currentStage:s==null?void 0:s.stage,message:s==null?void 0:s.message})}):r&&!h?e.jsx("div",{className:"grow",children:e.jsx(be,{renderingLibrary:"LibreOffice"})}):h?e.jsxs("div",{className:"h-full w-full relative bg-white",children:[!i&&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..."})]})}),(n||r)&&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(F,{size:"lg"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:n?"Generating...":"Rendering..."})]})}),e.jsx("iframe",{ref:l,className:"w-full h-full border-0",style:{backgroundColor:"white",opacity:i?1:0,transition:"opacity 0.2s ease-in-out"},src:t,srcDoc:a,onLoad:c,onError:u,sandbox:g,title:"Document preview","aria-label":`Preview of rendered ${W.toLowerCase()}`},a?"srcdoc":"src")]}):null});J.displayName="PreviewFrame";const je=L.memo(J);function we({warnings:r,className:t}){const[a,n]=L.useState(!1);return!r||r.length===0?null:e.jsxs("div",{className:t,children:[e.jsxs("button",{type:"button",onClick:()=>n(!a),className:"flex w-full items-center gap-2 rounded-md border border-amber-200 dark:border-amber-800 bg-amber-50 dark:bg-amber-950/50 px-3 py-1.5 text-left transition-colors hover:bg-amber-100 dark:hover:bg-amber-900/50",children:[e.jsx(fe,{className:`h-3.5 w-3.5 text-amber-600 dark:text-amber-400 transition-transform flex-shrink-0 ${a?"rotate-90":""}`}),e.jsx($,{className:"h-3.5 w-3.5 text-amber-500 flex-shrink-0"}),e.jsxs("span",{className:"text-xs font-medium text-amber-700 dark:text-amber-300",children:[r.length," warning",r.length!==1?"s":""]})]}),a&&e.jsx("div",{className:"mt-1.5 space-y-1.5",children:r.map((s,l)=>e.jsxs("div",{className:"flex items-start gap-2 rounded-md border border-amber-100 dark:border-amber-900 bg-white dark:bg-amber-950/30 px-3 py-2",children:[s.severity==="info"?e.jsx(he,{className:"h-3.5 w-3.5 mt-0.5 text-blue-500 flex-shrink-0"}):e.jsx($,{className:"h-3.5 w-3.5 mt-0.5 text-amber-500 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-amber-700 dark:text-amber-300 bg-amber-50 dark:bg-amber-900/40 px-1 py-0.5 rounded",children:s.component}),s.severity==="info"&&e.jsx("span",{className:"text-[10px] uppercase tracking-wide text-blue-500 font-medium",children:"info"})]}),e.jsx("p",{className:"mt-0.5 text-xs text-gray-600 dark:text-gray-400 leading-relaxed",children:s.message}),s.context&&Object.keys(s.context).length>0&&e.jsxs("details",{className:"mt-1.5",children:[e.jsx("summary",{className:"text-[11px] text-gray-400 dark:text-gray-500 cursor-pointer hover:text-gray-600 dark:hover:text-gray-300",children:"Context"}),e.jsx("pre",{className:"mt-1 text-[11px] bg-gray-50 dark:bg-gray-900/50 p-1.5 rounded overflow-x-auto text-gray-600 dark:text-gray-400",children:JSON.stringify(s.context,null,2)})]})]})]},l))})]})}async function ve(r){const t=await ne(()=>import("./docx-preview-DkKCgwb-.js"),__vite__mapDeps([0,1,2,3])),a=document.createElement("body"),n=document.createElement("head"),s=t.renderAsync(r,a,n,{inWrapper:!0}),l=new Promise((c,u)=>{setTimeout(()=>{u(new Error("DocxJS renderAsync timed out after 10 seconds"))},1e4)});await Promise.race([s,l]);const i=document.createElement("link");i.rel="stylesheet",i.href=`${ie.basePath}/css/preview/docxjs.css`,n.appendChild(i);const o=document.createElement("html");return o.appendChild(n),o.appendChild(a),{iframeSrcDoc:o.outerHTML}}async function Ne(r,t){const a=new FormData,n=C==="pptx"?".pptx":".docx";a.append("file",t,`${r}${n}`);const s=await fetch(q.preview.libreoffice,{method:"POST",body:a});if(!s.ok){let o=`LibreOffice preview failed (${s.status})`;try{const c=await s.json();c&&typeof c=="object"&&typeof c.error=="string"&&(o=c.error)}catch{}throw new Error(o)}const l=await s.blob();if(!l.size)throw new Error("LibreOffice preview returned an empty PDF");const i=URL.createObjectURL(l);return{iframeSrc:i,cleanup:()=>URL.revokeObjectURL(i)}}async function ye(r,t,a=C==="docx"?"docxjs":"LibreOffice",n){try{if(!t||t.size===0)throw new Error("Invalid or empty document blob");let s;return C==="docx"&&a==="docxjs"?s=await ve(t):s=await Ne(r,t),{status:"success",name:r,payload:s}}catch(s){const l=s instanceof Error?s.message:String(s);return{status:"error",name:r,payload:new Error(l)}}}const I=d.forwardRef(({className:r,value:t,...a},n)=>e.jsx(H,{ref:n,className:S("relative h-4 w-full overflow-hidden rounded-full bg-secondary",r),...a,children:e.jsx(ae,{className:"h-full w-full flex-1 bg-primary transition-all",style:{transform:`translateX(-${100-(t||0)}%)`}})}));I.displayName=H.displayName;function V(r){if(r===0)return"0 B";const t=1024,a=["B","KB","MB","GB"],n=Math.floor(Math.log(r)/Math.log(t));return parseFloat((r/Math.pow(t,n)).toFixed(2))+" "+a[n]}function Ee(){const[r,t]=d.useState(null),[a,n]=d.useState(!0),[s,l]=d.useState(null);if(d.useEffect(()=>{const c=new AbortController,u=async()=>{try{const f=await fetch(q.cacheStats,{signal:c.signal});if(!f.ok)throw new Error("Failed to fetch cache stats");const w=await f.json();c.signal.aborted||(t(w.data),l(null))}catch(f){c.signal.aborted||(f instanceof Error&&f.name!=="AbortError"?l(f.message):(!(f instanceof Error)||f.name!=="AbortError")&&l("Failed to load cache stats"))}finally{c.signal.aborted||n(!1)}};u();const g=setInterval(u,5e3),h=()=>u();return window.addEventListener("cache:cleared",h),()=>{c.abort(),clearInterval(g),window.removeEventListener("cache:cleared",h)}},[]),a)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(s)return e.jsxs("div",{className:"text-destructive p-4",children:["Error loading cache metrics: ",s]});if(!r)return null;const{document:i}=r,o=i.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:[o.toFixed(1),"%"]}),e.jsxs("span",{className:"text-muted-foreground ml-1",children:["(",i.hits," hits / ",i.misses," misses)"]})]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-muted-foreground",children:"Size"})," ",e.jsx("span",{className:"font-semibold",children:V(i.size)}),e.jsxs("span",{className:"text-muted-foreground ml-1",children:["(",i.itemCount," items)"]})]}),e.jsx(oe,{variant:i.enabled?"default":"secondary",children:i.enabled?"Enabled":"Disabled"})]}),e.jsx(I,{value:o,className:"h-2","aria-label":"Cache hit rate"}),C==="docx"&&r.components&&e.jsx(Se,{data:r.components})]})}function Se({data:r}){return r.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:r.componentStats.map(t=>{const a=(t.hitRate||0)*100,n=t.totalRequests||t.hits+t.misses,s=t.memoryUsage||t.entries*t.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:t.type}),e.jsxs("div",{className:"flex items-center gap-2 text-xs text-muted-foreground",children:[e.jsxs("span",{children:[a.toFixed(0),"% hit"]}),e.jsxs("span",{children:[n," req"]}),e.jsx("span",{children:V(s)})]})]}),e.jsx(I,{value:a,className:"h-1.5"})]},t.type)})})]})}const Re=d.memo(function({editTimestamp:t,debounceMs:a}){const[n,s]=d.useState(()=>Math.max(0,t+a-Date.now()));if(d.useEffect(()=>{let o;const c=()=>{const u=Math.max(0,t+a-Date.now());s(u),u<=0&&o!=null&&clearInterval(o)};return c(),o=setInterval(c,100),()=>{o!=null&&clearInterval(o)}},[t,a]),n<=0)return null;const l=(n/1e3).toFixed(1),i=Math.min(100,n/a*100);return e.jsxs("span",{className:"inline-flex items-center gap-1.5 text-xs text-amber-600 dark:text-amber-400 tabular-nums",children:[e.jsx("span",{className:"h-1 rounded-full bg-amber-500/60 transition-[width] duration-100",style:{width:`${Math.round(i*.4)}px`}}),l,"s"]})});function Te(){var U,_;const{autoReload:r,renderingLibrary:t,saveDocumentDebounceWait:a,setSettings:n}=B(m=>m),{name:s,text:l,blob:i,isGenerating:o,generationProgress:c,globalError:u,cacheStatus:g,cacheHitRate:h,warnings:f,isRendering:w,isPreviewStale:K,editSequence:X,lastBuiltSequence:Q,editTimestamp:T,hasValidationErrors:Y,setOutput:j}=pe(m=>m),b=G(m=>m.activeTab),A=G(m=>m.documentTypes),[v,Z]=d.useState(void 0),[ee,se]=d.useState(void 0),[te,D]=d.useState(!1),z=L.useRef(null),R=d.useRef(null),M=d.useRef(!1),k=d.useCallback(()=>{R.current&&(R.current(),R.current=null)},[]),N=d.useCallback(async(m,x)=>{j({isRendering:!0,isPreviewStale:!1,globalError:void 0});try{const{status:y,payload:p}=await ye(m,x,t);if(y!=="success")throw p instanceof Error?p:new Error(String(p));k(),Z(p.iframeSrc||void 0),se(p.iframeSrcDoc||void 0),R.current=p.cleanup||null}catch(y){const p=y instanceof Error?y.message:String(y);let O=p;p.includes("Failed to fetch")?O="Network error: Unable to load required resources. Check if the server is running properly.":p.includes("timeout")&&(O="Render timeout: The rendering process took too long."),j({globalError:O})}finally{j({isRendering:!1})}},[j,k,t]),re=d.useRef({name:s,blob:i,doRender:N});d.useEffect(()=>{re.current={name:s,blob:i,doRender:N}});const P=d.useCallback(async()=>{M.current=!0,window.dispatchEvent(new CustomEvent("preview:flushAndBuild"))},[]);return d.useEffect(()=>{i&&s&&r&&t==="docxjs"?N(s,i):i&&s&&M.current?(M.current=!1,N(s,i)):i&&s&&j({isPreviewStale:!0})},[i,s,r,t,N,j]),d.useEffect(()=>()=>{k()},[k]),d.useEffect(()=>{const m=()=>P(),x=()=>D(!0);return window.addEventListener("preview:manualRender",m),window.addEventListener("preview:showCacheMetrics",x),()=>{window.removeEventListener("preview:manualRender",m),window.removeEventListener("preview:showCacheMetrics",x)}},[P]),e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex h-full flex-col",children:[B&&((_=(U=B).getState)==null?void 0:_.call(U).useGlobalPreviewHeader)===!1&&e.jsx(le,{iframeRef:z,displayReloadButton:!!v&&!((v==null?void 0:v.startsWith("blob:"))??!1),name:(s==null?void 0:s.trim())||"Preview",blob:i,autoReload:r,onToggleAutoReload:()=>n({autoReload:!r}),onManualRender:P,isGenerating:o,isRendering:w,onShowCacheMetrics:()=>D(!0),documentText:l,warnings:f,renderingLibrary:t,setRenderingLibrary:m=>n({renderingLibrary:m})}),e.jsx(ce,{}),(()=>{const m=(X??0)>(Q??0),x=(K||m)&&!o&&!w;return(g&&g!=="UNKNOWN"||x)&&e.jsxs("div",{className:`px-3 py-1.5 flex items-center justify-between border-b overflow-hidden ${x?"bg-amber-500/10 border-amber-500/30":"bg-muted/30"}`,children:[e.jsx("div",{className:"flex items-center gap-2 min-w-0 truncate",children:g==="HIT"&&!x?e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"h-1.5 w-1.5 rounded-full bg-green-500 flex-shrink-0"}),e.jsx("span",{className:"text-xs text-muted-foreground truncate",children:"Cached"})]}):g==="MISS"&&!x?e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"h-1.5 w-1.5 rounded-full bg-orange-500 flex-shrink-0"}),e.jsxs("span",{className:"text-xs text-muted-foreground truncate",children:["Fresh ",W.toLowerCase()]})]}):x?e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"h-1.5 w-1.5 rounded-full bg-amber-500 flex-shrink-0"}),e.jsx("span",{className:"text-xs text-amber-600 dark:text-amber-400 truncate",children:"Outdated — click Run"}),T&&m&&!o&&!Y&&r&&t==="docxjs"&&e.jsx(Re,{editTimestamp:T,debounceMs:a+200})]}):null}),!x&&h&&h!=="0.0%"&&e.jsxs("span",{className:"text-xs text-muted-foreground flex-shrink-0 ml-2",children:[h," hit rate"]})]})})(),e.jsx(we,{warnings:f,className:"mx-3 my-2"}),u&&!o?e.jsx("div",{className:"flex-1 flex items-center justify-center p-6",children:e.jsxs("div",{className:"max-w-md rounded-lg border border-red-400/50 bg-red-400/10 px-4 py-3 text-sm text-red-400",children:[e.jsx("p",{className:"font-medium mb-1",children:"Generation failed"}),e.jsx("p",{className:"text-xs text-red-400/80 break-words",children:u})]})}):e.jsx(je,{ref:z,isLoading:!!w,iframeSrc:v,iframeSrcDoc:ee,isGenerating:(()=>{const m=r&&t==="docxjs",x=b&&s&&b!==s&&A[b]!=="application/json+theme";return!!o||!!(m&&x)})(),generationProgress:(()=>{const m=r&&t==="docxjs",x=b&&s&&b!==s&&A[b]!=="application/json+theme";return m&&x&&!o?{stage:"parsing",message:`Building preview for ${b}...`}:c})()})]}),e.jsx(de,{open:te,onOpenChange:D,children:e.jsxs(me,{className:"max-w-2xl",children:[e.jsx(ue,{children:e.jsx(xe,{children:"Cache Performance"})}),e.jsx(Ee,{})]})})]})}export{Te as Preview};
3
+ //# sourceMappingURL=preview-AJZwEEO9.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","renderDocxWithDocxJS","blob","docx_preview","__vitePreload","bodyEl","headEl","renderPromise","timeoutPromise","_","reject","overrideStyleEl","env","htmlEl","renderWithLibreOffice","formData","ext","FORMAT","response","API_ENDPOINTS","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","err","interval","handleCacheCleared","doc","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","activeTab","useDocumentsStore","documentTypes","setIframeSrc","setIframeSrcDoc","showCacheMetrics","setShowCacheMetrics","iframeRef","renderCleanupRef","useRef","pendingManualRenderRef","cleanupRenderedPreview","useCallback","doRender","docName","docBlob","status","displayMessage","manualRenderRef","handleManualRender","onManual","onShowCache","Fragment","_b","_a","PreviewHeaderMemoized","lib","Separator","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/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-green-500'\n : isActive\n ? 'bg-primary animate-pulse'\n : 'bg-transparent'\n )}\n style={{ width: isCompleted ? '100%' : isActive ? '100%' : '0%' }}\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 PPTX 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-lg bg-muted/50',\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-md border border-amber-200 dark:border-amber-800 bg-amber-50 dark:bg-amber-950/50 px-3 py-1.5 text-left transition-colors hover:bg-amber-100 dark:hover:bg-amber-900/50\"\n >\n <ChevronRight\n className={`h-3.5 w-3.5 text-amber-600 dark:text-amber-400 transition-transform flex-shrink-0 ${isExpanded ? 'rotate-90' : ''}`}\n />\n <AlertTriangle className=\"h-3.5 w-3.5 text-amber-500 flex-shrink-0\" />\n <span className=\"text-xs font-medium text-amber-700 dark:text-amber-300\">\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-md border border-amber-100 dark:border-amber-900 bg-white dark:bg-amber-950/30 px-3 py-2\"\n >\n {warning.severity === 'info' ? (\n <Info className=\"h-3.5 w-3.5 mt-0.5 text-blue-500 flex-shrink-0\" />\n ) : (\n <AlertTriangle className=\"h-3.5 w-3.5 mt-0.5 text-amber-500 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-amber-700 dark:text-amber-300 bg-amber-50 dark:bg-amber-900/40 px-1 py-0.5 rounded\">\n {warning.component}\n </code>\n {warning.severity === 'info' && (\n <span className=\"text-[10px] uppercase tracking-wide text-blue-500 font-medium\">\n info\n </span>\n )}\n </div>\n <p className=\"mt-0.5 text-xs text-gray-600 dark:text-gray-400 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-gray-400 dark:text-gray-500 cursor-pointer hover:text-gray-600 dark:hover:text-gray-300\">\n Context\n </summary>\n <pre className=\"mt-1 text-[11px] bg-gray-50 dark:bg-gray-900/50 p-1.5 rounded overflow-x-auto text-gray-600 dark:text-gray-400\">\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 { 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 {\n iframeSrcDoc: htmlEl.outerHTML,\n };\n}\n\nasync function renderWithLibreOffice(\n name: string,\n blob: Blob\n): Promise<RenderPayload> {\n const formData = new FormData();\n const ext = FORMAT === 'pptx' ? '.pptx' : '.docx';\n formData.append('file', blob, `${name}${ext}`);\n\n const response = await fetch(API_ENDPOINTS.preview.libreoffice, {\n method: 'POST',\n body: formData,\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) {\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);\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, { useCallback, useEffect, useRef, useState, memo } from 'react';\nimport { PreviewFrameMemoized } from './preview-frame';\nimport { PreviewHeaderMemoized } from './preview-header';\nimport { WarningsPanel } from './warnings-panel';\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 { CacheMetrics } from '../cache-metrics';\nimport { 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-amber-600 dark:text-amber-400 tabular-nums\">\n <span\n className=\"h-1 rounded-full bg-amber-500/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\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 (docName: string, docBlob: Blob) => {\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 );\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, doRender });\n useEffect(() => {\n manualRenderRef.current = { name, blob, 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);\n } else if (blob && name && pendingManualRenderRef.current) {\n pendingManualRenderRef.current = false;\n doRender(name, blob);\n } else if (blob && name) {\n setOutput({ isPreviewStale: true });\n }\n }, [blob, name, autoReload, renderingLibrary, doRender, setOutput]);\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 return (\n <>\n <div className=\"flex h-full flex-col\">\n {(useSettingsStore as any) &&\n (useSettingsStore as any).getState?.().useGlobalPreviewHeader ===\n false && (\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={() =>\n setSettings({ autoReload: !autoReload })\n }\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 <Separator />\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\n ? 'bg-amber-500/10 border-amber-500/30'\n : 'bg-muted/30'\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-green-500 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-orange-500 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-amber-500 flex-shrink-0\" />\n <span className=\"text-xs text-amber-600 dark:text-amber-400 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 {/* 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-lg border border-red-400/50 bg-red-400/10 px-4 py-3 text-sm text-red-400\">\n <p className=\"font-medium mb-1\">Generation failed</p>\n <p className=\"text-xs text-red-400/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-AJZwEEO9.js"}
@@ -8,7 +8,7 @@
8
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
9
  <link href="https://fonts.googleapis.com/css2?family=Geist:wght@300;400;500;600;700&family=Geist+Mono:wght@400;500&display=swap" rel="stylesheet" />
10
10
  <title>JSON to Office</title>
11
- <script type="module" crossorigin src="/assets/index-CslfgN2g.js"></script>
11
+ <script type="module" crossorigin src="/assets/index-D_aKAAFg.js"></script>
12
12
  <link rel="modulepreload" crossorigin href="/assets/radix-ui-DkKm4m5M.js">
13
13
  <link rel="modulepreload" crossorigin href="/assets/react-vendor-B_QXlk-T.js">
14
14
  <link rel="modulepreload" crossorigin href="/assets/query-vendor-CT0SoqJh.js">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@json-to-office/jto",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "JSON to Office CLI - Unified document and presentation generation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -86,11 +86,11 @@
86
86
  "vite": "6.0.5",
87
87
  "zod": "^4.0.0",
88
88
  "zustand": "5.0.2",
89
- "@json-to-office/core-docx": "^0.2.0",
90
- "@json-to-office/core-pptx": "^0.2.0",
89
+ "@json-to-office/core-docx": "^0.3.0",
90
+ "@json-to-office/core-pptx": "^0.3.0",
91
91
  "@json-to-office/shared": "^0.2.0",
92
- "@json-to-office/shared-docx": "^0.2.0",
93
- "@json-to-office/shared-pptx": "^0.2.0"
92
+ "@json-to-office/shared-docx": "^0.3.0",
93
+ "@json-to-office/shared-pptx": "^0.3.0"
94
94
  },
95
95
  "devDependencies": {
96
96
  "@types/lodash.debounce": "4.0.9",
@@ -1 +0,0 @@
1
- {"version":3,"mappings":"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","names":["promisifyRequest","request","resolve","reject","createStore","dbName","storeName","dbp","getDB","db","txMode","callback","defaultGetStoreFunc","defaultGetStore","get","key","customStore","store","set","value","del","idbStorage","name","MAX_OPEN_TABS","initDocumentsStore","defaultInitDocumentsState","createDocumentsStore","initState","devtools","persist","text","state","doc","docType","parsed","newDoc","newDocumentTypes","k","docIndex","oldName","newName","documents","i","openTabs","index","tab","activeTab","buildError","buildErrors","original","modified","applyId","accepted","diff","next","ids","acceptedApplyIds","createJSONStorage","DocumentsStoreContext","createContext","DocumentsStoreProvider","children","storeRef","useRef","useDocumentsStore","selector","documentsStoreContext","useContext","useStore","validateThemeJson","jsonString","getThemeName","themeJson","_ThemeChangeEmitter","event","handler","e","__publicField","ThemeChangeEmitter","themeChangeEmitter","initThemesStore","defaultInitThemesState","createThemesStore","documentName","content","validation","themeName","parsedContent","extractedName","existingTheme","isUpdate","hasContentChanged","customTheme","removedTheme","newCustomThemes","names","theme","ThemesStoreContext","ThemesStoreProvider","useThemesStore","themesStoreContext","initOutputStore","defaultInitOutputState","createOutputStore","partialState","s","OutputStoreContext","OutputStoreProvider","useOutputStore","outputStoreContext","usePluginsStore","create","plugin","newSelectedPlugins","newMetadata","pluginName","onSuccess","onError","selectedPluginNames","loader","__vitePreload","updateMonacoWithPlugins","n","monaco","error","errorMessage","str","serialized","initSettingsStore","FORMAT","defaultInitSettingsState","createSettingsStore","settings","SettingsStoreContext","SettingsStoreProvider","useSettingsStore","settingsStoreContext","emptyNames","subscribe","useCustomThemeNames","cachedRef","getSnapshot","useSyncExternalStore","useMonacoPlugins","monacoRef","selectedPlugins","customThemeNames","themesStore","themesStoreRef","previousPluginsRef","previousThemesRef","useEffect","currentPlugins","currentThemes","_a","success","pluginsChanged","themesChanged","MonacoPluginProvider"],"ignoreList":[0],"sources":["../../../../../node_modules/.pnpm/idb-keyval@6.2.2/node_modules/idb-keyval/dist/index.js","../../../src/client/lib/idb-storage.ts","../../../src/client/store/documents-store.ts","../../../src/client/store/documents-store-provider.tsx","../../../src/client/lib/theme-validation.ts","../../../src/client/utils/theme-change-emitter.ts","../../../src/client/store/themes-store.ts","../../../src/client/store/themes-store-provider.tsx","../../../src/client/store/output-store.ts","../../../src/client/store/output-store-provider.tsx","../../../src/client/store/plugins-store.ts","../../../src/client/store/settings-store.ts","../../../src/client/store/settings-store-provider.tsx","../../../src/client/hooks/useMonacoPlugins.ts","../../../src/client/components/MonacoPluginProvider.tsx"],"sourcesContent":["function promisifyRequest(request) {\n return new Promise((resolve, reject) => {\n // @ts-ignore - file size hacks\n request.oncomplete = request.onsuccess = () => resolve(request.result);\n // @ts-ignore - file size hacks\n request.onabort = request.onerror = () => reject(request.error);\n });\n}\nfunction createStore(dbName, storeName) {\n let dbp;\n const getDB = () => {\n if (dbp)\n return dbp;\n const request = indexedDB.open(dbName);\n request.onupgradeneeded = () => request.result.createObjectStore(storeName);\n dbp = promisifyRequest(request);\n dbp.then((db) => {\n // It seems like Safari sometimes likes to just close the connection.\n // It's supposed to fire this event when that happens. Let's hope it does!\n db.onclose = () => (dbp = undefined);\n }, () => { });\n return dbp;\n };\n return (txMode, callback) => getDB().then((db) => callback(db.transaction(storeName, txMode).objectStore(storeName)));\n}\nlet defaultGetStoreFunc;\nfunction defaultGetStore() {\n if (!defaultGetStoreFunc) {\n defaultGetStoreFunc = createStore('keyval-store', 'keyval');\n }\n return defaultGetStoreFunc;\n}\n/**\n * Get a value by its key.\n *\n * @param key\n * @param customStore Method to get a custom store. Use with caution (see the docs).\n */\nfunction get(key, customStore = defaultGetStore()) {\n return customStore('readonly', (store) => promisifyRequest(store.get(key)));\n}\n/**\n * Set a value with a key.\n *\n * @param key\n * @param value\n * @param customStore Method to get a custom store. Use with caution (see the docs).\n */\nfunction set(key, value, customStore = defaultGetStore()) {\n return customStore('readwrite', (store) => {\n store.put(value, key);\n return promisifyRequest(store.transaction);\n });\n}\n/**\n * Set multiple values at once. This is faster than calling set() multiple times.\n * It's also atomic – if one of the pairs can't be added, none will be added.\n *\n * @param entries Array of entries, where each entry is an array of `[key, value]`.\n * @param customStore Method to get a custom store. Use with caution (see the docs).\n */\nfunction setMany(entries, customStore = defaultGetStore()) {\n return customStore('readwrite', (store) => {\n entries.forEach((entry) => store.put(entry[1], entry[0]));\n return promisifyRequest(store.transaction);\n });\n}\n/**\n * Get multiple values by their keys\n *\n * @param keys\n * @param customStore Method to get a custom store. Use with caution (see the docs).\n */\nfunction getMany(keys, customStore = defaultGetStore()) {\n return customStore('readonly', (store) => Promise.all(keys.map((key) => promisifyRequest(store.get(key)))));\n}\n/**\n * Update a value. This lets you see the old value and update it as an atomic operation.\n *\n * @param key\n * @param updater A callback that takes the old value and returns a new value.\n * @param customStore Method to get a custom store. Use with caution (see the docs).\n */\nfunction update(key, updater, customStore = defaultGetStore()) {\n return customStore('readwrite', (store) => \n // Need to create the promise manually.\n // If I try to chain promises, the transaction closes in browsers\n // that use a promise polyfill (IE10/11).\n new Promise((resolve, reject) => {\n store.get(key).onsuccess = function () {\n try {\n store.put(updater(this.result), key);\n resolve(promisifyRequest(store.transaction));\n }\n catch (err) {\n reject(err);\n }\n };\n }));\n}\n/**\n * Delete a particular key from the store.\n *\n * @param key\n * @param customStore Method to get a custom store. Use with caution (see the docs).\n */\nfunction del(key, customStore = defaultGetStore()) {\n return customStore('readwrite', (store) => {\n store.delete(key);\n return promisifyRequest(store.transaction);\n });\n}\n/**\n * Delete multiple keys at once.\n *\n * @param keys List of keys to delete.\n * @param customStore Method to get a custom store. Use with caution (see the docs).\n */\nfunction delMany(keys, customStore = defaultGetStore()) {\n return customStore('readwrite', (store) => {\n keys.forEach((key) => store.delete(key));\n return promisifyRequest(store.transaction);\n });\n}\n/**\n * Clear all values in the store.\n *\n * @param customStore Method to get a custom store. Use with caution (see the docs).\n */\nfunction clear(customStore = defaultGetStore()) {\n return customStore('readwrite', (store) => {\n store.clear();\n return promisifyRequest(store.transaction);\n });\n}\nfunction eachCursor(store, callback) {\n store.openCursor().onsuccess = function () {\n if (!this.result)\n return;\n callback(this.result);\n this.result.continue();\n };\n return promisifyRequest(store.transaction);\n}\n/**\n * Get all keys in the store.\n *\n * @param customStore Method to get a custom store. Use with caution (see the docs).\n */\nfunction keys(customStore = defaultGetStore()) {\n return customStore('readonly', (store) => {\n // Fast path for modern browsers\n if (store.getAllKeys) {\n return promisifyRequest(store.getAllKeys());\n }\n const items = [];\n return eachCursor(store, (cursor) => items.push(cursor.key)).then(() => items);\n });\n}\n/**\n * Get all values in the store.\n *\n * @param customStore Method to get a custom store. Use with caution (see the docs).\n */\nfunction values(customStore = defaultGetStore()) {\n return customStore('readonly', (store) => {\n // Fast path for modern browsers\n if (store.getAll) {\n return promisifyRequest(store.getAll());\n }\n const items = [];\n return eachCursor(store, (cursor) => items.push(cursor.value)).then(() => items);\n });\n}\n/**\n * Get all entries in the store. Each entry is an array of `[key, value]`.\n *\n * @param customStore Method to get a custom store. Use with caution (see the docs).\n */\nfunction entries(customStore = defaultGetStore()) {\n return customStore('readonly', (store) => {\n // Fast path for modern browsers\n // (although, hopefully we'll get a simpler path some day)\n if (store.getAll && store.getAllKeys) {\n return Promise.all([\n promisifyRequest(store.getAllKeys()),\n promisifyRequest(store.getAll()),\n ]).then(([keys, values]) => keys.map((key, i) => [key, values[i]]));\n }\n const items = [];\n return customStore('readonly', (store) => eachCursor(store, (cursor) => items.push([cursor.key, cursor.value])).then(() => items));\n });\n}\n\nexport { clear, createStore, del, delMany, entries, get, getMany, keys, promisifyRequest, set, setMany, update, values };\n","/**\n * IndexedDB storage adapter for Zustand persist middleware using idb-keyval.\n */\nimport { get, set, del } from 'idb-keyval';\nimport type { StateStorage } from 'zustand/middleware';\n\nexport const idbStorage: StateStorage = {\n getItem: async (name: string): Promise<string | null> => {\n return (await get(name)) ?? null;\n },\n setItem: async (name: string, value: string): Promise<void> => {\n await set(name, value);\n },\n removeItem: async (name: string): Promise<void> => {\n await del(name);\n },\n};\n","import { persist, devtools, createJSONStorage } from 'zustand/middleware';\nimport { createStore } from 'zustand/vanilla';\nimport type { TextFile } from '../lib/types';\nimport { idbStorage } from '../lib/idb-storage';\n\nconst MAX_OPEN_TABS = 3;\n\nexport type DocumentType = 'application/json+report' | 'application/json+theme';\n\nexport type DocumentsState = {\n documents: TextFile[];\n openTabs: string[];\n activeTab: string;\n buildErrors: { [key: string]: string };\n documentTypes: { [key: string]: DocumentType };\n pendingDiffs: { [key: string]: { original: string; modified: string; applyId?: string } };\n acceptedApplyIds: string[];\n};\n\nexport type DocumentsActions = {\n createDocument: (name: string, text: string) => void;\n deleteDocument: (name: string) => void;\n saveDocument: (name: string, text: string) => void;\n renameDocument: (oldName: string, newName: string) => void;\n openDocument: (name: string) => void;\n closeDocument: (name: string) => void;\n setActiveTab: (name: string) => void;\n setBuildError: (name: string, buildError?: string) => void;\n setPendingDiff: (name: string, original: string, modified: string, applyId?: string) => void;\n clearPendingDiff: (name: string, accepted?: boolean) => void;\n};\n\nexport type DocumentsStore = DocumentsState & DocumentsActions;\n\nexport const initDocumentsStore = (): DocumentsState => {\n return {\n documents: [],\n openTabs: [],\n activeTab: '',\n buildErrors: {},\n documentTypes: {},\n pendingDiffs: {},\n acceptedApplyIds: [],\n };\n};\n\nexport const defaultInitDocumentsState: DocumentsState = {\n ...initDocumentsStore(),\n};\n\nexport const createDocumentsStore = (\n initState: DocumentsState = defaultInitDocumentsState\n) => {\n return createStore<DocumentsStore>()(\n devtools(\n persist(\n (set) => ({\n ...initState,\n createDocument: (name, text) =>\n set((state) => {\n const docIndex = state.documents.findIndex(\n (doc) => doc.name === name\n );\n if (docIndex === -1) {\n // if the document does not exist\n // Determine document type based on file name and content\n let docType: DocumentType = 'application/json+report';\n if (\n name.toLowerCase().includes('theme') ||\n name.toLowerCase().includes('.theme.')\n ) {\n docType = 'application/json+theme';\n } else {\n // Try to parse JSON to check if it's a theme\n try {\n const parsed = JSON.parse(text);\n if (parsed.colors && parsed.fonts && parsed.styles) {\n docType = 'application/json+theme';\n }\n } catch {\n // If not valid JSON, default to report\n }\n }\n\n const newDoc = {\n name,\n type: 'application/json',\n text,\n mtime: new Date(),\n ctime: new Date(),\n atime: new Date(),\n };\n return {\n documents: [...state.documents, newDoc],\n documentTypes: { ...state.documentTypes, [name]: docType },\n };\n }\n return state;\n }),\n deleteDocument: (name) =>\n set((state) => {\n const newDocumentTypes = { ...state.documentTypes };\n delete newDocumentTypes[name];\n return {\n documents: state.documents.filter((doc) => doc.name !== name),\n documentTypes: newDocumentTypes,\n pendingDiffs: Object.fromEntries(\n Object.entries(state.pendingDiffs).filter(([k]) => k !== name)\n ),\n };\n }),\n saveDocument: (name, text) =>\n set((state) => {\n const docIndex = state.documents.findIndex(\n (doc) => doc.name === name\n );\n if (docIndex === -1) return state;\n // Skip update if text is unchanged to avoid spurious re-renders\n if (state.documents[docIndex].text === text) return state;\n const documents = state.documents.map((doc, i) =>\n i === docIndex ? { ...doc, text, mtime: new Date() } : doc\n );\n return { documents };\n }),\n renameDocument: (oldName, newName) =>\n set((state) => {\n const docIndex = state.documents.findIndex(\n (doc) => doc.name === oldName\n );\n if (docIndex === -1) return state;\n const documents = state.documents.map((doc, i) =>\n i === docIndex ? { ...doc, name: newName, ctime: new Date() } : doc\n );\n const docType = state.documentTypes[oldName];\n if (docType) {\n const newDocumentTypes = { ...state.documentTypes };\n delete newDocumentTypes[oldName];\n newDocumentTypes[newName] = docType;\n return { documents, documentTypes: newDocumentTypes };\n }\n return { documents };\n }),\n openDocument: (name) =>\n set((state) => {\n const docIndex = state.documents.findIndex(\n (doc) => doc.name === name\n );\n if (docIndex === -1) return state;\n const documents = state.documents.map((doc, i) =>\n i === docIndex ? { ...doc, atime: new Date() } : doc\n );\n let openTabs = state.openTabs;\n if (!openTabs.includes(name)) {\n openTabs = openTabs.length >= MAX_OPEN_TABS\n ? [...openTabs.slice(1), name]\n : [...openTabs, name];\n }\n return { documents, openTabs, activeTab: name };\n }),\n closeDocument: (name) =>\n set((state) => {\n const index = state.openTabs.indexOf(name);\n if (index === -1) return state;\n const openTabs = state.openTabs.filter((tab) => tab !== name);\n let activeTab = state.activeTab;\n if (activeTab === name) {\n if (openTabs.length) {\n activeTab = index === 0 ? openTabs[0] : openTabs[index - 1];\n } else {\n activeTab = '';\n }\n }\n return { openTabs, activeTab };\n }),\n setActiveTab: (name) => set({ activeTab: name }),\n setBuildError: (name, buildError) =>\n set((state) => {\n if (state.buildErrors[name] === buildError) return state;\n const buildErrors = { ...state.buildErrors };\n if (buildError) buildErrors[name] = buildError;\n else delete buildErrors[name];\n return { buildErrors };\n }),\n setPendingDiff: (name, original, modified, applyId) =>\n set((state) => {\n return {\n pendingDiffs: {\n ...state.pendingDiffs,\n [name]: { original, modified, applyId },\n },\n };\n }),\n clearPendingDiff: (name, accepted) =>\n set((state) => {\n const diff = state.pendingDiffs[name];\n if (!diff) return state;\n const next = { ...state.pendingDiffs };\n delete next[name];\n const ids = state.acceptedApplyIds || [];\n const acceptedApplyIds =\n accepted && diff.applyId\n ? [...ids.slice(-(200 - 1)), diff.applyId]\n : ids;\n return { pendingDiffs: next, acceptedApplyIds };\n }),\n }),\n {\n name: 'documents-storage',\n version: 1,\n storage: createJSONStorage(() => idbStorage),\n partialize: (state) => ({\n documents: state.documents,\n openTabs: state.openTabs,\n activeTab: state.activeTab,\n documentTypes: state.documentTypes,\n // buildErrors + pendingDiffs + acceptedApplyIds excluded — transient UI state\n }),\n onRehydrateStorage: () => (state) => {\n if (state?.documents) {\n for (const doc of state.documents) {\n for (const key of ['mtime', 'ctime', 'atime'] as const) {\n if (doc[key] && typeof doc[key] === 'string') {\n (doc as Record<string, unknown>)[key] = new Date(doc[key] as unknown as string);\n }\n }\n }\n }\n },\n }\n )\n )\n );\n};\n","import { type ReactNode, createContext, useRef, useContext } from 'react';\nimport { useStore } from 'zustand';\nimport {\n type DocumentsStore,\n createDocumentsStore,\n initDocumentsStore,\n} from './documents-store';\n\nexport type DocumentsStoreApi = ReturnType<typeof createDocumentsStore>;\n\nexport const DocumentsStoreContext = createContext<\n DocumentsStoreApi | undefined\n>(undefined);\n\nexport interface DocumentsStoreProviderProps {\n children: ReactNode;\n}\n\nexport const DocumentsStoreProvider = ({\n children,\n}: DocumentsStoreProviderProps) => {\n const storeRef = useRef<DocumentsStoreApi | null>(null);\n if (!storeRef.current) {\n storeRef.current = createDocumentsStore(initDocumentsStore());\n }\n\n return (\n <DocumentsStoreContext.Provider value={storeRef.current}>\n {children}\n </DocumentsStoreContext.Provider>\n );\n};\n\nexport const useDocumentsStore = <T,>(\n selector: (store: DocumentsStore) => T\n): T => {\n const documentsStoreContext = useContext(DocumentsStoreContext);\n\n if (!documentsStoreContext) {\n throw new Error(\n 'useDocumentsStore must be used within DocumentsStoreProvider'\n );\n }\n\n return useStore(documentsStoreContext, selector);\n};\n","export interface ThemeValidationError {\n message: string;\n line?: number;\n column?: number;\n path?: string;\n}\n\nexport interface ThemeValidationResult {\n valid: boolean;\n errors?: ThemeValidationError[];\n}\n\n/**\n * Validates a theme JSON string - checks structure without requiring format-specific schemas\n */\nexport function validateThemeJson(jsonString: string): ThemeValidationResult {\n try {\n const parsed = JSON.parse(jsonString);\n\n if (!parsed || typeof parsed !== 'object') {\n return {\n valid: false,\n errors: [{ message: 'Theme must be a JSON object' }],\n };\n }\n\n if (typeof parsed.name !== 'string' || !parsed.name.trim()) {\n return {\n valid: false,\n errors: [{ message: 'Theme must have a non-empty \"name\" property' }],\n };\n }\n\n return { valid: true };\n } catch (e) {\n return {\n valid: false,\n errors: [\n {\n message:\n e instanceof SyntaxError\n ? `Invalid JSON: ${e.message}`\n : 'Failed to parse theme JSON',\n },\n ],\n };\n }\n}\n\n/**\n * Gets theme name from a theme JSON object\n */\nexport function getThemeName(themeJson: unknown): string | null {\n try {\n if (\n themeJson &&\n typeof themeJson === 'object' &&\n 'name' in (themeJson as any) &&\n typeof (themeJson as any).name === 'string'\n ) {\n return (themeJson as any).name as string;\n }\n return null;\n } catch {\n return null;\n }\n}\n","// Theme change event emitter for coordinating theme updates across components\nexport interface ThemeChangeEvent {\n themeName: string;\n timestamp: number;\n changeType: 'create' | 'update' | 'delete';\n}\n\nexport class ThemeChangeEmitter extends EventTarget {\n private static instance: ThemeChangeEmitter;\n\n private constructor() {\n super();\n }\n\n static getInstance(): ThemeChangeEmitter {\n if (!ThemeChangeEmitter.instance) {\n ThemeChangeEmitter.instance = new ThemeChangeEmitter();\n }\n return ThemeChangeEmitter.instance;\n }\n\n emitThemeChange(event: ThemeChangeEvent): void {\n this.dispatchEvent(new CustomEvent('themechange', { detail: event }));\n }\n\n onThemeChange(callback: (event: ThemeChangeEvent) => void): () => void {\n const handler = (e: Event) => {\n const customEvent = e as CustomEvent<ThemeChangeEvent>;\n callback(customEvent.detail);\n };\n\n this.addEventListener('themechange', handler);\n\n // Track listener count\n (this as any)._listenerCount = ((this as any)._listenerCount || 0) + 1;\n\n // Return unsubscribe function\n return () => {\n this.removeEventListener('themechange', handler);\n (this as any)._listenerCount = Math.max(\n 0,\n ((this as any)._listenerCount || 1) - 1\n );\n };\n }\n}\n\nexport const themeChangeEmitter = ThemeChangeEmitter.getInstance();\n","import { persist, devtools, createJSONStorage } from 'zustand/middleware';\nimport { createStore } from 'zustand/vanilla';\nimport type { ThemeConfigJson } from '@json-to-office/shared-pptx';\nimport { validateThemeJson, getThemeName } from '../lib/theme-validation';\nimport { themeChangeEmitter } from '../utils/theme-change-emitter';\nimport { idbStorage } from '../lib/idb-storage';\n\nexport type CustomTheme = {\n name: string;\n content: string;\n parsed?: ThemeConfigJson;\n valid: boolean;\n lastModified: Date;\n};\n\nexport type ThemesState = {\n customThemes: { [name: string]: CustomTheme };\n};\n\nexport type ThemesActions = {\n updateTheme: (documentName: string, content: string) => void;\n removeTheme: (documentName: string) => void;\n getTheme: (themeName: string) => ThemeConfigJson | null;\n getAllThemeNames: () => string[];\n isThemeValid: (themeName: string) => boolean;\n};\n\nexport type ThemesStore = ThemesState & ThemesActions;\n\nexport const initThemesStore = (): ThemesState => {\n return {\n customThemes: {},\n };\n};\n\nexport const defaultInitThemesState: ThemesState = {\n ...initThemesStore(),\n};\n\nexport const createThemesStore = (\n initState: ThemesState = defaultInitThemesState\n) => {\n return createStore<ThemesStore>()(\n devtools(\n persist(\n (set, get) => ({\n ...initState,\n\n updateTheme: (documentName, content) =>\n set((state) => {\n const validation = validateThemeJson(content);\n\n let parsed: ThemeConfigJson | undefined;\n let themeName = documentName;\n\n if (validation.valid) {\n try {\n const parsedContent = JSON.parse(content);\n parsed = parsedContent;\n const extractedName = getThemeName(parsedContent);\n if (extractedName) {\n themeName = extractedName;\n }\n } catch {\n // Keep the document name if parsing fails\n }\n }\n\n const existingTheme = state.customThemes[documentName];\n const isUpdate = !!existingTheme;\n const hasContentChanged = existingTheme?.content !== content;\n\n const customTheme: CustomTheme = {\n name: themeName,\n content,\n parsed,\n valid: validation.valid,\n lastModified: new Date(),\n };\n\n // Emit theme change if content changed OR if this is a newly valid theme\n const shouldEmitEvent =\n (hasContentChanged ||\n (!existingTheme?.valid && validation.valid)) &&\n validation.valid;\n\n if (shouldEmitEvent) {\n // Use setTimeout to ensure state is updated before event is processed\n setTimeout(() => {\n themeChangeEmitter.emitThemeChange({\n themeName,\n timestamp: Date.now(),\n changeType: isUpdate ? 'update' : 'create',\n });\n }, 0);\n }\n\n return {\n customThemes: {\n ...state.customThemes,\n [documentName]: customTheme,\n },\n };\n }),\n\n removeTheme: (documentName) =>\n set((state) => {\n const removedTheme = state.customThemes[documentName];\n const newCustomThemes = { ...state.customThemes };\n delete newCustomThemes[documentName];\n\n // Emit theme deletion event\n if (removedTheme) {\n setTimeout(() => {\n themeChangeEmitter.emitThemeChange({\n themeName: removedTheme.name,\n timestamp: Date.now(),\n changeType: 'delete',\n });\n }, 0);\n }\n\n return {\n customThemes: newCustomThemes,\n };\n }),\n\n getTheme: (themeName) => {\n const state = get();\n\n // First check custom themes by theme name\n for (const customTheme of Object.values(state.customThemes)) {\n if (\n customTheme.valid &&\n customTheme.name === themeName &&\n customTheme.parsed\n ) {\n return customTheme.parsed;\n }\n }\n\n // Theme not found in custom themes\n return null;\n },\n\n getAllThemeNames: () => {\n const state = get();\n const names: string[] = [];\n\n // Add custom theme names\n for (const customTheme of Object.values(state.customThemes)) {\n if (customTheme.valid && customTheme.name) {\n names.push(customTheme.name);\n }\n }\n\n return names;\n },\n\n isThemeValid: (themeName) => {\n const state = get();\n\n // Check custom themes\n for (const customTheme of Object.values(state.customThemes)) {\n if (customTheme.name === themeName) {\n return customTheme.valid;\n }\n }\n\n return false;\n },\n }),\n {\n name: 'themes-storage',\n version: 4, // v4: migrate from localStorage to IndexedDB\n storage: createJSONStorage(() => idbStorage),\n // Only persist the themes data, not the functions\n partialize: (state) => ({ customThemes: state.customThemes }),\n // Handle date deserialization when loading from storage\n onRehydrateStorage: () => (state) => {\n if (state && state.customThemes) {\n // Convert lastModified strings back to Date objects\n Object.keys(state.customThemes).forEach((key) => {\n const theme = state.customThemes[key];\n if (\n theme.lastModified &&\n typeof theme.lastModified === 'string'\n ) {\n theme.lastModified = new Date(theme.lastModified);\n }\n });\n }\n },\n }\n )\n )\n );\n};\n","import { type ReactNode, createContext, useRef, useContext } from 'react';\nimport { useStore } from 'zustand';\n\nimport {\n type ThemesStore,\n createThemesStore,\n initThemesStore,\n} from './themes-store';\n\nexport type ThemesStoreApi = ReturnType<typeof createThemesStore>;\n\nexport const ThemesStoreContext = createContext<ThemesStoreApi | undefined>(\n undefined\n);\n\nexport interface ThemesStoreProviderProps {\n children: ReactNode;\n}\n\nexport const ThemesStoreProvider = ({ children }: ThemesStoreProviderProps) => {\n const storeRef = useRef<ThemesStoreApi>(undefined);\n if (!storeRef.current) {\n storeRef.current = createThemesStore(initThemesStore());\n }\n\n return (\n <ThemesStoreContext.Provider value={storeRef.current}>\n {children}\n </ThemesStoreContext.Provider>\n );\n};\n\nexport const useThemesStore = <T,>(selector: (store: ThemesStore) => T): T => {\n const themesStoreContext = useContext(ThemesStoreContext);\n\n if (!themesStoreContext) {\n throw new Error('useThemesStore must be used within ThemesStoreProvider');\n }\n\n return useStore(themesStoreContext, selector);\n};\n","import { devtools } from 'zustand/middleware';\nimport { createStore } from 'zustand/vanilla';\n\nexport interface GenerationWarning {\n component: string;\n message: string;\n severity?: 'warning' | 'info';\n context?: Record<string, unknown>;\n}\n\nexport type OutputState = {\n name?: string; // last success => document name\n text?: string; // last success => code used to generate the pptx\n blob?: Blob; // last success => generated pptx\n globalError?: string; // last failed => global error message\n isGenerating?: boolean; // presentation generation in progress\n generationProgress?: {\n stage: 'parsing' | 'building' | 'rendering' | 'finalizing';\n message?: string;\n };\n cacheStatus?: 'HIT' | 'MISS' | 'UNKNOWN'; // cache hit/miss status\n cacheHitRate?: string; // cache hit rate percentage\n warnings?: GenerationWarning[] | null; // warnings from custom component processing\n isRendering?: boolean; // preview rendering in progress (iframe/LibreOffice)\n isPreviewStale?: boolean; // preview outdated (new blob generated but not yet rendered)\n editSequence?: number; // incremented on every Monaco keystroke (init 0)\n lastBuiltSequence?: number; // stamped when generation completes (init 0)\n editTimestamp?: number; // Date.now() of the last edit (for debounce countdown)\n // ⚠️ name doesn't necessarily correspond to the global error message\n};\n\nexport type OutputActions = {\n setOutput: (partialState: OutputState) => void;\n bumpEditSequence: () => void;\n};\n\nexport type OutputStore = OutputState & OutputActions;\n\nexport const initOutputStore = (): OutputState => {\n return {\n name: undefined,\n text: undefined,\n blob: undefined,\n globalError: undefined,\n isGenerating: false,\n generationProgress: undefined,\n isRendering: false,\n isPreviewStale: false,\n editSequence: 0,\n lastBuiltSequence: 0,\n };\n};\n\nexport const defaultInitOutputState: OutputState = {\n ...initOutputStore(),\n};\n\nexport const createOutputStore = (\n initState: OutputState = defaultInitOutputState\n) => {\n return createStore<OutputStore>()(\n devtools((set) => ({\n ...initState,\n setOutput: (partialState) => set({ ...partialState }),\n bumpEditSequence: () =>\n set((s) => ({\n editSequence: (s.editSequence ?? 0) + 1,\n editTimestamp: Date.now(),\n })),\n }))\n );\n};\n","import { type ReactNode, createContext, useRef, useContext } from 'react';\nimport { useStore } from 'zustand';\nimport {\n type OutputStore,\n createOutputStore,\n initOutputStore,\n} from './output-store';\n\nexport type OutputStoreApi = ReturnType<typeof createOutputStore>;\n\nexport const OutputStoreContext = createContext<OutputStoreApi | undefined>(\n undefined\n);\n\nexport interface OutputStoreProviderProps {\n children: ReactNode;\n}\n\nexport const OutputStoreProvider = ({ children }: OutputStoreProviderProps) => {\n const storeRef = useRef<OutputStoreApi | null>(null);\n if (!storeRef.current) {\n storeRef.current = createOutputStore(initOutputStore());\n }\n\n return (\n <OutputStoreContext.Provider value={storeRef.current}>\n {children}\n </OutputStoreContext.Provider>\n );\n};\n\nexport const useOutputStore = <T,>(selector: (store: OutputStore) => T): T => {\n const outputStoreContext = useContext(OutputStoreContext);\n\n if (!outputStoreContext) {\n throw new Error('useOutputStore must be used within OutputStoreProvider');\n }\n\n return useStore(outputStoreContext, selector);\n};\n","import { create } from 'zustand';\nimport { persist } from 'zustand/middleware';\nimport type { PluginMetadata } from '../hooks/useDiscovery';\n\ninterface PluginsState {\n // Selected plugin names (used for schema generation)\n selectedPlugins: Set<string>;\n\n // Full plugin metadata for selected plugins\n selectedPluginMetadata: Map<string, PluginMetadata>;\n\n // Schema application state\n isApplyingPlugins: boolean;\n lastApplyError: string | null;\n\n // Actions\n selectPlugin: (plugin: PluginMetadata) => void;\n deselectPlugin: (pluginName: string) => void;\n togglePlugin: (plugin: PluginMetadata) => void;\n clearSelections: () => void;\n isPluginSelected: (pluginName: string) => boolean;\n getSelectedPluginsList: () => PluginMetadata[];\n getSelectedPluginNames: () => string[];\n applyPluginsWithValidation: (\n onSuccess?: () => void,\n onError?: (error: string) => void\n ) => Promise<boolean>;\n}\n\nexport const usePluginsStore = create<PluginsState>()(\n persist(\n (set, get) => ({\n selectedPlugins: new Set<string>(),\n selectedPluginMetadata: new Map<string, PluginMetadata>(),\n isApplyingPlugins: false,\n lastApplyError: null,\n\n selectPlugin: (plugin: PluginMetadata) => {\n set((state) => {\n const newSelectedPlugins = new Set(state.selectedPlugins);\n const newMetadata = new Map(state.selectedPluginMetadata);\n\n newSelectedPlugins.add(plugin.name);\n newMetadata.set(plugin.name, plugin);\n\n return {\n selectedPlugins: newSelectedPlugins,\n selectedPluginMetadata: newMetadata,\n };\n });\n },\n\n deselectPlugin: (pluginName: string) => {\n set((state) => {\n const newSelectedPlugins = new Set(state.selectedPlugins);\n const newMetadata = new Map(state.selectedPluginMetadata);\n\n newSelectedPlugins.delete(pluginName);\n newMetadata.delete(pluginName);\n\n return {\n selectedPlugins: newSelectedPlugins,\n selectedPluginMetadata: newMetadata,\n };\n });\n },\n\n togglePlugin: (plugin: PluginMetadata) => {\n const isSelected = get().isPluginSelected(plugin.name);\n if (isSelected) {\n get().deselectPlugin(plugin.name);\n } else {\n get().selectPlugin(plugin);\n }\n // Auto-apply schema changes\n get().applyPluginsWithValidation().catch(console.error);\n },\n\n clearSelections: () => {\n set({\n selectedPlugins: new Set<string>(),\n selectedPluginMetadata: new Map<string, PluginMetadata>(),\n });\n // Auto-apply schema changes\n get().applyPluginsWithValidation().catch(console.error);\n },\n\n isPluginSelected: (pluginName: string) => {\n return get().selectedPlugins.has(pluginName);\n },\n\n getSelectedPluginsList: () => {\n return Array.from(get().selectedPluginMetadata.values());\n },\n\n getSelectedPluginNames: () => {\n return Array.from(get().selectedPlugins);\n },\n\n applyPluginsWithValidation: async (onSuccess, onError) => {\n set({ isApplyingPlugins: true, lastApplyError: null });\n\n try {\n // Get the current selected plugins\n const selectedPluginNames = Array.from(get().selectedPlugins);\n\n // Import Monaco and schema update function dynamically\n const { loader } = await import('@monaco-editor/react');\n const { updateMonacoWithPlugins } = await import(\n '../lib/monaco-config'\n );\n\n // Get Monaco instance\n const monaco = await loader.init();\n\n // Apply the plugins and wait for validation\n const success = await updateMonacoWithPlugins(\n monaco,\n selectedPluginNames\n );\n\n if (!success) {\n throw new Error('Schema validation failed');\n }\n\n // Success - schema has been applied and validated\n set({ isApplyingPlugins: false, lastApplyError: null });\n\n if (onSuccess) {\n onSuccess();\n }\n\n return true;\n } catch (error) {\n const errorMessage =\n error instanceof Error\n ? error.message\n : 'Failed to apply plugin schemas';\n\n set({\n isApplyingPlugins: false,\n lastApplyError: errorMessage,\n });\n\n if (onError) {\n onError(errorMessage);\n }\n\n console.error('Failed to apply plugins with validation:', error);\n return false;\n }\n },\n }),\n {\n name: 'jtp-selected-plugins',\n // Custom storage to handle Set and Map serialization\n storage: {\n getItem: (name) => {\n const str = localStorage.getItem(name);\n if (!str) return null;\n\n const { state } = JSON.parse(str);\n return {\n state: {\n ...state,\n selectedPlugins: new Set(state.selectedPlugins || []),\n selectedPluginMetadata: new Map(\n state.selectedPluginMetadata || []\n ),\n },\n };\n },\n setItem: (name, value) => {\n const { state } = value as { state: PluginsState };\n const serialized = JSON.stringify({\n state: {\n ...state,\n selectedPlugins: Array.from(state.selectedPlugins),\n selectedPluginMetadata: Array.from(\n state.selectedPluginMetadata.entries()\n ),\n },\n });\n localStorage.setItem(name, serialized);\n },\n removeItem: (name) => localStorage.removeItem(name),\n },\n }\n )\n);\n","import { devtools, persist } from 'zustand/middleware';\nimport { createStore } from 'zustand/vanilla';\nimport type { Settings } from '../lib/types';\nimport { FORMAT } from '../lib/env';\n\nexport type SettingsState = Settings;\n\nexport type SettingsActions = {\n setSettings: (settings: Partial<Settings>) => void;\n};\n\nexport type SettingsStore = SettingsState & SettingsActions;\n\nexport const initSettingsStore = (): SettingsState => {\n return {\n saveDocumentDebounceWait: 300,\n autoReload: true,\n renderingLibrary: FORMAT === 'docx' ? 'docxjs' : 'LibreOffice',\n // UI preference to use a single preview header spanning editor + preview\n useGlobalPreviewHeader: true,\n };\n};\n\nexport const defaultInitSettingsState: SettingsState = {\n ...initSettingsStore(),\n};\n\nexport const createSettingsStore = (\n initState: SettingsState = defaultInitSettingsState\n) => {\n return createStore<SettingsStore>()(\n devtools(\n persist(\n (set) => ({\n ...initState,\n setSettings: (settings) => set({ ...settings }),\n }),\n {\n name: 'settings-storage', // name of the item in the storage (must be unique)\n // (optional) by default, 'localStorage' is used as storage\n }\n )\n )\n );\n};\n","import { type ReactNode, createContext, useRef, useContext } from 'react';\nimport { useStore } from 'zustand';\nimport {\n type SettingsStore,\n createSettingsStore,\n initSettingsStore,\n} from './settings-store';\n\nexport type SettingsStoreApi = ReturnType<typeof createSettingsStore>;\n\nexport const SettingsStoreContext = createContext<SettingsStoreApi | undefined>(\n undefined\n);\n\nexport interface SettingsStoreProviderProps {\n children: ReactNode;\n}\n\nexport const SettingsStoreProvider = ({\n children,\n}: SettingsStoreProviderProps) => {\n const storeRef = useRef<SettingsStoreApi | null>(null);\n if (!storeRef.current) {\n storeRef.current = createSettingsStore(initSettingsStore());\n }\n\n return (\n <SettingsStoreContext.Provider value={storeRef.current}>\n {children}\n </SettingsStoreContext.Provider>\n );\n};\n\nexport const useSettingsStore = <T,>(\n selector: (store: SettingsStore) => T\n): T => {\n const settingsStoreContext = useContext(SettingsStoreContext);\n\n if (!settingsStoreContext) {\n throw new Error(\n 'useSettingsStore must be used within SettingsStoreProvider'\n );\n }\n\n return useStore(settingsStoreContext, selector);\n};\n","import { useEffect, useRef, useContext, useSyncExternalStore } from 'react';\nimport { loader } from '@monaco-editor/react';\nimport type { Monaco } from '@monaco-editor/react';\nimport { usePluginsStore } from '../store/plugins-store';\nimport { ThemesStoreContext } from '../store/themes-store-provider';\nimport { updateMonacoWithPlugins } from '../lib/monaco-config';\n\nconst emptyNames: string[] = [];\nconst subscribe = () => () => {};\n\n/**\n * Read custom theme names from the ThemesStore if available,\n * returning [] when outside a ThemesStoreProvider.\n *\n * getSnapshot must return a referentially stable value to avoid\n * infinite re-renders with useSyncExternalStore.\n */\nfunction useCustomThemeNames(): string[] {\n const store = useContext(ThemesStoreContext);\n const cachedRef = useRef<string[]>(emptyNames);\n\n const getSnapshot = store\n ? () => {\n const next = store.getState().getAllThemeNames();\n // Only update the cached reference when the list actually changes\n if (\n next.length !== cachedRef.current.length ||\n next.some((n, i) => n !== cachedRef.current[i])\n ) {\n cachedRef.current = next;\n }\n return cachedRef.current;\n }\n : () => emptyNames;\n\n return useSyncExternalStore(store ? store.subscribe : subscribe, getSnapshot);\n}\n\n/**\n * Hook to synchronize selected plugins and custom themes with Monaco editor schemas.\n * Ensures real-time validation and autocomplete include plugin-specific schemas\n * and custom theme names.\n */\nexport function useMonacoPlugins() {\n const monacoRef = useRef<Monaco | null>(null);\n const selectedPlugins = usePluginsStore((state) => state.selectedPlugins);\n const selectedPluginNames = Array.from(selectedPlugins);\n const customThemeNames = useCustomThemeNames();\n const themesStore = useContext(ThemesStoreContext);\n const themesStoreRef = useRef(themesStore);\n themesStoreRef.current = themesStore;\n const previousPluginsRef = useRef<string[]>([]);\n const previousThemesRef = useRef<string[]>([]);\n\n useEffect(() => {\n loader\n .init()\n .then((monaco) => {\n monacoRef.current = monaco;\n // Read current state from stores (not stale closure) since\n // zustand persist may have hydrated while loader.init() was pending\n const currentPlugins = Array.from(\n usePluginsStore.getState().selectedPlugins\n );\n const currentThemes =\n themesStoreRef.current?.getState().getAllThemeNames() ?? [];\n updateMonacoWithPlugins(monaco, currentPlugins, currentThemes)\n .then((success) => {\n if (success) {\n console.log(\n '[useMonacoPlugins] Monaco initialized with plugins:',\n currentPlugins\n );\n } else {\n console.warn(\n '[useMonacoPlugins] Monaco initialization completed with warnings'\n );\n }\n })\n .catch((error) => {\n console.error(\n '[useMonacoPlugins] Failed to update Monaco with plugins:',\n error\n );\n });\n previousPluginsRef.current = [...currentPlugins];\n previousThemesRef.current = [...currentThemes];\n })\n .catch((error) => {\n console.error('Failed to initialize Monaco:', error);\n });\n }, []); // Keep empty dependency to run only once\n\n useEffect(() => {\n const pluginsChanged =\n selectedPluginNames.length !== previousPluginsRef.current.length ||\n selectedPluginNames.some(\n (name, index) => name !== previousPluginsRef.current[index]\n );\n\n const themesChanged =\n customThemeNames.length !== previousThemesRef.current.length ||\n customThemeNames.some(\n (name, index) => name !== previousThemesRef.current[index]\n );\n\n if (!pluginsChanged && !themesChanged) {\n return;\n }\n\n if (monacoRef.current) {\n updateMonacoWithPlugins(\n monacoRef.current,\n selectedPluginNames,\n customThemeNames\n ).catch((error) => {\n console.error(\n '[useMonacoPlugins] Failed to update Monaco schemas:',\n error\n );\n });\n previousPluginsRef.current = [...selectedPluginNames];\n previousThemesRef.current = [...customThemeNames];\n }\n }, [selectedPluginNames, customThemeNames]);\n\n return {\n monaco: monacoRef.current,\n selectedPlugins: selectedPluginNames,\n };\n}\n","import { useMonacoPlugins } from '../hooks/useMonacoPlugins';\n\n/**\n * Provider component that manages Monaco plugin synchronization\n * This must be rendered within the app after stores are initialized\n */\nexport function MonacoPluginProvider({\n children,\n}: {\n children: React.ReactNode;\n}) {\n // Initialize Monaco plugin synchronization\n useMonacoPlugins();\n\n return <>{children}</>;\n}\n"],"file":"assets/MonacoPluginProvider-C_k1zpkx.js"}