@firstlovecenter/ai-chat 0.9.0 → 0.9.2
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 +26 -0
- package/dist/ui/index.cjs +65 -14
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.cts +18 -2
- package/dist/ui/index.d.ts +18 -2
- package/dist/ui/index.js +65 -14
- package/dist/ui/index.js.map +1 -1
- package/package.json +1 -1
package/dist/ui/index.d.cts
CHANGED
|
@@ -14,8 +14,15 @@ type AiChatProps$1 = {
|
|
|
14
14
|
* conversation. `null` (or omitted) renders the empty new-chat state.
|
|
15
15
|
*/
|
|
16
16
|
initialSessionId?: string | null;
|
|
17
|
+
/**
|
|
18
|
+
* URL prefix the chat is mounted at. Sidebar `<Link>`s and router
|
|
19
|
+
* pushes use this prefix so multiple chat surfaces (e.g. `/chat` and
|
|
20
|
+
* `/admin/ai`) can coexist without yanking users between shells.
|
|
21
|
+
* Defaults to `/chat`.
|
|
22
|
+
*/
|
|
23
|
+
basePath?: string;
|
|
17
24
|
};
|
|
18
|
-
declare function AiChat({ userFirstName, scopeLabel, initialProvider, initialSessionId }: AiChatProps$1): react_jsx_runtime.JSX.Element;
|
|
25
|
+
declare function AiChat({ userFirstName, scopeLabel, initialProvider, initialSessionId, basePath }: AiChatProps$1): react_jsx_runtime.JSX.Element;
|
|
19
26
|
|
|
20
27
|
type AiChatSessionSummary = {
|
|
21
28
|
id: string;
|
|
@@ -35,9 +42,18 @@ type AiChatProps = {
|
|
|
35
42
|
* empty "new chat" state.
|
|
36
43
|
*/
|
|
37
44
|
initialSessionId?: string | null;
|
|
45
|
+
/**
|
|
46
|
+
* URL prefix the chat surface is mounted at. Sidebar links resolve to
|
|
47
|
+
* `${basePath}/${sessionId}`, "+ New chat" goes to `${basePath}?new`,
|
|
48
|
+
* and the open-session push uses the same prefix. Defaults to `/chat`.
|
|
49
|
+
* Set to e.g. `/admin/ai` when embedding the chat inside another shell
|
|
50
|
+
* — the host then needs a matching `[id]` dynamic segment under that
|
|
51
|
+
* path so reload/bookmark URLs resolve.
|
|
52
|
+
*/
|
|
53
|
+
basePath?: string;
|
|
38
54
|
};
|
|
39
55
|
|
|
40
|
-
declare function VercelChat({ userFirstName, scopeLabel, initialProvider, initialSessionId }: AiChatProps): react_jsx_runtime.JSX.Element;
|
|
56
|
+
declare function VercelChat({ userFirstName, scopeLabel, initialProvider, initialSessionId, basePath }: AiChatProps): react_jsx_runtime.JSX.Element;
|
|
41
57
|
|
|
42
58
|
type ChartSpec = {
|
|
43
59
|
type: 'line' | 'bar' | 'stacked_bar' | 'pie';
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -14,8 +14,15 @@ type AiChatProps$1 = {
|
|
|
14
14
|
* conversation. `null` (or omitted) renders the empty new-chat state.
|
|
15
15
|
*/
|
|
16
16
|
initialSessionId?: string | null;
|
|
17
|
+
/**
|
|
18
|
+
* URL prefix the chat is mounted at. Sidebar `<Link>`s and router
|
|
19
|
+
* pushes use this prefix so multiple chat surfaces (e.g. `/chat` and
|
|
20
|
+
* `/admin/ai`) can coexist without yanking users between shells.
|
|
21
|
+
* Defaults to `/chat`.
|
|
22
|
+
*/
|
|
23
|
+
basePath?: string;
|
|
17
24
|
};
|
|
18
|
-
declare function AiChat({ userFirstName, scopeLabel, initialProvider, initialSessionId }: AiChatProps$1): react_jsx_runtime.JSX.Element;
|
|
25
|
+
declare function AiChat({ userFirstName, scopeLabel, initialProvider, initialSessionId, basePath }: AiChatProps$1): react_jsx_runtime.JSX.Element;
|
|
19
26
|
|
|
20
27
|
type AiChatSessionSummary = {
|
|
21
28
|
id: string;
|
|
@@ -35,9 +42,18 @@ type AiChatProps = {
|
|
|
35
42
|
* empty "new chat" state.
|
|
36
43
|
*/
|
|
37
44
|
initialSessionId?: string | null;
|
|
45
|
+
/**
|
|
46
|
+
* URL prefix the chat surface is mounted at. Sidebar links resolve to
|
|
47
|
+
* `${basePath}/${sessionId}`, "+ New chat" goes to `${basePath}?new`,
|
|
48
|
+
* and the open-session push uses the same prefix. Defaults to `/chat`.
|
|
49
|
+
* Set to e.g. `/admin/ai` when embedding the chat inside another shell
|
|
50
|
+
* — the host then needs a matching `[id]` dynamic segment under that
|
|
51
|
+
* path so reload/bookmark URLs resolve.
|
|
52
|
+
*/
|
|
53
|
+
basePath?: string;
|
|
38
54
|
};
|
|
39
55
|
|
|
40
|
-
declare function VercelChat({ userFirstName, scopeLabel, initialProvider, initialSessionId }: AiChatProps): react_jsx_runtime.JSX.Element;
|
|
56
|
+
declare function VercelChat({ userFirstName, scopeLabel, initialProvider, initialSessionId, basePath }: AiChatProps): react_jsx_runtime.JSX.Element;
|
|
41
57
|
|
|
42
58
|
type ChartSpec = {
|
|
43
59
|
type: 'line' | 'bar' | 'stacked_bar' | 'pie';
|
package/dist/ui/index.js
CHANGED
|
@@ -654,7 +654,8 @@ function AiChat({
|
|
|
654
654
|
userFirstName,
|
|
655
655
|
scopeLabel,
|
|
656
656
|
initialProvider,
|
|
657
|
-
initialSessionId = null
|
|
657
|
+
initialSessionId = null,
|
|
658
|
+
basePath = "/chat"
|
|
658
659
|
}) {
|
|
659
660
|
const router = useRouter();
|
|
660
661
|
const [sessions, setSessions] = useState([]);
|
|
@@ -744,16 +745,16 @@ function AiChat({
|
|
|
744
745
|
}, []);
|
|
745
746
|
const syncUrl = useCallback(
|
|
746
747
|
(id) => {
|
|
747
|
-
router.push(id == null ?
|
|
748
|
+
router.push(id == null ? basePath : `${basePath}/${id}`);
|
|
748
749
|
},
|
|
749
|
-
[router]
|
|
750
|
+
[router, basePath]
|
|
750
751
|
);
|
|
751
752
|
const newChat = useCallback(() => {
|
|
752
753
|
setActiveSessionId(null);
|
|
753
754
|
setAnswers([]);
|
|
754
755
|
setQuestion("");
|
|
755
|
-
router.push(
|
|
756
|
-
}, [router]);
|
|
756
|
+
router.push(`${basePath}?new`);
|
|
757
|
+
}, [router, basePath]);
|
|
757
758
|
const changeProvider = useCallback(
|
|
758
759
|
async (next) => {
|
|
759
760
|
if (next === provider || providerSaving) return;
|
|
@@ -849,7 +850,9 @@ function AiChat({
|
|
|
849
850
|
const data = await create.json();
|
|
850
851
|
sessionId = data.session.id;
|
|
851
852
|
setActiveSessionId(sessionId);
|
|
852
|
-
|
|
853
|
+
if (typeof window !== "undefined") {
|
|
854
|
+
window.history.replaceState(null, "", `${basePath}/${sessionId}`);
|
|
855
|
+
}
|
|
853
856
|
setSessions((prev) => [
|
|
854
857
|
{ id: data.session.id, title: data.session.title, updatedAt: null },
|
|
855
858
|
...prev
|
|
@@ -911,6 +914,17 @@ function AiChat({
|
|
|
911
914
|
const events = buffer.split("\n\n");
|
|
912
915
|
buffer = events.pop() ?? "";
|
|
913
916
|
for (const raw of events) {
|
|
917
|
+
const meta = parseMetaChatSessionId(raw);
|
|
918
|
+
if (meta != null) {
|
|
919
|
+
setActiveSessionId((prev) => prev ?? meta);
|
|
920
|
+
if (typeof window !== "undefined" && !window.location.pathname.endsWith(`/${meta}`)) {
|
|
921
|
+
window.history.replaceState(
|
|
922
|
+
null,
|
|
923
|
+
"",
|
|
924
|
+
`${basePath}/${meta}`
|
|
925
|
+
);
|
|
926
|
+
}
|
|
927
|
+
}
|
|
914
928
|
handleEvent(raw, setAnswers);
|
|
915
929
|
}
|
|
916
930
|
}
|
|
@@ -1006,7 +1020,7 @@ function AiChat({
|
|
|
1006
1020
|
/* @__PURE__ */ jsx(
|
|
1007
1021
|
Link,
|
|
1008
1022
|
{
|
|
1009
|
-
href:
|
|
1023
|
+
href: `${basePath}/${s.id}`,
|
|
1010
1024
|
onClick: () => setSidebarOpen(false),
|
|
1011
1025
|
className: cn(
|
|
1012
1026
|
"flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm",
|
|
@@ -1369,6 +1383,22 @@ function UserChip({ text }) {
|
|
|
1369
1383
|
)
|
|
1370
1384
|
] });
|
|
1371
1385
|
}
|
|
1386
|
+
function parseMetaChatSessionId(raw) {
|
|
1387
|
+
const lines = raw.split("\n");
|
|
1388
|
+
let event = "";
|
|
1389
|
+
let dataStr = "";
|
|
1390
|
+
for (const line of lines) {
|
|
1391
|
+
if (line.startsWith("event: ")) event = line.slice(7).trim();
|
|
1392
|
+
else if (line.startsWith("data: ")) dataStr += line.slice(6);
|
|
1393
|
+
}
|
|
1394
|
+
if (event !== "meta") return null;
|
|
1395
|
+
try {
|
|
1396
|
+
const parsed = JSON.parse(dataStr || "{}");
|
|
1397
|
+
return typeof parsed.chatSessionId === "string" && parsed.chatSessionId.length > 0 ? parsed.chatSessionId : null;
|
|
1398
|
+
} catch {
|
|
1399
|
+
return null;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1372
1402
|
function handleEvent(raw, setAnswers) {
|
|
1373
1403
|
const lines = raw.split("\n");
|
|
1374
1404
|
let event = "";
|
|
@@ -1489,7 +1519,8 @@ function VercelChat({
|
|
|
1489
1519
|
userFirstName,
|
|
1490
1520
|
scopeLabel,
|
|
1491
1521
|
initialProvider,
|
|
1492
|
-
initialSessionId = null
|
|
1522
|
+
initialSessionId = null,
|
|
1523
|
+
basePath = "/chat"
|
|
1493
1524
|
}) {
|
|
1494
1525
|
const router = useRouter();
|
|
1495
1526
|
const [sessions, setSessions] = useState([]);
|
|
@@ -1620,6 +1651,20 @@ function VercelChat({
|
|
|
1620
1651
|
cancelled = true;
|
|
1621
1652
|
};
|
|
1622
1653
|
}, [initialSessionId, setMessages]);
|
|
1654
|
+
useEffect(() => {
|
|
1655
|
+
if (!Array.isArray(data)) return;
|
|
1656
|
+
for (const raw of data) {
|
|
1657
|
+
const part = asDataPart(raw);
|
|
1658
|
+
if (!part || part.type !== "meta") continue;
|
|
1659
|
+
const id = part.value.chatSessionId;
|
|
1660
|
+
if (!id) continue;
|
|
1661
|
+
setActiveSessionId((prev) => prev ?? id);
|
|
1662
|
+
if (typeof window !== "undefined" && !window.location.pathname.endsWith(`/${id}`)) {
|
|
1663
|
+
window.history.replaceState(null, "", `${basePath}/${id}`);
|
|
1664
|
+
}
|
|
1665
|
+
break;
|
|
1666
|
+
}
|
|
1667
|
+
}, [data, basePath]);
|
|
1623
1668
|
const answers = useMemo(() => {
|
|
1624
1669
|
const liveBlocks = [];
|
|
1625
1670
|
const liveErrors = [];
|
|
@@ -1720,9 +1765,9 @@ function VercelChat({
|
|
|
1720
1765
|
}, [answers.length]);
|
|
1721
1766
|
const syncUrl = useCallback(
|
|
1722
1767
|
(id) => {
|
|
1723
|
-
router.push(id == null ?
|
|
1768
|
+
router.push(id == null ? basePath : `${basePath}/${id}`);
|
|
1724
1769
|
},
|
|
1725
|
-
[router]
|
|
1770
|
+
[router, basePath]
|
|
1726
1771
|
);
|
|
1727
1772
|
const newChat = useCallback(() => {
|
|
1728
1773
|
setActiveSessionId(null);
|
|
@@ -1732,8 +1777,8 @@ function VercelChat({
|
|
|
1732
1777
|
setHydratedErrors({});
|
|
1733
1778
|
setStartedAt({});
|
|
1734
1779
|
setInput("");
|
|
1735
|
-
router.push(
|
|
1736
|
-
}, [setMessages, setInput, router]);
|
|
1780
|
+
router.push(`${basePath}?new`);
|
|
1781
|
+
}, [setMessages, setInput, router, basePath]);
|
|
1737
1782
|
const changeProvider = useCallback(
|
|
1738
1783
|
async (next) => {
|
|
1739
1784
|
if (next === provider || providerSaving) return;
|
|
@@ -1842,7 +1887,13 @@ function VercelChat({
|
|
|
1842
1887
|
const json = await create.json();
|
|
1843
1888
|
activeSessionIdRef.current = json.session.id;
|
|
1844
1889
|
setActiveSessionId(json.session.id);
|
|
1845
|
-
|
|
1890
|
+
if (typeof window !== "undefined") {
|
|
1891
|
+
window.history.replaceState(
|
|
1892
|
+
null,
|
|
1893
|
+
"",
|
|
1894
|
+
`${basePath}/${json.session.id}`
|
|
1895
|
+
);
|
|
1896
|
+
}
|
|
1846
1897
|
setSessions((prev) => [
|
|
1847
1898
|
{ id: json.session.id, title: json.session.title, updatedAt: null },
|
|
1848
1899
|
...prev
|
|
@@ -1963,7 +2014,7 @@ function VercelChat({
|
|
|
1963
2014
|
/* @__PURE__ */ jsx(
|
|
1964
2015
|
Link,
|
|
1965
2016
|
{
|
|
1966
|
-
href:
|
|
2017
|
+
href: `${basePath}/${s.id}`,
|
|
1967
2018
|
onClick: () => setSidebarOpen(false),
|
|
1968
2019
|
className: cn(
|
|
1969
2020
|
"flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm",
|