@syncagent/react 0.1.9 → 0.2.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.
Files changed (2) hide show
  1. package/README.md +116 -112
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -8,9 +8,7 @@ React SDK for [SyncAgent](https://syncagent.dev) — drop-in AI database chat wi
8
8
  npm install @syncagent/react @syncagent/js
9
9
  ```
10
10
 
11
- ## Quick Start — Drop-in Widget
12
-
13
- The fastest way to add an AI assistant to your app:
11
+ ## Quick Start
14
12
 
15
13
  ```tsx
16
14
  import { SyncAgentChat } from "@syncagent/react";
@@ -21,85 +19,103 @@ export default function App() {
21
19
  config={{
22
20
  apiKey: "sa_your_api_key",
23
21
  connectionString: process.env.DATABASE_URL,
22
+ // baseUrl: "http://localhost:3100", // dev only
24
23
  }}
25
24
  />
26
25
  );
27
26
  }
28
27
  ```
29
28
 
30
- This renders a floating chat button (bottom-right) that opens a chat panel. Your database URL is sent at runtime and never stored on SyncAgent servers.
31
-
32
29
  ## `<SyncAgentChat>` Props
33
30
 
34
- | Prop | Type | Default | Description |
35
- | ---------------- | --------------------------------- | ------------------------ | ---------------------------------------- |
36
- | `config` | `SyncAgentConfig` | Required* | API key, connection string, tools |
37
- | `mode` | `"floating" \| "inline"` | `"floating"` | Floating FAB or embedded inline panel |
38
- | `position` | `"bottom-right" \| "bottom-left"` | `"bottom-right"` | FAB position (floating mode only) |
39
- | `defaultOpen` | `boolean` | `false` | Start with the panel open |
40
- | `title` | `string` | `"✨ AI Assistant"` | Header title |
41
- | `placeholder` | `string` | `"Ask about your data..."` | Input placeholder text |
42
- | `welcomeMessage` | `string` | `"Ask me anything..."` | Message shown when chat is empty |
43
- | `accentColor` | `string` | `"#10b981"` | Brand color for header, FAB, send button |
44
- | `className` | `string` | | CSS class on the panel container |
45
- | `style` | `CSSProperties` | — | Inline styles on the panel container |
46
-
47
- *`config` is required unless you wrap in `<SyncAgentProvider>`.
31
+ | Prop | Type | Default | Description |
32
+ | ---------------- | --------------------------------- | -------------------------- | ------------------------------------------------ |
33
+ | `config` | `SyncAgentConfig` | Required* | API key, connection string, tools, baseUrl |
34
+ | `mode` | `"floating" \| "inline"` | `"floating"` | Floating FAB or embedded inline panel |
35
+ | `position` | `"bottom-right" \| "bottom-left"` | `"bottom-right"` | FAB position (floating mode only) |
36
+ | `defaultOpen` | `boolean` | `false` | Start with the panel open |
37
+ | `title` | `string` | `"SyncAgent"` | Header title |
38
+ | `subtitle` | `string` | `"AI Database Assistant"` | Header subtitle (replaced by status while active)|
39
+ | `placeholder` | `string` | `"Ask anything..."` | Input placeholder |
40
+ | `welcomeMessage` | `string` | `"Hi! I can query..."` | Empty state message |
41
+ | `accentColor` | `string` | `"#10b981"` | Brand color for header, FAB, send button |
42
+ | `suggestions` | `string[]` | `["Show all records", ...]`| Quick-start suggestion chips |
43
+ | `persistKey` | `string` | — | localStorage key for conversation persistence |
44
+ | `context` | `Record<string, any>` | — | Extra context injected into every message |
45
+ | `onReaction` | `(idx, reaction, content) => void`| — | Called when user reacts 👍/👎 to a message |
46
+ | `onData` | `(data: ToolData) => void` | — | Called when a DB tool returns structured data |
47
+ | `className` | `string` | — | CSS class on the panel container |
48
+ | `style` | `CSSProperties` | — | Inline styles on the panel container |
49
+
50
+ *`config` is required unless wrapped in `<SyncAgentProvider>`.
51
+
52
+ ## Features
53
+
54
+ - **Live status** — header subtitle shows `● Querying users...` while the agent works
55
+ - **Markdown rendering** — tables, code blocks, bold, italic, lists, headers
56
+ - **Streaming cursor** — blinking cursor while text streams in
57
+ - **Copy button** — on every AI response
58
+ - **Reactions** — 👍/👎 on AI messages, fires `onReaction`
59
+ - **Retry** — retry button on failed messages
60
+ - **Conversation persistence** — pass `persistKey` to save history to localStorage
61
+ - **New conversation** — "New" button in header clears history
62
+ - **Suggestion chips** — configurable quick-start prompts, with pin/unpin to localStorage
63
+ - **Query history** — ↑/↓ in input to cycle through previous messages
64
+ - **Export CSV** — "⬇ CSV" button on messages containing markdown tables
65
+ - **Bar charts** — auto-renders aggregation results as a mini bar chart
66
+ - **Resize handle** — drag the top edge to resize the floating panel
67
+ - **Mobile responsive** — full-width on small screens
68
+ - **Dark mode** — respects `prefers-color-scheme`
69
+
70
+ ## Conversation Persistence
48
71
 
49
- ### `SyncAgentConfig`
50
-
51
- | Option | Type | Required | Description |
52
- | ------------------ | -------------------------------- | -------- | ------------------------------------------------ |
53
- | `apiKey` | `string` | ✅ | Your SyncAgent API key (`sa_...`) |
54
- | `connectionString` | `string` | ✅ | Your database URL — sent at runtime, never stored |
55
- | `tools` | `Record<string, ToolDefinition>` | — | Custom tools the agent can call client-side |
72
+ ```tsx
73
+ <SyncAgentChat
74
+ config={{ apiKey: "...", connectionString: "..." }}
75
+ persistKey="project-123" // unique per project/user
76
+ />
77
+ ```
56
78
 
57
- ## Inline Mode
79
+ History saves to `localStorage` under `sa_chat_project-123`. The "New" button clears it.
58
80
 
59
- Embed the chat panel directly in your layout instead of a floating widget:
81
+ ## Context injection
60
82
 
61
83
  ```tsx
