@syncagent/react 0.1.5 → 0.1.7

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/index.js CHANGED
@@ -63,6 +63,7 @@ function useSyncAgent(options = {}) {
63
63
  const [messages, setMessages] = (0, import_react2.useState)([]);
64
64
  const [isLoading, setIsLoading] = (0, import_react2.useState)(false);
65
65
  const [error, setError] = (0, import_react2.useState)(null);
66
+ const [status, setStatus] = (0, import_react2.useState)(null);
66
67
  const abortRef = (0, import_react2.useRef)(null);
67
68
  const sendMessage = (0, import_react2.useCallback)(
68
69
  async (content) => {
@@ -72,12 +73,14 @@ function useSyncAgent(options = {}) {
72
73
  setMessages(updated);
73
74
  setIsLoading(true);
74
75
  setError(null);
76
+ setStatus(null);
75
77
  const placeholder = { role: "assistant", content: "" };
76
78
  setMessages([...updated, placeholder]);
77
79
  abortRef.current = new AbortController();
78
80
  try {
79
81
  await client.chat(updated, {
80
82
  signal: abortRef.current.signal,
83
+ onStatus: (step, label) => setStatus({ step, label }),
81
84
  onToken: (token) => {
82
85
  placeholder.content += token;
83
86
  setMessages((prev) => {
@@ -87,6 +90,7 @@ function useSyncAgent(options = {}) {
87
90
  });
88
91
  },
89
92
  onComplete: (text) => {
93
+ setStatus(null);
90
94
  setMessages((prev) => {
91
95
  const next = [...prev];
92
96
  next[next.length - 1] = { role: "assistant", content: text };
@@ -105,6 +109,7 @@ function useSyncAgent(options = {}) {
105
109
  }
106
110
  } finally {
107
111
  setIsLoading(false);
112
+ setStatus(null);
108
113
  abortRef.current = null;
109
114
  }
110
115
  },
@@ -118,69 +123,115 @@ function useSyncAgent(options = {}) {
118
123
  setMessages([]);
119
124
  setError(null);
120
125
  setIsLoading(false);
126
+ setStatus(null);
121
127
  }, []);
122
- return { messages, isLoading, error, sendMessage, stop, reset };
128
+ return { messages, isLoading, error, status, sendMessage, stop, reset };
123
129
  }
124
130
 
125
131
  // src/chat.tsx
126
132
  var import_react3 = require("react");
127
133
  var import_jsx_runtime2 = require("react/jsx-runtime");
128
- function renderMarkdown(text) {
129
- return text.replace(/```[\w]*\n?([\s\S]*?)```/g, `<pre style="background:#1e1e2e;color:#cdd6f4;padding:12px 14px;border-radius:8px;overflow-x:auto;font-size:12px;line-height:1.6;margin:8px 0;font-family:'Fira Code','Cascadia Code',monospace">$1</pre>`).replace(/`([^`]+)`/g, '<code style="background:rgba(0,0,0,0.08);padding:2px 6px;border-radius:4px;font-size:12px;font-family:monospace">$1</code>').replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>").replace(/\*(.+?)\*/g, "<em>$1</em>").replace(/^### (.+)$/gm, '<div style="font-weight:700;font-size:13px;margin:10px 0 4px">$1</div>').replace(/^## (.+)$/gm, '<div style="font-weight:700;font-size:14px;margin:12px 0 4px">$1</div>').replace(/^# (.+)$/gm, '<div style="font-weight:700;font-size:15px;margin:12px 0 6px">$1</div>').replace(/\|(.+)\|\n\|[-| :]+\|\n((?:\|.+\|\n?)+)/g, (_, header, rows) => {
130
- const ths = header.split("|").filter((c) => c.trim()).map((c) => `<th style="padding:6px 10px;text-align:left;font-weight:600;border-bottom:2px solid rgba(0,0,0,0.1)">${c.trim()}</th>`).join("");
131
- const trs = rows.trim().split("\n").map((row) => {
132
- const tds = row.split("|").filter((c) => c.trim()).map((c) => `<td style="padding:6px 10px;border-bottom:1px solid rgba(0,0,0,0.06)">${c.trim()}</td>`).join("");
134
+ var GLOBAL_CSS = (accent) => `
135
+ @keyframes sa-bounce { 0%,80%,100%{transform:translateY(0);opacity:.35} 40%{transform:translateY(-5px);opacity:1} }
136
+ @keyframes sa-fadein { from{opacity:0;transform:translateY(8px)} to{opacity:1;transform:translateY(0)} }
137
+ @keyframes sa-cursor { 0%,100%{opacity:1} 50%{opacity:0} }
138
+ @keyframes sa-pulse { 0%,100%{opacity:.6} 50%{opacity:1} }
139
+ .sa-msg { animation: sa-fadein .22s cubic-bezier(.16,1,.3,1) }
140
+ .sa-fab { transition: transform .2s, box-shadow .2s }
141
+ .sa-fab:hover { transform: scale(1.08) }
142
+ .sa-fab:active { transform: scale(.96) }
143
+ .sa-chip:hover { background: ${accent}22 !important; border-color: ${accent}88 !important }
144
+ .sa-send:hover:not(:disabled) { filter: brightness(1.1); transform: scale(1.05) }
145
+ .sa-send:active:not(:disabled) { transform: scale(.96) }
146
+ .sa-send:disabled { opacity: .4; cursor: not-allowed }
147
+ .sa-copy:hover { opacity: 1 !important; background: rgba(0,0,0,.06) !important }
148
+ .sa-scroll::-webkit-scrollbar { width: 4px }
149
+ .sa-scroll::-webkit-scrollbar-track { background: transparent }
150
+ .sa-scroll::-webkit-scrollbar-thumb { background: rgba(0,0,0,.12); border-radius: 4px }
151
+ .sa-cursor { display:inline-block; width:2px; height:1em; background:currentColor; margin-left:1px; vertical-align:text-bottom; animation: sa-cursor .7s infinite }
152
+ .sa-md table { width:100%; border-collapse:collapse; font-size:12.5px; margin:10px 0 }
153
+ .sa-md th { padding:7px 10px; text-align:left; font-weight:600; background:rgba(0,0,0,.04); border-bottom:2px solid rgba(0,0,0,.1); white-space:nowrap }
154
+ .sa-md td { padding:6px 10px; border-bottom:1px solid rgba(0,0,0,.06); vertical-align:top }
155
+ .sa-md tr:last-child td { border-bottom:none }
156
+ .sa-md tr:hover td { background:rgba(0,0,0,.02) }
157
+ .sa-md pre { background:#0f1117; color:#e2e8f0; padding:14px 16px; border-radius:10px; overflow-x:auto; font-size:12px; line-height:1.7; margin:10px 0; font-family:'Fira Code','Cascadia Code','JetBrains Mono',monospace; border:1px solid rgba(255,255,255,.06) }
158
+ .sa-md code { background:rgba(0,0,0,.07); padding:2px 6px; border-radius:4px; font-size:12px; font-family:monospace; color:#1e293b }
159
+ .sa-md pre code { background:none; padding:0; color:inherit; font-size:inherit }
160
+ .sa-md ul,.sa-md ol { margin:6px 0; padding-left:20px }
161
+ .sa-md li { margin:3px 0; line-height:1.6 }
162
+ .sa-md h1 { font-size:16px; font-weight:700; margin:14px 0 6px; color:#0f172a }
163
+ .sa-md h2 { font-size:14px; font-weight:700; margin:12px 0 5px; color:#0f172a }
164
+ .sa-md h3 { font-size:13px; font-weight:600; margin:10px 0 4px; color:#1e293b }
165
+ .sa-md p { margin:4px 0; line-height:1.65 }
166
+ .sa-md strong { font-weight:600; color:#0f172a }
167
+ .sa-md em { font-style:italic; color:#475569 }
168
+ .sa-md blockquote { border-left:3px solid rgba(0,0,0,.15); padding:4px 12px; margin:8px 0; color:#64748b; font-style:italic }
169
+ .sa-md hr { border:none; border-top:1px solid rgba(0,0,0,.1); margin:12px 0 }
170
+ .sa-md a { color:${accent}; text-decoration:underline }
171
+ @media (max-width:480px) {
172
+ .sa-panel-float { width:calc(100vw - 24px) !important; right:12px !important; left:12px !important; bottom:76px !important }
173
+ .sa-panel { border-radius:14px !important; height:calc(100vh - 100px) !important; max-height:none !important }
174
+ }
175
+ `;
176
+ function md(text) {
177
+ let s = text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/```(\w*)\n?([\s\S]*?)```/g, (_, lang, code) => `<pre><code class="lang-${lang}">${code.trimEnd()}</code></pre>`).replace(/`([^`\n]+)`/g, "<code>$1</code>").replace(/^### (.+)$/gm, "<h3>$1</h3>").replace(/^## (.+)$/gm, "<h2>$1</h2>").replace(/^# (.+)$/gm, "<h1>$1</h1>").replace(/\*\*\*(.+?)\*\*\*/g, "<strong><em>$1</em></strong>").replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>").replace(/\*(.+?)\*/g, "<em>$1</em>").replace(/^> (.+)$/gm, "<blockquote>$1</blockquote>").replace(/^---$/gm, "<hr/>").replace(/\|(.+)\|\r?\n\|[-| :]+\|\r?\n((?:\|.+\|\r?\n?)+)/g, (_, hdr, body) => {
178
+ const ths = hdr.split("|").filter((c) => c.trim()).map((c) => `<th>${c.trim()}</th>`).join("");
179
+ const trs = body.trim().split("\n").map((row) => {
180
+ const tds = row.split("|").filter((c) => c.trim()).map((c) => `<td>${c.trim()}</td>`).join("");
133
181
  return `<tr>${tds}</tr>`;
134
182
  }).join("");
135
- return `<div style="overflow-x:auto;margin:8px 0"><table style="width:100%;border-collapse:collapse;font-size:13px"><thead><tr>${ths}</tr></thead><tbody>${trs}</tbody></table></div>`;
136
- }).replace(/^[-*] (.+)$/gm, '<li style="margin:2px 0;padding-left:4px">$1</li>').replace(/(<li[^>]*>.*<\/li>\n?)+/g, (m) => `<ul style="margin:6px 0;padding-left:18px;list-style:disc">${m}</ul>`).replace(/^\d+\. (.+)$/gm, '<li style="margin:2px 0;padding-left:4px">$1</li>').replace(/\n\n/g, "<br/><br/>").replace(/\n/g, "<br/>");
183
+ return `<div style="overflow-x:auto"><table><thead><tr>${ths}</tr></thead><tbody>${trs}</tbody></table></div>`;
184
+ }).replace(/^[-*+] (.+)$/gm, "<li>$1</li>").replace(/(<li>[\s\S]*?<\/li>\n?)+/g, (m) => `<ul>${m}</ul>`).replace(/^\d+\. (.+)$/gm, "<li>$1</li>").replace(/\n\n+/g, "</p><p>").replace(/\n/g, "<br/>");
185
+ return `<p>${s}</p>`;
137
186
  }
138
- function TypingDots({ color }) {
139
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { display: "inline-flex", gap: 4, alignItems: "center", padding: "2px 0" }, children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: {
140
- width: 6,
141
- height: 6,
187
+ function Dots({ color }) {
188
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { display: "inline-flex", gap: 4, alignItems: "center", padding: "4px 2px" }, children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: {
189
+ width: 7,
190
+ height: 7,
142
191
  borderRadius: "50%",
143
192
  background: color,
144
- animation: "sa-bounce 1.2s infinite",
145
- animationDelay: `${i * 0.2}s`,
146
- display: "inline-block"
193
+ display: "inline-block",
194
+ animation: "sa-bounce 1.3s infinite",
195
+ animationDelay: `${i * 0.18}s`
147
196
  } }, i)) });
148
197
  }
149
- function CopyBtn({ text }) {
150
- const [copied, setCopied] = (0, import_react3.useState)(false);
151
- const copy = (0, import_react3.useCallback)(() => {
198
+ function Copy({ text }) {
199
+ const [ok, setOk] = (0, import_react3.useState)(false);
200
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "sa-copy", onClick: () => {
152
201
  navigator.clipboard?.writeText(text).then(() => {
153
- setCopied(true);
154
- setTimeout(() => setCopied(false), 1500);
202
+ setOk(true);
203
+ setTimeout(() => setOk(false), 1600);
155
204
  });
156
- }, [text]);
157
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: copy, title: "Copy", style: {
205
+ }, title: "Copy response", style: {
158
206
  background: "none",
159
207
  border: "none",
160
208
  cursor: "pointer",
161
- padding: "2px 6px",
162
- borderRadius: 4,
209
+ padding: "3px 7px",
210
+ borderRadius: 5,
163
211
  fontSize: 11,
164
- color: "#9ca3af",
165
- opacity: 0.7,
166
- transition: "opacity 0.15s"
167
- }, children: copied ? "\u2713" : "\u2398" });
212
+ color: "#94a3b8",
213
+ opacity: 0.65,
214
+ transition: "all .15s",
215
+ display: "flex",
216
+ alignItems: "center",
217
+ gap: 3
218
+ }, children: ok ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
219
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "11", height: "11", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z" }) }),
220
+ " Copied"
221
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
222
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "11", height: "11", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z" }) }),
223
+ " Copy"
224
+ ] }) });
168
225
  }
169
- function MessageBubble({
170
- role,
171
- content,
172
- isStreaming,
173
- accentColor,
174
- timestamp
175
- }) {
226
+ function Bubble({ role, content, streaming, accent, time }) {
176
227
  const isUser = role === "user";
177
- const timeStr = timestamp.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
178
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
228
+ const t = time.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
229
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "sa-msg", style: {
179
230
  display: "flex",
180
231
  flexDirection: "column",
181
232
  alignItems: isUser ? "flex-end" : "flex-start",
182
- gap: 3,
183
- maxWidth: "88%",
233
+ gap: 4,
234
+ maxWidth: "90%",
184
235
  alignSelf: isUser ? "flex-end" : "flex-start"
185
236
  }, children: [
186
237
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
@@ -190,48 +241,53 @@ function MessageBubble({
190
241
  flexDirection: isUser ? "row-reverse" : "row"
191
242
  }, children: [
192
243
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
193
- width: 24,
194
- height: 24,
244
+ width: 26,
245
+ height: 26,
195
246
  borderRadius: "50%",
196
- background: isUser ? accentColor : "linear-gradient(135deg,#6366f1,#8b5cf6)",
247
+ flexShrink: 0,
248
+ background: isUser ? `linear-gradient(135deg,${accent},${adj(accent, -25)})` : "linear-gradient(135deg,#6366f1,#8b5cf6)",
197
249
  display: "flex",
198
250
  alignItems: "center",
199
251
  justifyContent: "center",
200
252
  fontSize: 11,
201
253
  color: "white",
202
254
  fontWeight: 700,
203
- flexShrink: 0
255
+ boxShadow: isUser ? `0 2px 8px ${accent}44` : "0 2px 8px #6366f144"
204
256
  }, children: isUser ? "U" : "\u2726" }),
205
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 11, color: "#9ca3af", fontWeight: 500 }, children: isUser ? "You" : "SyncAgent" }),
206
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 10, color: "#d1d5db" }, children: timeStr })
257
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 11.5, fontWeight: 600, color: isUser ? "#475569" : "#6366f1" }, children: isUser ? "You" : "SyncAgent" }),
258
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 10, color: "#cbd5e1" }, children: t })
207
259
  ] }),
