const { useState, useRef, useEffect } = React; const ACCENTS=["#0078d4","#0063b1","#744da9","#e3008c","#c50f1f","#ca5010","#e87722","#b5911b","#498205","#107c10","#00b7c3","#4e6b8c"]; const FONT="'Segoe UI Variable','Segoe UI',system-ui,sans-serif"; const DEFAULT_LOGIN_BG="./login-bg-default.jpg"; function mkTheme(dark,accent){ return{accent,accentHov:blend(accent,dark?"#ffffff":"#000000",0.15),accentBg:accent+"18",accentBorder:accent+"40", bg:dark?"#1f1f1f":"#f3f3f3",sidebar:dark?"#181818":"#f0f0f0",surface:dark?"#2a2a2a":"#ffffff", surface2:dark?"#222222":"#fafafa",surface3:dark?"#323232":"#f5f5f5", border:dark?"rgba(255,255,255,0.08)":"rgba(0,0,0,0.08)",text:dark?"#f0f0f0":"#1c1c1c", muted:dark?"#a8a8a8":"#5f5f5f",subtle:dark?"#606060":"#9e9e9e",hover:dark?"rgba(255,255,255,0.06)":"rgba(0,0,0,0.05)", inputBg:dark?"#333":"#fafafa",inBorder:dark?"rgba(255,255,255,0.14)":"rgba(0,0,0,0.12)", titleBar:"#1a1a1a",danger:dark?"#ff6b6b":"#c50f1f",selectedBg:accent+"18",selectedLeft:accent+"80"}; } function blend(hex,w,t){ const p=x=>parseInt(x,16); const r=[p(hex.slice(1,3)),p(hex.slice(3,5)),p(hex.slice(5,7))]; const ww=[p(w.slice(1,3)),p(w.slice(3,5)),p(w.slice(5,7))]; return "#"+r.map((v,i)=>Math.round(v+(ww[i]-v)*t).toString(16).padStart(2,"0")).join(""); } // ── Background catalogue ────────────────────────────────────────────────────── const BG_SOLID=[ {id:"white", label:"White", preview:"#ffffff",dark:false}, {id:"smoke", label:"Smoke Gray", preview:"#e4e4e4",dark:false}, {id:"solarized",label:"Solarized", preview:"#fdf6e3",dark:false}, {id:"dark", label:"Dark", preview:"#1a1a1a",dark:true}, {id:"navy", label:"Deep Navy", preview:"#0d2040",dark:true}, {id:"charcoal", label:"Charcoal", preview:"#2b2b2b",dark:true}, ]; const BG_IMAGES=[ {id:"mountains",label:"Mountains",swatch:"linear-gradient(180deg,#1b3a5e 0%,#4a7fb5 35%,#7aad6a 65%,#8b7355 100%)",app:"linear-gradient(150deg,#1b3a5e 0%,#4a7fb5 45%,#7aad6a 80%,#8b7355 100%)"}, {id:"beach", label:"Beach", swatch:"linear-gradient(180deg,#87ceeb 0%,#b8e0f0 40%,#f5dfa8 70%,#c8a04a 100%)",app:"linear-gradient(150deg,#87ceeb 0%,#b8e0f0 40%,#f5dfa8 75%,#c8a04a 100%)"}, {id:"ocean", label:"Ocean", swatch:"linear-gradient(180deg,#0d47a1 0%,#1976d2 40%,#42a5f5 75%,#80deea 100%)",app:"linear-gradient(150deg,#0d47a1 0%,#1976d2 40%,#42a5f5 80%,#80deea 100%)"}, {id:"landscape",label:"Landscape",swatch:"linear-gradient(180deg,#1a4a1a 0%,#2e7d32 35%,#7cb342 65%,#c8e6c9 100%)",app:"linear-gradient(150deg,#1a4a1a 0%,#2e7d32 40%,#7cb342 75%,#c8e6c9 100%)"}, {id:"flowers", label:"Flowers", swatch:"linear-gradient(180deg,#880e4f 0%,#e91e63 35%,#f48fb1 65%,#fce4ec 100%)",app:"linear-gradient(150deg,#880e4f 0%,#c2185b 40%,#f48fb1 75%,#fce4ec 100%)"}, {id:"city", label:"City", swatch:"linear-gradient(180deg,#0a0a1a 0%,#1a1a3e 35%,#2d2d6e 60%,#e94560 100%)",app:"linear-gradient(150deg,#0a0a1a 0%,#1a1a3e 40%,#2d2d6e 75%,#e94560 100%)"}, {id:"sunset", label:"Sunset", swatch:"linear-gradient(180deg,#1a0533 0%,#7b1fa2 30%,#e65100 65%,#ff8f00 100%)",app:"linear-gradient(150deg,#1a0533 0%,#7b1fa2 35%,#e65100 70%,#ff8f00 100%)"}, {id:"aurora", label:"Aurora", swatch:"linear-gradient(180deg,#0d1b2a 0%,#1a5276 35%,#1abc9c 65%,#a9f0d1 100%)",app:"linear-gradient(150deg,#0d1b2a 0%,#1a5276 40%,#1abc9c 70%,#a9f0d1 100%)"}, {id:"desert", label:"Desert", swatch:"linear-gradient(180deg,#1a0a00 0%,#b35900 35%,#e8a020 65%,#f5d080 100%)",app:"linear-gradient(150deg,#1a0a00 0%,#b35900 40%,#e8a020 70%,#f5d080 100%)"}, {id:"arctic", label:"Arctic", swatch:"linear-gradient(180deg,#e8f4ff 0%,#b8d8f0 35%,#d0eaff 65%,#f0f8ff 100%)",app:"linear-gradient(150deg,#dceefb 0%,#b8d8f0 40%,#d0eaff 75%,#f0f8ff 100%)"}, ]; function getBgStyle(bgId){ if(!bgId||bgId==="white")return{}; if(typeof bgId==="string"&&bgId.startsWith("#"))return{background:bgId}; const solid=BG_SOLID.find(b=>b.id===bgId); if(solid)return{background:solid.preview}; const img=BG_IMAGES.find(b=>b.id===bgId); if(img)return{background:img.app}; return{}; } function bgIsImage(bgId){return BG_IMAGES.some(b=>b.id===bgId);} function bgIsDark(bgId){ if(typeof bgId==="string"&&/^#[0-9a-f]{6}$/i.test(bgId)){ const r=parseInt(bgId.slice(1,3),16),g=parseInt(bgId.slice(3,5),16),b=parseInt(bgId.slice(5,7),16); return (r*0.299+g*0.587+b*0.114)<128; } const solid=BG_SOLID.find(b=>b.id===bgId); if(solid)return solid.dark; const imgDark=["city","sunset","aurora","ocean","mountains"]; return imgDark.includes(bgId); } // ── Profile system ──────────────────────────────────────────────────────────── const INIT_PROFILES=[ {id:"p1",name:"Alex Johnson",initials:"AJ",color:"#744da9",password:"demo", accountIds:["alex@example-corp.com","default"], settings:{accent:"#0078d4",isDark:false,bg:"white",notifications:true,sound:false,readReceipts:false,autoMark:true}}, {id:"p2",name:"Sales Team",initials:"ST",color:"#107c10",password:"sales", accountIds:["sales@example-corp.com"], settings:{accent:"#107c10",isDark:false,bg:"white",notifications:true,sound:true,readReceipts:true,autoMark:true}}, {id:"p3",name:"J. Doe Personal",initials:"JD",color:"#ca5010",password:"demo3", accountIds:["j.doe@mailbox.org"], settings:{accent:"#ca5010",isDark:true,bg:"dark",notifications:false,sound:false,readReceipts:false,autoMark:false}}, ]; // ── Accounts + folders ──────────────────────────────────────────────────────── const ALL_ACCOUNTS=[ {id:"default", display:"Default Account", email:null, av:"DA",avColor:"#0078d4"}, {id:"alex@example-corp.com", display:"Alex", email:"alex@example-corp.com", av:"AJ",avColor:"#744da9"}, {id:"sales@example-corp.com", display:"Sales", email:"sales@example-corp.com",av:"SA",avColor:"#107c10"}, {id:"j.doe@mailbox.org", display:"J. Doe", email:"j.doe@mailbox.org", av:"JD",avColor:"#ca5010"}, ]; const STD_FOLDERS=[ {id:"inbox", label:"Inbox", icon:"ti-inbox", badge:true}, {id:"sent", label:"Sent", icon:"ti-send", badge:false}, {id:"drafts", label:"Drafts", icon:"ti-file-text", badge:true}, {id:"out", label:"Outbox", icon:"ti-upload", badge:false}, {id:"archive", label:"Archive", icon:"ti-archive", badge:false}, {id:"templates",label:"Templates",icon:"ti-layout-grid", badge:false}, {id:"trash", label:"Trash", icon:"ti-trash", badge:false}, ]; // ── Seed emails ─────────────────────────────────────────────────────────────── const SEED=[ {id:1,account:"alex@example-corp.com",folder:"inbox",from:"Sarah Chen",from_email:"sarah@acme.com",to:"alex@example-corp.com",subject:"Q3 Budget Review — Action Required",preview:"Hi team, please review the attached budget spreadsheet before Friday's meeting...",body:"Hi Alex,\n\nPlease review the attached budget spreadsheet before Friday's meeting. We need everyone's input on proposed changes.\n\nKey items:\n • Marketing budget +15%\n • IT infrastructure refresh ($240K)\n • Travel policy revisions\n\nReturn by EOD Thursday.\n\nBest,\nSarah",date:"2:34 PM",read:false,starred:true,atts:["Q3_Budget_2024.xlsx","Policy_Updates.pdf"],av:"SC",avColor:"#0078d4"}, {id:2,account:"alex@example-corp.com",folder:"inbox",from:"James Holloway",from_email:"james@dev.io",to:"alex@example-corp.com",subject:"Re: Deployment pipeline — staging env down",preview:"The issue was traced to a misconfigured Nginx proxy rule...",body:"Hey Alex,\n\nIssue traced to a misconfigured Nginx proxy rule added during last night's security patch. Fix pushed — health check should go green in ~10 min.\n\nJames",date:"11:20 AM",read:false,starred:false,atts:[],av:"JH",avColor:"#744da9"}, {id:3,account:"alex@example-corp.com",folder:"inbox",from:"Priya Nair",from_email:"priya@design.studio",to:"alex@example-corp.com",subject:"Brand refresh assets — final delivery",preview:"Attached are the final approved assets for the brand refresh...",body:"Hi Alex,\n\nAttached are the final approved assets. SVG, PNG @1x/2x, and Figma source.\n\nPriya",date:"Yesterday",read:true,starred:true,atts:["Brand_Assets_Final.zip"],av:"PN",avColor:"#e87722"}, {id:4,account:"alex@example-corp.com",folder:"sent",from:"Alex",from_email:"alex@example-corp.com",to:"sarah@acme.com",subject:"Re: Q3 Budget Review — Action Required",preview:"Hi Sarah, I've reviewed the spreadsheet...",body:"Hi Sarah,\n\nReviewed the spreadsheet. Aligned on the marketing increase — would like to discuss travel policy changes.\n\nAlex",date:"3:10 PM",read:true,starred:false,atts:[],av:"AJ",avColor:"#744da9"}, {id:5,account:"sales@example-corp.com",folder:"inbox",from:"Mark Okafor",from_email:"mark@partners.net",to:"sales@example-corp.com",subject:"Partnership proposal — follow-up",preview:"Thanks for the meeting yesterday. Revised proposal attached...",body:"Hi,\n\nRevised proposal attached. 90-day pilot, 30% lower upfront, dedicated account manager.\n\nMark",date:"Yesterday",read:false,starred:false,atts:["Partnership_Proposal_v3.pptx"],av:"MO",avColor:"#00b7c3"}, {id:6,account:"sales@example-corp.com",folder:"inbox",from:"Lena Fischer",from_email:"lena@gmbh.de",to:"sales@example-corp.com",subject:"Order #4821 — shipping inquiry",preview:"Good morning, I wanted to follow up on my order placed two weeks ago...",body:"Good morning,\n\nFollowing up on order #4821 placed two weeks ago. No shipping confirmation received.\n\nLena Fischer",date:"10:05 AM",read:false,starred:false,atts:[],av:"LF",avColor:"#0063b1"}, {id:7,account:"j.doe@mailbox.org",folder:"inbox",from:"Tech Digest",from_email:"news@techdigest.io",to:"j.doe@mailbox.org",subject:"This week in open source",preview:"Mutt 2.3.2 released, Neovim 0.11 ships treesitter overhaul...",body:"This Week in Open Source\n─────────────────────────\n• Mutt 2.3.2 released\n• Neovim 0.11 treesitter overhaul\n• Linux 6.9 released",date:"9:00 AM",read:true,starred:false,atts:[],av:"TD",avColor:"#498205"}, {id:8,account:"default",folder:"inbox",from:"Mutt Daemon",from_email:"mutt@localhost",to:"default",subject:"System notification — 4 new messages",preview:"Your mailbox has received 4 new messages...",body:"Mutt Mail Notification\n──────────────────────\nMailbox: .Mails/default/inbox\nNew msgs: 4\nSMB: \\\\mailserver\\mail (connected)",date:"10:55 AM",read:true,starred:false,atts:[],av:"MD",avColor:"#107c10"}, ]; const INIT_RULES=[ {id:"r1",name:"Newsletter auto-archive",enabled:true,condLogic:"any",conditions:[{id:"c1",field:"subject",op:"contains",value:"unsubscribe"},{id:"c2",field:"from",op:"contains",value:"newsletter"}],actions:[{id:"a1",type:"move",folder:"archive"},{id:"a2",type:"markRead"}]}, {id:"r2",name:"Flag partnership emails",enabled:true,condLogic:"any",conditions:[{id:"c3",field:"subject",op:"contains",value:"partnership"}],actions:[{id:"a3",type:"star"}]}, ]; const INIT_CONTACTS=[ {id:"ct1",name:"Sarah Chen", email:"sarah@acme.com", av:"SC",avColor:"#0078d4"}, {id:"ct2",name:"James Holloway", email:"james@dev.io", av:"JH",avColor:"#744da9"}, {id:"ct3",name:"Priya Nair", email:"priya@design.studio", av:"PN",avColor:"#e87722"}, {id:"ct4",name:"Mark Okafor", email:"mark@partners.net", av:"MO",avColor:"#00b7c3"}, {id:"ct5",name:"Lena Fischer", email:"lena@gmbh.de", av:"LF",avColor:"#0063b1"}, {id:"ct6",name:"Tech Digest", email:"news@techdigest.io", av:"TD",avColor:"#498205"}, ]; // ── Utilities ───────────────────────────────────────────────────────────────── const quoteBody=(text,from)=>`\n\n— On ${new Date().toDateString()}, ${from} wrote:\n${(text??"").split("\n").map(l=>`> ${l}`).join("\n")}`; const fwdBody=e=>`\n\n---------- Forwarded message ----------\nFrom: ${e.from}\nDate: ${e.date}\nSubject: ${e.subject}\nTo: ${e.to}\n\n${e.body??""}`; const formatSize=b=>b<1024?b+" B":b<1048576?(b/1024).toFixed(1)+" KB":(b/1048576).toFixed(1)+" MB"; function getAttIcon(name){ const ext=(name.split(".").pop()||"").toLowerCase(); if(["jpg","jpeg","png","gif","svg","webp"].includes(ext))return "ti-photo"; if(ext==="pdf")return "ti-file-type-pdf"; if(["doc","docx"].includes(ext))return "ti-file-type-doc"; if(["xls","xlsx"].includes(ext))return "ti-file-type-xls"; if(["ppt","pptx"].includes(ext))return "ti-file-type-ppt"; if(["zip","gz","tar","7z"].includes(ext))return "ti-file-zip"; if(["eml","msg"].includes(ext))return "ti-mail"; if(["txt","md"].includes(ext))return "ti-file-text"; return "ti-file"; } function printAttachment(name,srcEmail){ const ext=(name.split(".").pop()||"").toLowerCase(); let html; if(ext==="eml"&&srcEmail){ html=`${name}
From
${srcEmail.from||""} <${srcEmail.from_email||""}>
To
${srcEmail.to||""}
Subject
${srcEmail.subject||""}
Date
${srcEmail.date||""}
${(srcEmail.body||"").replace(/&/g,"&").replace(//g,">")}
`; } else { html=`${name}

📎 ${name}

Attachment: ${name}

From email: ${srcEmail?.subject||"(no subject)"}

Date: ${new Date().toLocaleString()}

This is a demo attachment — print preview via DashMail.

`; } const w=window.open("","_blank","width=800,height=600"); if(!w)return; w.document.write(html); w.document.close(); w.focus(); setTimeout(()=>{w.print();},400); } function downloadAttachment(name,srcEmail){ const ext=(name.split(".").pop()||"").toLowerCase(); let content,mime; if(ext==="eml"&&srcEmail){ content=[ `From: ${srcEmail.from||""} <${srcEmail.from_email||""}>`, `To: ${srcEmail.to||""}`, `Subject: ${srcEmail.subject||""}`, `Date: ${srcEmail.date||new Date().toUTCString()}`, `MIME-Version: 1.0`, `Content-Type: text/plain; charset=UTF-8`, ``, srcEmail.body||"" ].join("\r\n"); mime="message/rfc822"; } else { content=`Demo attachment: ${name}\nGenerated by DashMail\nDate: ${new Date().toLocaleString()}\n\nThis is a placeholder file. In a live deployment this file would be fetched from the SMB share.`; mime="text/plain"; } const blob=new Blob([content],{type:mime}); const url=URL.createObjectURL(blob); const a=document.createElement("a"); a.href=url; a.download=name; a.click(); setTimeout(()=>URL.revokeObjectURL(url),5000); } const uid=()=>Math.random().toString(36).slice(2,9); function printEmail(email){ const w=window.open("","_blank","width=720,height=960"); w.document.write(`${email.subject}

${email.subject}

From:${email.from} <${email.from_email}>
To:${email.to}
Date:${email.date}
${email.atts?.length?`
Attachments: ${email.atts.join(", ")}
`:""}
${(email.body??"").replace(/`); w.document.close();w.focus();setTimeout(()=>w.print(),400); } // ── Base components ─────────────────────────────────────────────────────────── function Avatar({initials,color,size=36}){ return
{initials}
; } function IBtn({icon,title,onClick,C,danger=false,active=false,size=16,label=""}){ const[h,setH]=useState(false); return