@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/dist/ui/index.cjs CHANGED
@@ -2,12 +2,16 @@
2
2
  'use strict';
3
3
 
4
4
  var React = require('react');
5
+ var navigation = require('next/navigation');
6
+ var Link = require('next/link');
5
7
  var lucideReact = require('lucide-react');
6
8
  var radixUi = require('radix-ui');
7
9
  var jsxRuntime = require('react/jsx-runtime');
8
10
  var RechartsPrimitive = require('recharts');
9
11
  var react = require('@ai-sdk/react');
10
12
 
13
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
14
+
11
15
  function _interopNamespace(e) {
12
16
  if (e && e.__esModule) return e;
13
17
  var n = Object.create(null);
@@ -27,6 +31,7 @@ function _interopNamespace(e) {
27
31
  }
28
32
 
29
33
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
34
+ var Link__default = /*#__PURE__*/_interopDefault(Link);
30
35
  var RechartsPrimitive__namespace = /*#__PURE__*/_interopNamespace(RechartsPrimitive);
31
36
 
32
37
  // src/ui/_shared/cn.ts
@@ -672,10 +677,14 @@ var PROVIDER_DESCRIPTIONS = {
672
677
  function AiChat({
673
678
  userFirstName,
674
679
  scopeLabel,
675
- initialProvider
680
+ initialProvider,
681
+ initialSessionId = null
676
682
  }) {
683
+ const router = navigation.useRouter();
677
684
  const [sessions, setSessions] = React.useState([]);
678
- const [activeSessionId, setActiveSessionId] = React.useState(null);
685
+ const [activeSessionId, setActiveSessionId] = React.useState(
686
+ initialSessionId
687
+ );
679
688
  const [answers, setAnswers] = React.useState([]);
680
689
  const [pending, setPending] = React.useState(false);
681
690
  const [question, setQuestion] = React.useState("");
@@ -689,7 +698,6 @@ function AiChat({
689
698
  const textareaRef = React.useRef(null);
690
699
  const lastAnswerRef = React.useRef(null);
691
700
  const prevAnswersLen = React.useRef(0);
692
- const autoOpenedRef = React.useRef(false);
693
701
  React.useLayoutEffect(() => {
694
702
  const el = textareaRef.current;
695
703
  if (!el) return;
@@ -705,33 +713,7 @@ function AiChat({
705
713
  if (!res.ok) return;
706
714
  const data = await res.json();
707
715
  if (cancelled) return;
708
- const list = data.sessions ?? [];
709
- setSessions(list);
710
- if (!autoOpenedRef.current && list.length > 0) {
711
- autoOpenedRef.current = true;
712
- const mostRecentId = list[0].id;
713
- setLoadingSession(true);
714
- setActiveSessionId(mostRecentId);
715
- try {
716
- const sres = await fetch(`/api/chat/sessions/${mostRecentId}`, {
717
- cache: "no-store"
718
- });
719
- if (cancelled) return;
720
- if (!sres.ok) {
721
- setAnswers([]);
722
- return;
723
- }
724
- const sdata = await sres.json();
725
- if (cancelled) return;
726
- setAnswers(messagesToAnswers(sdata.messages ?? []));
727
- } catch {
728
- if (!cancelled) setAnswers([]);
729
- } finally {
730
- if (!cancelled) setLoadingSession(false);
731
- }
732
- } else if (!autoOpenedRef.current) {
733
- autoOpenedRef.current = true;
734
- }
716
+ setSessions(data.sessions ?? []);
735
717
  } catch {
736
718
  }
737
719
  }
@@ -740,6 +722,35 @@ function AiChat({
740
722
  cancelled = true;
741
723
  };
742
724
  }, []);
725
+ React.useEffect(() => {
726
+ if (initialSessionId == null) {
727
+ setAnswers([]);
728
+ return;
729
+ }
730
+ let cancelled = false;
731
+ async function load(id) {
732
+ setLoadingSession(true);
733
+ try {
734
+ const res = await fetch(`/api/chat/sessions/${id}`, { cache: "no-store" });
735
+ if (cancelled) return;
736
+ if (!res.ok) {
737
+ setAnswers([]);
738
+ return;
739
+ }
740
+ const data = await res.json();
741
+ if (cancelled) return;
742
+ setAnswers(messagesToAnswers(data.messages ?? []));
743
+ } catch {
744
+ if (!cancelled) setAnswers([]);
745
+ } finally {
746
+ if (!cancelled) setLoadingSession(false);
747
+ }
748
+ }
749
+ void load(initialSessionId);
750
+ return () => {
751
+ cancelled = true;
752
+ };
753
+ }, [initialSessionId]);
743
754
  React.useEffect(() => {
744
755
  if (answers.length > prevAnswersLen.current && lastAnswerRef.current) {
745
756
  lastAnswerRef.current.scrollIntoView({ behavior: "smooth", block: "start" });
@@ -755,11 +766,18 @@ function AiChat({
755
766
  } catch {
756
767
  }
757
768
  }, []);
769
+ const syncUrl = React.useCallback(
770
+ (id) => {
771
+ router.push(id == null ? "/chat" : `/chat/${id}`);
772
+ },
773
+ [router]
774
+ );
758
775
  const newChat = React.useCallback(() => {
759
776
  setActiveSessionId(null);
760
777
  setAnswers([]);
761
778
  setQuestion("");
762
- }, []);
779
+ router.push("/chat?new");
780
+ }, [router]);
763
781
  const changeProvider = React.useCallback(
764
782
  async (next) => {
765
783
  if (next === provider || providerSaving) return;
@@ -782,23 +800,27 @@ function AiChat({
782
800
  },
783
801
  [provider, providerSaving]
784
802
  );
785
- const openSession = React.useCallback(async (id) => {
786
- setLoadingSession(true);
787
- setActiveSessionId(id);
788
- try {
789
- const res = await fetch(`/api/chat/sessions/${id}`, { cache: "no-store" });
790
- if (!res.ok) {
803
+ React.useCallback(
804
+ async (id) => {
805
+ setLoadingSession(true);
806
+ setActiveSessionId(id);
807
+ syncUrl(id);
808
+ try {
809
+ const res = await fetch(`/api/chat/sessions/${id}`, { cache: "no-store" });
810
+ if (!res.ok) {
811
+ setAnswers([]);
812
+ return;
813
+ }
814
+ const data = await res.json();
815
+ setAnswers(messagesToAnswers(data.messages ?? []));
816
+ } catch {
791
817
  setAnswers([]);
792
- return;
818
+ } finally {
819
+ setLoadingSession(false);
793
820
  }
794
- const data = await res.json();
795
- setAnswers(messagesToAnswers(data.messages ?? []));
796
- } catch {
797
- setAnswers([]);
798
- } finally {
799
- setLoadingSession(false);
800
- }
801
- }, []);
821
+ },
822
+ [syncUrl]
823
+ );
802
824
  const persistTitle = React.useCallback(
803
825
  async (id, title) => {
804
826
  const trimmed = title.trim();
@@ -828,9 +850,10 @@ function AiChat({
828
850
  if (activeSessionId === id) {
829
851
  setActiveSessionId(null);
830
852
  setAnswers([]);
853
+ syncUrl(null);
831
854
  }
832
855
  },
833
- [activeSessionId]
856
+ [activeSessionId, syncUrl]
834
857
  );
835
858
  const submit = React.useCallback(
836
859
  async (q) => {
@@ -850,6 +873,7 @@ function AiChat({
850
873
  const data = await create.json();
851
874
  sessionId = data.session.id;
852
875
  setActiveSessionId(sessionId);
876
+ syncUrl(sessionId);
853
877
  setSessions((prev) => [
854
878
  { id: data.session.id, title: data.session.title, updatedAt: null },
855
879
  ...prev
@@ -930,7 +954,7 @@ function AiChat({
930
954
  void refreshSessions();
931
955
  }
932
956
  },
933
- [activeSessionId, pending, refreshSessions]
957
+ [activeSessionId, pending, refreshSessions, syncUrl]
934
958
  );
935
959
  const heroVisible = answers.length === 0 && !loadingSession;
936
960
  const greeting = React.useMemo(
@@ -1004,13 +1028,10 @@ function AiChat({
1004
1028
  }
1005
1029
  return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "group relative", children: [
1006
1030
  /* @__PURE__ */ jsxRuntime.jsx(
1007
- "button",
1031
+ Link__default.default,
1008
1032
  {
1009
- type: "button",
1010
- onClick: () => {
1011
- void openSession(s.id);
1012
- setSidebarOpen(false);
1013
- },
1033
+ href: `/chat/${s.id}`,
1034
+ onClick: () => setSidebarOpen(false),
1014
1035
  className: cn(
1015
1036
  "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm",
1016
1037
  active ? "bg-sidebar-accent text-sidebar-accent-foreground" : "text-sidebar-foreground/80 hover:bg-sidebar-accent/60 hover:text-sidebar-foreground"
@@ -1491,10 +1512,14 @@ function asDataPart(v) {
1491
1512
  function VercelChat({
1492
1513
  userFirstName,
1493
1514
  scopeLabel,
1494
- initialProvider
1515
+ initialProvider,
1516
+ initialSessionId = null
1495
1517
  }) {
1518
+ const router = navigation.useRouter();
1496
1519
  const [sessions, setSessions] = React.useState([]);
1497
- const [activeSessionId, setActiveSessionId] = React.useState(null);
1520
+ const [activeSessionId, setActiveSessionId] = React.useState(
1521
+ initialSessionId
1522
+ );
1498
1523
  const [sidebarOpen, setSidebarOpen] = React.useState(false);
1499
1524
  const [loadingSession, setLoadingSession] = React.useState(false);
1500
1525
  const [provider, setProvider] = React.useState(initialProvider);
@@ -1508,7 +1533,6 @@ function VercelChat({
1508
1533
  const textareaRef = React.useRef(null);
1509
1534
  const lastAnswerRef = React.useRef(null);
1510
1535
  const prevAnswersLen = React.useRef(0);
1511
- const autoOpenedRef = React.useRef(false);
1512
1536
  const activeSessionIdRef = React.useRef(activeSessionId);
1513
1537
  const providerRef = React.useRef(provider);
1514
1538
  React.useEffect(() => {
@@ -1564,48 +1588,7 @@ function VercelChat({
1564
1588
  if (!res.ok) return;
1565
1589
  const json = await res.json();
1566
1590
  if (cancelled) return;
1567
- const list = json.sessions ?? [];
1568
- setSessions(list);
1569
- if (!autoOpenedRef.current && list.length > 0) {
1570
- autoOpenedRef.current = true;
1571
- const mostRecentId = list[0].id;
1572
- setLoadingSession(true);
1573
- setActiveSessionId(mostRecentId);
1574
- try {
1575
- const sres = await fetch(`/api/chat/sessions/${mostRecentId}`, {
1576
- cache: "no-store"
1577
- });
1578
- if (cancelled) return;
1579
- if (!sres.ok) {
1580
- setMessages([]);
1581
- setHydratedBlocks({});
1582
- setHydratedProse({});
1583
- setHydratedErrors({});
1584
- return;
1585
- }
1586
- const sjson = await sres.json();
1587
- if (cancelled) return;
1588
- const { uiMessages, blocksMap, proseMap, errorsMap } = storedToUseChat(
1589
- sjson.messages ?? []
1590
- );
1591
- setMessages(uiMessages);
1592
- setHydratedBlocks(blocksMap);
1593
- setHydratedProse(proseMap);
1594
- setHydratedErrors(errorsMap);
1595
- setStartedAt({});
1596
- } catch {
1597
- if (!cancelled) {
1598
- setMessages([]);
1599
- setHydratedBlocks({});
1600
- setHydratedProse({});
1601
- setHydratedErrors({});
1602
- }
1603
- } finally {
1604
- if (!cancelled) setLoadingSession(false);
1605
- }
1606
- } else if (!autoOpenedRef.current) {
1607
- autoOpenedRef.current = true;
1608
- }
1591
+ setSessions(json.sessions ?? []);
1609
1592
  } catch {
1610
1593
  }
1611
1594
  }
@@ -1613,7 +1596,54 @@ function VercelChat({
1613
1596
  return () => {
1614
1597
  cancelled = true;
1615
1598
  };
1616
- }, [setMessages]);
1599
+ }, []);
1600
+ React.useEffect(() => {
1601
+ if (initialSessionId == null) {
1602
+ setMessages([]);
1603
+ setHydratedBlocks({});
1604
+ setHydratedProse({});
1605
+ setHydratedErrors({});
1606
+ return;
1607
+ }
1608
+ let cancelled = false;
1609
+ async function load(id) {
1610
+ setLoadingSession(true);
1611
+ try {
1612
+ const res = await fetch(`/api/chat/sessions/${id}`, { cache: "no-store" });
1613
+ if (cancelled) return;
1614
+ if (!res.ok) {
1615
+ setMessages([]);
1616
+ setHydratedBlocks({});
1617
+ setHydratedProse({});
1618
+ setHydratedErrors({});
1619
+ return;
1620
+ }
1621
+ const json = await res.json();
1622
+ if (cancelled) return;
1623
+ const { uiMessages, blocksMap, proseMap, errorsMap } = storedToUseChat(
1624
+ json.messages ?? []
1625
+ );
1626
+ setMessages(uiMessages);
1627
+ setHydratedBlocks(blocksMap);
1628
+ setHydratedProse(proseMap);
1629
+ setHydratedErrors(errorsMap);
1630
+ setStartedAt({});
1631
+ } catch {
1632
+ if (!cancelled) {
1633
+ setMessages([]);
1634
+ setHydratedBlocks({});
1635
+ setHydratedProse({});
1636
+ setHydratedErrors({});
1637
+ }
1638
+ } finally {
1639
+ if (!cancelled) setLoadingSession(false);
1640
+ }
1641
+ }
1642
+ void load(initialSessionId);
1643
+ return () => {
1644
+ cancelled = true;
1645
+ };
1646
+ }, [initialSessionId, setMessages]);
1617
1647
  const answers = React.useMemo(() => {
1618
1648
  const liveBlocks = [];
1619
1649
  const liveErrors = [];
@@ -1712,6 +1742,12 @@ function VercelChat({
1712
1742
  }
1713
1743
  prevAnswersLen.current = answers.length;
1714
1744
  }, [answers.length]);
1745
+ const syncUrl = React.useCallback(
1746
+ (id) => {
1747
+ router.push(id == null ? "/chat" : `/chat/${id}`);
1748
+ },
1749
+ [router]
1750
+ );
1715
1751
  const newChat = React.useCallback(() => {
1716
1752
  setActiveSessionId(null);
1717
1753
  setMessages([]);
@@ -1720,7 +1756,8 @@ function VercelChat({
1720
1756
  setHydratedErrors({});
1721
1757
  setStartedAt({});
1722
1758
  setInput("");
1723
- }, [setMessages, setInput]);
1759
+ router.push("/chat?new");
1760
+ }, [setMessages, setInput, router]);
1724
1761
  const changeProvider = React.useCallback(
1725
1762
  async (next) => {
1726
1763
  if (next === provider || providerSaving) return;
@@ -1743,10 +1780,11 @@ function VercelChat({
1743
1780
  },
1744
1781
  [provider, providerSaving]
1745
1782
  );
1746
- const openSession = React.useCallback(
1783
+ React.useCallback(
1747
1784
  async (id) => {
1748
1785
  setLoadingSession(true);
1749
1786
  setActiveSessionId(id);
1787
+ syncUrl(id);
1750
1788
  try {
1751
1789
  const res = await fetch(`/api/chat/sessions/${id}`, { cache: "no-store" });
1752
1790
  if (!res.ok) {
@@ -1774,7 +1812,7 @@ function VercelChat({
1774
1812
  setLoadingSession(false);
1775
1813
  }
1776
1814
  },
1777
- [setMessages]
1815
+ [setMessages, syncUrl]
1778
1816
  );
1779
1817
  const persistTitle = React.useCallback(
1780
1818
  async (id, title) => {
@@ -1808,9 +1846,10 @@ function VercelChat({
1808
1846
  setHydratedBlocks({});
1809
1847
  setHydratedProse({});
1810
1848
  setHydratedErrors({});
1849
+ syncUrl(null);
1811
1850
  }
1812
1851
  },
1813
- [activeSessionId, setMessages]
1852
+ [activeSessionId, setMessages, syncUrl]
1814
1853
  );
1815
1854
  const submitForm = React.useCallback(
1816
1855
  async (e) => {
@@ -1827,6 +1866,7 @@ function VercelChat({
1827
1866
  const json = await create.json();
1828
1867
  activeSessionIdRef.current = json.session.id;
1829
1868
  setActiveSessionId(json.session.id);
1869
+ syncUrl(json.session.id);
1830
1870
  setSessions((prev) => [
1831
1871
  { id: json.session.id, title: json.session.title, updatedAt: null },
1832
1872
  ...prev
@@ -1837,7 +1877,7 @@ function VercelChat({
1837
1877
  }
1838
1878
  handleSubmit(e);
1839
1879
  },
1840
- [input, status, handleSubmit]
1880
+ [input, status, handleSubmit, syncUrl]
1841
1881
  );
1842
1882
  React.useEffect(() => {
1843
1883
  setStartedAt((prev) => {
@@ -1945,13 +1985,10 @@ function VercelChat({
1945
1985
  }
1946
1986
  return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "group relative", children: [
1947
1987
  /* @__PURE__ */ jsxRuntime.jsx(
1948
- "button",
1988
+ Link__default.default,
1949
1989
  {
1950
- type: "button",
1951
- onClick: () => {
1952
- void openSession(s.id);
1953
- setSidebarOpen(false);
1954
- },
1990
+ href: `/chat/${s.id}`,
1991
+ onClick: () => setSidebarOpen(false),
1955
1992
  className: cn(
1956
1993
  "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm",
1957
1994
  active ? "bg-sidebar-accent text-sidebar-accent-foreground" : "text-sidebar-foreground/80 hover:bg-sidebar-accent/60 hover:text-sidebar-foreground"