208
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
209
- padding: "10px 14px",
210
- borderRadius: isUser ? "16px 4px 16px 16px" : "4px 16px 16px 16px",
211
- background: isUser ? accentColor : "#f8fafc",
260
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
261
+ padding: isUser ? "10px 14px" : "12px 16px",
262
+ borderRadius: isUser ? "18px 4px 18px 18px" : "4px 18px 18px 18px",
263
+ background: isUser ? `linear-gradient(135deg,${accent},${adj(accent, -20)})` : "#ffffff",
212
264
  color: isUser ? "white" : "#1e293b",
213
265
  fontSize: 13.5,
214
- lineHeight: 1.6,
266
+ lineHeight: 1.65,
215
267
  wordBreak: "break-word",
216
- border: isUser ? "none" : "1px solid #e2e8f0",
217
- boxShadow: isUser ? `0 2px 8px ${accentColor}33` : "0 1px 4px rgba(0,0,0,0.06)",
218
- position: "relative"
219
- }, children: isStreaming && !content ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TypingDots, { color: accentColor }) : isUser ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { whiteSpace: "pre-wrap" }, children: content }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { dangerouslySetInnerHTML: { __html: renderMarkdown(content) } }) }),
220
- !isUser && content && !isStreaming && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { paddingLeft: 30 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CopyBtn, { text: content }) })
268
+ border: isUser ? "none" : "1px solid #e8edf3",
269
+ boxShadow: isUser ? `0 4px 16px ${accent}33` : "0 2px 12px rgba(0,0,0,0.07)",
270
+ maxWidth: "100%"
271
+ }, children: [
272
+ streaming && !content ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Dots, { color: accent }) : isUser ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { whiteSpace: "pre-wrap" }, children: content }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "sa-md", dangerouslySetInnerHTML: { __html: md(content) } }),
273
+ streaming && content && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "sa-cursor", style: { color: accent } })
274
+ ] }),
275
+ !isUser && content && !streaming && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { paddingLeft: 32, display: "flex", gap: 2 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Copy, { text: content }) })
221
276
  ] });
