diff --git a/src/components/BreadcrumbBar.tsx b/src/components/BreadcrumbBar.tsx
index ee3af859..af15c0d5 100644
--- a/src/components/BreadcrumbBar.tsx
+++ b/src/components/BreadcrumbBar.tsx
@@ -1,6 +1,7 @@
import { memo } from 'react'
import type { VaultEntry, NoteStatus } from '../types'
import { cn } from '@/lib/utils'
+import { isEmoji } from '../utils/emoji'
import {
MagnifyingGlass,
GitBranch,
@@ -179,7 +180,10 @@ export const BreadcrumbBar = memo(function BreadcrumbBar({
{entry.isA || 'Note'}
›
-
{entry.title}
+
+ {entry.icon && isEmoji(entry.icon) && {entry.icon}}
+ {entry.title}
+
·
{wordCount.toLocaleString()} words
{noteStatus === 'pendingSave' && (
diff --git a/src/components/TabBar.tsx b/src/components/TabBar.tsx
index 89fcd8b1..cf61bf11 100644
--- a/src/components/TabBar.tsx
+++ b/src/components/TabBar.tsx
@@ -5,6 +5,7 @@ import { cn } from '@/lib/utils'
import { X } from 'lucide-react'
import { Plus, Columns, ArrowsOutSimple, ArrowLeft, ArrowRight } from '@phosphor-icons/react'
import { computeTabMaxWidth } from '@/utils/tabLayout'
+import { isEmoji } from '../utils/emoji'
interface Tab {
entry: VaultEntry
@@ -242,6 +243,7 @@ function TabItem({ tab, isActive, isEditing, noteStatus, isDragging, showDropBef
) : (
{ e.stopPropagation(); onDoubleClick() }}>
+ {tab.entry.icon && isEmoji(tab.entry.icon) && {tab.entry.icon}}
{tab.entry.title}
)}
diff --git a/src/components/note-list/PinnedCard.tsx b/src/components/note-list/PinnedCard.tsx
index db9bc09e..a7971fea 100644
--- a/src/components/note-list/PinnedCard.tsx
+++ b/src/components/note-list/PinnedCard.tsx
@@ -2,6 +2,7 @@ import type { VaultEntry } from '../../types'
import { getTypeColor, getTypeLightColor } from '../../utils/typeColors'
import { getTypeIcon } from '../NoteItem'
import { relativeDate, getDisplayDate } from '../../utils/noteListHelpers'
+import { isEmoji } from '../../utils/emoji'
export function PinnedCard({ entry, typeEntryMap, onClickNote, showDate }: {
entry: VaultEntry
@@ -17,7 +18,10 @@ export function PinnedCard({ entry, typeEntryMap, onClickNote, showDate }: {
onClickNote(entry, e)}>
{/* eslint-disable-next-line react-hooks/static-components */}
-
{entry.title}
+
+ {entry.icon && isEmoji(entry.icon) && {entry.icon}}
+ {entry.title}
+
{entry.snippet}
{showDate &&
{relativeDate(getDisplayDate(entry))}
}
diff --git a/tests/smoke/emoji-icon-everywhere.spec.ts b/tests/smoke/emoji-icon-everywhere.spec.ts
new file mode 100644
index 00000000..746247e6
--- /dev/null
+++ b/tests/smoke/emoji-icon-everywhere.spec.ts
@@ -0,0 +1,65 @@
+import { test, expect } from '@playwright/test'
+
+test.describe('Emoji icon shown everywhere title appears', () => {
+ test.beforeEach(async ({ page }) => {
+ await page.goto('/')
+ await page.waitForLoadState('networkidle')
+ await page.waitForTimeout(2500)
+ })
+
+ test('emoji icon appears in tab bar, breadcrumb, and note list after setting it', async ({ page }) => {
+ // Open a note
+ const noteItem = page.locator('[data-testid="note-list-container"] .cursor-pointer').first()
+ await noteItem.waitFor({ timeout: 5000 })
+ await noteItem.click()
+ await page.waitForTimeout(500)
+
+ // Remove any existing icon first for a clean state
+ const iconDisplay = page.locator('[data-testid="note-icon-display"]')
+ if (await iconDisplay.isVisible()) {
+ await iconDisplay.click()
+ await page.locator('[data-testid="note-icon-remove"]').click()
+ await page.waitForTimeout(300)
+ }
+
+ // Set an emoji icon
+ const iconArea = page.locator('[data-testid="note-icon-area"]')
+ await iconArea.hover()
+ await page.locator('[data-testid="note-icon-add"]').click()
+ const firstEmoji = page.locator('[data-testid="emoji-option"]').first()
+ const emojiText = await firstEmoji.textContent()
+ expect(emojiText).toBeTruthy()
+ await firstEmoji.click()
+ await page.waitForTimeout(500)
+
+ // Verify emoji in note list item
+ const noteListText = await noteItem.textContent()
+ expect(noteListText).toContain(emojiText!)
+
+ // Verify emoji in the tab (active tab has the truncate span with title)
+ const tabArea = page.locator('.group .truncate').first()
+ const tabText = await tabArea.textContent()
+ expect(tabText).toContain(emojiText!)
+ })
+
+ test('note without emoji shows no emoji span in tab or note list', async ({ page }) => {
+ // Open a note
+ const noteItem = page.locator('[data-testid="note-list-container"] .cursor-pointer').first()
+ await noteItem.waitFor({ timeout: 5000 })
+ await noteItem.click()
+ await page.waitForTimeout(500)
+
+ // Remove icon if present
+ const iconDisplay = page.locator('[data-testid="note-icon-display"]')
+ if (await iconDisplay.isVisible()) {
+ await iconDisplay.click()
+ await page.locator('[data-testid="note-icon-remove"]').click()
+ await page.waitForTimeout(300)
+ }
+
+ // The note list item title row should not contain an emoji span (mr-1)
+ const titleRow = noteItem.locator('.truncate.text-foreground').first()
+ const emojiSpans = titleRow.locator('.mr-1')
+ await expect(emojiSpans).toHaveCount(0)
+ })
+})