@firstlovecenter/ai-chat 0.6.1 → 0.8.0
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 +50 -0
- package/dist/drizzle/index.cjs +14 -0
- package/dist/drizzle/index.cjs.map +1 -1
- package/dist/drizzle/index.d.cts +18 -1
- package/dist/drizzle/index.d.ts +18 -1
- package/dist/drizzle/index.js +14 -0
- package/dist/drizzle/index.js.map +1 -1
- package/dist/prisma/index.cjs +6 -0
- package/dist/prisma/index.cjs.map +1 -1
- package/dist/prisma/index.d.cts +4 -1
- package/dist/prisma/index.d.ts +4 -1
- package/dist/prisma/index.js +6 -0
- package/dist/prisma/index.js.map +1 -1
- package/dist/server/index.cjs +169 -12
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +27 -3
- package/dist/server/index.d.ts +27 -3
- package/dist/server/index.js +169 -12
- package/dist/server/index.js.map +1 -1
- package/dist/{types-CQntnyDJ.d.cts → types-BnwUkqKb.d.cts} +8 -0
- package/dist/{types-CQntnyDJ.d.ts → types-BnwUkqKb.d.ts} +8 -0
- package/dist/ui/index.cjs +137 -98
- 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 +137 -98
- package/dist/ui/index.js.map +1 -1
- package/package.json +1 -1
- package/prisma/chat-models.prisma +5 -0
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,7 @@
|
|
|
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';
|
|
4
5
|
import { PanelLeftClose, Plus, Pencil, Trash2, Menu, Sparkles, Loader2, ChevronDown, Check, ArrowUp, Copy, RotateCcw, ChevronUp } from 'lucide-react';
|
|
5
6
|
import { DropdownMenu as DropdownMenu$1 } from 'radix-ui';
|
|
6
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -651,10 +652,14 @@ var PROVIDER_DESCRIPTIONS = {
|
|
|
651
652
|
function AiChat({
|
|
652
653
|
userFirstName,
|
|
653
654
|
scopeLabel,
|
|
654
|
-
initialProvider
|
|
655
|
+
initialProvider,
|
|
656
|
+
initialSessionId = null
|
|
655
657
|
}) {
|
|
658
|
+
const router = useRouter();
|
|
656
659
|
const [sessions, setSessions] = useState([]);
|
|
657
|
-
const [activeSessionId, setActiveSessionId] = useState(
|
|
660
|
+
const [activeSessionId, setActiveSessionId] = useState(
|
|
661
|
+
initialSessionId
|
|
662
|
+
);
|
|
658
663
|
const [answers, setAnswers] = useState([]);
|
|
659
664
|
const [pending, setPending] = useState(false);
|
|
660
665
|
const [question, setQuestion] = useState("");
|
|
@@ -668,7 +673,6 @@ function AiChat({
|
|
|
668
673
|
const textareaRef = useRef(null);
|
|
669
674
|
const lastAnswerRef = useRef(null);
|
|
670
675
|
const prevAnswersLen = useRef(0);
|
|
671
|
-
const autoOpenedRef = useRef(false);
|
|
672
676
|
useLayoutEffect(() => {
|
|
673
677
|
const el = textareaRef.current;
|
|
674
678
|
if (!el) return;
|
|
@@ -684,33 +688,7 @@ function AiChat({
|
|
|
684
688
|
if (!res.ok) return;
|
|
685
689
|
const data = await res.json();
|
|
686
690
|
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
|
-
}
|
|
691
|
+
setSessions(data.sessions ?? []);
|
|
714
692
|
} catch {
|
|
715
693
|
}
|
|
716
694
|
}
|
|
@@ -719,6 +697,35 @@ function AiChat({
|
|
|
719
697
|
cancelled = true;
|
|
720
698
|
};
|
|
721
699
|
}, []);
|
|
700
|
+
useEffect(() => {
|
|
701
|
+
if (initialSessionId == null) {
|
|
702
|
+
setAnswers([]);
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
let cancelled = false;
|
|
706
|
+
async function load(id) {
|
|
707
|
+
setLoadingSession(true);
|
|
708
|
+
try {
|
|
709
|
+
const res = await fetch(`/api/chat/sessions/${id}`, { cache: "no-store" });
|
|
710
|
+
if (cancelled) return;
|
|
711
|
+
if (!res.ok) {
|
|
712
|
+
setAnswers([]);
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
const data = await res.json();
|
|
716
|
+
if (cancelled) return;
|
|
717
|
+
setAnswers(messagesToAnswers(data.messages ?? []));
|
|
718
|
+
} catch {
|
|
719
|
+
if (!cancelled) setAnswers([]);
|
|
720
|
+
} finally {
|
|
721
|
+
if (!cancelled) setLoadingSession(false);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
void load(initialSessionId);
|
|
725
|
+
return () => {
|
|
726
|
+
cancelled = true;
|
|
727
|
+
};
|
|
728
|
+
}, [initialSessionId]);
|
|
722
729
|
useEffect(() => {
|
|
723
730
|
if (answers.length > prevAnswersLen.current && lastAnswerRef.current) {
|
|
724
731
|
lastAnswerRef.current.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
@@ -734,11 +741,18 @@ function AiChat({
|
|
|
734
741
|
} catch {
|
|
735
742
|
}
|
|
736
743
|
}, []);
|
|
744
|
+
const syncUrl = useCallback(
|
|
745
|
+
(id) => {
|
|
746
|
+
router.push(id == null ? "/chat" : `/chat/${id}`);
|
|
747
|
+
},
|
|
748
|
+
[router]
|
|
749
|
+
);
|
|
737
750
|
const newChat = useCallback(() => {
|
|
738
751
|
setActiveSessionId(null);
|
|
739
752
|
setAnswers([]);
|
|
740
753
|
setQuestion("");
|
|
741
|
-
|
|
754
|
+
syncUrl(null);
|
|
755
|
+
}, [syncUrl]);
|
|
742
756
|
const changeProvider = useCallback(
|
|
743
757
|
async (next) => {
|
|
744
758
|
if (next === provider || providerSaving) return;
|
|
@@ -761,23 +775,27 @@ function AiChat({
|
|
|
761
775
|
},
|
|
762
776
|
[provider, providerSaving]
|
|
763
777
|
);
|
|
764
|
-
const openSession = useCallback(
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
778
|
+
const openSession = useCallback(
|
|
779
|
+
async (id) => {
|
|
780
|
+
setLoadingSession(true);
|
|
781
|
+
setActiveSessionId(id);
|
|
782
|
+
syncUrl(id);
|
|
783
|
+
try {
|
|
784
|
+
const res = await fetch(`/api/chat/sessions/${id}`, { cache: "no-store" });
|
|
785
|
+
if (!res.ok) {
|
|
786
|
+
setAnswers([]);
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
const data = await res.json();
|
|
790
|
+
setAnswers(messagesToAnswers(data.messages ?? []));
|
|
791
|
+
} catch {
|
|
770
792
|
setAnswers([]);
|
|
771
|
-
|
|
793
|
+
} finally {
|
|
794
|
+
setLoadingSession(false);
|
|
772
795
|
}
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
setAnswers([]);
|
|
777
|
-
} finally {
|
|
778
|
-
setLoadingSession(false);
|
|
779
|
-
}
|
|
780
|
-
}, []);
|
|
796
|
+
},
|
|
797
|
+
[syncUrl]
|
|
798
|
+
);
|
|
781
799
|
const persistTitle = useCallback(
|
|
782
800
|
async (id, title) => {
|
|
783
801
|
const trimmed = title.trim();
|
|
@@ -807,9 +825,10 @@ function AiChat({
|
|
|
807
825
|
if (activeSessionId === id) {
|
|
808
826
|
setActiveSessionId(null);
|
|
809
827
|
setAnswers([]);
|
|
828
|
+
syncUrl(null);
|
|
810
829
|
}
|
|
811
830
|
},
|
|
812
|
-
[activeSessionId]
|
|
831
|
+
[activeSessionId, syncUrl]
|
|
813
832
|
);
|
|
814
833
|
const submit = useCallback(
|
|
815
834
|
async (q) => {
|
|
@@ -829,6 +848,7 @@ function AiChat({
|
|
|
829
848
|
const data = await create.json();
|
|
830
849
|
sessionId = data.session.id;
|
|
831
850
|
setActiveSessionId(sessionId);
|
|
851
|
+
syncUrl(sessionId);
|
|
832
852
|
setSessions((prev) => [
|
|
833
853
|
{ id: data.session.id, title: data.session.title, updatedAt: null },
|
|
834
854
|
...prev
|
|
@@ -909,7 +929,7 @@ function AiChat({
|
|
|
909
929
|
void refreshSessions();
|
|
910
930
|
}
|
|
911
931
|
},
|
|
912
|
-
[activeSessionId, pending, refreshSessions]
|
|
932
|
+
[activeSessionId, pending, refreshSessions, syncUrl]
|
|
913
933
|
);
|
|
914
934
|
const heroVisible = answers.length === 0 && !loadingSession;
|
|
915
935
|
const greeting = useMemo(
|
|
@@ -1470,10 +1490,14 @@ function asDataPart(v) {
|
|
|
1470
1490
|
function VercelChat({
|
|
1471
1491
|
userFirstName,
|
|
1472
1492
|
scopeLabel,
|
|
1473
|
-
initialProvider
|
|
1493
|
+
initialProvider,
|
|
1494
|
+
initialSessionId = null
|
|
1474
1495
|
}) {
|
|
1496
|
+
const router = useRouter();
|
|
1475
1497
|
const [sessions, setSessions] = useState([]);
|
|
1476
|
-
const [activeSessionId, setActiveSessionId] = useState(
|
|
1498
|
+
const [activeSessionId, setActiveSessionId] = useState(
|
|
1499
|
+
initialSessionId
|
|
1500
|
+
);
|
|
1477
1501
|
const [sidebarOpen, setSidebarOpen] = useState(false);
|
|
1478
1502
|
const [loadingSession, setLoadingSession] = useState(false);
|
|
1479
1503
|
const [provider, setProvider] = useState(initialProvider);
|
|
@@ -1487,7 +1511,6 @@ function VercelChat({
|
|
|
1487
1511
|
const textareaRef = useRef(null);
|
|
1488
1512
|
const lastAnswerRef = useRef(null);
|
|
1489
1513
|
const prevAnswersLen = useRef(0);
|
|
1490
|
-
const autoOpenedRef = useRef(false);
|
|
1491
1514
|
const activeSessionIdRef = useRef(activeSessionId);
|
|
1492
1515
|
const providerRef = useRef(provider);
|
|
1493
1516
|
useEffect(() => {
|
|
@@ -1543,48 +1566,7 @@ function VercelChat({
|
|
|
1543
1566
|
if (!res.ok) return;
|
|
1544
1567
|
const json = await res.json();
|
|
1545
1568
|
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
|
-
}
|
|
1569
|
+
setSessions(json.sessions ?? []);
|
|
1588
1570
|
} catch {
|
|
1589
1571
|
}
|
|
1590
1572
|
}
|
|
@@ -1592,7 +1574,54 @@ function VercelChat({
|
|
|
1592
1574
|
return () => {
|
|
1593
1575
|
cancelled = true;
|
|
1594
1576
|
};
|
|
1595
|
-
}, [
|
|
1577
|
+
}, []);
|
|
1578
|
+
useEffect(() => {
|
|
1579
|
+
if (initialSessionId == null) {
|
|
1580
|
+
setMessages([]);
|
|
1581
|
+
setHydratedBlocks({});
|
|
1582
|
+
setHydratedProse({});
|
|
1583
|
+
setHydratedErrors({});
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
let cancelled = false;
|
|
1587
|
+
async function load(id) {
|
|
1588
|
+
setLoadingSession(true);
|
|
1589
|
+
try {
|
|
1590
|
+
const res = await fetch(`/api/chat/sessions/${id}`, { cache: "no-store" });
|
|
1591
|
+
if (cancelled) return;
|
|
1592
|
+
if (!res.ok) {
|
|
1593
|
+
setMessages([]);
|
|
1594
|
+
setHydratedBlocks({});
|
|
1595
|
+
setHydratedProse({});
|
|
1596
|
+
setHydratedErrors({});
|
|
1597
|
+
return;
|
|
1598
|
+
}
|
|
1599
|
+
const json = await res.json();
|
|
1600
|
+
if (cancelled) return;
|
|
1601
|
+
const { uiMessages, blocksMap, proseMap, errorsMap } = storedToUseChat(
|
|
1602
|
+
json.messages ?? []
|
|
1603
|
+
);
|
|
1604
|
+
setMessages(uiMessages);
|
|
1605
|
+
setHydratedBlocks(blocksMap);
|
|
1606
|
+
setHydratedProse(proseMap);
|
|
1607
|
+
setHydratedErrors(errorsMap);
|
|
1608
|
+
setStartedAt({});
|
|
1609
|
+
} catch {
|
|
1610
|
+
if (!cancelled) {
|
|
1611
|
+
setMessages([]);
|
|
1612
|
+
setHydratedBlocks({});
|
|
1613
|
+
setHydratedProse({});
|
|
1614
|
+
setHydratedErrors({});
|
|
1615
|
+
}
|
|
1616
|
+
} finally {
|
|
1617
|
+
if (!cancelled) setLoadingSession(false);
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
void load(initialSessionId);
|
|
1621
|
+
return () => {
|
|
1622
|
+
cancelled = true;
|
|
1623
|
+
};
|
|
1624
|
+
}, [initialSessionId, setMessages]);
|
|
1596
1625
|
const answers = useMemo(() => {
|
|
1597
1626
|
const liveBlocks = [];
|
|
1598
1627
|
const liveErrors = [];
|
|
@@ -1691,6 +1720,12 @@ function VercelChat({
|
|
|
1691
1720
|
}
|
|
1692
1721
|
prevAnswersLen.current = answers.length;
|
|
1693
1722
|
}, [answers.length]);
|
|
1723
|
+
const syncUrl = useCallback(
|
|
1724
|
+
(id) => {
|
|
1725
|
+
router.push(id == null ? "/chat" : `/chat/${id}`);
|
|
1726
|
+
},
|
|
1727
|
+
[router]
|
|
1728
|
+
);
|
|
1694
1729
|
const newChat = useCallback(() => {
|
|
1695
1730
|
setActiveSessionId(null);
|
|
1696
1731
|
setMessages([]);
|
|
@@ -1699,7 +1734,8 @@ function VercelChat({
|
|
|
1699
1734
|
setHydratedErrors({});
|
|
1700
1735
|
setStartedAt({});
|
|
1701
1736
|
setInput("");
|
|
1702
|
-
|
|
1737
|
+
syncUrl(null);
|
|
1738
|
+
}, [setMessages, setInput, syncUrl]);
|
|
1703
1739
|
const changeProvider = useCallback(
|
|
1704
1740
|
async (next) => {
|
|
1705
1741
|
if (next === provider || providerSaving) return;
|
|
@@ -1726,6 +1762,7 @@ function VercelChat({
|
|
|
1726
1762
|
async (id) => {
|
|
1727
1763
|
setLoadingSession(true);
|
|
1728
1764
|
setActiveSessionId(id);
|
|
1765
|
+
syncUrl(id);
|
|
1729
1766
|
try {
|
|
1730
1767
|
const res = await fetch(`/api/chat/sessions/${id}`, { cache: "no-store" });
|
|
1731
1768
|
if (!res.ok) {
|
|
@@ -1753,7 +1790,7 @@ function VercelChat({
|
|
|
1753
1790
|
setLoadingSession(false);
|
|
1754
1791
|
}
|
|
1755
1792
|
},
|
|
1756
|
-
[setMessages]
|
|
1793
|
+
[setMessages, syncUrl]
|
|
1757
1794
|
);
|
|
1758
1795
|
const persistTitle = useCallback(
|
|
1759
1796
|
async (id, title) => {
|
|
@@ -1787,9 +1824,10 @@ function VercelChat({
|
|
|
1787
1824
|
setHydratedBlocks({});
|
|
1788
1825
|
setHydratedProse({});
|
|
1789
1826
|
setHydratedErrors({});
|
|
1827
|
+
syncUrl(null);
|
|
1790
1828
|
}
|
|
1791
1829
|
},
|
|
1792
|
-
[activeSessionId, setMessages]
|
|
1830
|
+
[activeSessionId, setMessages, syncUrl]
|
|
1793
1831
|
);
|
|
1794
1832
|
const submitForm = useCallback(
|
|
1795
1833
|
async (e) => {
|
|
@@ -1806,6 +1844,7 @@ function VercelChat({
|
|
|
1806
1844
|
const json = await create.json();
|
|
1807
1845
|
activeSessionIdRef.current = json.session.id;
|
|
1808
1846
|
setActiveSessionId(json.session.id);
|
|
1847
|
+
syncUrl(json.session.id);
|
|
1809
1848
|
setSessions((prev) => [
|
|
1810
1849
|
{ id: json.session.id, title: json.session.title, updatedAt: null },
|
|
1811
1850
|
...prev
|
|
@@ -1816,7 +1855,7 @@ function VercelChat({
|
|
|
1816
1855
|
}
|
|
1817
1856
|
handleSubmit(e);
|
|
1818
1857
|
},
|
|
1819
|
-
[input, status, handleSubmit]
|
|
1858
|
+
[input, status, handleSubmit, syncUrl]
|
|
1820
1859
|
);
|
|
1821
1860
|
useEffect(() => {
|
|
1822
1861
|
setStartedAt((prev) => {
|