222
277
  }
223
- function Suggestions({ items, onSelect, accentColor }) {
224
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, padding: "0 16px 12px" }, children: items.map((s2) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: () => onSelect(s2), style: {
225
- padding: "5px 12px",
278
+ function Chips({ items, onPick, accent }) {
279
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, padding: "0 14px 10px" }, children: items.map((s) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "sa-chip", onClick: () => onPick(s), style: {
280
+ padding: "5px 13px",
226
281
  borderRadius: 20,
227
282
  fontSize: 12,
228
283
  cursor: "pointer",
229
- border: `1px solid ${accentColor}44`,
230
- background: `${accentColor}0d`,
231
- color: accentColor,
284
+ border: `1px solid ${accent}33`,
285
+ background: `${accent}0a`,
286
+ color: accent,
232
287
  fontWeight: 500,
233
- transition: "all 0.15s"
234
- }, children: s2 }, s2)) });
288
+ transition: "all .15s",
289
+ whiteSpace: "nowrap"
290
+ }, children: s }, s)) });
235
291
  }
236
292
  function ChatInner({
237
293
  mode = "floating",
@@ -240,135 +296,155 @@ function ChatInner({
240
296
  title = "SyncAgent",
241
297
  subtitle = "AI Database Assistant",
242
298
  placeholder = "Ask anything about your data...",
243
- welcomeMessage = "Hi! I can query, analyze, and manage your database using natural language. What would you like to know?",
299
+ welcomeMessage = "Hi! I can query, analyze, and manage your database using natural language.",
244
300
  accentColor = "#10b981",
245
301
  className,
246
302
  style: customStyle,
247
303
  suggestions = ["Show all records", "Count total entries", "Show recent activity"]
248
304
  }) {
249
- const { messages, isLoading, error, sendMessage, stop, reset } = useSyncAgent();
250
- const [isOpen, setIsOpen] = (0, import_react3.useState)(defaultOpen);
305
+ const { messages, isLoading, error, status, sendMessage, stop, reset } = useSyncAgent();
306
+ const [open, setOpen] = (0, import_react3.useState)(defaultOpen);
251
307
  const [input, setInput] = (0, import_react3.useState)("");
252
- const [timestamps] = (0, import_react3.useState)(() => /* @__PURE__ */ new Map());
253
- const messagesEndRef = (0, import_react3.useRef)(null);
308
+ const [ts] = (0, import_react3.useState)(() => /* @__PURE__ */ new Map());
309
+ const endRef = (0, import_react3.useRef)(null);
254
310
  const inputRef = (0, import_react3.useRef)(null);
255
- const msgCount = (0, import_react3.useRef)(0);
256
- if (messages.length > msgCount.current) {
257
- for (let i = msgCount.current; i < messages.length; i++) {
258
- if (!timestamps.has(i)) timestamps.set(i, /* @__PURE__ */ new Date());
259
- }
260
- msgCount.current = messages.length;
311
+ const prevLen = (0, import_react3.useRef)(0);
312
+ if (messages.length > prevLen.current) {
313
+ for (let i = prevLen.current; i < messages.length; i++)
314
+ if (!ts.has(i)) ts.set(i, /* @__PURE__ */ new Date());
315
+ prevLen.current = messages.length;
261
316
  }
262
317
  (0, import_react3.useEffect)(() => {
263
- messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
318
+ endRef.current?.scrollIntoView({ behavior: "smooth" });
264
319
  }, [messages, isLoading]);
265
320
  (0, import_react3.useEffect)(() => {
266
- if (isOpen) setTimeout(() => inputRef.current?.focus(), 100);
267
- }, [isOpen]);
268
- const handleSend = (0, import_react3.useCallback)(() => {
269
- const text = input.trim();
270
- if (!text || isLoading) return;
321
+ if (open) setTimeout(() => inputRef.current?.focus(), 120);
322
+ }, [open]);
323
+ const send = (0, import_react3.useCallback)(() => {
324
+ const t = input.trim();
325
+ if (!t || isLoading) return;
271
326
  setInput("");
272
- sendMessage(text);
327
+ sendMessage(t);
273
328
  }, [input, isLoading, sendMessage]);
274
- const handleKeyDown = (e) => {
329
+ const onKey = (e) => {
275
330
  if (e.key === "Enter" && !e.shiftKey) {
276
331
  e.preventDefault();
277
- handleSend();
332
+ send();
278
333
  }
279
334
  };
280
- const showSuggestions = messages.length === 0 && suggestions.length > 0;
281
- const panel = /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
282
- ...s.panel,
283
- ...mode === "inline" ? s.panelInline : {},
335
+ const noMsgs = messages.length === 0;
336
+ const panel = /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `sa-panel ${className || ""}`, style: {
337
+ height: mode === "inline" ? "100%" : 600,
338
+ maxHeight: mode === "inline" ? "none" : "calc(100vh - 110px)",
339
+ background: "#f8fafc",
340
+ borderRadius: mode === "inline" ? 14 : 20,
341
+ boxShadow: mode === "inline" ? "0 2px 20px rgba(0,0,0,.08)" : "0 24px 64px rgba(0,0,0,.18), 0 4px 24px rgba(0,0,0,.08)",
342
+ display: "flex",
343
+ flexDirection: "column",
344
+ overflow: "hidden",
345
+ fontFamily: "-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif",
346
+ border: "1px solid rgba(0,0,0,.07)",
284
347
  ...customStyle
285
- }, className, children: [
286
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: `
287
- @keyframes sa-bounce {
288
- 0%,80%,100% { transform: translateY(0); opacity:0.4 }
289
- 40% { transform: translateY(-5px); opacity:1 }
290
- }
291
- @keyframes sa-fadein {
292
- from { opacity:0; transform:translateY(6px) }
293
- to { opacity:1; transform:translateY(0) }
294
- }
295
- .sa-msg-wrap { animation: sa-fadein 0.2s ease }
296
- .sa-input:focus { outline:none; border-color:${accentColor} !important; box-shadow:0 0 0 3px ${accentColor}22 !important }
297
- .sa-send:hover:not(:disabled) { opacity:0.88; transform:scale(1.04) }
298
- .sa-send:disabled { opacity:0.4; cursor:not-allowed }
299
- .sa-fab-btn:hover { transform:scale(1.08) }
300
- ` }),
348
+ }, children: [
349
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: GLOBAL_CSS(accentColor) }),
301
350
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
302
- padding: "14px 16px",
303
- background: `linear-gradient(135deg, ${accentColor}, ${adjustColor(accentColor, -30)})`,
351
+ padding: "13px 16px",
352
+ background: `linear-gradient(135deg,${accentColor},${adj(accentColor, -28)})`,
304
353
  display: "flex",
305
354
  alignItems: "center",
306
355
  justifyContent: "space-between",
307
- flexShrink: 0
356
+ flexShrink: 0,
357
+ boxShadow: "0 2px 12px rgba(0,0,0,.12)"
308
358
  }, children: [
309
359
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10 }, children: [
310
360
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
311
- width: 34,
312
- height: 34,
361
+ width: 36,
362
+ height: 36,
313
363
  borderRadius: "50%",
314
- background: "rgba(255,255,255,0.2)",
364
+ background: "rgba(255,255,255,.18)",
365
+ backdropFilter: "blur(8px)",
315
366
  display: "flex",
316
367
  alignItems: "center",
317
368
  justifyContent: "center",
318
- fontSize: 16
369
+ fontSize: 17,
370
+ boxShadow: "0 2px 8px rgba(0,0,0,.15)"
319
371
  }, children: "\u2726" }),
