|
@@ -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">
|