@firstlovecenter/ai-chat 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -0
- package/dist/drizzle/index.cjs +1 -0
- package/dist/drizzle/index.cjs.map +1 -1
- package/dist/drizzle/index.js +1 -0
- package/dist/drizzle/index.js.map +1 -1
- package/dist/prisma/index.cjs +4 -0
- package/dist/prisma/index.cjs.map +1 -1
- package/dist/prisma/index.js +4 -0
- package/dist/prisma/index.js.map +1 -1
- package/dist/server/index.cjs +140 -5
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +20 -0
- package/dist/server/index.d.ts +20 -0
- package/dist/server/index.js +140 -5
- package/dist/server/index.js.map +1 -1
- package/dist/ui/index.cjs +148 -111
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.cts +15 -2
- package/dist/ui/index.d.ts +15 -2
- package/dist/ui/index.js +145 -111
- package/dist/ui/index.js.map +1 -1
- package/package.json +1 -1
package/dist/ui/index.d.cts
CHANGED
|
@@ -8,8 +8,14 @@ type AiChatProps$1 = {
|
|
|
8
8
|
scopeLabel: string;
|
|
9
9
|
/** User's stored narrative provider, surfaced as the model picker default. */
|
|
10
10
|
initialProvider: NarrativeProvider;
|
|
11
|
+
/**
|
|
12
|
+
* Session to open on mount. The host resolves this from the URL
|
|
13
|
+
* (`/chat/[id]`) so reload/bookmark/multi-tab restore the exact
|
|
14
|
+
* conversation. `null` (or omitted) renders the empty new-chat state.
|
|
15
|
+
*/
|
|
16
|
+
initialSessionId?: number | null;
|
|
11
17
|
};
|
|
12
|
-
declare function AiChat({ userFirstName, scopeLabel, initialProvider }: AiChatProps$1): react_jsx_runtime.JSX.Element;
|
|
18
|
+
declare function AiChat({ userFirstName, scopeLabel, initialProvider, initialSessionId }: AiChatProps$1): react_jsx_runtime.JSX.Element;
|
|
13
19
|
|
|
14
20
|
type AiChatSessionSummary = {
|
|
15
21
|
id: number;
|
|
@@ -22,9 +28,16 @@ type AiChatProps = {
|
|
|
22
28
|
scopeLabel: string;
|
|
23
29
|
/** User's stored narrative provider, surfaced as the model picker default. */
|
|
24
30
|
initialProvider: 'claude' | 'grok' | 'gemini';
|
|
31
|
+
/**
|
|
32
|
+
* Session to open on mount. The host resolves this from the URL
|
|
33
|
+
* (`/chat/[id]`) so reload/bookmark/multi-tab restore the exact
|
|
34
|
+
* conversation the user was viewing. Pass `null` (or omit) for the
|
|
35
|
+
* empty "new chat" state.
|
|
36
|
+
*/
|
|
37
|
+
initialSessionId?: number | null;
|
|
25
38
|
};
|
|
26
39
|
|
|
27
|
-
declare function VercelChat({ userFirstName, scopeLabel, initialProvider }: AiChatProps): react_jsx_runtime.JSX.Element;
|
|
40
|
+
declare function VercelChat({ userFirstName, scopeLabel, initialProvider, initialSessionId }: AiChatProps): react_jsx_runtime.JSX.Element;
|
|
28
41
|
|
|
29
42
|
type ChartSpec = {
|
|
30
43
|
type: 'line' | 'bar' | 'stacked_bar' | 'pie';
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -8,8 +8,14 @@ type AiChatProps$1 = {
|
|
|
8
8
|
scopeLabel: string;
|
|
9
9
|
/** User's stored narrative provider, surfaced as the model picker default. */
|
|
10
10
|
initialProvider: NarrativeProvider;
|
|
11
|
+
/**
|
|
12
|
+
* Session to open on mount. The host resolves this from the URL
|
|
13
|
+
* (`/chat/[id]`) so reload/bookmark/multi-tab restore the exact
|
|
14
|
+
* conversation. `null` (or omitted) renders the empty new-chat state.
|
|
15
|
+
*/
|
|
16
|
+
initialSessionId?: number | null;
|
|
11
17
|
};
|
|
12
|
-
declare function AiChat({ userFirstName, scopeLabel, initialProvider }: AiChatProps$1): react_jsx_runtime.JSX.Element;
|
|
18
|
+
declare function AiChat({ userFirstName, scopeLabel, initialProvider, initialSessionId }: AiChatProps$1): react_jsx_runtime.JSX.Element;
|
|
13
19
|
|
|
14
20
|
type AiChatSessionSummary = {
|
|
15
21
|
id: number;
|
|
@@ -22,9 +28,16 @@ type AiChatProps = {
|
|
|
22
28
|
scopeLabel: string;
|
|
23
29
|
/** User's stored narrative provider, surfaced as the model picker default. */
|
|
24
30
|
initialProvider: 'claude' | 'grok' | 'gemini';
|
|
31
|
+
/**
|
|
32
|
+
* Session to open on mount. The host resolves this from the URL
|
|
33
|
+
* (`/chat/[id]`) so reload/bookmark/multi-tab restore the exact
|
|
34
|
+
* conversation the user was viewing. Pass `null` (or omit) for the
|
|
35
|
+
* empty "new chat" state.
|
|
36
|
+
*/
|
|
37
|
+
initialSessionId?: number | null;
|
|
25
38
|
};
|
|
26
39
|
|
|
27
|
-
declare function VercelChat({ userFirstName, scopeLabel, initialProvider }: AiChatProps): react_jsx_runtime.JSX.Element;
|
|
40
|
+
declare function VercelChat({ userFirstName, scopeLabel, initialProvider, initialSessionId }: AiChatProps): react_jsx_runtime.JSX.Element;
|
|
28
41
|
|
|
29
42
|
type ChartSpec = {
|
|
30
43
|
type: 'line' | 'bar' | 'stacked_bar' | 'pie';
|
package/dist/ui/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { useMemo, useState, useRef, useLayoutEffect, useEffect, useCallback } from 'react';
|
|
4
|
+
import { useRouter } from 'next/navigation';
|
|
5
|
+
import Link from 'next/link';
|
|
4
6
|
import { PanelLeftClose, Plus, Pencil, Trash2, Menu, Sparkles, Loader2, ChevronDown, Check, ArrowUp, Copy, RotateCcw, ChevronUp } from 'lucide-react';
|
|
5
7
|
import { DropdownMenu as DropdownMenu$1 } from 'radix-ui';
|
|
6
8
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -651,10 +653,14 @@ var PROVIDER_DESCRIPTIONS = {
|
|
|
651
653
|
function AiChat({
|
|
652
654
|
userFirstName,
|
|
653
655
|
scopeLabel,
|
|
654
|
-
initialProvider
|
|
656
|
+
initialProvider,
|
|
657
|
+
initialSessionId = null
|
|
655
658
|
}) {
|
|
659
|
+
const router = useRouter();
|
|
656
660
|
const [sessions, setSessions] = useState([]);
|
|
657
|
-
const [activeSessionId, setActiveSessionId] = useState(
|
|
661
|
+
const [activeSessionId, setActiveSessionId] = useState(
|
|
662
|
+
initialSessionId
|
|
663
|
+
);
|
|
658
664
|
const [answers, setAnswers] = useState([]);
|
|
659
665
|
const [pending, setPending] = useState(false);
|
|
660
666
|
const [question, setQuestion] = useState("");
|
|
@@ -668,7 +674,6 @@ function AiChat({
|
|
|
668
674
|
const textareaRef = useRef(null);
|
|
669
675
|
const lastAnswerRef = useRef(null);
|
|
670
676
|
const prevAnswersLen = useRef(0);
|
|
671
|
-
const autoOpenedRef = useRef(false);
|
|
672
677
|
useLayoutEffect(() => {
|
|
673
678
|
const el = textareaRef.current;
|
|
674
679
|
if (!el) return;
|
|
@@ -684,33 +689,7 @@ function AiChat({
|
|
|
684
689
|
if (!res.ok) return;
|
|
685
690
|
const data = await res.json();
|
|
686
691
|
if (cancelled) return;
|
|
687
|
-
|
|
688
|
-
setSessions(list);
|
|
689
|
-
if (!autoOpenedRef.current && list.length > 0) {
|
|
690
|
-
autoOpenedRef.current = true;
|
|
691
|
-
const mostRecentId = list[0].id;
|
|
692
|
-
setLoadingSession(true);
|
|
693
|
-
setActiveSessionId(mostRecentId);
|
|
694
|
-
try {
|
|
695
|
-
const sres = await fetch(`/api/chat/sessions/${mostRecentId}`, {
|
|
696
|
-
cache: "no-store"
|
|
697
|
-
});
|
|
698
|
-
if (cancelled) return;
|
|
699
|
-
if (!sres.ok) {
|
|
700
|
-
setAnswers([]);
|
|
701
|
-
return;
|
|
702
|
-
}
|
|
703
|
-
const sdata = await sres.json();
|
|
704
|
-
if (cancelled) return;
|
|
705
|
-
setAnswers(messagesToAnswers(sdata.messages ?? []));
|
|
706
|
-
} catch {
|
|
707
|
-
if (!cancelled) setAnswers([]);
|
|
708
|
-
} finally {
|
|
709
|
-
if (!cancelled) setLoadingSession(false);
|
|
710
|
-
}
|
|
711
|
-
} else if (!autoOpenedRef.current) {
|
|
712
|
-
autoOpenedRef.current = true;
|
|
713
|
-
}
|
|
692
|
+
setSessions(data.sessions ?? []);
|
|
714
693
|
} catch {
|
|
715
694
|
}
|
|
716
695
|
}
|
|
@@ -719,6 +698,35 @@ function AiChat({
|
|
|
719
698
|
cancelled = true;
|
|
720
699
|
};
|
|
721
700
|
}, []);
|
|
701
|
+
useEffect(() => {
|
|
702
|
+
if (initialSessionId == null) {
|
|
703
|
+
setAnswers([]);
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
let cancelled = false;
|
|
707
|
+
async function load(id) {
|
|
708
|
+
setLoadingSession(true);
|
|
709
|
+
try {
|
|
710
|
+
const res = await fetch(`/api/chat/sessions/${id}`, { cache: "no-store" });
|
|
711
|
+
if (cancelled) return;
|
|
712
|
+
if (!res.ok) {
|
|
713
|
+
setAnswers([]);
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
const data = await res.json();
|
|
717
|
+
if (cancelled) return;
|
|
718
|
+
setAnswers(messagesToAnswers(data.messages ?? []));
|
|
719
|
+
} catch {
|
|
720
|
+
if (!cancelled) setAnswers([]);
|
|
721
|
+
} finally {
|
|
722
|
+
if (!cancelled) setLoadingSession(false);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
void load(initialSessionId);
|
|
726
|
+
return () => {
|
|
727
|
+
cancelled = true;
|
|
728
|
+
};
|
|
729
|
+
}, [initialSessionId]);
|
|
722
730
|
useEffect(() => {
|
|
723
731
|
if (answers.length > prevAnswersLen.current && lastAnswerRef.current) {
|
|
724
732
|
lastAnswerRef.current.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
@@ -734,11 +742,18 @@ function AiChat({
|
|
|
734
742
|
} catch {
|
|
735
743
|
}
|
|
736
744
|
}, []);
|
|
745
|
+
const syncUrl = useCallback(
|
|
746
|
+
(id) => {
|
|
747
|
+
router.push(id == null ? "/chat" : `/chat/${id}`);
|
|
748
|
+
},
|
|
749
|
+
[router]
|
|
750
|
+
);
|
|
737
751
|
const newChat = useCallback(() => {
|
|
738
752
|
setActiveSessionId(null);
|
|
739
753
|
setAnswers([]);
|
|
740
754
|
setQuestion("");
|
|
741
|
-
|
|
755
|
+
router.push("/chat?new");
|
|
756
|
+
}, [router]);
|
|
742
757
|
const changeProvider = useCallback(
|
|
743
758
|
async (next) => {
|
|
744
759
|
if (next === provider || providerSaving) return;
|
|
@@ -761,23 +776,27 @@ function AiChat({
|
|
|
761
776
|
},
|
|
762
777
|
[provider, providerSaving]
|
|
763
778
|
);
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
779
|
+
useCallback(
|
|
780
|
+
async (id) => {
|
|
781
|
+
setLoadingSession(true);
|
|
782
|
+
setActiveSessionId(id);
|
|
783
|
+
syncUrl(id);
|
|
784
|
+
try {
|
|
785
|
+
const res = await fetch(`/api/chat/sessions/${id}`, { cache: "no-store" });
|
|
786
|
+
if (!res.ok) {
|
|
787
|
+
setAnswers([]);
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
const data = await res.json();
|
|
791
|
+
setAnswers(messagesToAnswers(data.messages ?? []));
|
|
792
|
+
} catch {
|
|
770
793
|
setAnswers([]);
|
|
771
|
-
|
|
794
|
+
} finally {
|
|
795
|
+
setLoadingSession(false);
|
|
772
796
|
}
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
setAnswers([]);
|
|
777
|
-
} finally {
|
|
778
|
-
setLoadingSession(false);
|
|
779
|
-
}
|
|
780
|
-
}, []);
|
|
797
|
+
},
|
|
798
|
+
[syncUrl]
|
|
799
|
+
);
|
|
781
800
|
const persistTitle = useCallback(
|
|
782
801
|
async (id, title) => {
|
|
783
802
|
const trimmed = title.trim();
|
|
@@ -807,9 +826,10 @@ function AiChat({
|
|
|
807
826
|
if (activeSessionId === id) {
|
|
808
827
|
setActiveSessionId(null);
|
|
809
828
|
setAnswers([]);
|
|
829
|
+
syncUrl(null);
|
|
810
830
|
}
|
|
811
831
|
},
|
|
812
|
-
[activeSessionId]
|
|
832
|
+
[activeSessionId, syncUrl]
|
|
813
833
|
);
|
|
814
834
|
const submit = useCallback(
|
|
815
835
|
async (q) => {
|
|
@@ -829,6 +849,7 @@ function AiChat({
|
|
|
829
849
|
const data = await create.json();
|
|
830
850
|
sessionId = data.session.id;
|
|
831
851
|
setActiveSessionId(sessionId);
|
|
852
|
+
syncUrl(sessionId);
|
|
832
853
|
setSessions((prev) => [
|
|
833
854
|
{ id: data.session.id, title: data.session.title, updatedAt: null },
|
|
834
855
|
...prev
|
|
@@ -909,7 +930,7 @@ function AiChat({
|
|
|
909
930
|
void refreshSessions();
|
|
910
931
|
}
|
|
911
932
|
},
|
|
912
|
-
[activeSessionId, pending, refreshSessions]
|
|
933
|
+
[activeSessionId, pending, refreshSessions, syncUrl]
|
|
913
934
|
);
|
|
914
935
|
const heroVisible = answers.length === 0 && !loadingSession;
|
|
915
936
|
const greeting = useMemo(
|
|
@@ -983,13 +1004,10 @@ function AiChat({
|
|
|
983
1004
|
}
|
|
984
1005
|
return /* @__PURE__ */ jsxs("li", { className: "group relative", children: [
|
|
985
1006
|
/* @__PURE__ */ jsx(
|
|
986
|
-
|
|
1007
|
+
Link,
|
|
987
1008
|
{
|
|
988
|
-
|
|
989
|
-
onClick: () =>
|
|
990
|
-
void openSession(s.id);
|
|
991
|
-
setSidebarOpen(false);
|
|
992
|
-
},
|
|
1009
|
+
href: `/chat/${s.id}`,
|
|
1010
|
+
onClick: () => setSidebarOpen(false),
|
|
993
1011
|
className: cn(
|
|
994
1012
|
"flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm",
|
|
995
1013
|
active ? "bg-sidebar-accent text-sidebar-accent-foreground" : "text-sidebar-foreground/80 hover:bg-sidebar-accent/60 hover:text-sidebar-foreground"
|
|
@@ -1470,10 +1488,14 @@ function asDataPart(v) {
|
|
|
1470
1488
|
function VercelChat({
|
|
1471
1489
|
userFirstName,
|
|
1472
1490
|
scopeLabel,
|
|
1473
|
-
initialProvider
|
|
1491
|
+
initialProvider,
|
|
1492
|
+
initialSessionId = null
|
|
1474
1493
|
}) {
|
|
1494
|
+
const router = useRouter();
|
|
1475
1495
|
const [sessions, setSessions] = useState([]);
|
|
1476
|
-
const [activeSessionId, setActiveSessionId] = useState(
|
|
1496
|
+
const [activeSessionId, setActiveSessionId] = useState(
|
|
1497
|
+
initialSessionId
|
|
1498
|
+
);
|
|
1477
1499
|
const [sidebarOpen, setSidebarOpen] = useState(false);
|
|
1478
1500
|
const [loadingSession, setLoadingSession] = useState(false);
|
|
1479
1501
|
const [provider, setProvider] = useState(initialProvider);
|
|
@@ -1487,7 +1509,6 @@ function VercelChat({
|
|
|
1487
1509
|
const textareaRef = useRef(null);
|
|
1488
1510
|
const lastAnswerRef = useRef(null);
|
|
1489
1511
|
const prevAnswersLen = useRef(0);
|
|
1490
|
-
const autoOpenedRef = useRef(false);
|
|
1491
1512
|
const activeSessionIdRef = useRef(activeSessionId);
|
|
1492
1513
|
const providerRef = useRef(provider);
|
|
1493
1514
|
useEffect(() => {
|
|
@@ -1543,48 +1564,7 @@ function VercelChat({
|
|
|
1543
1564
|
if (!res.ok) return;
|
|
1544
1565
|
const json = await res.json();
|
|
1545
1566
|
if (cancelled) return;
|
|
1546
|
-
|
|
1547
|
-
setSessions(list);
|
|
1548
|
-
if (!autoOpenedRef.current && list.length > 0) {
|
|
1549
|
-
autoOpenedRef.current = true;
|
|
1550
|
-
const mostRecentId = list[0].id;
|
|
1551
|
-
setLoadingSession(true);
|
|
1552
|
-
setActiveSessionId(mostRecentId);
|
|
1553
|
-
try {
|
|
1554
|
-
const sres = await fetch(`/api/chat/sessions/${mostRecentId}`, {
|
|
1555
|
-
cache: "no-store"
|
|
1556
|
-
});
|
|
1557
|
-
if (cancelled) return;
|
|
1558
|
-
if (!sres.ok) {
|
|
1559
|
-
setMessages([]);
|
|
1560
|
-
setHydratedBlocks({});
|
|
1561
|
-
setHydratedProse({});
|
|
1562
|
-
setHydratedErrors({});
|
|
1563
|
-
return;
|
|
1564
|
-
}
|
|
1565
|
-
const sjson = await sres.json();
|
|
1566
|
-
if (cancelled) return;
|
|
1567
|
-
const { uiMessages, blocksMap, proseMap, errorsMap } = storedToUseChat(
|
|
1568
|
-
sjson.messages ?? []
|
|
1569
|
-
);
|
|
1570
|
-
setMessages(uiMessages);
|
|
1571
|
-
setHydratedBlocks(blocksMap);
|
|
1572
|
-
setHydratedProse(proseMap);
|
|
1573
|
-
setHydratedErrors(errorsMap);
|
|
1574
|
-
setStartedAt({});
|
|
1575
|
-
} catch {
|
|
1576
|
-
if (!cancelled) {
|
|
1577
|
-
setMessages([]);
|
|
1578
|
-
setHydratedBlocks({});
|
|
1579
|
-
setHydratedProse({});
|
|
1580
|
-
setHydratedErrors({});
|
|
1581
|
-
}
|
|
1582
|
-
} finally {
|
|
1583
|
-
if (!cancelled) setLoadingSession(false);
|
|
1584
|
-
}
|
|
1585
|
-
} else if (!autoOpenedRef.current) {
|
|
1586
|
-
autoOpenedRef.current = true;
|
|
1587
|
-
}
|
|
1567
|
+
setSessions(json.sessions ?? []);
|
|
1588
1568
|
} catch {
|
|
1589
1569
|
}
|
|
1590
1570
|
}
|
|
@@ -1592,7 +1572,54 @@ function VercelChat({
|
|
|
1592
1572
|
return () => {
|
|
1593
1573
|
cancelled = true;
|
|
1594
1574
|
};
|
|
1595
|
-
}, [
|
|
1575
|
+
}, []);
|
|
1576
|
+
useEffect(() => {
|
|
1577
|
+
if (initialSessionId == null) {
|
|
1578
|
+
setMessages([]);
|
|
1579
|
+
setHydratedBlocks({});
|
|
1580
|
+
setHydratedProse({});
|
|
1581
|
+
setHydratedErrors({});
|
|
1582
|
+
return;
|
|
1583
|
+
}
|
|
1584
|
+
let cancelled = false;
|
|
1585
|
+
async function load(id) {
|
|
1586
|
+
setLoadingSession(true);
|
|
1587
|
+
try {
|
|
1588
|
+
const res = await fetch(`/api/chat/sessions/${id}`, { cache: "no-store" });
|
|
1589
|
+
if (cancelled) return;
|
|
1590
|
+
if (!res.ok) {
|
|
1591
|
+
setMessages([]);
|
|
1592
|
+
setHydratedBlocks({});
|
|
1593
|
+
setHydratedProse({});
|
|
1594
|
+
setHydratedErrors({});
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
const json = await res.json();
|
|
1598
|
+
if (cancelled) return;
|
|
1599
|
+
const { uiMessages, blocksMap, proseMap, errorsMap } = storedToUseChat(
|
|
1600
|
+
json.messages ?? []
|
|
1601
|
+
);
|
|
1602
|
+
setMessages(uiMessages);
|
|
1603
|
+
setHydratedBlocks(blocksMap);
|
|
1604
|
+
setHydratedProse(proseMap);
|
|
1605
|
+
setHydratedErrors(errorsMap);
|
|
1606
|
+
setStartedAt({});
|
|
1607
|
+
} catch {
|
|
1608
|
+
if (!cancelled) {
|
|
1609
|
+
setMessages([]);
|
|
1610
|
+
setHydratedBlocks({});
|
|
1611
|
+
setHydratedProse({});
|
|
1612
|
+
setHydratedErrors({});
|
|
1613
|
+
}
|
|
1614
|
+
} finally {
|
|
1615
|
+
if (!cancelled) setLoadingSession(false);
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
void load(initialSessionId);
|
|
1619
|
+
return () => {
|
|
1620
|
+
cancelled = true;
|
|
1621
|
+
};
|
|
1622
|
+
}, [initialSessionId, setMessages]);
|
|
1596
1623
|
const answers = useMemo(() => {
|
|
1597
1624
|
const liveBlocks = [];
|
|
1598
1625
|
const liveErrors = [];
|
|
@@ -1691,6 +1718,12 @@ function VercelChat({
|
|
|
1691
1718
|
}
|
|
1692
1719
|
prevAnswersLen.current = answers.length;
|
|
1693
1720
|
}, [answers.length]);
|
|
1721
|
+
const syncUrl = useCallback(
|
|
1722
|
+
(id) => {
|
|
1723
|
+
router.push(id == null ? "/chat" : `/chat/${id}`);
|
|
1724
|
+
},
|
|
1725
|
+
[router]
|
|
1726
|
+
);
|
|
1694
1727
|
const newChat = useCallback(() => {
|
|
1695
1728
|
setActiveSessionId(null);
|
|
1696
1729
|
setMessages([]);
|
|
@@ -1699,7 +1732,8 @@ function VercelChat({
|
|
|
1699
1732
|
setHydratedErrors({});
|
|
1700
1733
|
setStartedAt({});
|
|
1701
1734
|
setInput("");
|
|
1702
|
-
|
|
1735
|
+
router.push("/chat?new");
|
|
1736
|
+
}, [setMessages, setInput, router]);
|
|
1703
1737
|
const changeProvider = useCallback(
|
|
1704
1738
|
async (next) => {
|
|
1705
1739
|
if (next === provider || providerSaving) return;
|
|
@@ -1722,10 +1756,11 @@ function VercelChat({
|
|
|
1722
1756
|
},
|
|
1723
1757
|
[provider, providerSaving]
|
|
1724
1758
|
);
|
|
1725
|
-
|
|
1759
|
+
useCallback(
|
|
1726
1760
|
async (id) => {
|
|
1727
1761
|
setLoadingSession(true);
|
|
1728
1762
|
setActiveSessionId(id);
|
|
1763
|
+
syncUrl(id);
|
|
1729
1764
|
try {
|
|
1730
1765
|
const res = await fetch(`/api/chat/sessions/${id}`, { cache: "no-store" });
|
|
1731
1766
|
if (!res.ok) {
|
|
@@ -1753,7 +1788,7 @@ function VercelChat({
|
|
|
1753
1788
|
setLoadingSession(false);
|
|
1754
1789
|
}
|
|
1755
1790
|
},
|
|
1756
|
-
[setMessages]
|
|
1791
|
+
[setMessages, syncUrl]
|
|
1757
1792
|
);
|
|
1758
1793
|
const persistTitle = useCallback(
|
|
1759
1794
|
async (id, title) => {
|
|
@@ -1787,9 +1822,10 @@ function VercelChat({
|
|
|
1787
1822
|
setHydratedBlocks({});
|
|
1788
1823
|
setHydratedProse({});
|
|
1789
1824
|
setHydratedErrors({});
|
|
1825
|
+
syncUrl(null);
|
|
1790
1826
|
}
|
|
1791
1827
|
},
|
|
1792
|
-
[activeSessionId, setMessages]
|
|
1828
|
+
[activeSessionId, setMessages, syncUrl]
|
|
1793
1829
|
);
|
|
1794
1830
|
const submitForm = useCallback(
|
|
1795
1831
|
async (e) => {
|
|
@@ -1806,6 +1842,7 @@ function VercelChat({
|
|
|
1806
1842
|
const json = await create.json();
|
|
1807
1843
|
activeSessionIdRef.current = json.session.id;
|
|
1808
1844
|
setActiveSessionId(json.session.id);
|
|
1845
|
+
syncUrl(json.session.id);
|
|
1809
1846
|
setSessions((prev) => [
|
|
1810
1847
|
{ id: json.session.id, title: json.session.title, updatedAt: null },
|
|
1811
1848
|
...prev
|
|
@@ -1816,7 +1853,7 @@ function VercelChat({
|
|
|
1816
1853
|
}
|
|
1817
1854
|
handleSubmit(e);
|
|
1818
1855
|
},
|
|
1819
|
-
[input, status, handleSubmit]
|
|
1856
|
+
[input, status, handleSubmit, syncUrl]
|
|
1820
1857
|
);
|
|
1821
1858
|
useEffect(() => {
|
|
1822
1859
|
setStartedAt((prev) => {
|
|
@@ -1924,13 +1961,10 @@ function VercelChat({
|
|
|
1924
1961
|
}
|
|
1925
1962
|
return /* @__PURE__ */ jsxs("li", { className: "group relative", children: [
|
|
1926
1963
|
/* @__PURE__ */ jsx(
|
|
1927
|
-
|
|
1964
|
+
Link,
|
|
1928
1965
|
{
|
|
1929
|
-
|
|
1930
|
-
onClick: () =>
|
|
1931
|
-
void openSession(s.id);
|
|
1932
|
-
setSidebarOpen(false);
|
|
1933
|
-
},
|
|
1966
|
+
href: `/chat/${s.id}`,
|
|
1967
|
+
onClick: () => setSidebarOpen(false),
|
|
1934
1968
|
className: cn(
|
|
1935
1969
|
"flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm",
|
|
1936
1970
|
active ? "bg-sidebar-accent text-sidebar-accent-foreground" : "text-sidebar-foreground/80 hover:bg-sidebar-accent/60 hover:text-sidebar-foreground"
|