320
372
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
321
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { color: "white", fontWeight: 700, fontSize: 14, lineHeight: 1.2 }, children: title }),
322
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { color: "rgba(255,255,255,0.75)", fontSize: 11 }, children: subtitle })
373
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { color: "white", fontWeight: 700, fontSize: 14, lineHeight: 1.2, letterSpacing: "-0.01em" }, children: title }),
374
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { color: "rgba(255,255,255,.72)", fontSize: 11, marginTop: 1 }, children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { animation: "sa-pulse 1.2s infinite" }, children: [
375
+ "\u25CF ",
376
+ status?.label || "Thinking..."
377
+ ] }) : subtitle })
323
378
  ] })
324
379
  ] }),
325
380
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: 4 }, children: [
326
- messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: reset, title: "Clear chat", style: {
327
- background: "rgba(255,255,255,0.15)",
381
+ messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: reset, style: {
382
+ background: "rgba(255,255,255,.15)",
328
383
  border: "none",
329
384
  color: "white",
330
385
  cursor: "pointer",
331
- borderRadius: 6,
332
- padding: "4px 8px",
333
- fontSize: 11
386
+ borderRadius: 7,
387
+ padding: "4px 9px",
388
+ fontSize: 11,
389
+ fontWeight: 500,
390
+ backdropFilter: "blur(4px)"
334
391
  }, children: "Clear" }),
