mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-14 20:55:15 +02:00
refactor: update title generation logic to improve user experience by generating titles in parallel with assistant responses
This commit is contained in:
parent
bd91b0bef2
commit
e8cf677b25
5 changed files with 120 additions and 61 deletions
|
|
@ -465,10 +465,7 @@ export default function NewChatPage() {
|
|||
let isNewThread = false;
|
||||
if (!currentThreadId) {
|
||||
try {
|
||||
// Create thread with truncated prompt as initial title
|
||||
const initialTitle =
|
||||
userQuery.trim().slice(0, 100) + (userQuery.trim().length > 100 ? "..." : "");
|
||||
const newThread = await createThread(searchSpaceId, initialTitle);
|
||||
const newThread = await createThread(searchSpaceId, "New Chat");
|
||||
currentThreadId = newThread.id;
|
||||
setThreadId(currentThreadId);
|
||||
// Set currentThread so share button in header appears immediately
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import {
|
|||
} from "@/components/ui/dropdown-menu";
|
||||
import { useLongPress } from "@/hooks/use-long-press";
|
||||
import { useIsMobile } from "@/hooks/use-mobile";
|
||||
import { useTypewriter } from "@/hooks/use-typewriter";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface ChatListItemProps {
|
||||
|
|
@ -44,6 +45,7 @@ export function ChatListItem({
|
|||
const t = useTranslations("sidebar");
|
||||
const isMobile = useIsMobile();
|
||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||
const animatedName = useTypewriter(name);
|
||||
|
||||
const { handlers: longPressHandlers, wasLongPress } = useLongPress(
|
||||
useCallback(() => setDropdownOpen(true), [])
|
||||
|
|
@ -69,7 +71,7 @@ export function ChatListItem({
|
|||
)}
|
||||
>
|
||||
<MessageSquare className="h-4 w-4 shrink-0 text-muted-foreground" />
|
||||
<span className="w-[calc(100%-3rem)] ">{name}</span>
|
||||
<span className="w-[calc(100%-3rem)] ">{animatedName}</span>
|
||||
</button>
|
||||
|
||||
{/* Actions dropdown - trigger hidden on mobile, long-press opens it instead */}
|
||||
|
|
|
|||
49
surfsense_web/hooks/use-typewriter.ts
Normal file
49
surfsense_web/hooks/use-typewriter.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
/**
|
||||
* Animates text changes with a typewriter reveal effect, but only when
|
||||
* transitioning away from the `skipFor` placeholder (default "New Chat").
|
||||
* All other text values are shown instantly without animation.
|
||||
*/
|
||||
export function useTypewriter(text: string, speed = 35, skipFor = "New Chat"): string {
|
||||
const [displayed, setDisplayed] = useState(text);
|
||||
const prevTextRef = useRef(text);
|
||||
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
intervalRef.current = null;
|
||||
}
|
||||
|
||||
const prevText = prevTextRef.current;
|
||||
prevTextRef.current = text;
|
||||
|
||||
const shouldAnimate = prevText === skipFor && text !== skipFor && !!text;
|
||||
|
||||
if (!shouldAnimate) {
|
||||
setDisplayed(text);
|
||||
return;
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
setDisplayed("");
|
||||
intervalRef.current = setInterval(() => {
|
||||
i++;
|
||||
setDisplayed(text.slice(0, i));
|
||||
if (i >= text.length) {
|
||||
if (intervalRef.current) clearInterval(intervalRef.current);
|
||||
intervalRef.current = null;
|
||||
}
|
||||
}, speed);
|
||||
|
||||
return () => {
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
intervalRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [text, speed, skipFor]);
|
||||
|
||||
return displayed;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue