diff --git a/.claude/launch.json b/.claude/launch.json
new file mode 100644
index 0000000..d669910
--- /dev/null
+++ b/.claude/launch.json
@@ -0,0 +1,11 @@
+{
+ "version": "0.0.1",
+ "configurations": [
+ {
+ "name": "dashmail",
+ "runtimeExecutable": "npx",
+ "runtimeArgs": ["serve", "-p", "3000", "."],
+ "port": 3000
+ }
+ ]
+}
diff --git a/.gitignore b/.gitignore
index 023098f..22c8931 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,10 @@ desktop.ini
# Node (if added later)
node_modules/
+
+# Local scripts
+push.bat
+push.ps1
+
+# Claude Code local settings (machine-specific)
+.claude/settings.local.json
diff --git a/DashMailClient_3.jsx b/DashMailClient_3.jsx
index ef37b0c..aba2223 100644
--- a/DashMailClient_3.jsx
+++ b/DashMailClient_3.jsx
@@ -2896,11 +2896,19 @@ function OnboardingWizard({accountId,onComplete,C}){
// ── Email Toolbar ─────────────────────────────────────────────────────────────
-function EmailToolbar({email,folder,C,onReply,onReplyAll,onForward,onArchive,onDelete,onJunk,onStar,onUnread,onPrint}){
+function EmailToolbar({email,folder,C,onReply,onReplyAll,onForward,onArchive,onDelete,onJunk,onStar,onUnread,onPrint,onAddToContacts}){
const[moreOpen,setMore]=useState(false);
+ const[addedToast,setAddedToast]=useState(null); // null | "added" | "exists"
const isSent=folder==="sent"||folder==="drafts"||folder==="out";
+
+ function handleAddToContacts(){
+ setMore(false);
+ const result=onAddToContacts?.({name:email.from,email:email.from_email,av:email.av,avColor:email.avColor});
+ setAddedToast(result||"added");
+ setTimeout(()=>setAddedToast(null),3000);
+ }
return
+ borderBottom:`1px solid ${C.border}`,background:C.surface,flexShrink:0,flexWrap:"wrap",position:"relative"}}>
{!isSent&&<>
@@ -2922,7 +2930,7 @@ function EmailToolbar({email,folder,C,onReply,onReplyAll,onForward,onArchive,onD
{[["ti-alert-triangle","Mark as junk",()=>{onJunk();setMore(false);}],
["ti-copy","Copy to folder",()=>setMore(false)],
["ti-code","View source",()=>setMore(false)],
- ["ti-user-plus","Add to contacts",()=>setMore(false)]].map(([ic,lbl,fn])=>(
+ ["ti-user-plus","Add to contacts",handleAddToContacts]].map(([ic,lbl,fn])=>(
+ {addedToast&&
+
+
+ {addedToast==="added"?"Contact added successfully":"Already in contacts"}
+
+
}
;
}
@@ -3694,6 +3712,16 @@ function EmailApp({activeProfile,profiles,setActiveProfile,saveProfile,loginBg,s
function doDelete(){if(selEmail){setEmails(p=>p.map(e=>e.id===selEmail.id?{...e,folder:"trash"}:e));setSelEmail(null);}}
function doJunk(){if(selEmail){setEmails(p=>p.map(e=>e.id===selEmail.id?{...e,folder:"trash"}:e));setSelEmail(null);}}
function doStar(){if(selEmail)setEmails(p=>p.map(e=>e.id===selEmail.id?{...e,starred:!e.starred}:e));}
+ function doAddToContacts(data){
+ if(!data?.email)return"added";
+ const exists=contacts.some(c=>c.email?.toLowerCase()===data.email.toLowerCase());
+ if(!exists){
+ setContacts(p=>[...p,{id:"ct"+uid(),name:data.name||data.email,email:data.email,
+ av:data.av||data.email.slice(0,2).toUpperCase(),avColor:data.avColor||"#0078d4",phone:"",address:""}]);
+ return"added";
+ }
+ return"exists";
+ }
function doUnread(){if(selEmail){setEmails(p=>p.map(e=>e.id===selEmail.id?{...e,read:false}:e));setSelEmail(null);}}
function openReply(mode){
if(!selEmail)return;
@@ -4029,7 +4057,8 @@ function EmailApp({activeProfile,profiles,setActiveProfile,saveProfile,loginBg,s
openReply("reply")} onReplyAll={()=>openReply("replyAll")}
onForward={()=>openReply("forward")} onArchive={doArchive} onDelete={doDelete}
- onJunk={doJunk} onStar={doStar} onUnread={doUnread} onPrint={()=>printEmail(selEmail)}/>
+ onJunk={doJunk} onStar={doStar} onUnread={doUnread} onPrint={()=>printEmail(selEmail)}
+ onAddToContacts={doAddToContacts}/>
{selEmail.subject}
diff --git a/push.bat b/push.bat
deleted file mode 100644
index d8de90d..0000000
--- a/push.bat
+++ /dev/null
@@ -1,35 +0,0 @@
-@echo off
-cd /d "%~dp0"
-
-echo.
-echo DashMail ^— Push to Gitea
-echo =========================================
-echo.
-
-git status --short
-echo.
-
-set /p msg=" Commit message: "
-
-if "%msg%"=="" (
- echo.
- echo Cancelled -- no message entered.
- echo.
- pause
- exit /b 1
-)
-
-echo.
-git add -A
-git commit -m "%msg%"
-git push
-
-echo.
-if %errorlevel%==0 (
- echo Pushed successfully.
-) else (
- echo Push failed -- check your connection to Gitea.
-)
-
-echo.
-pause
diff --git a/push.ps1 b/push.ps1
deleted file mode 100644
index f26c9d1..0000000
--- a/push.ps1
+++ /dev/null
@@ -1,57 +0,0 @@
-# DashMail — quick push to Gitea
-Set-Location $PSScriptRoot
-
-Write-Host ""
-Write-Host " DashMail — Push to Gitea" -ForegroundColor Cyan
-Write-Host " ─────────────────────────────────────" -ForegroundColor DarkGray
-
-# Show what has changed
-$status = git status --short
-if (-not $status) {
- Write-Host ""
- Write-Host " Nothing to commit — everything is up to date." -ForegroundColor Green
- Write-Host ""
- Read-Host " Press Enter to close"
- exit 0
-}
-
-Write-Host ""
-Write-Host " Changed files:" -ForegroundColor Yellow
-$status | ForEach-Object { Write-Host " $_" -ForegroundColor DarkYellow }
-Write-Host ""
-
-# Prompt for commit message
-$msg = Read-Host " Commit message"
-if (-not $msg.Trim()) {
- Write-Host ""
- Write-Host " Cancelled — no message entered." -ForegroundColor Red
- Write-Host ""
- Read-Host " Press Enter to close"
- exit 1
-}
-
-Write-Host ""
-
-# Stage, commit, push
-git add -A
-git commit -m $msg
-
-if ($LASTEXITCODE -ne 0) {
- Write-Host ""
- Write-Host " Commit failed." -ForegroundColor Red
- Read-Host " Press Enter to close"
- exit 1
-}
-
-git push
-
-if ($LASTEXITCODE -eq 0) {
- Write-Host ""
- Write-Host " Pushed successfully." -ForegroundColor Green
-} else {
- Write-Host ""
- Write-Host " Push failed — check your connection to Gitea." -ForegroundColor Red
-}
-
-Write-Host ""
-Read-Host " Press Enter to close"