diff --git a/docs/inbox-ui-gap-analysis.md b/docs/inbox-ui-gap-analysis.md
new file mode 100644
index 0000000..6bdb84d
--- /dev/null
+++ b/docs/inbox-ui-gap-analysis.md
@@ -0,0 +1,116 @@
+# AGMail Inbox UI — Expectation vs Reality
+
+Reference: target mock (populated, high-density inbox).
+Current: implementation screenshot (sparse real data, 3 emails).
+
+---
+
+## 1. Email rows — structure & density
+
+| Area | Expected | Current | Gap |
+|------|----------|---------|-----|
+| Grid columns | Fixed: checkbox, star, avatar, sender, subject—preview, labels, attachment, date/actions | Partial grid; labels column collapses when empty; date/actions compete for space | Labels need reserved column; rail must overlay date |
+| Row height | Fixed 44–48px; hover never changes height | Row 1 visibly taller on hover | **Bug:** hover actions affect layout |
+| Hover actions | 30×30 hit areas, 14–16px icons, replace date in-place | Icons crammed at far right; row expands | Absolute overlay in date rail |
+| Unread | Blue dot + semibold sender/subject + warmer row bg | Weak/unused unread pip; read/unread similar | Stronger unread treatment |
+| Stars | Outline inactive, amber fill active | Present but low contrast | Tune star states |
+| Labels | Dedicated column, vertically aligned across rows | Labels drift near date when present | Fixed label column always reserved |
+| Attachments | Paperclip column before date | Works when present | Keep dedicated column |
+| Sender avatars | Brand logos + deterministic initials | All senders same purple SD | Vary initials colors; logos when domain known |
+| Overflow | Three-dot menu at row end | Missing | Optional future |
+
+## 2. Category navigation
+
+| Area | Expected | Current | Gap |
+|------|----------|---------|-----|
+| Zero badges | Hidden | Hidden ✓ | OK |
+| Non-zero badges | Coral pills when data exists | None shown (only 3 emails, counts may be 0) | Mechanism OK; needs real unread per category |
+| Active tab | 600 weight, coral underline under label width | Present but weak | Stronger active typography |
+| Icons | Distinct per category | Present | OK |
+| Pagination in tab bar | `1–10 of 243` + controls on right | `1–3` floating in tab bar only | Move to footer control cluster |
+
+## 3. Surface hierarchy
+
+| Area | Expected | Current | Gap |
+|------|----------|---------|-----|
+| Canvas | Warm off-white, slightly darker than cards | `#F4F3F1` — very close to white cards | Darken canvas ~2–3% |
+| Inbox card | White, subtle shadow, light border | White, very subtle contrast | Increase shadow, not border |
+| Layering | Canvas → workspace tone → white cards | Mostly two layers | Add workspace header tone |
+
+## 4. Right panel — AG Agent
+
+| Area | Expected | Current | Gap |
+|------|----------|---------|-----|
+| Empty state | Caught-up card when all metrics zero | Present ✓ | Improve green success tint |
+| Populated state | 4 metric cards + greeting + agenda + needs attention | Only shows when `hasSignals`; user sees empty with 3 emails | Threshold/data wiring OK; greeting missing in populated mode |
+| Quick actions | Equal-size buttons, no wrap | "Summarize inbox" wraps | Fix button width/height |
+| Today's agenda | Time + title + venue + color bar | Implemented when meeting emails exist | Needs richer extraction |
+| Needs attention | Avatar + name + status + age | Text only | Add mini avatars |
+| Ask input | Paperclip + send | Send only | Add attach icon |
+| Header | 15px/650 title, larger spark container | Slightly small | Increase presence |
+
+## 5. Sidebar
+
+| Area | Expected | Current | Gap |
+|------|----------|---------|-----|
+| Badges | Inbox 12, Drafts 3, labels, Action Items 3 | Only Clients 1 | Mechanism exists; apply consistent badge component |
+| Label badge style | Dark translucent pill | Same coral as inbox | Neutral pill for labels |
+| More | Collapsible after Personal | Missing | Add More row |
+| Compose | Gradient + coral shadow + split dropdown | Flat coral, caret not separate hit area | Split button styling |
+| Profile block | Anchored bottom, not clipped | May clip on short viewports | Flex column + `margin-top: auto` |
+| Active inbox | Coral indicator + count | Active state weak | Polish active row |
+
+## 6. Header / toolbar
+
+| Area | Expected | Current | Gap |
+|------|----------|---------|-----|
+| Search width | Wide command surface | OK but large dead zone before filter | Tighten action cluster |
+| Icon contrast | Secondary but readable | Too faint (~disabled) | +10–15% contrast |
+| Account block | Name 12px/600, email 10px, chevron | Typography too small | Increase legibility |
+| Ask Agent | Lavender pill | Present ✓ | OK |
+
+## 7. Pagination / list controls
+
+| Area | Expected | Current | Gap |
+|------|----------|---------|-----|
+| Footer cluster | Range + prev/next + density toggles | Only when paginating; minimal | Always show when emails exist |
+| Range format | `1–10 of 243` | `1–3` in tab bar | Full footer cluster |
+
+## 8. Notes utility
+
+| Area | Expected | Current | Gap |
+|------|----------|---------|-----|
+| Zero notes | Low emphasis | Same weight as agent card | Reduce contrast when `Notes (0)` |
+| With notes | More visible | Collapsible ✓ | OK |
+
+## 9. Micro-interactions
+
+| Area | Expected | Current | Gap |
+|------|----------|---------|-----|
+| Row hover | Actions fade in, no layout shift | Layout shift | Fix overlay |
+| Nav/category hover | Subtle surfaces | Partial | Consistent transitions |
+| Compose press | Slight scale/brightness | Minimal | Add press feedback |
+
+## 10. Color anchors
+
+| Area | Expected | Current | Gap |
+|------|----------|---------|-----|
+| Contextual labels | Blue, green, amber, lavender pills in rows | Some labels on row 2 | More label types in `emailLabels` ✓ |
+| Sender diversity | Brand colors/logos | Same purple for self-sent | Deterministic initials colors |
+
+---
+
+## Priority fix order
+
+1. **Row height bug** — hover rail overlay (blocking)
+2. **Surface contrast** — canvas + shadow
+3. **Row grid** — fixed columns, unread, labels column
+4. **Pagination footer** — full control cluster
+5. **Sidebar** — More, compose split, profile anchor, badge variants
+6. **Agent panel** — greeting, quick action sizing, attention avatars, empty-state tint
+7. **Header** — icon contrast, account typography, toolbar spacing
+8. **Notes** — zero-state de-emphasis
+
+## Visual rule
+
+Every component: **absent → present → interactive**. Zero data reduces complexity; real data increases density. **No state change may alter row dimensions.**
diff --git a/frontend/src/components/AgAgentPanel.jsx b/frontend/src/components/AgAgentPanel.jsx
index efc7b4a..25f0f6a 100644
--- a/frontend/src/components/AgAgentPanel.jsx
+++ b/frontend/src/components/AgAgentPanel.jsx
@@ -5,6 +5,7 @@ import {
ChevronRight,
FileSearch,
ListChecks,
+ Paperclip,
PenLine,
Receipt,
Send,
@@ -12,6 +13,7 @@ import {
} from "lucide-react";
import AgentInsight from "./AgentInsight";
import AgentSpark from "./AgentSpark";
+import SenderAvatar from "./SenderAvatar";
import { inboxContext } from "../utils/inboxSignals";
const ICON = { size: 14, strokeWidth: 1.75 };
@@ -19,10 +21,16 @@ const ICON = { size: 14, strokeWidth: 1.75 };
const cards = [
{ key: "important", label: "Important emails", captionKey: "importantFromClients", captionSuffix: "from clients", icon: Star, tone: "rose" },
{ key: "actionItems", label: "Action items", hint: "Need response", icon: ListChecks, tone: "blue" },
- { key: "meetings", label: "Meetings mentioned", hint: "On calendar", icon: Calendar, tone: "green" },
+ { key: "meetings", label: "Meetings mentioned", hint: "In your emails", icon: Calendar, tone: "green" },
{ key: "invoices", label: "Invoices & bills", hint: "This week", icon: Receipt, tone: "amber" },
];
+function greeting(name) {
+ const hour = new Date().getHours();
+ const period = hour < 12 ? "Good morning" : hour < 17 ? "Good afternoon" : "Good evening";
+ return `${period}, ${name}`;
+}
+
export default function AgAgentPanel({
account,
emails,
@@ -34,6 +42,7 @@ export default function AgAgentPanel({
}) {
const { stats, needsReply, today, deadlines } = inboxContext(emails);
const [agentQuery, setAgentQuery] = useState("");
+ const firstName = account?.first_name || "there";
const hasSignals = stats.important + stats.actionItems + stats.meetings + stats.invoices > 0;
function submitAgentQuery(event) {
@@ -56,9 +65,16 @@ export default function AgAgentPanel({
+ {!insight && hasSignals && (
+
+ {greeting(firstName)}
+ Here's what needs your attention today.
+
+ )}
+
{!insight && !hasSignals && (
-
You're all caught up.
+
You're all caught up.
No urgent messages or outstanding actions.
Inbox is quiet right now.
@@ -79,7 +95,7 @@ export default function AgAgentPanel({
className={`agent-stat agent-stat-${card.tone}`}
onClick={() => onQuickAction(card.key)}
>
-
+
{count}
{card.label}
{caption}
@@ -95,16 +111,20 @@ export default function AgAgentPanel({
Quick actions
@@ -138,8 +158,12 @@ export default function AgAgentPanel({
{needsReply.slice(0, 3).map((item) => (
))}
@@ -167,8 +191,11 @@ export default function AgAgentPanel({
onChange={(event) => setAgentQuery(event.target.value)}
placeholder="Ask about your emails..."
/>
-
))}
+ setLabelsExpanded((open) => !open)}
+ >
+
+ {labelsExpanded ? "Less" : "More"}
+
diff --git a/frontend/src/components/MailRow.jsx b/frontend/src/components/MailRow.jsx
index 451134b..a1ae1f1 100644
--- a/frontend/src/components/MailRow.jsx
+++ b/frontend/src/components/MailRow.jsx
@@ -2,6 +2,7 @@ import {
Archive,
Clock3,
MailOpen,
+ MoreHorizontal,
Paperclip,
Star,
Trash2,
@@ -11,7 +12,7 @@ import SenderAvatar from "./SenderAvatar";
import { senderDisplayName } from "../utils/senderAvatar";
import { attachmentCount, emailLabels } from "../utils/inboxSignals";
-const ICON = { size: 14, strokeWidth: 1.75 };
+const ICON = { size: 15, strokeWidth: 1.75 };
function formatTimestamp(value) {
if (!value) return "";
@@ -101,10 +102,13 @@ export default function MailRow({
title={starred ? "Unstar" : "Star"}
aria-pressed={starred}
>
-
+
- {senderName}
+
+ {unread && }
+ {senderName}
+
{subject}
{snippet && (
@@ -114,43 +118,36 @@ export default function MailRow({
>
)}
- {labels.length > 0 && (
-
- {labels.map((label) => (
-
{label.name}
- ))}
+
+ {labels.map((label) => (
+ {label.name}
+ ))}
+
+
0 ? "" : " empty"}`} title={files > 0 ? `${files} attachment${files === 1 ? "" : "s"}` : undefined}>
+ {files > 0 && }
+
+
+
+
+
stop(event, () => onArchive?.(email))}>
+
+
+
stop(event, () => onTrash?.(email))}>
+
+
+
stop(event, () => onMarkUnread?.(email))}>
+
+
+
stop(event, () => onSnooze?.(email))}>
+
+
+
stop(event, () => onSummarize?.(email))}>
+
+
+
event.stopPropagation()}>
+
+
- )}
- {files > 0 ? (
-
-
-
- ) : (
-
- )}
-
-
-
stop(event, () => onArchive?.(email))}>
-
-
-
stop(event, () => onTrash?.(email))}>
-
-
-
stop(event, () => onMarkUnread?.(email))}>
-
-
-
stop(event, () => onSnooze?.(email))}>
-
-
-
stop(event, () => onSummarize?.(email))}
- >
-
-
);
diff --git a/frontend/src/components/MailboxView.jsx b/frontend/src/components/MailboxView.jsx
index e729f81..efa94f1 100644
--- a/frontend/src/components/MailboxView.jsx
+++ b/frontend/src/components/MailboxView.jsx
@@ -7,6 +7,7 @@ import {
Filter,
Inbox,
LayoutGrid,
+ List,
Mail,
RefreshCw,
Search,
@@ -592,9 +593,6 @@ export default function MailboxView({
)}
))}
-
- {listedEmails.length ? `${rangeStart}–${rangeEnd}` : title}
-
)}
@@ -657,11 +655,24 @@ export default function MailboxView({
))}
- {(cursorHistory.length > 0 || nextCursor) && (
+ {!status.loading && listedEmails.length > 0 && (
)}
diff --git a/frontend/src/components/StickyNotesPanel.jsx b/frontend/src/components/StickyNotesPanel.jsx
index ee16b9c..832452b 100644
--- a/frontend/src/components/StickyNotesPanel.jsx
+++ b/frontend/src/components/StickyNotesPanel.jsx
@@ -204,7 +204,7 @@ export default function StickyNotesPanel({ compact = false, contextEmail = null
const contextLabel = contextEmail ? senderDisplayName(contextEmail) : null;
return (
-