335
- mode === "floating" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: () => setIsOpen(false), style: {
336
- background: "rgba(255,255,255,0.15)",
392
+ mode === "floating" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: () => setOpen(false), style: {
393
+ background: "rgba(255,255,255,.15)",
337
394
  border: "none",
338
395
  color: "white",
339
396
  cursor: "pointer",
340
- borderRadius: 6,
341
- padding: "4px 8px",
342
- fontSize: 16,
343
- lineHeight: 1
397
+ borderRadius: 7,
398
+ padding: "4px 9px",
399
+ fontSize: 18,
400
+ lineHeight: 1,
401
+ backdropFilter: "blur(4px)"
344
402
  }, children: "\xD7" })
345
403
  ] })
346
404
  ] }),
347
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: s.messages, children: [
348
- messages.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: s.welcome, children: [
405
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "sa-scroll", style: {
406
+ flex: 1,
407
+ overflowY: "auto",
408
+ padding: "16px 14px 8px",
409
+ display: "flex",
410
+ flexDirection: "column",
411
+ gap: 16,
412
+ background: "#f8fafc"
413
+ }, children: [
414
+ noMsgs && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
415
+ flex: 1,
416
+ display: "flex",
417
+ flexDirection: "column",
418
+ alignItems: "center",
419
+ justifyContent: "center",
420
+ padding: "32px 20px",
421
+ textAlign: "center"
422
+ }, children: [
349
423
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
350
- width: 48,
351
- height: 48,
424
+ width: 56,
425
+ height: 56,
352
426
  borderRadius: "50%",
353
- margin: "0 auto 12px",
354
- background: `linear-gradient(135deg, ${accentColor}22, ${accentColor}44)`,
427
+ marginBottom: 14,
428
+ background: `linear-gradient(135deg,${accentColor}18,${accentColor}35)`,
355
429
  display: "flex",
356
430
  alignItems: "center",
357
431
  justifyContent: "center",
358
- fontSize: 22
432
+ fontSize: 24,
433
+ boxShadow: `0 4px 20px ${accentColor}22`
359
434
  }, children: "\u2726" }),
