diff --git a/apps/web/src/actions/db.ts b/apps/web/src/actions/db.ts
index cd1a0f1d..20aa4de6 100644
--- a/apps/web/src/actions/db.ts
+++ b/apps/web/src/actions/db.ts
@@ -4,14 +4,12 @@ import { db } from "@/server/db";
import {
contentToSpace,
sessions,
- StoredContent,
storedContent,
- StoredSpace,
users,
space,
} from "@/server/db/schema";
import { SearchResult } from "@/contexts/MemoryContext";
-import { like, eq, and, sql, exists, asc, notExists } from "drizzle-orm";
+import { like, eq, and, sql, exists, asc, notExists, inArray, notInArray } from "drizzle-orm";
import { union } from "drizzle-orm/sqlite-core";
import { env } from "@/env";
@@ -82,6 +80,22 @@ export async function searchMemoriesAndSpaces(
}
}
+export async function getMemoriesFromUrl(urls: string[]) {
+
+ const user = await getUser();
+
+ if (!user) {
+ return [];
+ }
+
+ return urls.length > 0 ? await db.select()
+ .from(storedContent)
+ .where(and(
+ inArray(storedContent.url, urls),
+ eq(storedContent.user, user.id)
+ )).all() : []
+}
+
async function getUser() {
const token =
cookies().get("next-auth.session-token")?.value ??
@@ -167,6 +181,38 @@ export async function addSpace(name: string, memories: number[]) {
};
}
+export async function fetchContent(id: number) {
+
+
+ const user = await getUser();
+
+ if (!user) {
+ return null;
+ }
+
+ const fetchedMemory = await db.select()
+ .from(storedContent)
+ .where(and(
+ eq(storedContent.id, id),
+ eq(storedContent.user, user.id)
+ ));
+
+ const memory = fetchedMemory.length > 0 ? fetchedMemory[0] : null
+
+ const spaces = memory ? await db.select()
+ .from(contentToSpace)
+ .where(
+ eq(contentToSpace.contentId, memory.id)
+ ) : []
+
+
+ return {
+ memory,
+ spaces: spaces.map(s => s.spaceId)
+ }
+
+}
+
export async function fetchContentForSpace(
spaceId: number,
range?: {
@@ -174,6 +220,13 @@ export async function fetchContentForSpace(
limit: number;
},
) {
+
+ const user = await getUser();
+
+ if (!user) {
+ return null;
+ }
+
const query = db
.select()
.from(storedContent)
@@ -184,9 +237,19 @@ export async function fetchContentForSpace(
.from(contentToSpace)
.where(
and(
- eq(contentToSpace.spaceId, spaceId),
- eq(contentToSpace.contentId, storedContent.id),
- ),
+ and(
+ eq(contentToSpace.spaceId, spaceId),
+ eq(contentToSpace.contentId, storedContent.id),
+ ),
+ exists(
+ db.select()
+ .from(space)
+ .where(and(
+ eq(space.user, user.id),
+ eq(space.id, contentToSpace.spaceId)
+ ))
+ )
+ )
),
),
)
@@ -207,25 +270,30 @@ export async function fetchFreeMemories(range?: {
return [];
}
- const query = db
- .select()
- .from(storedContent)
- .where(
- and(
- notExists(
- db
- .select()
- .from(contentToSpace)
- .where(eq(contentToSpace.contentId, storedContent.id)),
- ),
- eq(storedContent.user, user.id),
- ),
- )
- .orderBy(asc(storedContent.savedAt));
+ try {
+ const query = db
+ .select()
+ .from(storedContent)
+ .where(
+ and(
+ notExists(
+ db
+ .select()
+ .from(contentToSpace)
+ .where(eq(contentToSpace.contentId, storedContent.id)),
+ ),
+ eq(storedContent.user, user.id),
+ ),
+ )
+ .orderBy(asc(storedContent.savedAt));
+
+ return range
+ ? await query.limit(range.limit).offset(range.offset)
+ : await query.all();
+ } catch {
+ return []
+ }
- return range
- ? await query.limit(range.limit).offset(range.offset)
- : await query.all();
}
export async function addMemory(
@@ -238,7 +306,7 @@ export async function addMemory(
return null;
}
- if (!content.content || content.content == "") {
+ if (!content.content || content.content.trim() === "") {
const resp = await fetch(
`https://cf-ai-backend.dhravya.workers.dev/getPageContent?url=${content.url}`,
{
@@ -259,30 +327,7 @@ export async function addMemory(
return null;
}
- console.log(content);
-
- console.log({ ...content, user: user.email });
-
- // Add to vectorDB
- const res = (await Promise.race([
- fetch("https://cf-ai-backend.dhravya.workers.dev/add", {
- method: "POST",
- headers: {
- "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY,
- },
- body: JSON.stringify({
- pageContent: content.content,
- title: content.title,
- url: content.url,
- user: user.email,
- }),
- }),
- new Promise((_, reject) =>
- setTimeout(() => reject(new Error("Request timed out")), 40000),
- ),
- ])) as Response;
-
- const [addedMemory] = await db
+ let [addedMemory] = await db
.insert(storedContent)
.values({
user: user.id,
@@ -303,12 +348,142 @@ export async function addMemory(
.returning()
: [];
+ if (content.type === 'note') {
+ addedMemory = (await db.update(storedContent)
+ .set({
+ url: addedMemory.url + addedMemory.id
+ })
+ .where(eq(storedContent.id, addedMemory.id))
+ .returning())[0]
+ }
+
+
+ // Add to vectorDB
+ const res = (await Promise.race([
+ fetch("https://cf-ai-backend.dhravya.workers.dev/add", {
+ method: "POST",
+ headers: {
+ "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY,
+ },
+ body: JSON.stringify({
+ pageContent: addedMemory.content,
+ title: addedMemory.title,
+ url: addedMemory.url,
+ user: user.email,
+ }),
+ }),
+ new Promise((_, reject) =>
+ setTimeout(() => reject(new Error("Request timed out")), 40000),
+ ),
+ ])) as Response;
+
return {
memory: addedMemory,
addedToSpaces,
};
}
+
+export async function updateMemory(
+ id: number,
+ { title, content, spaces }: {
+ title?: string;
+ content?: string;
+ spaces?: number[]
+ }
+) {
+ const user = await getUser();
+
+ if (!user) {
+ return null;
+ }
+
+ console.log("updating")
+
+ const [prev] = await db.select()
+ .from(storedContent)
+ .where(and(
+ eq(storedContent.user, user.id),
+ eq(storedContent.id, id)
+ ));
+
+ if (!prev) {
+ return null
+ }
+
+ const newContent = {
+ ...(title ? { title }: {}),
+ ...(content ? { content }: {}),
+ }
+
+ const updated = {
+ ...newContent,
+ ...prev
+ }
+
+ // Add to vectorDB
+ const res = (await Promise.race([
+ fetch("https://cf-ai-backend.dhravya.workers.dev/edit?uniqueUrl="+updated.url , {
+ method: "POST",
+ headers: {
+ "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY,
+ },
+ body: JSON.stringify({
+ pageContent: updated.content,
+ title: updated.title,
+ url: updated.url,
+ user: user.email,
+ }),
+ }),
+ new Promise((_, reject) =>
+ setTimeout(() => reject(new Error("Request timed out")), 40000),
+ ),
+ ])) as Response;
+
+ const [updatedMemory] = await db
+ .update(storedContent)
+ .set(newContent)
+ .where(
+ eq(storedContent.id, id)
+ )
+ .returning();
+
+ console.log(updatedMemory, newContent)
+
+ const removedFromSpaces = spaces ?
+ spaces.length > 0 ?
+ await db.delete(contentToSpace)
+ .where(and(
+ notInArray(contentToSpace.spaceId, spaces),
+ eq(contentToSpace.contentId, id)
+ )).returning()
+ : await db.delete(contentToSpace)
+ .where(
+ eq(contentToSpace.contentId, id)
+ )
+ : [];
+
+ const addedToSpaces =
+ (spaces && spaces.length > 0)
+ ? await db
+ .insert(contentToSpace)
+ .values(
+ spaces.map((s) => ({
+ contentId: id,
+ spaceId: s,
+ })),
+ )
+ .onConflictDoNothing()
+ .returning()
+ : [];
+
+ return {
+ memory: updatedMemory,
+ addedToSpaces,
+ removedFromSpaces
+ };
+}
+
export async function deleteSpace(id: number) {
const user = await getUser();
@@ -340,5 +515,20 @@ export async function deleteMemory(id: number) {
.where(and(eq(storedContent.user, user.id), eq(storedContent.id, id)))
.returning();
+ if (deleted) {
+
+ const res = (await Promise.race([
+ fetch(`https://cf-ai-backend.dhravya.workers.dev/delete?websiteUrl=${deleted.url}&user=${user.email}` , {
+ method: "DELETE",
+ headers: {
+ "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY,
+ }
+ }),
+ new Promise((_, reject) =>
+ setTimeout(() => reject(new Error("Request timed out")), 40000),
+ ),
+ ])) as Response;
+ }
+
return deleted;
}
diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css
index b09627ba..895b87e4 100644
--- a/apps/web/src/app/globals.css
+++ b/apps/web/src/app/globals.css
@@ -57,18 +57,22 @@ body {
padding-bottom: 15dvh;
}
-.chat-answer pre {
- @apply bg-rgray-3 rounded-md border border-rgray-5 p-3 text-sm my-5;
+.chat-answer code {
+ @apply bg-rgray-3 rounded-md border border-rgray-5 p-1 text-sm text-rgray-11;
}
.novel-editor pre {
- @apply bg-rgray-3 rounded-md border border-rgray-5 p-4 text-sm text-rgray-11;
+ @apply bg-rgray-3 rounded-md border border-rgray-5 p-4 my-5 text-sm text-rgray-11;
}
.chat-answer h1 {
@apply text-rgray-11 my-5 text-xl font-medium;
}
+.chat-answer a {
+ @apply underline underline-offset-1 opacity-90 hover:opacity-100;
+}
+
.chat-answer img {
@apply rounded-md font-medium my-5;
}
@@ -122,4 +126,4 @@ body {
.novel-editor .drag-handle {
@apply hidden;
-}
\ No newline at end of file
+}
diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx
index 1cc21adf..8df5dad3 100644
--- a/apps/web/src/app/page.tsx
+++ b/apps/web/src/app/page.tsx
@@ -64,8 +64,6 @@ export default async function Home() {
// Fetch only first 3 content of each spaces
let contents: ChachedSpaceContent[] = [];
- //console.log(await db.select().from(storedContent).)
-
await Promise.all([
collectedSpaces.forEach(async (space) => {
console.log("fetching ");
@@ -82,7 +80,7 @@ export default async function Home() {
}),
]);
- console.log(contents);
+ console.log('contents', contents);
// freeMemories
const freeMemories = await fetchFreeMemories(userData.id);
diff --git a/apps/web/src/components/ChatMessage.tsx b/apps/web/src/components/ChatMessage.tsx
index db0778c4..1567a9ac 100644
--- a/apps/web/src/components/ChatMessage.tsx
+++ b/apps/web/src/components/ChatMessage.tsx
@@ -1,9 +1,10 @@
import React, { useEffect } from "react";
import { motion } from "framer-motion";
-import { ArrowUpRight, Globe } from "lucide-react";
+import { ArrowUpRight, Globe, Text } from "lucide-react";
import { convertRemToPixels } from "@/lib/utils";
import { SpaceIcon } from "@/assets/Memories";
import Markdown from "react-markdown";
+import { ChatHistory } from "../../types/memory";
export function ChatAnswer({
children: message,
@@ -11,7 +12,7 @@ export function ChatAnswer({
loading = false,
}: {
children: string;
- sources?: string[];
+ sources?: ChatHistory['answer']['sources'];
loading?: boolean;
}) {
return (
@@ -29,15 +30,22 @@ export function ChatAnswer({
Related Memories
-
- {sources?.map((source) => (
+
diff --git a/apps/web/src/components/Main.tsx b/apps/web/src/components/Main.tsx
index bbdb630d..df6a08bf 100644
--- a/apps/web/src/components/Main.tsx
+++ b/apps/web/src/components/Main.tsx
@@ -13,6 +13,7 @@ import { useRouter, useSearchParams } from "next/navigation";
import { useMemory } from "@/contexts/MemoryContext";
import Image from "next/image";
+import { getMemoriesFromUrl } from "@/actions/db";
function supportsDVH() {
try {
@@ -185,11 +186,25 @@ export default function Main({ sidebarOpen }: { sidebarOpen: boolean }) {
},
);
- const sourcesInJson = (await sourcesResponse.json()) as {
- ids: string[];
- };
- console.log(sourcesInJson);
+ const sourcesInJson = getIdsFromSource(((await sourcesResponse.json()) as {
+ ids: string[]
+ }).ids) ?? [];
+
+
+ const notesInSources = sourcesInJson.filter(
+ (urls) => urls.startsWith("https://notes.supermemory.dhr.wtf/")
+ )
+ const nonNotes = sourcesInJson.filter(
+ i => !notesInSources.includes(i)
+ )
+
+ const fetchedTitles = await getMemoriesFromUrl(notesInSources);
+
+ const sources = [
+ ...nonNotes.map(n => ({ isNote: false, source: n ?? "
" })),
+ ...fetchedTitles.map(n => ({ isNote: true, source: n.title ?? "" }))
+ ]
setIsAiLoading(false);
setChatHistory((prev) => {
@@ -200,7 +215,7 @@ export default function Main({ sidebarOpen }: { sidebarOpen: boolean }) {
...lastMessage,
answer: {
parts: lastMessage.answer.parts,
- sources: getIdsFromSource(sourcesInJson.ids) ?? [],
+ sources
},
},
];
diff --git a/apps/web/src/components/Sidebar/AddMemoryDialog.tsx b/apps/web/src/components/Sidebar/AddMemoryDialog.tsx
index 95bb3d22..f6a7224f 100644
--- a/apps/web/src/components/Sidebar/AddMemoryDialog.tsx
+++ b/apps/web/src/components/Sidebar/AddMemoryDialog.tsx
@@ -165,13 +165,12 @@ export function NoteAddPage({ closeDialog }: { closeDialog: () => void }) {
onClick={() => {
if (check()) {
setLoading(true);
- const randomId = Math.floor(Math.random() * 1000000);
addMemory(
{
content,
title: name,
type: "note",
- url: `https://notes.supermemory.dhr.wtf/${randomId}`,
+ url: `https://notes.supermemory.dhr.wtf/`,
image: "",
savedAt: new Date(),
},
diff --git a/apps/web/src/components/Sidebar/DeleteConfirmation.tsx b/apps/web/src/components/Sidebar/DeleteConfirmation.tsx
new file mode 100644
index 00000000..9324b147
--- /dev/null
+++ b/apps/web/src/components/Sidebar/DeleteConfirmation.tsx
@@ -0,0 +1,35 @@
+import { Dialog, DialogContent, DialogTrigger, DialogTitle, DialogDescription, DialogClose, DialogFooter } from "../ui/dialog";
+
+export default function DeleteConfirmation({ onDelete, trigger = true, children }: { trigger?: boolean, onDelete?: () => void; children: React.ReactNode }) {
+ return (
+
+ )
+}
diff --git a/apps/web/src/components/Sidebar/EditNoteDialog.tsx b/apps/web/src/components/Sidebar/EditNoteDialog.tsx
new file mode 100644
index 00000000..b7760656
--- /dev/null
+++ b/apps/web/src/components/Sidebar/EditNoteDialog.tsx
@@ -0,0 +1,152 @@
+
+import { Editor } from "novel";
+import {
+ DialogClose,
+ DialogFooter,
+} from "../ui/dialog";
+import { Input } from "../ui/input";
+import { Markdown } from "tiptap-markdown";
+import { useEffect, useRef, useState } from "react";
+import { FilterSpaces } from "./FilterCombobox";
+import { useMemory } from "@/contexts/MemoryContext";
+import { Loader, Plus, Trash, X } from "lucide-react";
+import { motion } from "framer-motion";
+import { StoredContent } from "@/server/db/schema";
+import { fetchContent } from "@/actions/db";
+import { isArraysEqual } from "@/lib/utils";
+import DeleteConfirmation from "./DeleteConfirmation";
+
+
+export function NoteEdit({ memory, closeDialog }: { memory: StoredContent, closeDialog: () => any }) {
+ const { updateMemory, deleteMemory } = useMemory();
+
+ const [initialSpaces, setInitialSpaces] = useState([])
+ const [selectedSpacesId, setSelectedSpacesId] = useState([]);
+
+ const inputRef = useRef(null);
+ const [name, setName] = useState(memory.title ?? "");
+ const [content, setContent] = useState(memory.content);
+ const [loading, setLoading] = useState(false);
+
+ function check(): boolean {
+ const data = {
+ name: name.trim(),
+ content,
+ };
+ if (!data.name || data.name.length < 1) {
+ if (!inputRef.current) {
+ alert("Please enter a name for the note");
+ return false;
+ }
+ inputRef.current.value = "";
+ inputRef.current.placeholder = "Please enter a title for the note";
+ inputRef.current.dataset["error"] = "true";
+ setTimeout(() => {
+ inputRef.current!.placeholder = "Title of the note";
+ inputRef.current!.dataset["error"] = "false";
+ }, 500);
+ inputRef.current.focus();
+ return false;
+ }
+ return true;
+ }
+
+ useEffect(() => {
+ fetchContent(memory.id).then((data) => {
+ if (data?.spaces) {
+ setInitialSpaces(data.spaces)
+ setSelectedSpacesId(data.spaces)
+ }
+ })
+ }, [])
+
+ return (
+
+ setName(e.target.value)}
+ />
+ {
+ if (!editor) return;
+ setContent(editor.storage.markdown.getMarkdown());
+ }}
+ extensions={[Markdown]}
+ className="novel-editor bg-rgray-4 border-rgray-7 dark mt-5 max-h-[60vh] min-h-[40vh] w-[50vw] overflow-y-auto rounded-lg border [&>div>div]:p-5"
+ />
+
+
+ {
+ deleteMemory(memory.id)
+ }}>
+
+
+
+
+ Cancel
+
+
+
+ );
+}
+
diff --git a/apps/web/src/components/Sidebar/FilterCombobox.tsx b/apps/web/src/components/Sidebar/FilterCombobox.tsx
index f93ae710..7625e2b6 100644
--- a/apps/web/src/components/Sidebar/FilterCombobox.tsx
+++ b/apps/web/src/components/Sidebar/FilterCombobox.tsx
@@ -90,6 +90,7 @@ export function FilterSpaces({
align={align}
side={side}
className="w-[200px] p-0"
+ onCloseAutoFocus={e => e.preventDefault()}
>
@@ -128,7 +129,7 @@ export function FilterSpaces({
className="text-rgray-11"
>
- {space.name}
+ {space.name.length > 10 ? space.name.slice(0, 10) + "..." : space.name}
{selectedSpaces.includes(space.id)}
- {m.title}
+ {(m.title && m.title?.length > 14) ? m.title?.slice(0, 14) + "..." : m.title}
i.id === m.id) !== undefined
diff --git a/apps/web/src/components/Sidebar/MemoriesBar.tsx b/apps/web/src/components/Sidebar/MemoriesBar.tsx
index 6c640e26..1218407b 100644
--- a/apps/web/src/components/Sidebar/MemoriesBar.tsx
+++ b/apps/web/src/components/Sidebar/MemoriesBar.tsx
@@ -37,15 +37,15 @@ import {
DialogFooter,
DialogClose,
} from "../ui/dialog";
-import { Label } from "../ui/label";
import useViewport from "@/hooks/useViewport";
import useTouchHold from "@/hooks/useTouchHold";
import { DialogTrigger } from "@radix-ui/react-dialog";
import { AddMemoryPage, NoteAddPage, SpaceAddPage } from "./AddMemoryDialog";
import { ExpandedSpace } from "./ExpandedSpace";
import { StoredContent, StoredSpace } from "@/server/db/schema";
-import Image from "next/image";
import { useDebounce } from "@/hooks/useDebounce";
+import { NoteEdit } from "./EditNoteDialog";
+import DeleteConfirmation from "./DeleteConfirmation";
export function MemoriesBar() {
const [parent, enableAnimations] = useAutoAnimate();
@@ -194,39 +194,58 @@ const SpaceExitVariant: Variant = {
},
};
-export function MemoryItem({ id, title, image, type }: StoredContent) {
+export function MemoryItem(props: StoredContent) {
+
+ const { id, title, image, type } = props
+
const name = title
? title.length > 10
? title.slice(0, 10) + "..."
: title
: "";
- return (
-
-
+ const [isDialogOpen, setIsDialogOpen] = useState(false);
-
- {type === "page" ? (
-

{
- (e.target as HTMLImageElement).src =
- "/icons/white_without_bg.png";
- }}
- />
- ) : type === "note" ? (
-
-
-
- ) : (
- <>>
- )}
-
-
+ return (
+
);
}
@@ -254,6 +273,9 @@ export function SpaceItem({
}, [cachedMemories]);
const _name = name.length > 10 ? name.slice(0, 10) + "..." : name;
+
+ console.log(spaceMemories)
+
return (
void;
}) {
return (
-
+
);
}
diff --git a/apps/web/src/contexts/MemoryContext.tsx b/apps/web/src/contexts/MemoryContext.tsx
index e10984bb..d8a147a5 100644
--- a/apps/web/src/contexts/MemoryContext.tsx
+++ b/apps/web/src/contexts/MemoryContext.tsx
@@ -14,6 +14,7 @@ import {
deleteSpace,
deleteMemory,
fetchFreeMemories,
+ updateMemory,
} from "@/actions/db";
import { User } from "next-auth";
@@ -33,6 +34,7 @@ export const MemoryContext = React.createContext<{
search: typeof searchMemoriesAndSpaces;
deleteSpace: typeof deleteSpace;
deleteMemory: typeof deleteMemory;
+ updateMemory: typeof updateMemory;
}>({
spaces: [],
freeMemories: [],
@@ -42,6 +44,7 @@ export const MemoryContext = React.createContext<{
search: async () => [],
deleteMemory: (() => {}) as unknown as typeof deleteMemory,
deleteSpace: (() => {}) as unknown as typeof deleteSpace,
+ updateMemory: (() => {}) as unknown as typeof updateMemory,
});
export const MemoryProvider: React.FC<
@@ -98,7 +101,7 @@ export const MemoryProvider: React.FC<
await fetchContentForSpace(addedSpace.id, {
offset: 0,
limit: 3,
- })
+ }) ?? []
).map((m) => ({ ...m, space: addedSpace.id }));
setCachedMemories((prev) => [...prev, ...cachedMemories]);
@@ -132,6 +135,40 @@ export const MemoryProvider: React.FC<
};
};
+ const _updateMemory: typeof updateMemory = async (id, _data) => {
+ const data = await updateMemory(id, _data);
+
+ console.log(data)
+
+ if (data) {
+ if (!_data.spaces) {
+ console.log("non spaces", freeMemories.map(i => i.id === data.memory.id ? data.memory : i ))
+ setCachedMemories(prev => prev.map(i => i.id === data.memory.id ? { ...data.memory, space: i.space } : i ))
+ setFreeMemories(prev => prev.map(i => i.id === data.memory.id ? data.memory : i ))
+ return data
+ }
+ setCachedMemories(prev => prev.filter(i => i.id !== data.memory.id))
+ setFreeMemories(prev => prev.filter(i => i.id !== data.memory.id))
+ if (_data.spaces.length > 0) {
+ console.log('has space')
+ setCachedMemories(
+ prev => [
+ ...prev,
+ ..._data.spaces!.map(s => ({
+ ...data.memory,
+ space: s
+ }))
+ ]
+ )
+ } else {
+ console.log('does nto have space')
+ setFreeMemories(prev => [...prev, data.memory])
+ }
+ }
+
+ return data
+ }
+
return (
{children}
diff --git a/apps/web/src/lib/utils.ts b/apps/web/src/lib/utils.ts
index f50b526d..0fe5bdfd 100644
--- a/apps/web/src/lib/utils.ts
+++ b/apps/web/src/lib/utils.ts
@@ -87,3 +87,27 @@ export function countLines(textarea: HTMLTextAreaElement): number {
export function convertRemToPixels(rem: number) {
return rem * parseFloat(getComputedStyle(document.documentElement).fontSize);
}
+
+export function isArraysEqual(a: any[], b: any[]) {
+ if (a === b) return true;
+ if (a == null || b == null) return false;
+ if (a.length !== b.length) return false;
+
+ let isEqual = true;
+
+ a.forEach(i => {
+ if (!isEqual) return
+ isEqual = b.includes(i)
+ })
+
+ if (!isEqual)
+ return isEqual
+
+ b.forEach(i => {
+ if (!isEqual) return
+ isEqual = a.includes(i)
+ })
+
+ return isEqual
+
+}
diff --git a/apps/web/types/memory.tsx b/apps/web/types/memory.tsx
index 6bfda971..f5939224 100644
--- a/apps/web/types/memory.tsx
+++ b/apps/web/types/memory.tsx
@@ -125,6 +125,6 @@ export type ChatHistory = {
question: string;
answer: {
parts: { text: string }[];
- sources: string[];
+ sources: { isNote: boolean; source: string }[];
};
};