PageSourceSearch

https://propstage.ai/assets/VirtuallyStagedDisclosurePage-BvK8ZBtw.js

js propstage.ai collected 2026-10-05 12:32:02 UTC 9,394 bytes, 2 lines download raw bytes

1import{j as e}from"./vendor-radix-Dj-Vv7Vn.js";import{u as W,a as n}from"./vendor-router-CpAdbEv8.js";import{a2 as V,am as j,aZ as O,af as v,z as E,a_ as H,a7 as F,A as _,B as z}from"./index-CqcBfdM-.js";import{S as G}from"./SEOMetaTags-B5jvJd8q.js";import{y as S,m as q,I as B,q as C}from"./vendor-lucide-DXIEnzBa.js";import"./vendor-supabase-BjUapqqF.js";import"./vendor-tanstack-41Gwm3-d.js";import"./vendor-utils-CkMMIn0L.js";import"./vendor-forms-s3zmfq69.js";import"./vendor-recharts-DHevPU9W.js";import"./vendor-quill-DRdhlNEd.js";import"./vendor-stripe-B2HAEqKn.js";import"./vendor-state-DX-t0KHu.js";(function(){try{var s=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{};s.SENTRY_RELEASE={id:"production-1789139411949"};var r=new s.Error().stack;r&&(s._sentryDebugIds=s._sentryDebugIds||{},s._sentryDebugIds[r]="aa1e0a24-2ba9-4d91-8094-e3bde09bbdb6",s._sentryDebugIdIdentifier="sentry-dbid-aa1e0a24-2ba9-4d91-8094-e3bde09bbdb6")}catch{}})();const M="virtually-staged-disclosure",k="Virtually Staged";function Y(s){return new Promise((r,i)=>{const c=document.createElement("canvas");c.width=s.naturalWidth,c.height=s.naturalHeight;const t=c.getContext("2d");if(!t){i(new Error("No canvas context"));return}t.drawImage(s,0,0);const o=Math.max(14,Math.round(s.naturalWidth*.02));t.font=`500 ${o}px -apple-system, BlinkMacSystemFont, sans-serif`;const N=t.measureText(k),u=o*.7,w=o*.55,p=N.width+u*2,l=o+w*2,d=Math.max(12,Math.round(s.naturalWidth*.015)),g=s.naturalWidth-p-d,h=s.naturalHeight-l-d,f=Math.round(l*.35);t.fillStyle="rgba(0, 0, 0, 0.52)",t.beginPath(),t.roundRect(g,h,p,l,f),t.fill(),t.fillStyle="rgba(255, 255, 255, 0.88)",t.textBaseline="middle",t.fillText(k,g+u,h+l/2),c.toBlob(b=>{if(!b){i(new Error("Canvas toBlob failed"));return}r(b)},"image/jpeg",.93)})}function J(s,r){var b;const i=((b=s.parentElement)==null?void 0:b.clientWidth)||800,c=Math.min(1,i/r.naturalWidth);s.width=Math.round(r.naturalWidth*c),s.height=Math.round(r.naturalHeight*c);const t=s.getContext("2d");if(!t)return;t.drawImage(r,0,0,s.width,s.height);const o=Math.max(10,Math.round(s.width*.02));t.font=`500 ${o}px -apple-system, BlinkMacSystemFont, sans-serif`;const N=t.measureText(k),u=o*.7,w=o*.55,p=N.width+u*2,l=o+w*2,d=Math.max(8,Math.round(s.width*.015)),g=s.width-p-d,h=s.height-l-d,f=Math.round(l*.35);t.fillStyle="rgba(0, 0, 0, 0.52)",t.beginPath(),t.roundRect(g,h,p,l,f),t.fill(),t.fillStyle="rgba(255, 255, 255, 0.88)",t.textBaseline="middle",t.fillText(k,g+u,h+l/2)}function ce(){const s=W(),{user:r}=V(),[i,c]=n.useState(null),[t,o]=n.useState(null),[N,u]=n.useState(!1),[w,p]=n.useState(!1),l=n.useRef(null),d=n.useRef(null),g=n.useRef(null);n.useEffect(()=>()=>{t&&URL.revokeObjectURL(t)},[t]);const h=n.useCallback(a=>{t&&URL.revokeObjectURL(t);const x=URL.createObjectURL(a);c(a),o(x);const y=new Image;y.onload=()=>{d.current=y,l.current&&J(l.current,y)},y.src=x},[t]),f=n.useCallback(a=>
1{if(!a||a.length===0)return;const x=a[0];if(!x.type.startsWith("image/")){j.error("Please upload an image file.");return}h(x)},[h]),b=n.useCallback(a=>{a.preventDefault(),u(!1),f(a.dataTransfer.files)},[f]),L=n.useCallback(async()=>{if(!i||!d.current){j.error("Please upload an image first.");return}if(!r){O({caseTitle:"Virtually Staged Disclosure"});return}p(!0),j.info("Creating disclosure badge…");try{const a=await Y(d.current),x=Date.now(),y=Math.random().toString(36).substring(7),I=`temp-uploads/disclosure-input-${r.id}-${x}-${y}.jpg`,D=`temp-uploads/disclosure-output-${r.id}-${x}-${y}.jpg`,[P,R]=await Promise.all([v.storage.from("case-examples").upload(I,i,{contentType:i.type,upsert:!1}),v.storage.from("case-examples").upload(D,a,{contentType:"image/jpeg",upsert:!1})]);if(P.error)throw new Error(`Input upload failed: ${P.error.message}`);if(R.error)throw new Error(`Output upload failed: ${R.error.message}`);const $=v.storage.from("case-examples").getPublicUrl(I).data.publicUrl,A=v.storage.from("case-examples").getPublicUrl(D).data.publicUrl,T=crypto.randomUUID(),{data:m,error:U}=await v.functions.invoke("add-ai-disclosure",{body:{outputImageUrl:A,resultHash:T,caseSlug:M,inputImageUrl:$}});if(U||!(m!=null&&m.success)){if(E.error("[VirtuallyStagedDisclosurePage] edge function error:",U??m),(m==null?void 0:m.error)==="Insufficient credits")j.error("Not enough credits. Please upgrade your plan.");else throw new Error("Failed to create disclosure record");return}H(r.id),j.success("Disclosure badge added! Redirecting…"),s(`/result/${m.resultHash}?case=${M}&variantId=${m.generationId}`)}catch(a){E.error("[VirtuallyStagedDisclosurePage] error:",a),j.error("Something went wrong. Please try again.")}finally{p(!1)}},[i,r,s]);return e.jsxs("div",{className:"min-h-screen bg-background",children:[e.jsx(G,{title:"Add 'Virtually Staged' Disclosure Badge | PropStage.ai",description:"Stay MLS compliant in seconds. Upload your virtually staged photo and we'll add a professional 'Virtually Staged' label to the bottom-right corner — for just 1 credit.",canonical:`https://propstage.ai/tools/${M}/generate`}),e.jsx(F,{}),e.jsxs("main",{className:"container max-w-3xl mx-auto px-4 py-12",children:[e.jsxs("div",{className:"text-center mb-10",children:[e.jsxs("div",{className:"inline-flex items-center gap-2 bg-primary/10 text-primary rounded-full px-4 py-1.5 text-sm font-medium mb-4",children:[e.jsx(S,{className:"w-4 h-4"}),"MLS Compliance"]}),e.jsx("h1",{className:"text-3xl md:text-4xl font-bold tracking-tight mb-3",children:'Add "Virtually Staged" Disclosure'}),e.jsx("p",{className:"text-muted-foreground text-lg max-w-xl mx-auto",children:"Upload your virtually staged image and we'll stamp a professional, semi-transparent disclosure badge in the bottom-right corner — required by NAR/MLS guidelines."})]}),e.jsx("div",{className:"rounded-2xl border-2 border-dashed border-border bg-muted/30 overflow-hidden mb-6 transition-colors hover:border-primary/40",children:t?e.jsxs("div",{className:"relative",children:[e.jsx("canvas",{ref:l,className:"w-full h-auto block",style:{display:"block"}}),e.jsx("button",{className:"absolute top-3 right-3 bg-black/60 hover:bg-black/80 text-white rounded-full p-1.5 transition-colors",onClick:()=>{c(null),t&&(URL.revokeObjectURL(t),o(null)),d.current=null},children:e.jsx("svg",{className:"w-4 h-4",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:e.jsx("path",{d:"M18 6 6 18M6 6l12 12"})})})]}):e.jsxs("div",{className:_("flex flex-col items-center justify-center gap-4 py-16 px-6 cursor-pointer select-none transition-colors",N&&"bg-primary/5"),onClick:()=>{var a;return(a=g.current)==null?void 0:a.click()},onDragOver:a=>{a.preventDefault(),u(!0)},onDragLeave:()=>u(!1),onDrop:b,children:[e.jsx("div",{className:"rounded-full bg-primary/10 p-4",children:e.jsx(q,{className:"w-8 h-8 text-primary"})}),e.jsxs("div",{className:"text-center",children:[e.jsx("p",{className:"font-semibold text-base",children:"Drop your image here"}),e.jsx("p",{className:"text-muted-foreground text-sm mt-1",children:"or click to browse · JPG, PNG, WebP"})]}),e.jsxs("div",{className:"flex items-center gap-3 text-xs text-muted-foreground",children:[e.jsxs("span",{className:"flex items-center gap-1.5",children:[e.jsx(B,{className:"w-3.5 h-3.5"}),"Virtually staged photos"]}),e.jsx("span",{children:"·"}),e.jsxs("span",{className:"flex items-center gap-1.5",children:[e.jsx(S,{className:"w-3.5 h-3.5"}),"NAR / MLS compliant"]})]})]})}),e.jsx("input",{ref:g,type:"file",accept:"image/*",className:"hidden",onChange:a=>f(a.target.files)}),t&&e.jsx("p",{className:"text-sm text-muted-foreground text-center mb-6",children:"Preview shows the badge as it will appear. The full-resolution image is processed when you click below."}),e.jsx(z,{size:"lg",className:"w-full gap-2 text-base",disabled:!t||w,onClick:L,children:w?e.jsxs(e.Fragment,{children:[e.jsxs("svg",{className:"w-4 h-4 animate-spin",viewBox:"0 0 24 24",fill:"none",children:[e.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),e.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8v8z"})]}),"Adding badge…"]}):e.jsxs(e.Fragment,{children:[e.jsx(S,{className:"w-5 h-5"}),"Add Disclosure Badge",e.jsx("span",{className:"ml-1 text-xs font-normal opacity-70",children:"· 1 credit"}),e.jsx(C,{className:"w-4 h-4 ml-auto"})]})}),!r&&e.jsx("p",{className:"text-center text-sm text-muted-foreground mt-3",children:"You'll be prompted to sign in before the badge is applied."}),e.jsx("div",{className:"grid grid-cols-1 sm:grid-cols-3 gap-4 mt-12 text-sm",children:[{icon:S,title:"NAR / MLS compliant",desc:"Meets disclosure requirements for virtually staged listing photos across all major MLSs."},{icon:B,title:"Non-destructive",desc:"Your original image is preserved. The labeled ver
1sion is saved as a separate variant."},{icon:C,title:"Instant result",desc:"Badge is stamped client-side in seconds and saved directly to your PropStage library."}].map(a=>e.jsxs("div",{className:"rounded-xl border bg-card p-4",children:[e.jsx(a.icon,{className:"w-5 h-5 text-primary mb-2"}),e.jsx("p",{className:"font-medium mb-1",children:a.title}),e.jsx("p",{className:"text-muted-foreground text-xs",children:a.desc})]},a.title))})]})]})}export{ce as default};
2//# sourceMappingURL=VirtuallyStagedDisclosurePage-BvK8ZBtw.js.map

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.