diff --git a/src/components/BreadcrumbBar.test.tsx b/src/components/BreadcrumbBar.test.tsx
index 81d60225..26445de6 100644
--- a/src/components/BreadcrumbBar.test.tsx
+++ b/src/components/BreadcrumbBar.test.tsx
@@ -111,6 +111,38 @@ describe('BreadcrumbBar — archive/unarchive', () => {
})
})
+describe('BreadcrumbBar — title in breadcrumb on scroll', () => {
+ it('does not show title when titleHidden is false', () => {
+ render()
+ expect(screen.queryByText('Test Note')).not.toBeInTheDocument()
+ })
+
+ it('shows type and title when titleHidden is true', () => {
+ render()
+ expect(screen.getByText('Note')).toBeInTheDocument()
+ expect(screen.getByText('›')).toBeInTheDocument()
+ expect(screen.getByText('Test Note')).toBeInTheDocument()
+ })
+
+ it('shows emoji icon when entry has an emoji icon', () => {
+ const entryWithEmoji = { ...baseEntry, icon: '🚀' }
+ render()
+ expect(screen.getByText('🚀')).toBeInTheDocument()
+ })
+
+ it('does not show icon when entry has a non-emoji icon', () => {
+ const entryWithPhosphor = { ...baseEntry, icon: 'cooking-pot' }
+ render()
+ expect(screen.queryByText('cooking-pot')).not.toBeInTheDocument()
+ })
+
+ it('falls back to "Note" when isA is null', () => {
+ const entryNoType = { ...baseEntry, isA: null }
+ render()
+ expect(screen.getByText('Note')).toBeInTheDocument()
+ })
+})
+
describe('BreadcrumbBar — raw editor toggle', () => {
it('shows Raw editor button with tooltip "Raw editor" when rawMode is off', () => {
const onToggleRaw = vi.fn()
diff --git a/src/components/BreadcrumbBar.tsx b/src/components/BreadcrumbBar.tsx
index 55b3ca5b..93b76ae4 100644
--- a/src/components/BreadcrumbBar.tsx
+++ b/src/components/BreadcrumbBar.tsx
@@ -32,6 +32,8 @@ interface BreadcrumbBarProps {
onRestore?: () => void
onArchive?: () => void
onUnarchive?: () => void
+ /** When true, the note title is scrolled out of view — show it inline. */
+ titleHidden?: boolean
}
const DISABLED_ICON_STYLE = { opacity: 0.4, cursor: 'not-allowed' } as const
@@ -161,19 +163,38 @@ function BreadcrumbActions({ entry, showDiffToggle, diffMode, diffLoading, onTog
)
}
+function BreadcrumbTitle({ entry }: { entry: VaultEntry }) {
+ const typeLabel = entry.isA ?? 'Note'
+ const icon = entry.icon
+ const emojiIcon = icon && /^\p{Emoji}/u.test(icon) ? icon : null
+ return (
+
+ {typeLabel}
+ ›
+ {emojiIcon && {emojiIcon}}
+ {entry.title}
+
+ )
+}
+
export const BreadcrumbBar = memo(function BreadcrumbBar({
- entry, ...actionProps
+ entry, titleHidden, ...actionProps
}: BreadcrumbBarProps) {
return (
)
diff --git a/src/components/EditorContent.tsx b/src/components/EditorContent.tsx
index c82de615..8fa39b8a 100644
--- a/src/components/EditorContent.tsx
+++ b/src/components/EditorContent.tsx
@@ -1,5 +1,5 @@
import type React from 'react'
-import { useCallback } from 'react'
+import { useCallback, useRef, useState, useEffect } from 'react'
import type { VaultEntry, NoteStatus } from '../types'
import type { useCreateBlockNote } from '@blocknote/react'
import { DiffView } from './DiffView'
@@ -120,8 +120,9 @@ function bindPath(cb: ((path: string) => void) | undefined, path: string) {
return cb ? () => cb(path) : undefined
}
-function ActiveTabBreadcrumb({ activeTab, props }: {
+function ActiveTabBreadcrumb({ activeTab, titleHidden, props }: {
activeTab: Tab
+ titleHidden: boolean
props: Omit
}) {
const wordCount = countWords(activeTab.content)
@@ -130,6 +131,7 @@ function ActiveTabBreadcrumb({ activeTab, props }: {
(null)
+ const [titleScrolledAway, setTitleScrolledAway] = useState(false)
+ const titleHidden = showEditor && titleScrolledAway
+
+ useEffect(() => {
+ const el = titleSectionRef.current
+ if (!el) return
+ const observer = new IntersectionObserver(
+ ([e]) => setTitleScrolledAway(!e.isIntersecting),
+ { threshold: 0 },
+ )
+ observer.observe(el)
+ return () => observer.disconnect()
+ }, [activeTab?.entry.path, showEditor])
+
const handleSetIcon = useCallback((emoji: string) => {
if (activeTab) onSetNoteIcon?.(activeTab.entry.path, emoji)
}, [activeTab, onSetNoteIcon])
@@ -181,6 +198,7 @@ export function EditorContent({
{activeTab && (
)}
@@ -203,7 +221,7 @@ export function EditorContent({
{showEditor && activeTab && (