360
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { fontSize: 14, color: "#64748b", lineHeight: 1.6, maxWidth: 260, margin: "0 auto" }, children: welcomeMessage })
435
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { fontSize: 14, color: "#64748b", lineHeight: 1.65, maxWidth: 260, margin: 0 }, children: welcomeMessage })
361
436
  ] }),
362
- messages.map((msg, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "sa-msg-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
363
- MessageBubble,
437
+ messages.map((msg, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
438
+ Bubble,
364
439
  {
365
440
  role: msg.role,
366
441
  content: msg.content,
367
- isStreaming: isLoading && i === messages.length - 1 && msg.role === "assistant",
368
- accentColor,
369
- timestamp: timestamps.get(i) ?? /* @__PURE__ */ new Date()
370
- }
371
- ) }, i)),
442
+ streaming: isLoading && i === messages.length - 1 && msg.role === "assistant",
443
+ accent: accentColor,
444
+ time: ts.get(i) ?? /* @__PURE__ */ new Date()
445
+ },
446
+ i
447
+ )),
372
448
  error && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
373
449
  padding: "10px 14px",
374
450
  borderRadius: 10,
@@ -376,52 +452,87 @@ function ChatInner({
376
452
  background: "#fef2f2",
377
453
  color: "#dc2626",
378
454
  border: "1px solid #fecaca",
379
- alignSelf: "flex-start"
455
+ alignSelf: "flex-start",
456
+ display: "flex",
457
+ alignItems: "center",
458
+ gap: 6
380
459
  }, children: [
381
- "\u26A0\uFE0F ",
460
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z" }) }),
382
461
  error.message
383
462
  ] }),
384
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref: messagesEndRef })
463
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref: endRef })
464
+ ] }),
465
+ noMsgs && suggestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Chips, { items: suggestions, onPick: (s) => {
466
+ setInput(s);
467
+ inputRef.current?.focus();
468
+ }, accent: accentColor }),
469
+ isLoading && status && status.step !== "done" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
470
+ padding: "7px 14px",
471
+ borderTop: "1px solid #f0f4f8",
472
+ background: "#fafbfc",
473
+ display: "flex",
474
+ alignItems: "center",
475
+ gap: 8,
476
+ flexShrink: 0
477
+ }, children: [
478
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", gap: 3 }, children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
479
+ width: 5,
480
+ height: 5,
481
+ borderRadius: "50%",
482
+ background: accentColor,
483
+ animation: "sa-bounce 1.2s infinite",
484
+ animationDelay: `${i * 0.15}s`
485
+ } }, i)) }),
486
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { fontSize: 11.5, color: "#64748b", fontWeight: 500 }, children: [
487
+ status.step === "connecting" && "\u{1F50C} ",
488
+ status.step === "schema" && "\u{1F4CB} ",
489
+ status.step === "thinking" && "\u{1F9E0} ",
490
+ status.step === "querying" && "\u{1F50D} ",
491
+ status.step === "writing" && "\u270F\uFE0F ",
492
+ status.label
493
+ ] })
385
494
  ] }),
386
- showSuggestions && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
387
- Suggestions,
388
- {
389
- items: suggestions,
390
- onSelect: (s2) => {
391
- setInput(s2);
392
- inputRef.current?.focus();
393
- },
394
- accentColor
395
- }
396
- ),
397
495
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
398
- padding: "10px 12px",
399
- borderTop: "1px solid #f1f5f9",
400
- background: "#fff",
496
+ padding: "10px 12px 12px",
497
+ borderTop: "1px solid #e8edf3",
498
+ background: "#ffffff",
401
499
  flexShrink: 0
