@syncagent/react 0.1.4 → 0.1.6

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