From edbfe02ee03c5c65040e39c7db4f102fdd45e6d4 Mon Sep 17 00:00:00 2001 From: patrick Date: Tue, 3 Mar 2026 16:13:22 -0500 Subject: [PATCH] feat: add notes UI components and routes Co-Authored-By: Claude Opus 4.5 --- src/routes/(app)/notes/+layout.svelte | 10 ++ src/routes/(app)/notes/+page.svelte | 157 +++++++++++++++++ src/routes/(app)/notes/NoteActionMenu.svelte | 135 ++++++++++++++ src/routes/(app)/notes/NoteCard.svelte | 107 +++++++++++ src/routes/(app)/notes/NoteEditor.svelte | 176 +++++++++++++++++++ src/routes/(app)/notes/NoteSearchBar.svelte | 38 ++++ src/routes/(app)/notes/NotesGrid.svelte | 68 +++++++ src/routes/(app)/notes/TagInput.svelte | 65 +++++++ src/routes/(app)/notes/[id]/+page.svelte | 122 +++++++++++++ src/routes/(app)/notes/archive/+page.svelte | 78 ++++++++ src/routes/(app)/notes/trash/+page.svelte | 112 ++++++++++++ 11 files changed, 1068 insertions(+) create mode 100644 src/routes/(app)/notes/+layout.svelte create mode 100644 src/routes/(app)/notes/+page.svelte create mode 100644 src/routes/(app)/notes/NoteActionMenu.svelte create mode 100644 src/routes/(app)/notes/NoteCard.svelte create mode 100644 src/routes/(app)/notes/NoteEditor.svelte create mode 100644 src/routes/(app)/notes/NoteSearchBar.svelte create mode 100644 src/routes/(app)/notes/NotesGrid.svelte create mode 100644 src/routes/(app)/notes/TagInput.svelte create mode 100644 src/routes/(app)/notes/[id]/+page.svelte create mode 100644 src/routes/(app)/notes/archive/+page.svelte create mode 100644 src/routes/(app)/notes/trash/+page.svelte diff --git a/src/routes/(app)/notes/+layout.svelte b/src/routes/(app)/notes/+layout.svelte new file mode 100644 index 0000000..5f43fd8 --- /dev/null +++ b/src/routes/(app)/notes/+layout.svelte @@ -0,0 +1,10 @@ + + +{@render children()} diff --git a/src/routes/(app)/notes/+page.svelte b/src/routes/(app)/notes/+page.svelte new file mode 100644 index 0000000..a6a1c10 --- /dev/null +++ b/src/routes/(app)/notes/+page.svelte @@ -0,0 +1,157 @@ + + +
+ +
+
+

Notes

+

Capture your thoughts with markdown

