Add context-sensitive help panel to Settings

The ? button in Settings now opens a floating tips card that shows
relevant hints for whichever section is active. Tips update instantly
when switching sections. Panel is dismissible via its own × button.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Devon
2026-05-25 10:35:56 -04:00
parent dc096207ae
commit 65dbf479b1
+84 -2
View File
@@ -362,6 +362,60 @@ function SettingsEmailAddr({accounts,C,smbConfig,onDiscoverMore}){
</div>;
}
const HELP_TIPS={
general:{title:"General",tips:[
{icon:"ti-user", text:"Your name and initials appear in the sidebar and profile switcher."},
{icon:"ti-palette", text:"Profile colour helps distinguish accounts at a glance."},
{icon:"ti-language", text:"Language and date format apply to this profile only."},
]},
theme:{title:"Appearance",tips:[
{icon:"ti-moon", text:"Dark mode reduces eye strain in low-light environments."},
{icon:"ti-color-swatch",text:"Accent colour is used for highlights, buttons, and active states."},
{icon:"ti-photo", text:"Login background — landscape photos at 1920×1080 or wider work best."},
]},
notifications:{title:"Notifications",tips:[
{icon:"ti-bell", text:"Desktop notifications require browser permission — click Allow when prompted."},
{icon:"ti-volume", text:"Use the Preview button to audition each sound before saving."},
{icon:"ti-moon", text:"Do Not Disturb silences all alerts without disabling them permanently."},
]},
accounts:{title:"Accounts",tips:[
{icon:"ti-server", text:"Accounts map to folders on your SMB mail share."},
{icon:"ti-mail", text:"Each account has its own inbox, sent, drafts, and custom folders."},
]},
rules:{title:"Filters & Rules",tips:[
{icon:"ti-filter", text:"Rules run automatically on incoming mail."},
{icon:"ti-stack", text:"Combine multiple conditions with AND / OR logic."},
{icon:"ti-arrow-right",text:"Actions: move, star, mark as read, or send to junk."},
]},
privacy:{title:"Privacy",tips:[
{icon:"ti-eye-off", text:"Blocking tracking pixels prevents senders from knowing when you open mail."},
{icon:"ti-photo-off", text:"Disabling external images stops remote servers from logging your IP."},
{icon:"ti-receipt", text:"Read receipts are off by default — enable only if required."},
]},
emailaddr:{title:"Email addresses",tips:[
{icon:"ti-at", text:"Each address maps to a folder on your SMB mail share."},
{icon:"ti-refresh", text:"Scan for new accounts to detect folders added since last setup."},
]},
encryption:{title:"Encryption",tips:[
{icon:"ti-lock", text:"End-to-end encryption will be available in a future update."},
]},
spam:{title:"Spam & Trash",tips:[
{icon:"ti-trash", text:"Deleted items stay in Trash until the auto-empty period expires."},
{icon:"ti-ban", text:"Spam is automatically removed after the retention period you set."},
{icon:"ti-user-check", text:"Marking Not Spam whitelists the sender for future mail."},
]},
backup:{title:"Backup",tips:[
{icon:"ti-database", text:"Backups are stored locally — the SMB share is never modified."},
{icon:"ti-calendar", text:"Schedule daily or weekly backups to run automatically."},
{icon:"ti-history", text:"Older backup sets are pruned based on your retention setting."},
]},
advanced:{title:"Advanced",tips:[
{icon:"ti-lock", text:"Set a PIN to prevent unauthorised access to this profile."},
{icon:"ti-server", text:"SMB Share settings control which mail server folder DashMail reads."},
{icon:"ti-shield", text:"Auto-lock clears the session after a period of inactivity."},
]},
};
function FullSettings({profile,onSave,onClose,C,accounts,updateAccount,rules,setRules,allFolders,smbConfig,setSmbConfig,loginBg,setLoginBg,loginDark,setLoginDark,onSetupNew}){
const[section,setSection]=useState("general");
const[search,setSearch]=useState("");
@@ -369,6 +423,7 @@ function FullSettings({profile,onSave,onClose,C,accounts,updateAccount,rules,set
const[profName,setProfName]=useState(profile.name);
const[profInitials,setProfInitials]=useState(profile.initials);
const[profColor,setProfColor]=useState(profile.color);
const[helpOpen,setHelpOpen]=useState(false);
const[advancedTab,setAdvancedTab]=useState("security");
const filtered=search
@@ -391,7 +446,7 @@ function FullSettings({profile,onSave,onClose,C,accounts,updateAccount,rules,set
<div style={{display:"flex",alignItems:"center",justifyContent:"space-between",padding:"16px 20px",borderBottom:`1px solid ${C.border}`,flexShrink:0}}>
<span style={{fontSize:20,fontWeight:700,color:C.text}}>Settings</span>
<div style={{display:"flex",gap:4}}>
<IBtn icon="ti-help" title="Help" C={C} size={18}/>
<IBtn icon="ti-help" title="Help" C={C} size={18} active={helpOpen} onClick={()=>setHelpOpen(p=>!p)}/>
<IBtn icon="ti-x" title="Close" C={C} onClick={onClose} size={18}/>
</div>
</div>
@@ -424,7 +479,34 @@ function FullSettings({profile,onSave,onClose,C,accounts,updateAccount,rules,set
</div>
{/* Content area */}
<div style={{flex:1,overflowY:"auto",background:C.surface}}>
<div style={{flex:1,overflowY:"auto",background:C.surface,position:"relative"}}>
{/* Help panel */}
{helpOpen&&(()=>{
const tips=(HELP_TIPS[section]||HELP_TIPS.general);
return <div style={{position:"sticky",top:12,float:"right",width:230,marginRight:16,marginTop:12,marginLeft:8,
background:C.surface2,border:`1px solid ${C.border}`,borderRadius:10,
boxShadow:`0 6px 20px rgba(0,0,0,${C.isDark?0.4:0.12})`,zIndex:10,padding:"14px 16px",
fontSize:12.5,color:C.text}}>
<div style={{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:10}}>
<span style={{fontWeight:700,fontSize:13,color:C.accent,display:"flex",alignItems:"center",gap:6}}>
<i className="ti ti-help-circle" style={{fontSize:15}} aria-hidden="true"/>
{tips.title}
</span>
<button onClick={()=>setHelpOpen(false)} style={{background:"none",border:"none",cursor:"pointer",
color:C.muted,padding:2,fontSize:14,lineHeight:1}}>
<i className="ti ti-x" aria-hidden="true"/>
</button>
</div>
<div style={{display:"flex",flexDirection:"column",gap:10}}>
{tips.tips.map((t,i)=>(
<div key={i} style={{display:"flex",gap:9,alignItems:"flex-start"}}>
<i className={`ti ${t.icon}`} style={{fontSize:14,color:C.accent,marginTop:1,flexShrink:0}} aria-hidden="true"/>
<span style={{lineHeight:1.5,color:C.subtle}}>{t.text}</span>
</div>
))}
</div>
</div>;
})()}
<div style={{padding:"22px 28px"}}>
<h2 style={{fontSize:20,fontWeight:700,color:C.text,margin:"0 0 20px"}}>
{section==="advanced"