62
- <div style={{ height: 600 }}>
63
- <SyncAgentChat
64
- config={{ apiKey: "...", connectionString: "..." }}
65
- mode="inline"
66
- />
67
- </div>
84
+ <SyncAgentChat
85
+ config={{ apiKey: "...", connectionString: "..." }}
86
+ context={{ userId: currentUser.id, page: "orders" }}
87
+ />
68
88
  ```
69
89
 
70
- ## Custom Accent Color
90
+ ## `onData` react to query results
71
91
 
72
92
  ```tsx
73
93
  <SyncAgentChat
74
94
  config={{ apiKey: "...", connectionString: "..." }}
75
- accentColor="#6366f1"
76
- title="Data Assistant"
77
- position="bottom-left"
95
+ onData={(data) => {
96
+ // Update your own table when the agent queries data
97
+ if (data.collection === "orders") setOrders(data.data);
98
+ }}
78
99
  />
79
100
  ```
80
101
 
81
102
  ## Custom Tools
82
103
 
83
- Give the agent capabilities beyond your database. Tools run entirely in your app — SyncAgent only sees the schema and the result you return.
84
-
85
104
  ```tsx
86
- import { SyncAgentChat } from "@syncagent/react";
87
-
88
105
  <SyncAgentChat
89
106
  config={{
90
- apiKey: "sa_your_api_key",
107
+ apiKey: "sa_your_key",
91
108
  connectionString: process.env.DATABASE_URL,
92
109
  tools: {
93
- sendEmail: {
94
- description: "Send an email to a user",
110
+ createInvoice: {
111
+ description: "Create a Stripe invoice for a customer",
95
112
  inputSchema: {
96
- to: { type: "string", description: "Recipient email address" },
97
- subject: { type: "string", description: "Email subject line" },
98
- body: { type: "string", description: "Email body" },
113
+ customerId: { type: "string", description: "Stripe customer ID" },
114
+ amount: { type: "number", description: "Amount in cents" },
99
115
  },
100
- execute: async ({ to, subject, body }) => {
101
- await mailer.send({ to, subject, text: body });
102
- return { sent: true };
116
+ execute: async ({ customerId, amount }) => {
117
+ const inv = await stripe.invoices.create({ customer: customerId });
118
+ return { invoiceId: inv.id };
103
119
  },
104
120
  },
105
121
  },
@@ -109,13 +125,10 @@ import { SyncAgentChat } from "@syncagent/react";
109
125
 
110
126
  ## Custom UI with `useSyncAgent`
111
127
 
112
- Build your own chat UI using the hook:
113
-
114
128
  ```tsx
115
129
  import { SyncAgentProvider, useSyncAgent } from "@syncagent/react";
116
130
 
117
- // 1. Wrap your app (or just the chat component)
118
- function App() {
131
+ export default function App() {
119
132
  return (
120
133
  <SyncAgentProvider config={{ apiKey: "...", connectionString: "..." }}>
121
134
  <MyChat />
@@ -123,22 +136,17 @@ function App() {
123
136
  );
124
137
  }
125
138
 
126
- // 2. Use the hook inside
127
139
  function MyChat() {
128
- const { messages, isLoading, error, sendMessage, stop, reset } = useSyncAgent();
129
- const [input, setInput] = useState("");
140
+ const { messages, isLoading, error, status, lastData, sendMessage, stop, reset } = useSyncAgent();
130
141
 
131
142
  return (
132
143
  <div>
144
+ {/* status.label shows "Querying users..." etc. */}
145
+ {status && <div>⏳ {status.label}</div>}
133
146
  {messages.map((msg, i) => (
134
- <div key={i} className={msg.role === "user" ? "user" : "assistant"}>
135
- {msg.content}
136
- </div>
147
+ <div key={i}><strong>{msg.role}:</strong> {msg.content}</div>
137
148
  ))}
138
- {isLoading && <div>Thinking...</div>}
139
- {error && <div>Error: {error.message}</div>}
140
- <input value={input} onChange={(e) => setInput(e.target.value)} />
141
- <button onClick={() => { sendMessage(input); setInput(""); }}>Send</button>
149
+ <button onClick={() => sendMessage("Show all users")}>Ask</button>
142
150
  <button onClick={stop}>Stop</button>
143
151
  <button onClick={reset}>Clear</button>
144
152
  </div>
@@ -146,64 +154,60 @@ function MyChat() {
146
154
  }
147
155
  ```
148
156
 
149
- ### `useSyncAgent` Returns
157
+ ### `useSyncAgent` options
158
+
159
+ ```typescript
160
+ useSyncAgent({
161
+ client?: SyncAgentClient, // pass directly instead of using Provider
162
+ context?: Record<string, any>, // injected into every message
163
+ onData?: (data: ToolData) => void, // called on DB tool results
164
+ })
165
+ ```
166
+
167
+ ### `useSyncAgent` returns
150
168
 
151
169
  | Return | Type | Description |
152
170
  | ------------- | ---------------------------- | ------------------------------------ |
153
171
  | `messages` | `Message[]` | Full conversation history |
154
172
  | `isLoading` | `boolean` | `true` while streaming |
155
- | `error` | `Error \| null` | Last error, `null` if none |
173
+ | `error` | `Error \| null` | Last error |
174
+ | `status` | `{ step, label } \| null` | Live status while agent is working |
175
+ | `lastData` | `ToolData \| null` | Last structured data from a DB tool |
156
176
  | `sendMessage` | `(content: string) => void` | Send a user message |
157
- | `stop` | `() => void` | Abort the current streaming response |
158
- | `reset` | `() => void` | Clear all messages and reset state |
159
-
160
- ### Pass a client directly (without Provider)
161
-
162
- ```tsx
163
- import { SyncAgentClient } from "@syncagent/js";
164
- import { useSyncAgent } from "@syncagent/react";
165
-
166
- const client = new SyncAgentClient({
167
- apiKey: "sa_your_api_key",
168
- connectionString: process.env.DATABASE_URL,
169
- });
170
-
171
- function MyChat() {
172
- const { messages, sendMessage } = useSyncAgent({ client });
173
- // ...
174
- }
177
+ | `stop` | `() => void` | Abort the current stream |
178
+ | `reset` | `() => void` | Clear all messages |
179
+
180
+ ## Vanilla JS Widget
181
+
182
+ No npm required — drop a script tag into any HTML page:
183
+
184
+ ```html
185
+ <script src="https://syncagent.dev/api/v1/widget"></script>
186
+ <script>
187
+ SyncAgent.init({
188
+ apiKey: "sa_your_key",
189
+ connectionString: "your_database_url",
190
+ position: "right", // "right" or "left"
191
+ accentColor: "#10b981", // brand color
192
+ title: "AI Assistant",
193
+ persistKey: "my-app", // localStorage persistence
194
+ open: false,
195
+ });
196
+
197
+ // Programmatic control
198
+ SyncAgent.open();
199
+ SyncAgent.close();
200
+ SyncAgent.toggle();
201
+ SyncAgent.clearHistory();
202
+ </script>
175
203
  ```
176
204
 
177
- ## `<SyncAgentProvider>`
178
-
179
- Provides a shared `SyncAgentClient` instance to all child components via context.
180
-
181
- ```tsx
182
- import { SyncAgentProvider, useSyncAgentClient } from "@syncagent/react";
183
-
184
- <SyncAgentProvider config={{ apiKey: "...", connectionString: "..." }}>
185
- <App />
186
- </SyncAgentProvider>
187
-
188
- // Access the raw client anywhere inside
189
- function Somewhere() {
190
- const client = useSyncAgentClient();
191
- // client.chat(...), client.getSchema(), etc.
192
- }
193
- ```
194
-
195
- ## TypeScript Types
196
-
197
- All types are re-exported from `@syncagent/js` for convenience:
205
+ ## TypeScript types
198
206
 
199
207
  ```typescript
200
208
  import type {
201
- SyncAgentConfig,
202
- Message,
203
- ChatOptions,
204
- ToolDefinition,
205
- ToolParameter,
206
- SyncAgentChatProps,
209
+ SyncAgentConfig, Message, ChatOptions, ToolDefinition,
210
+ ToolParameter, ToolData, SyncAgentChatProps,
207
211
  } from "@syncagent/react";
208
212
  ```
209
213
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syncagent/react",
3
- "version": "0.1.9",
3
+ "version": "0.2.1",
4
4
  "description": "SyncAgent React SDK — AI database chat widget & hooks",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",