+
+ +
+ +
+ + +
+ + + +
+
+ + + + +
+ + + (currentFilter = v as any)}> + + + All + {#if !searchQuery} + + ({noteService.activeNotes.length}) + + {/if} + + + Pinned + {#if !searchQuery} + + ({noteService.pinnedNotes.length}) + + {/if} + + + Archived + {#if !searchQuery} + + ({noteService.archivedNotes.length}) + + {/if} + + + Trash + {#if !searchQuery} + + ({noteService.trashedNotes.length}) + + {/if} + + + +
+ + + openNote(note.id)} + /> +
diff --git a/src/routes/(app)/notes/NoteActionMenu.svelte b/src/routes/(app)/notes/NoteActionMenu.svelte new file mode 100644 index 0000000..023882a --- /dev/null +++ b/src/routes/(app)/notes/NoteActionMenu.svelte @@ -0,0 +1,135 @@ + + + + + + + + + Actions + + + {#if !note.deleted} + + + + {note.pinned ? 'Unpin' : 'Pin note'} + + + + {#if !note.archived} + + + Archive + + {:else} + + + Unarchive + + {/if} + + + + + (deleteDialog = true)}> + + Delete + + {:else} + + + + Restore + + + + + + (permanentDeleteDialog = true)}> + + Delete forever + + {/if} + + + + + + + + + Move to trash? + + This note will be moved to trash. You can restore it within 30 days. + + + + Cancel + + + + + + + + + + Delete permanently? + + This action cannot be undone. This will permanently delete the note. + + + + Cancel + + + + diff --git a/src/routes/(app)/notes/NoteCard.svelte b/src/routes/(app)/notes/NoteCard.svelte new file mode 100644 index 0000000..822d55b --- /dev/null +++ b/src/routes/(app)/notes/NoteCard.svelte @@ -0,0 +1,107 @@ + + + { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + onclick?.(); + } + }} +> +
+
+ {#if note.pinned} + + {/if} +

{displayTitle}

+
+
e.stopPropagation()}> + +
+
+ + + + {#if note.content} +
+ +
+ {/if} + + + {#if tags.length > 0} +
+ {#each tags.slice(0, 3) as tag (tag)} + + {tag} + + {/each} + {#if tags.length > 3} + + +{tags.length - 3} + + {/if} +
+ {/if} +
+ + + + {formatRelativeTime(note.updatedAt)} + + +
+ + diff --git a/src/routes/(app)/notes/NoteEditor.svelte b/src/routes/(app)/notes/NoteEditor.svelte new file mode 100644 index 0000000..914a484 --- /dev/null +++ b/src/routes/(app)/notes/NoteEditor.svelte @@ -0,0 +1,176 @@ + + +{#if note} +
+ +
+ +
+ + + + +
+ + +
+ + +
+
+ + {#if isSaving} + Saving... + {:else if hasUnsavedChanges} + Unsaved changes + {:else if lastSaved} + {formatSaveTime(lastSaved)} + {:else} + No changes + {/if} + + + {content.length} characters + +
+
+
+{:else} +
+

Note not found

+
+{/if} + diff --git a/src/routes/(app)/notes/NoteSearchBar.svelte b/src/routes/(app)/notes/NoteSearchBar.svelte new file mode 100644 index 0000000..b272511 --- /dev/null +++ b/src/routes/(app)/notes/NoteSearchBar.svelte @@ -0,0 +1,38 @@ + + +
+ + + {#if value} + + {/if} +
diff --git a/src/routes/(app)/notes/NotesGrid.svelte b/src/routes/(app)/notes/NotesGrid.svelte new file mode 100644 index 0000000..877fdc8 --- /dev/null +++ b/src/routes/(app)/notes/NotesGrid.svelte @@ -0,0 +1,68 @@ + + +{#if loading} + +
+ {#each Array(6) as _} +
+ +
+ {/each} +
+{:else if notes.length === 0} + +
+
+ + + +
+

{emptyMessage}

+

+ {#if emptyMessage === 'No notes found'} + Create your first note to get started. + {:else} + {emptyMessage} + {/if} +

+
+{:else} + +
+ {#each notes as note (note.id)} + onNoteClick?.(note)} /> + {/each} +
+{/if} diff --git a/src/routes/(app)/notes/TagInput.svelte b/src/routes/(app)/notes/TagInput.svelte new file mode 100644 index 0000000..6b6c966 --- /dev/null +++ b/src/routes/(app)/notes/TagInput.svelte @@ -0,0 +1,65 @@ + + +
+ + + + + {#if tags.length > 0} +
+ {#each tags as tag (tag)} + + {tag} + + + {/each} +
+ {/if} +
diff --git a/src/routes/(app)/notes/[id]/+page.svelte b/src/routes/(app)/notes/[id]/+page.svelte new file mode 100644 index 0000000..1a25b88 --- /dev/null +++ b/src/routes/(app)/notes/[id]/+page.svelte @@ -0,0 +1,122 @@ + + +{#if note} +
+ +
+
+
+
+ + Back to notes +
+ +
+ + + + + + + + + + + + + +
+
+
+
+ + +
+ +
+
+ + + + + + Manage Tags + + Add or remove tags to organize your notes. + + +
+ +
+ + + +
+
+{:else} +
+
+

Note not found

+

The note you're looking for doesn't exist.

+ +
+
+{/if} diff --git a/src/routes/(app)/notes/archive/+page.svelte b/src/routes/(app)/notes/archive/+page.svelte new file mode 100644 index 0000000..a0ddb72 --- /dev/null +++ b/src/routes/(app)/notes/archive/+page.svelte @@ -0,0 +1,78 @@ + + +
+ +
+
+ +
+

Archived Notes

+

+ {noteService.archivedNotes.length} + {noteService.archivedNotes.length === 1 ? 'note' : 'notes'} archived +

+
+
+ + +
+ + +
+
+ + + + + + + + openNote(note.id)} + /> +
diff --git a/src/routes/(app)/notes/trash/+page.svelte b/src/routes/(app)/notes/trash/+page.svelte new file mode 100644 index 0000000..4e25bcc --- /dev/null +++ b/src/routes/(app)/notes/trash/+page.svelte @@ -0,0 +1,112 @@ + + +
+ +
+
+ +
+

Trash

+

+ {noteService.trashedNotes.length} + {noteService.trashedNotes.length === 1 ? 'note' : 'notes'} in trash • Auto-delete after + 30 days +

+
+
+ +
+ +
+ + +
+ + + {#if noteService.trashedNotes.length > 0} + + {/if} +
+
+ + + + + + + + openNote(note.id)} + /> +
+ + + + + + Empty trash? + + This will permanently delete all notes in the trash. This action cannot be undone. + + + + Cancel + + + +