@firstlovecenter/ai-chat 0.8.0 → 0.9.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.
@@ -114,8 +114,14 @@ type SystemBlock = {
114
114
  * regardless of the chosen ORM.
115
115
  */
116
116
 
117
+ /**
118
+ * Chat session and message identifiers are short URL-safe UIDs (12 chars,
119
+ * ~72 bits of entropy, alphabet matches nanoid's URL-safe set). The
120
+ * package generates them in `createSession`/`appendMessage` so adapters
121
+ * never need to know about auto-increment vs UID.
122
+ */
117
123
  type ChatSession = {
118
- id: number;
124
+ id: string;
119
125
  userId: number;
120
126
  title: string;
121
127
  createdAt: Date;
@@ -129,8 +135,8 @@ type ChatMessageRole = 'user' | 'assistant';
129
135
  * narrator. `errorJson` is set when an assistant turn failed mid-stream.
130
136
  */
131
137
  type ChatMessage = {
132
- id: number;
133
- sessionId: number;
138
+ id: string;
139
+ sessionId: string;
134
140
  role: ChatMessageRole;
135
141
  question: string | null;
136
142
  blocks: unknown | null;
@@ -244,7 +250,7 @@ type CreateSessionInput = {
244
250
  title: string;
245
251
  };
246
252
  type AppendMessageInput = {
247
- sessionId: number;
253
+ sessionId: string;
248
254
  role: ChatMessageRole;
249
255
  question?: string | null;
250
256
  blocks?: unknown | null;
@@ -277,15 +283,15 @@ type AiSettingsPatch = {
277
283
  type PersistencePort = {
278
284
  createSession(input: CreateSessionInput): Promise<ChatSession>;
279
285
  /** Returns null when the session doesn't exist OR doesn't belong to this user. */
280
- getSession(id: number, userId: number): Promise<ChatSession | null>;
286
+ getSession(id: string, userId: number): Promise<ChatSession | null>;
281
287
  listSessionsForUser(userId: number, opts?: ListSessionsOpts): Promise<ChatSession[]>;
282
288
  /** No-op when session doesn't exist or doesn't belong to user — caller asserted authorisation. */
283
- updateSession(id: number, userId: number, patch: {
289
+ updateSession(id: string, userId: number, patch: {
284
290
  title?: string;
285
291
  }): Promise<void>;
286
- deleteSession(id: number, userId: number): Promise<void>;
292
+ deleteSession(id: string, userId: number): Promise<void>;
287
293
  appendMessage(input: AppendMessageInput): Promise<ChatMessage>;
288
- listMessagesForSession(sessionId: number, userId: number): Promise<ChatMessage[]>;
294
+ listMessagesForSession(sessionId: string, userId: number): Promise<ChatMessage[]>;
289
295
  /** Returns the singleton row, applying defaults if it's never been written. */
290
296
  getAiSettings(): Promise<AiSettings>;
291
297
  /**
@@ -114,8 +114,14 @@ type SystemBlock = {
114
114
  * regardless of the chosen ORM.
115
115
  */
116
116
 
117
+ /**
118
+ * Chat session and message identifiers are short URL-safe UIDs (12 chars,
119
+ * ~72 bits of entropy, alphabet matches nanoid's URL-safe set). The
120
+ * package generates them in `createSession`/`appendMessage` so adapters
121
+ * never need to know about auto-increment vs UID.
122
+ */
117
123
  type ChatSession = {
118
- id: number;
124
+ id: string;
119
125
  userId: number;
120
126
  title: string;
121
127
  createdAt: Date;
@@ -129,8 +135,8 @@ type ChatMessageRole = 'user' | 'assistant';
129
135
  * narrator. `errorJson` is set when an assistant turn failed mid-stream.
130
136
  */
131
137
  type ChatMessage = {
132
- id: number;
133
- sessionId: number;
138
+ id: string;
139
+ sessionId: string;
134
140
  role: ChatMessageRole;
135
141
  question: string | null;
136
142
  blocks: unknown | null;
@@ -244,7 +250,7 @@ type CreateSessionInput = {
244
250
  title: string;
245
251
  };
246
252
  type AppendMessageInput = {
247
- sessionId: number;
253
+ sessionId: string;
248
254
  role: ChatMessageRole;
249
255
  question?: string | null;
250
256
  blocks?: unknown | null;
@@ -277,15 +283,15 @@ type AiSettingsPatch = {
277
283
  type PersistencePort = {
278
284
  createSession(input: CreateSessionInput): Promise<ChatSession>;
279
285
  /** Returns null when the session doesn't exist OR doesn't belong to this user. */
280
- getSession(id: number, userId: number): Promise<ChatSession | null>;
286
+ getSession(id: string, userId: number): Promise<ChatSession | null>;
281
287
  listSessionsForUser(userId: number, opts?: ListSessionsOpts): Promise<ChatSession[]>;
282
288
  /** No-op when session doesn't exist or doesn't belong to user — caller asserted authorisation. */
283
- updateSession(id: number, userId: number, patch: {
289
+ updateSession(id: string, userId: number, patch: {
284
290
  title?: string;
285
291
  }): Promise<void>;
286
- deleteSession(id: number, userId: number): Promise<void>;
292
+ deleteSession(id: string, userId: number): Promise<void>;
287
293
  appendMessage(input: AppendMessageInput): Promise<ChatMessage>;
288
- listMessagesForSession(sessionId: number, userId: number): Promise<ChatMessage[]>;
294
+ listMessagesForSession(sessionId: string, userId: number): Promise<ChatMessage[]>;
289
295
  /** Returns the singleton row, applying defaults if it's never been written. */
290
296
  getAiSettings(): Promise<AiSettings>;
291
297
  /**
package/dist/ui/index.cjs CHANGED
@@ -3,12 +3,15 @@
3
3
 
4
4
  var React = require('react');
5
5
  var navigation = require('next/navigation');
6
+ var Link = require('next/link');
6
7
  var lucideReact = require('lucide-react');
7
8
  var radixUi = require('radix-ui');
8
9
  var jsxRuntime = require('react/jsx-runtime');
9
10
  var RechartsPrimitive = require('recharts');
10
11
  var react = require('@ai-sdk/react');
11
12
 
13
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
14
+
12
15
  function _interopNamespace(e) {
13
16
  if (e && e.__esModule) return e;
14
17
  var n = Object.create(null);
@@ -28,6 +31,7 @@ function _interopNamespace(e) {
28
31
  }
29
32
 
30
33
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
34
+ var Link__default = /*#__PURE__*/_interopDefault(Link);
31
35
  var RechartsPrimitive__namespace = /*#__PURE__*/_interopNamespace(RechartsPrimitive);
32
36
 
33
37
  // src/ui/_shared/cn.ts
@@ -772,8 +776,8 @@ function AiChat({
772
776
  setActiveSessionId(null);
773
777
  setAnswers([]);
774
778
  setQuestion("");
775
- syncUrl(null);
776
- }, [syncUrl]);
779
+ router.push("/chat?new");
780
+ }, [router]);
777
781
  const changeProvider = React.useCallback(
778
782
  async (next) => {
779
783
  if (next === provider || providerSaving) return;
@@ -796,7 +800,7 @@ function AiChat({
796
800
  },
797
801
  [provider, providerSaving]
798
802
  );
799
- const openSession = React.useCallback(
803
+ React.useCallback(
800
804
  async (id) => {
801
805
  setLoadingSession(true);
802
806
  setActiveSessionId(id);
@@ -1024,13 +1028,10 @@ function AiChat({
1024
1028
  }
1025
1029
  return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "group relative", children: [
1026
1030
  /* @__PURE__ */ jsxRuntime.jsx(
1027
- "button",
1031
+ Link__default.default,
1028
1032
  {
1029
- type: "button",
1030
- onClick: () => {
1031
- void openSession(s.id);
1032
- setSidebarOpen(false);
1033
- },
1033
+ href: `/chat/${s.id}`,
1034
+ onClick: () => setSidebarOpen(false),
1034
1035
  className: cn(
1035
1036
  "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm",
1036
1037
  active ? "bg-sidebar-accent text-sidebar-accent-foreground" : "text-sidebar-foreground/80 hover:bg-sidebar-accent/60 hover:text-sidebar-foreground"
@@ -1755,8 +1756,8 @@ function VercelChat({
1755
1756
  setHydratedErrors({});
1756
1757
  setStartedAt({});
1757
1758
  setInput("");
1758
- syncUrl(null);
1759
- }, [setMessages, setInput, syncUrl]);
1759
+ router.push("/chat?new");
1760
+ }, [setMessages, setInput, router]);
1760
1761
  const changeProvider = React.useCallback(
1761
1762
  async (next) => {
1762
1763
  if (next === provider || providerSaving) return;
@@ -1779,7 +1780,7 @@ function VercelChat({
1779
1780
  },
1780
1781
  [provider, providerSaving]
1781
1782
  );
1782
- const openSession = React.useCallback(
1783
+ React.useCallback(
1783
1784
  async (id) => {
1784
1785
  setLoadingSession(true);
1785
1786
  setActiveSessionId(id);
@@ -1984,13 +1985,10 @@ function VercelChat({
1984
1985
  }
1985
1986
  return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "group relative", children: [
1986
1987
  /* @__PURE__ */ jsxRuntime.jsx(
1987
- "button",
1988
+ Link__default.default,
1988
1989
  {
1989
- type: "button",
1990
- onClick: () => {
1991
- void openSession(s.id);
1992
- setSidebarOpen(false);
1993
- },
1990
+ href: `/chat/${s.id}`,
1991
+ onClick: () => setSidebarOpen(false),
1994
1992
  className: cn(
1995
1993
  "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm",
1996
1994
  active ? "bg-sidebar-accent text-sidebar-accent-foreground" : "text-sidebar-foreground/80 hover:bg-sidebar-accent/60 hover:text-sidebar-foreground"