enh: reference note in chat

This commit is contained in:
Timothy Jaeryang Baek
2025-07-09 01:17:25 +04:00
parent f2ee99d760
commit d5f9bbc7a7
8 changed files with 105 additions and 24 deletions

View File

@@ -39,7 +39,7 @@ export const createNewNote = async (token: string, note: NoteItem) => {
return res;
};
export const getNotes = async (token: string = '') => {
export const getNotes = async (token: string = '', raw: boolean = false) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/notes/`, {
@@ -67,6 +67,10 @@ export const getNotes = async (token: string = '') => {
throw error;
}
if (raw) {
return res; // Return raw response if requested
}
if (!Array.isArray(res)) {
return {}; // or throw new Error("Notes response is not an array")
}
@@ -87,6 +91,37 @@ export const getNotes = async (token: string = '') => {
return grouped;
};
export const getNoteList = async (token: string = '') => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/notes/list`, {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
authorization: `Bearer ${token}`
}
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.then((json) => {
return json;
})
.catch((err) => {
error = err.detail;
console.error(err);
return null;
});
if (error) {
throw error;
}
return res;
};
export const getNoteById = async (token: string, id: string) => {
let error = null;

View File

@@ -1597,9 +1597,8 @@
let files = JSON.parse(JSON.stringify(chatFiles));
files.push(
...(userMessage?.files ?? []).filter((item) =>
['doc', 'file', 'collection'].includes(item.type)
),
...(responseMessage?.files ?? []).filter((item) => ['web_search_results'].includes(item.type))
['doc', 'file', 'note', 'collection'].includes(item.type)
)
);
// Remove duplicates
files = files.filter(

View File

@@ -9,6 +9,7 @@
import { tick, getContext, onMount, onDestroy } from 'svelte';
import { removeLastWordFromString, isValidHttpUrl } from '$lib/utils';
import { knowledge } from '$lib/stores';
import { getNoteList, getNotes } from '$lib/apis/notes';
const i18n = getContext('i18n');
@@ -75,10 +76,23 @@
}
};
onMount(() => {
onMount(async () => {
window.addEventListener('resize', adjustHeight);
adjustHeight();
let notes = await getNoteList(localStorage.token).catch(() => {
return [];
});
notes = notes.map((note) => {
return {
...note,
type: 'note',
name: note.title,
description: dayjs(note.updated_at / 1000000).fromNow()
};
});
let legacy_documents = $knowledge
.filter((item) => item?.meta?.document)
.map((item) => ({
@@ -144,14 +158,18 @@
]
: [];
items = [...collections, ...collection_files, ...legacy_collections, ...legacy_documents].map(
(item) => {
return {
...item,
...(item?.legacy || item?.meta?.legacy || item?.meta?.document ? { legacy: true } : {})
};
}
);
items = [
...notes,
...collections,
...collection_files,
...legacy_collections,
...legacy_documents
].map((item) => {
return {
...item,
...(item?.legacy || item?.meta?.legacy || item?.meta?.document ? { legacy: true } : {})
};
});
fuse = new Fuse(items, {
keys: ['name', 'description']
@@ -210,6 +228,12 @@
>
File
</div>
{:else if item?.type === 'note'}
<div
class="bg-blue-500/20 text-blue-700 dark:text-blue-200 rounded-sm uppercase text-xs font-bold px-1 shrink-0"
>
Note
</div>
{:else}
<div
class="bg-green-500/20 text-green-700 dark:text-green-200 rounded-sm uppercase text-xs font-bold px-1 shrink-0"

View File

@@ -442,7 +442,10 @@
const downloadHandler = async (type) => {
console.log('downloadHandler', type);
if (type === 'md') {
if (type === 'txt') {
const blob = new Blob([note.data.content.md], { type: 'text/plain' });
saveAs(blob, `${note.title}.txt`);
} else if (type === 'md') {
const blob = new Blob([note.data.content.md], { type: 'text/markdown' });
saveAs(blob, `${note.title}.md`);
} else if (type === 'pdf') {

View File

@@ -302,7 +302,7 @@
</div>
<div
class="mb-5 gap-2.5 grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4"
class="mb-5 gap-2.5 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5"
>
{#each notes[timeRange] as note, idx (note.id)}
<div
@@ -340,7 +340,7 @@
</div>
<div
class=" text-xs text-gray-500 dark:text-gray-500 mb-3 line-clamp-5 min-h-18"
class=" text-xs text-gray-500 dark:text-gray-500 mb-3 line-clamp-3 min-h-10"
>
{#if note.data?.content?.md}
{note.data?.content?.md}

View File

@@ -57,6 +57,15 @@
transition={flyAndScale}
sideOffset={8}
>
<DropdownMenu.Item
class="flex gap-2 items-center px-3 py-2 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
on:click={() => {
onDownload('txt');
}}
>
<div class="flex items-center line-clamp-1">{$i18n.t('Plain text (.txt)')}</div>
</DropdownMenu.Item>
<DropdownMenu.Item
class="flex gap-2 items-center px-3 py-2 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
on:click={() => {