diff --git a/web/src/app.css b/web/src/app.css index 1c2b5c9..42e0eea 100644 --- a/web/src/app.css +++ b/web/src/app.css @@ -226,6 +226,14 @@ @apply bg-[var(--dark-background)]; } + .sidebar-item-content { + @apply flex flex-col; + } + + .sidebar-item-bottom-row { + @apply mt-1 flex items-center justify-between; + } + .sidebar-item-text { @apply text-xs text-[var(--light-text)]/60; } @@ -234,6 +242,30 @@ @apply text-[var(--dark-text)]/60; } + .sidebar-item-delete { + @apply flex h-6.5 w-6.5 items-center justify-center rounded-lg border-1 border-[var(--light-accent)]/20 bg-transparent p-1; + } + + .dark .sidebar-item-delete { + @apply border-[var(--dark-accent)]/20; + } + + .sidebar-item-delete > svg { + @apply text-[var(--light-accent)]/50; + } + + .dark .sidebar-item-delete > svg { + @apply text-[var(--dark-accent)]/50; + } + + .sidebar-item-delete:hover > svg { + @apply text-[var(--light-accent)] transition-colors delay-100; + } + + .dark .sidebar-item-delete:hover > svg { + @apply text-[var(--dark-accent)]; + } + .sidebar-divider { @apply divide-y divide-[var(--light-text)]/20; } diff --git a/web/src/lib/components/NoteView.svelte b/web/src/lib/components/NoteView.svelte index f1edce7..3e9040c 100644 --- a/web/src/lib/components/NoteView.svelte +++ b/web/src/lib/components/NoteView.svelte @@ -164,6 +164,22 @@ } } + const deleteNote = async (noteID: string) => { + try { + await apiClient.deleteNote(noteID) + + // If we're deleting the currently active note, clear the current note + if ($currentFullNote && $currentFullNote.id === noteID) { + currentFullNote.set(null) + } + + // Refresh the notes list due to updates being pushed to server + await loadNotes() + } catch (err) { + cError.set(`Failed to delete note: ${err instanceof Error ? err.message : "Unknown error"}`) + } + } + const logout = async () => { await apiClient.logout() } @@ -190,6 +206,7 @@ {logout} {createNewNote} {selectNote} + {deleteNote} on:close={closeSidebar} /> diff --git a/web/src/lib/components/Sidebar.svelte b/web/src/lib/components/Sidebar.svelte index da1377e..c1b7cef 100644 --- a/web/src/lib/components/Sidebar.svelte +++ b/web/src/lib/components/Sidebar.svelte @@ -2,6 +2,7 @@ import type { NoteMetadata, FullNote } from "$lib/client" import Close from "$lib/icons/Close.svelte" import CreateNew from "$lib/icons/CreateNew.svelte" + import Delete from "$lib/icons/Delete.svelte" import Logout from "$lib/icons/Logout.svelte" import Search from "$lib/icons/Search.svelte" import Settings from "$lib/icons/Settings.svelte" @@ -13,7 +14,8 @@ export let toggleSettings: () => void export let logout: () => Promise export let createNewNote: () => Promise - export let selectNote: (noteId: string, fetchRemote: boolean) => Promise + export let selectNote: (noteID: string, fetchRemote: boolean) => Promise + export let deleteNote: (noteID: string) => Promise const formatDate = (dateString: string | Date): string => { if (!dateString) { @@ -35,6 +37,14 @@ sidebarOpen = false } + const handleDeleteNote = (event: MouseEvent, noteID: string) => { + event.stopPropagation() + + if (confirm("Are you sure you want to delete this note?")) { + deleteNote(noteID) + } + } + const handleNoteKeydown = (event: KeyboardEvent, noteID: string) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault() // Prevent page scroll on space @@ -103,10 +113,24 @@ role="option" aria-selected={currentNote && note.id === currentNote.id} > -

{note.title || "Untitled Note"}

- + {/each} diff --git a/web/src/lib/icons/Delete.svelte b/web/src/lib/icons/Delete.svelte new file mode 100644 index 0000000..c5f3eda --- /dev/null +++ b/web/src/lib/icons/Delete.svelte @@ -0,0 +1,17 @@ + + + + + + +