From fe8c9f574bfd7539965603f096d9997c49c481a0 Mon Sep 17 00:00:00 2001 From: Luca Rossi Date: Tue, 3 Mar 2026 02:31:10 +0100 Subject: [PATCH] feat: show archived note indicator banner in editor (#183) Adds a subtle ArchivedNoteBanner component below the breadcrumb bar when a note is archived. Banner includes: - Muted gray background with archive icon + 'Archived' label - 'Unarchive' button (ArrowUUpLeft icon) wired to same handler as Cmd+E - Keyboard hint shown in button title Editor remains fully editable (banner is purely informational). Indicator appears/disappears reactively via entry.archived from store. Co-authored-by: Test --- design/archived-note-indicator.pen | 1 + src/components/ArchivedNoteBanner.test.tsx | 26 ++++++++++++ src/components/ArchivedNoteBanner.tsx | 48 ++++++++++++++++++++++ src/components/EditorContent.tsx | 4 ++ 4 files changed, 79 insertions(+) create mode 100644 design/archived-note-indicator.pen create mode 100644 src/components/ArchivedNoteBanner.test.tsx create mode 100644 src/components/ArchivedNoteBanner.tsx diff --git a/design/archived-note-indicator.pen b/design/archived-note-indicator.pen new file mode 100644 index 00000000..ebcc2d64 --- /dev/null +++ b/design/archived-note-indicator.pen @@ -0,0 +1 @@ +{"children":[],"variables":{}} \ No newline at end of file diff --git a/src/components/ArchivedNoteBanner.test.tsx b/src/components/ArchivedNoteBanner.test.tsx new file mode 100644 index 00000000..9d8c7985 --- /dev/null +++ b/src/components/ArchivedNoteBanner.test.tsx @@ -0,0 +1,26 @@ +import { render, screen, fireEvent } from '@testing-library/react' +import { describe, it, expect, vi } from 'vitest' +import { ArchivedNoteBanner } from './ArchivedNoteBanner' + +describe('ArchivedNoteBanner', () => { + it('renders archive icon and label', () => { + render() + expect(screen.getByTestId('archived-note-banner')).toBeTruthy() + expect(screen.getByText('Archived')).toBeTruthy() + }) + + it('renders unarchive button with keyboard hint', () => { + render() + const btn = screen.getByTestId('unarchive-btn') + expect(btn).toBeTruthy() + expect(btn.textContent).toContain('Unarchive') + expect(btn.title).toBe('Unarchive (Cmd+E)') + }) + + it('calls onUnarchive when button is clicked', () => { + const onUnarchive = vi.fn() + render() + fireEvent.click(screen.getByTestId('unarchive-btn')) + expect(onUnarchive).toHaveBeenCalledOnce() + }) +}) diff --git a/src/components/ArchivedNoteBanner.tsx b/src/components/ArchivedNoteBanner.tsx new file mode 100644 index 00000000..e55e1b78 --- /dev/null +++ b/src/components/ArchivedNoteBanner.tsx @@ -0,0 +1,48 @@ +import { Archive, ArrowUUpLeft } from '@phosphor-icons/react' + +interface ArchivedNoteBannerProps { + onUnarchive: () => void +} + +export function ArchivedNoteBanner({ onUnarchive }: ArchivedNoteBannerProps) { + return ( +
+ + Archived + +
+ ) +} diff --git a/src/components/EditorContent.tsx b/src/components/EditorContent.tsx index 8a03e5ef..784352da 100644 --- a/src/components/EditorContent.tsx +++ b/src/components/EditorContent.tsx @@ -3,6 +3,7 @@ import type { useCreateBlockNote } from '@blocknote/react' import { DiffView } from './DiffView' import { BreadcrumbBar } from './BreadcrumbBar' import { TrashedNoteBanner } from './TrashedNoteBanner' +import { ArchivedNoteBanner } from './ArchivedNoteBanner' import { RawEditorView } from './RawEditorView' import { countWords } from '../utils/wikilinks' import { SingleEditorView } from './SingleEditorView' @@ -173,6 +174,9 @@ export function EditorContent({ onDeletePermanently={() => onDeleteNote?.(activeTab.entry.path)} /> )} + {activeTab?.entry.archived && breadcrumbProps.onUnarchiveNote && ( + breadcrumbProps.onUnarchiveNote!(activeTab.entry.path)} /> + )} )