diff --git a/frontend/src/components/MessageList.jsx b/frontend/src/components/MessageList.jsx
index 43ebe780..63d9c7e9 100644
--- a/frontend/src/components/MessageList.jsx
+++ b/frontend/src/components/MessageList.jsx
@@ -10,6 +10,7 @@ import { useSwipeRow } from '../hooks/useSwipeRow.js';
import ContextMenu from './ContextMenu.jsx';
import RowHoverActions from './RowHoverActions.jsx';
import GtdTabList from './GtdTabList.jsx';
+import GtdZeroPet from './GtdZeroPet.jsx';
import { useUiScale, descale } from '../hooks/useUiScale.js';
import {
gtdActiveForContext, buildGtdDisplaySections, GTD_COLORS, GTD_CHIP_BG, sectionBadge, isSelectedRow,
@@ -20,6 +21,7 @@ import { openReplyFromMessage, openForwardFromMessage } from '../utils/composeFr
import SenderAvatarImage from './SenderAvatarImage.jsx';
import { shortcutBus } from '../utils/shortcutBus.js';
import { createLatestRequest } from '../utils/latestRequest.js';
+import { resolveMessageListEmptyVisual } from '../utils/messageListEmptyState.js';
import { pendingMarkReadMap, completedMarkReadMap, setPending } from '../utils/pendingReads.js';
import { applyDeleteGuard, clearDeleteGuard, clearPendingDelete, setCompletedDelete, setPendingDelete } from '../utils/pendingDeletes.js';
@@ -3798,8 +3800,15 @@ function UndoBar({ notification, onDismiss, showTopBorder }) {
function EmptyState({ folderSyncing, searchQuery, unreadOnly, selectedFolder, accounts, onClearSearch, onShowAll, onCompose }) {
const { t } = useTranslation();
+ const visual = resolveMessageListEmptyVisual({
+ folderSyncing,
+ searchQuery,
+ unreadOnly,
+ hasAccounts: accounts.length > 0,
+ selectedFolder,
+ });
- if (folderSyncing) {
+ if (visual === 'syncing') {
return (
-
- {isInbox ? (
-
- ) : (
+ {isInbox ? (
+
+
+
+ ) : (
+
+
+ )}
{isInbox ? 'Inbox is empty' : 'Nothing here'}
diff --git a/frontend/src/utils/messageListEmptyState.js b/frontend/src/utils/messageListEmptyState.js
new file mode 100644
index 00000000..ff03fb2d
--- /dev/null
+++ b/frontend/src/utils/messageListEmptyState.js
@@ -0,0 +1,13 @@
+export function resolveMessageListEmptyVisual({
+ folderSyncing,
+ searchQuery,
+ unreadOnly,
+ hasAccounts,
+ selectedFolder,
+}) {
+ if (folderSyncing) return 'syncing';
+ if (searchQuery) return 'search';
+ if (unreadOnly) return 'unread';
+ if (!hasAccounts) return 'no-accounts';
+ return !selectedFolder || selectedFolder === 'INBOX' ? 'inbox-zero' : 'folder';
+}
diff --git a/frontend/src/utils/messageListEmptyState.test.js b/frontend/src/utils/messageListEmptyState.test.js
new file mode 100644
index 00000000..12f06f51
--- /dev/null
+++ b/frontend/src/utils/messageListEmptyState.test.js
@@ -0,0 +1,21 @@
+import assert from 'node:assert/strict';
+import test from 'node:test';
+import { resolveMessageListEmptyVisual } from './messageListEmptyState.js';
+
+test('uses the pet visual only for an unfiltered empty inbox', () => {
+ const base = {
+ folderSyncing: false,
+ searchQuery: '',
+ unreadOnly: false,
+ hasAccounts: true,
+ selectedFolder: 'INBOX',
+ };
+
+ assert.equal(resolveMessageListEmptyVisual(base), 'inbox-zero');
+ assert.equal(resolveMessageListEmptyVisual({ ...base, selectedFolder: null }), 'inbox-zero');
+ assert.equal(resolveMessageListEmptyVisual({ ...base, selectedFolder: 'Archive' }), 'folder');
+ assert.equal(resolveMessageListEmptyVisual({ ...base, searchQuery: 'invoice' }), 'search');
+ assert.equal(resolveMessageListEmptyVisual({ ...base, unreadOnly: true }), 'unread');
+ assert.equal(resolveMessageListEmptyVisual({ ...base, folderSyncing: true }), 'syncing');
+ assert.equal(resolveMessageListEmptyVisual({ ...base, hasAccounts: false }), 'no-accounts');
+});