From 22aca3cfd1c45261e36f6a484ed97dec19082f52 Mon Sep 17 00:00:00 2001 From: Devon Date: Mon, 25 May 2026 09:07:43 -0400 Subject: [PATCH] =?UTF-8?q?Initial=20commit=20=E2=80=94=20DashMail=20proto?= =?UTF-8?q?type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 17 + DashMailClient_3.jsx | 4210 +++++++++++++++++++++++++++++++++ DashMail_Architecture_v2.html | 376 +++ DashMail_Brand_Identity.html | 720 ++++++ DashMail_Logo.svg | 21 + DashMail_Logo_Preview.html | 435 ++++ index.html | 41 + login-bg-default.jpg | Bin 0 -> 1687960 bytes push.bat | 35 + push.ps1 | 57 + 10 files changed, 5912 insertions(+) create mode 100644 .gitignore create mode 100644 DashMailClient_3.jsx create mode 100644 DashMail_Architecture_v2.html create mode 100644 DashMail_Brand_Identity.html create mode 100644 DashMail_Logo.svg create mode 100644 DashMail_Logo_Preview.html create mode 100644 index.html create mode 100644 login-bg-default.jpg create mode 100644 push.bat create mode 100644 push.ps1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..023098f --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# Backups created during Claude sessions +backup_*/ + +# Old file versions +DashMailClient.jsx +DashMail_Architecture.html + +# Screenshots +screenshot_*.jpg + +# System +.DS_Store +Thumbs.db +desktop.ini + +# Node (if added later) +node_modules/ diff --git a/DashMailClient_3.jsx b/DashMailClient_3.jsx new file mode 100644 index 0000000..ef37b0c --- /dev/null +++ b/DashMailClient_3.jsx @@ -0,0 +1,4210 @@ +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