402
500
  }, children: [
403
501
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
404
502
  display: "flex",
405
503
  gap: 8,
406
504
  alignItems: "flex-end",
407
- background: "#f8fafc",
408
- borderRadius: 12,
409
- border: "1px solid #e2e8f0",
410
- padding: "6px 6px 6px 12px",
411
- transition: "border-color 0.15s, box-shadow 0.15s"
505
+ background: "#f1f5f9",
506
+ borderRadius: 14,
507
+ border: "1.5px solid #e2e8f0",
508
+ padding: "8px 8px 8px 14px",
509
+ transition: "border-color .15s, box-shadow .15s"
412
510
  }, children: [
413
511
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
414
512
  "textarea",
415
513
  {
416
514
  ref: inputRef,
417
- className: "sa-input",
418
515
  value: input,
419
516
  onChange: (e) => {
420
517
  setInput(e.target.value);
421
518
  e.target.style.height = "auto";
422
- e.target.style.height = Math.min(e.target.scrollHeight, 120) + "px";
519
+ e.target.style.height = Math.min(e.target.scrollHeight, 130) + "px";
520
+ },
521
+ onKeyDown: onKey,
522
+ onFocus: (e) => {
523
+ const p = e.target.parentElement;
524
+ if (p) {
525
+ p.style.borderColor = accentColor;
526
+ p.style.boxShadow = `0 0 0 3px ${accentColor}1a`;
527
+ }
528
+ },
529
+ onBlur: (e) => {
530
+ const p = e.target.parentElement;
531
+ if (p) {
532
+ p.style.borderColor = "#e2e8f0";
533
+ p.style.boxShadow = "none";
534
+ }
423
535
  },
424
- onKeyDown: handleKeyDown,
425
536
  placeholder,
426
537
  disabled: isLoading,
427
538
  rows: 1,
@@ -431,161 +542,117 @@ function ChatInner({
431
542
  border: "none",
432
543
  resize: "none",
433
544
  fontSize: 13.5,
434
- lineHeight: 1.5,
545
+ lineHeight: 1.55,
435
546
  color: "#1e293b",
436
547
  fontFamily: "inherit",
437
548
  padding: 0,
438
- maxHeight: 120,
549
+ maxHeight: 130,
439
550
  outline: "none"
440
551
  }
441
552
  }
442
553
  ),
443
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: 4, alignItems: "flex-end", flexShrink: 0 }, children: [
444
- isLoading && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: stop, title: "Stop", style: {
554
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: 5, alignItems: "flex-end", flexShrink: 0 }, children: [
555
+ isLoading && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { onClick: stop, style: {
445
556
  background: "#fef2f2",
446
557
  border: "1px solid #fecaca",
447
558
  color: "#dc2626",
448
- borderRadius: 8,
449
- padding: "6px 10px",
559
+ borderRadius: 9,
560
+ padding: "6px 11px",
450
561
  cursor: "pointer",
451
- fontSize: 12,
452
- fontWeight: 600
453
- }, children: "\u25A0 Stop" }),
454
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
455
- "button",
456
- {
457
- className: "sa-send",
458
- onClick: handleSend,
459
- disabled: isLoading || !input.trim(),
460
- style: {
461
- width: 34,
462
- height: 34,
463
- borderRadius: 8,
464
- border: "none",
465
- background: input.trim() && !isLoading ? accentColor : "#e2e8f0",
466
- color: input.trim() && !isLoading ? "white" : "#94a3b8",
467
- cursor: "pointer",
468
- display: "flex",
469
- alignItems: "center",
470
- justifyContent: "center",
471
- transition: "all 0.15s",
472
- flexShrink: 0
473
- },
474
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" }) })
475
- }
476
- )
562
+ fontSize: 11.5,
563
+ fontWeight: 600,
564
+ display: "flex",
565
+ alignItems: "center",
566
+ gap: 4
567
+ }, children: [
568
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "4", y: "4", width: "16", height: "16" }) }),
569
+ "Stop"
570
+ ] }),
571
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "sa-send", onClick: send, disabled: isLoading || !input.trim(), style: {
572
+ width: 36,
573
+ height: 36,
574
+ borderRadius: 10,
575
+ border: "none",
576
+ background: input.trim() && !isLoading ? `linear-gradient(135deg,${accentColor},${adj(accentColor, -20)})` : "#e2e8f0",
577
+ color: input.trim() && !isLoading ? "white" : "#94a3b8",
578
+ cursor: "pointer",
579
+ display: "flex",
580
+ alignItems: "center",
581
+ justifyContent: "center",
582
+ transition: "all .15s",
583
+ flexShrink: 0,
584
+ boxShadow: input.trim() && !isLoading ? `0 3px 12px ${accentColor}44` : "none"
585
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" }) }) })
477
586
  ] })
478
587
  ] }),
479
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { textAlign: "center", marginTop: 6, fontSize: 10, color: "#cbd5e1" }, children: [
588
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { textAlign: "center", marginTop: 7, fontSize: 10, color: "#c4cdd8", letterSpacing: ".01em" }, children: [
480
589
  "Powered by ",
481
590
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: accentColor, fontWeight: 600 }, children: "SyncAgent" }),
482
- " \xB7 Enter to send \xB7 Shift+Enter for new line"
591
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { margin: "0 5px", opacity: 0.5 }, children: "\xB7" }),
592
+ "Enter to send \xB7 Shift+Enter for new line"
483
593
  ] })
484
594
  ] })
485
595
  ] });
486
596
  if (mode === "inline") return panel;
487
- const isLeft = position === "bottom-left";
597
+ const left = position === "bottom-left";
488
598
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
489
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
599
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "sa-panel-float", style: {
490
600
  position: "fixed",
491
- bottom: 88,
601
+ bottom: 84,
492
602
  zIndex: 99998,
493
- ...isLeft ? { left: 20 } : { right: 20 },
494
- width: 420,
495
- maxWidth: "calc(100vw - 40px)",
496
- transition: "opacity 0.25s, transform 0.25s",
497
- opacity: isOpen ? 1 : 0,
498
- transform: isOpen ? "translateY(0) scale(1)" : "translateY(16px) scale(0.96)",
499
- pointerEvents: isOpen ? "auto" : "none"
603
+ ...left ? { left: 16 } : { right: 16 },
604
+ width: 430,
605
+ maxWidth: "calc(100vw - 32px)",
606
+ transition: "opacity .28s cubic-bezier(.16,1,.3,1), transform .28s cubic-bezier(.16,1,.3,1)",
607
+ opacity: open ? 1 : 0,
608
+ transform: open ? "translateY(0) scale(1)" : "translateY(20px) scale(.95)",
609
+ pointerEvents: open ? "auto" : "none"
500
610
  }, children: panel }),
