enh: note search

This commit is contained in:
Timothy Jaeryang Baek 2025-07-12 23:08:46 +04:00
parent f5613fb3c2
commit de343021c1

View file

@ -1,6 +1,8 @@
<script lang="ts">
import { toast } from 'svelte-sonner';
import fileSaver from 'file-saver';
import Fuse from 'fuse.js';
const { saveAs } = fileSaver;
import jsPDF from 'jspdf';
@ -32,7 +34,7 @@
import { WEBUI_NAME, config, prompts as _prompts, user } from '$lib/stores';
import { createNewNote, deleteNoteById, getNotes } from '$lib/apis/notes';
import { capitalizeFirstLetter, copyToClipboard } from '$lib/utils';
import { capitalizeFirstLetter, copyToClipboard, getTimeRange } from '$lib/utils';
import EllipsisHorizontal from '../icons/EllipsisHorizontal.svelte';
import DeleteConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
@ -44,6 +46,7 @@
import NoteMenu from './Notes/NoteMenu.svelte';
import FilesOverlay from '../chat/MessageInput/FilesOverlay.svelte';
import { marked } from 'marked';
import XMark from '../icons/XMark.svelte';
const i18n = getContext('i18n');
let loaded = false;
@ -51,13 +54,50 @@
let importFiles = '';
let query = '';
let notes = [];
let noteItems = [];
let fuse = null;
let selectedNote = null;
let notes = {};
$: if (fuse) {
notes = groupNotes(
query
? fuse.search(query).map((e) => {
return e.item;
})
: noteItems
);
}
let showDeleteConfirm = false;
const groupNotes = (res) => {
console.log(res);
if (!Array.isArray(res)) {
return {}; // or throw new Error("Notes response is not an array")
}
// Build the grouped object
const grouped: Record<string, any[]> = {};
for (const note of res) {
const timeRange = getTimeRange(note.updated_at / 1000000000);
if (!grouped[timeRange]) {
grouped[timeRange] = [];
}
grouped[timeRange].push({
...note,
timeRange
});
}
return grouped;
};
const init = async () => {
notes = await getNotes(localStorage.token);
noteItems = await getNotes(localStorage.token, true);
fuse = new Fuse(noteItems, {
keys: ['title']
});
};
const createNoteHandler = async () => {
@ -293,6 +333,34 @@
</div>
</DeleteConfirmDialog>
<div class="flex flex-col gap-1 px-3.5">
<div class=" flex flex-1 items-center w-full space-x-2">
<div class="flex flex-1 items-center">
<div class=" self-center ml-1 mr-3">
<Search className="size-3.5" />
</div>
<input
class=" w-full text-sm py-1 rounded-r-xl outline-hidden bg-transparent"
bind:value={query}
placeholder={$i18n.t('Search Notes')}
/>
{#if query}
<div class="self-center pl-1.5 translate-y-[0.5px] rounded-l-xl bg-transparent">
<button
class="p-0.5 rounded-full hover:bg-gray-100 dark:hover:bg-gray-900 transition"
on:click={() => {
query = '';
}}
>
<XMark className="size-3" strokeWidth="2" />
</button>
</div>
{/if}
</div>
</div>
</div>
<div class="px-4.5 @container h-full pt-2">
{#if Object.keys(notes).length > 0}
<div class="pb-10">