diff options
| author | Mahesh Sanikommu <[email protected]> | 2025-10-29 15:35:57 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-10-29 15:35:57 -0700 |
| commit | 399f7beeeb8ded52ed1e000df056e08e79dba1d4 (patch) | |
| tree | bf2776ae000c11e8fd904f77769ac9904e476641 | |
| parent | chore: remove unused files (#540) (diff) | |
| download | supermemory-399f7beeeb8ded52ed1e000df056e08e79dba1d4.tar.xz supermemory-399f7beeeb8ded52ed1e000df056e08e79dba1d4.zip | |
fix: chat messages scroll effect (#544)
| -rw-r--r-- | apps/web/app/(navigation)/chat/[id]/page.tsx | 10 | ||||
| -rw-r--r-- | apps/web/app/(navigation)/layout.tsx | 4 | ||||
| -rw-r--r-- | apps/web/components/views/chat/chat-messages.tsx | 326 |
3 files changed, 170 insertions, 170 deletions
diff --git a/apps/web/app/(navigation)/chat/[id]/page.tsx b/apps/web/app/(navigation)/chat/[id]/page.tsx index da64a79d..0f429595 100644 --- a/apps/web/app/(navigation)/chat/[id]/page.tsx +++ b/apps/web/app/(navigation)/chat/[id]/page.tsx @@ -18,14 +18,8 @@ export default function ChatPage() { }, [chatId, setCurrentChatId]) return ( - <div className="flex flex-col w-full"> - <div className="flex flex-col h-[93vh]"> - <div className="flex-1 flex justify-center min-h-0 w-full md:px-4"> - <div className="flex flex-col min-h-0 w-full max-w-4xl"> - <ChatMessages /> - </div> - </div> - </div> + <div className="h-full overflow-hidden"> + <ChatMessages /> </div> ) } diff --git a/apps/web/app/(navigation)/layout.tsx b/apps/web/app/(navigation)/layout.tsx index 4c37c10e..7b7628bb 100644 --- a/apps/web/app/(navigation)/layout.tsx +++ b/apps/web/app/(navigation)/layout.tsx @@ -42,11 +42,11 @@ export default function NavigationLayout({ } }, []) return ( - <div className="relative min-h-screen"> + <div className="relative h-screen flex flex-col"> <div className="sticky top-0 z-50 bg-background/80 backdrop-blur-md border-b border-white/10"> <Header onAddMemory={() => setShowAddMemoryView(true)} /> </div> - {children} + <div className="flex-1">{children}</div> {showAddMemoryView && ( <AddMemoryView initialTab="note" diff --git a/apps/web/components/views/chat/chat-messages.tsx b/apps/web/components/views/chat/chat-messages.tsx index 05e8afdf..22bc8a2a 100644 --- a/apps/web/components/views/chat/chat-messages.tsx +++ b/apps/web/components/views/chat/chat-messages.tsx @@ -412,177 +412,183 @@ export function ChatMessages() { <div className="h-full flex flex-col w-full"> <div className="flex-1 relative"> <div - className="flex flex-col gap-2 absolute inset-0 overflow-y-auto px-4 pt-4 pb-7 scroll-pb-7 custom-scrollbar" + className="absolute inset-0 overflow-y-auto custom-scrollbar" onScroll={onScroll} ref={scrollContainerRef} > - {messages.map((message) => ( - <div - className={cn( - "flex my-2", - message.role === "user" - ? "items-center flex-row-reverse gap-2" - : "flex-col", - )} - key={message.id} - > + <div className="flex flex-col gap-2 max-w-4xl mx-auto px-4 md:px-2 pt-4 pb-7 scroll-pb-7"> + {messages.map((message) => ( <div className={cn( - "flex flex-col gap-2 md:max-w-4/5", + "flex my-2", message.role === "user" - ? "bg-accent/50 px-3 py-1.5 border border-border rounded-lg" - : "", + ? "items-center flex-row-reverse gap-2" + : "flex-col", )} + key={message.id} > - {message.parts - .filter((part) => - ["text", "tool-searchMemories", "tool-addMemory"].includes( - part.type, - ), - ) - .map((part, index) => { - switch (part.type) { - case "text": - return ( - <div key={`${message.id}-${part.type}-${index}`}> - <Streamdown>{part.text}</Streamdown> - </div> - ) - case "tool-searchMemories": { - switch (part.state) { - case "input-available": - case "input-streaming": - return ( - <div - className="text-sm flex items-center gap-2 text-muted-foreground" - key={`${message.id}-${part.type}-${index}`} - > - <Spinner className="size-4" /> Searching - memories... - </div> - ) - case "output-error": - return ( - <div - className="text-sm flex items-center gap-2 text-muted-foreground" - key={`${message.id}-${part.type}-${index}`} - > - <X className="size-4" /> Error recalling - memories - </div> - ) - case "output-available": { - const output = part.output - const foundCount = - typeof output === "object" && - output !== null && - "count" in output - ? Number(output.count) || 0 - : 0 - // @ts-expect-error - const results = Array.isArray(output?.results) - ? // @ts-expect-error - output.results - : [] - - return ( - <ExpandableMemories - foundCount={foundCount} - key={`${message.id}-${part.type}-${index}`} - results={results} - /> - ) + <div + className={cn( + "flex flex-col gap-2 ", + message.role === "user" + ? "bg-accent/50 px-3 py-1.5 border border-border rounded-lg" + : "", + )} + > + {message.parts + .filter((part) => + [ + "text", + "tool-searchMemories", + "tool-addMemory", + ].includes(part.type), + ) + .map((part, index) => { + switch (part.type) { + case "text": + return ( + <div key={`${message.id}-${part.type}-${index}`}> + <Streamdown>{part.text}</Streamdown> + </div> + ) + case "tool-searchMemories": { + switch (part.state) { + case "input-available": + case "input-streaming": + return ( + <div + className="text-sm flex items-center gap-2 text-muted-foreground" + key={`${message.id}-${part.type}-${index}`} + > + <Spinner className="size-4" /> Searching + memories... + </div> + ) + case "output-error": + return ( + <div + className="text-sm flex items-center gap-2 text-muted-foreground" + key={`${message.id}-${part.type}-${index}`} + > + <X className="size-4" /> Error recalling + memories + </div> + ) + case "output-available": { + const output = part.output + const foundCount = + typeof output === "object" && + output !== null && + "count" in output + ? Number(output.count) || 0 + : 0 + // @ts-expect-error + const results = Array.isArray(output?.results) + ? // @ts-expect-error + output.results + : [] + + return ( + <ExpandableMemories + foundCount={foundCount} + key={`${message.id}-${part.type}-${index}`} + results={results} + /> + ) + } + default: + return null } - default: - return null } - } - case "tool-addMemory": { - switch (part.state) { - case "input-available": - return ( - <div - className="text-sm flex items-center gap-2 text-muted-foreground" - key={`${message.id}-${part.type}-${index}`} - > - <Spinner className="size-4" /> Adding memory... - </div> - ) - case "output-error": - return ( - <div - className="text-sm flex items-center gap-2 text-muted-foreground" - key={`${message.id}-${part.type}-${index}`} - > - <X className="size-4" /> Error adding memory - </div> - ) - case "output-available": - return ( - <div - className="text-sm flex items-center gap-2 text-muted-foreground" - key={`${message.id}-${part.type}-${index}`} - > - <Check className="size-4" /> Memory added - </div> - ) - case "input-streaming": - return ( - <div - className="text-sm flex items-center gap-2 text-muted-foreground" - key={`${message.id}-${part.type}-${index}`} - > - <Spinner className="size-4" /> Adding memory... - </div> - ) - default: - return null + case "tool-addMemory": { + switch (part.state) { + case "input-available": + return ( + <div + className="text-sm flex items-center gap-2 text-muted-foreground" + key={`${message.id}-${part.type}-${index}`} + > + <Spinner className="size-4" /> Adding + memory... + </div> + ) + case "output-error": + return ( + <div + className="text-sm flex items-center gap-2 text-muted-foreground" + key={`${message.id}-${part.type}-${index}`} + > + <X className="size-4" /> Error adding memory + </div> + ) + case "output-available": + return ( + <div + className="text-sm flex items-center gap-2 text-muted-foreground" + key={`${message.id}-${part.type}-${index}`} + > + <Check className="size-4" /> Memory added + </div> + ) + case "input-streaming": + return ( + <div + className="text-sm flex items-center gap-2 text-muted-foreground" + key={`${message.id}-${part.type}-${index}`} + > + <Spinner className="size-4" /> Adding + memory... + </div> + ) + default: + return null + } } + default: + return null } - default: - return null - } - })} - </div> - {message.role === "assistant" && ( - <div className="flex items-center gap-0.5 mt-0.5"> - <Button - className="size-7 text-muted-foreground hover:text-foreground" - onClick={() => { - navigator.clipboard.writeText( - message.parts - .filter((p) => p.type === "text") - ?.map((p) => (p as MessagePart).text ?? "") - .join("\n") ?? "", - ) - toast.success("Copied to clipboard") - }} - size="icon" - variant="ghost" - > - <Copy className="size-3.5" /> - </Button> - <Button - className="size-6 text-muted-foreground hover:text-foreground" - onClick={() => regenerate({ messageId: message.id })} - size="icon" - variant="ghost" - > - <RotateCcw className="size-3.5" /> - </Button> + })} </div> - )} - </div> - ))} - {status === "submitted" && ( - <div className="flex text-muted-foreground justify-start gap-2 px-4 py-3 items-center w-full"> - <Spinner className="size-4" /> - <TextShimmer className="text-sm" duration={1.5}> - Thinking... - </TextShimmer> - </div> - )} - <div ref={bottomRef} /> + {message.role === "assistant" && ( + <div className="flex items-center gap-0.5 mt-0.5"> + <Button + className="size-7 text-muted-foreground hover:text-foreground" + onClick={() => { + navigator.clipboard.writeText( + message.parts + .filter((p) => p.type === "text") + ?.map((p) => (p as MessagePart).text ?? "") + .join("\n") ?? "", + ) + toast.success("Copied to clipboard") + }} + size="icon" + variant="ghost" + > + <Copy className="size-3.5" /> + </Button> + <Button + className="size-6 text-muted-foreground hover:text-foreground" + onClick={() => regenerate({ messageId: message.id })} + size="icon" + variant="ghost" + > + <RotateCcw className="size-3.5" /> + </Button> + </div> + )} + </div> + ))} + {status === "submitted" && ( + <div className="flex text-muted-foreground justify-start gap-2 px-4 py-3 items-center w-full"> + <Spinner className="size-4" /> + <TextShimmer className="text-sm" duration={1.5}> + Thinking... + </TextShimmer> + </div> + )} + <div ref={bottomRef} /> + </div> </div> <Button @@ -605,9 +611,9 @@ export function ChatMessages() { </Button> </div> - <div className="px-4 pb-4 pt-1 relative flex-shrink-0"> + <div className="pb-4 px-4 md:px-2 max-w-4xl mx-auto w-full"> <form - className="flex flex-col items-end gap-3 bg-card border border-border rounded-[22px] p-3 relative shadow-lg dark:shadow-2xl" + className="flex flex-col items-end gap-3 border border-border rounded-[22px] p-3 relative shadow-lg dark:shadow-2xl" onSubmit={(e) => { e.preventDefault() if (status === "submitted") return |