501
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
502
- "button",
503
- {
504
- className: "sa-fab-btn",
505
- onClick: () => setIsOpen(!isOpen),
506
- style: {
507
- position: "fixed",
508
- bottom: 20,
509
- zIndex: 99999,
510
- ...isLeft ? { left: 20 } : { right: 20 },
511
- width: 56,
512
- height: 56,
513
- borderRadius: "50%",
514
- border: "none",
515
- background: `linear-gradient(135deg, ${accentColor}, ${adjustColor(accentColor, -30)})`,
516
- cursor: "pointer",
517
- boxShadow: `0 4px 20px ${accentColor}55, 0 2px 8px rgba(0,0,0,0.15)`,
518
- display: "flex",
519
- alignItems: "center",
520
- justifyContent: "center",
521
- transition: "transform 0.2s, box-shadow 0.2s"
522
- },
523
- children: [
524
- isOpen ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "white", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "white", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z" }) }),
525
- !isOpen && messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
526
- position: "absolute",
527
- top: 2,
528
- right: 2,
529
- width: 12,
530
- height: 12,
531
- borderRadius: "50%",
532
- background: "#ef4444",
533
- border: "2px solid white"
534
- } })
535
- ]
536
- }
537
- )
611
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { className: "sa-fab", onClick: () => setOpen((o) => !o), style: {
612
+ position: "fixed",
613
+ bottom: 16,
614
+ zIndex: 99999,
615
+ ...left ? { left: 16 } : { right: 16 },
616
+ width: 58,
617
+ height: 58,
618
+ borderRadius: "50%",
619
+ border: "none",
620
+ background: `linear-gradient(135deg,${accentColor},${adj(accentColor, -28)})`,
621
+ cursor: "pointer",
622
+ boxShadow: `0 6px 24px ${accentColor}55, 0 2px 8px rgba(0,0,0,.18)`,
623
+ display: "flex",
624
+ alignItems: "center",
625
+ justifyContent: "center"
626
+ }, children: [
627
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
628
+ transition: "transform .3s cubic-bezier(.16,1,.3,1), opacity .2s",
629
+ transform: open ? "rotate(90deg) scale(.9)" : "rotate(0) scale(1)",
630
+ opacity: open ? 0.85 : 1,
631
+ display: "flex"
632
+ }, children: open ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "white", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "white", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z" }) }) }),
633
+ !open && messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
634
+ position: "absolute",
635
+ top: 3,
636
+ right: 3,
637
+ width: 13,
638
+ height: 13,
639
+ borderRadius: "50%",
640
+ background: "#ef4444",
641
+ border: "2.5px solid white",
642
+ animation: "sa-pulse 2s infinite"
643
+ } })
644
+ ] })
538
645
  ] });
539
646
  }
540
- var s = {
541
- panel: {
542
- height: 580,
543
- maxHeight: "calc(100vh - 110px)",
544
- background: "#ffffff",
545
- borderRadius: 18,
546
- boxShadow: "0 20px 60px rgba(0,0,0,0.15), 0 4px 20px rgba(0,0,0,0.08)",
547
- display: "flex",
548
- flexDirection: "column",
549
- overflow: "hidden",
550
- fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
551
- border: "1px solid rgba(0,0,0,0.06)"
552
- },
553
- panelInline: {
554
- height: "100%",
555
- maxHeight: "none",
556
- borderRadius: 14,
557
- boxShadow: "0 2px 16px rgba(0,0,0,0.08)"
558
- },
559
- messages: {
560
- flex: 1,
561
- overflowY: "auto",
562
- padding: "16px 16px 8px",
563
- display: "flex",
564
- flexDirection: "column",
565
- gap: 14,
566
- scrollbarWidth: "thin"
567
- },
568
- welcome: {
569
- textAlign: "center",
570
- padding: "32px 20px",
571
- flex: 1,
572
- display: "flex",
573
- flexDirection: "column",
574
- alignItems: "center",
575
- justifyContent: "center"
576
- }
577
- };
578
- function adjustColor(hex, amount) {
579
- const num = parseInt(hex.replace("#", ""), 16);
580
- const r = Math.min(255, Math.max(0, (num >> 16 & 255) + amount));
581
- const g = Math.min(255, Math.max(0, (num >> 8 & 255) + amount));
582
- const b = Math.min(255, Math.max(0, (num & 255) + amount));
647
+ function adj(hex, amt) {
648
+ const n = parseInt(hex.replace("#", ""), 16);
649
+ const r = Math.min(255, Math.max(0, (n >> 16 & 255) + amt));
650
+ const g = Math.min(255, Math.max(0, (n >> 8 & 255) + amt));
651
+ const b = Math.min(255, Math.max(0, (n & 255) + amt));
583
652
  return `#${(r << 16 | g << 8 | b).toString(16).padStart(6, "0")}`;
584
653
  }
585
654
  function SyncAgentChat({ config, ...props }) {
586
- if (config) {
587
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SyncAgentProvider, { config, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChatInner, { ...props }) });
588
- }
655
+ if (config) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SyncAgentProvider, { config, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChatInner, { ...props }) });
589
656
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChatInner, { ...props });
590
657
  }
591
658