@syncagent/react 0.1.7 → 0.1.9

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.mjs CHANGED
@@ -35,6 +35,7 @@ function useSyncAgent(options = {}) {
35
35
  const [isLoading, setIsLoading] = useState(false);
36
36
  const [error, setError] = useState(null);
37
37
  const [status, setStatus] = useState(null);
38
+ const [lastData, setLastData] = useState(null);
38
39
  const abortRef = useRef(null);
39
40
  const sendMessage = useCallback(
40
41
  async (content) => {
@@ -45,13 +46,19 @@ function useSyncAgent(options = {}) {
45
46
  setIsLoading(true);
46
47
  setError(null);
47
48
  setStatus(null);
49
+ setLastData(null);
48
50
  const placeholder = { role: "assistant", content: "" };
49
51
  setMessages([...updated, placeholder]);
50
52
  abortRef.current = new AbortController();
51
53
  try {
52
54
  await client.chat(updated, {
53
55
  signal: abortRef.current.signal,
56
+ context: options.context,
54
57
  onStatus: (step, label) => setStatus({ step, label }),
58
+ onData: (data) => {
59
+ setLastData(data);
60
+ options.onData?.(data);
61
+ },
55
62
  onToken: (token) => {
56
63
  placeholder.content += token;
57
64
  setMessages((prev) => {
@@ -84,7 +91,7 @@ function useSyncAgent(options = {}) {
84
91
  abortRef.current = null;
85
92
  }
86
93
  },
87
- [client, messages, isLoading]
94
+ [client, messages, isLoading, options.context]
88
95
  );
89
96
  const stop = useCallback(() => {
90
97
  abortRef.current?.abort();
@@ -95,8 +102,9 @@ function useSyncAgent(options = {}) {
95
102
  setError(null);
96
103
  setIsLoading(false);
97
104
  setStatus(null);
105
+ setLastData(null);
98
106
  }, []);
99
- return { messages, isLoading, error, status, sendMessage, stop, reset };
107
+ return { messages, isLoading, error, status, lastData, sendMessage, stop, reset };
100
108
  }
101
109
 
102
110
  // src/chat.tsx
@@ -107,20 +115,78 @@ import {
107
115
  useCallback as useCallback2
108
116
  } from "react";
109
117
  import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
110
- var GLOBAL_CSS = (accent) => `
118
+ var LS = {
119
+ load: (key) => {
120
+ try {
121
+ const r = localStorage.getItem(`sa_${key}`);
122
+ return r ? JSON.parse(r) : null;
123
+ } catch {
124
+ return null;
125
+ }
126
+ },
127
+ save: (key, v) => {
128
+ try {
129
+ localStorage.setItem(`sa_${key}`, JSON.stringify(v));
130
+ } catch {
131
+ }
132
+ },
133
+ del: (key) => {
134
+ try {
135
+ localStorage.removeItem(`sa_${key}`);
136
+ } catch {
137
+ }
138
+ }
139
+ };
140
+ function mdTableToCSV(text) {
141
+ const match = text.match(/\|(.+)\|\r?\n\|[-| :]+\|\r?\n((?:\|.+\|\r?\n?)+)/);
142
+ if (!match) return null;
143
+ const headers = match[1].split("|").map((c) => c.trim()).filter(Boolean);
144
+ const rows = match[2].trim().split("\n").map(
145
+ (row) => row.split("|").map((c) => c.trim()).filter(Boolean)
146
+ );
147
+ const escape = (v) => `"${v.replace(/"/g, '""')}"`;
148
+ return [headers.map(escape).join(","), ...rows.map((r) => r.map(escape).join(","))].join("\n");
149
+ }
150
+ function downloadCSV(csv, filename = "syncagent-export.csv") {
151
+ const blob = new Blob([csv], { type: "text/csv" });
152
+ const url = URL.createObjectURL(blob);
153
+ const a = document.createElement("a");
154
+ a.href = url;
155
+ a.download = filename;
156
+ a.click();
157
+ URL.revokeObjectURL(url);
158
+ }
159
+ function BarChart({ data, accent }) {
160
+ const max = Math.max(...data.map((d) => d.value), 1);
161
+ return /* @__PURE__ */ jsx2("div", { style: { margin: "10px 0", padding: "12px", background: "#f8fafc", borderRadius: 10, border: "1px solid #e2e8f0" }, children: /* @__PURE__ */ jsx2("div", { style: { display: "flex", alignItems: "flex-end", gap: 6, height: 80 }, children: data.slice(0, 12).map((d, i) => /* @__PURE__ */ jsxs("div", { style: { flex: 1, display: "flex", flexDirection: "column", alignItems: "center", gap: 3, minWidth: 0 }, children: [
162
+ /* @__PURE__ */ jsx2("div", { style: { width: "100%", background: `${accent}cc`, borderRadius: "3px 3px 0 0", height: `${d.value / max * 68}px`, minHeight: 2, transition: "height .3s" }, title: `${d.label}: ${d.value}` }),
163
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: 9, color: "#94a3b8", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", width: "100%", textAlign: "center" }, children: d.label })
164
+ ] }, i)) }) });
165
+ }
166
+ function detectChartData(data) {
167
+ if (!data?.data?.length) return null;
168
+ const rows = data.data;
169
+ const keys = Object.keys(rows[0]);
170
+ const labelKey = keys.find((k) => typeof rows[0][k] === "string" || k === "_id");
171
+ const valueKey = keys.find((k) => typeof rows[0][k] === "number" && k !== labelKey);
172
+ if (!labelKey || !valueKey) return null;
173
+ return rows.map((r) => ({ label: String(r[labelKey] ?? ""), value: Number(r[valueKey] ?? 0) }));
174
+ }
175
+ var CSS = (accent) => `
111
176
  @keyframes sa-bounce { 0%,80%,100%{transform:translateY(0);opacity:.35} 40%{transform:translateY(-5px);opacity:1} }
112
177
  @keyframes sa-fadein { from{opacity:0;transform:translateY(8px)} to{opacity:1;transform:translateY(0)} }
113
178
  @keyframes sa-cursor { 0%,100%{opacity:1} 50%{opacity:0} }
114
179
  @keyframes sa-pulse { 0%,100%{opacity:.6} 50%{opacity:1} }
180
+ @keyframes sa-shake { 0%,100%{transform:translateX(0)} 25%{transform:translateX(-4px)} 75%{transform:translateX(4px)} }
115
181
  .sa-msg { animation: sa-fadein .22s cubic-bezier(.16,1,.3,1) }
116
- .sa-fab { transition: transform .2s, box-shadow .2s }
117
182
  .sa-fab:hover { transform: scale(1.08) }
118
183
  .sa-fab:active { transform: scale(.96) }
119
184
  .sa-chip:hover { background: ${accent}22 !important; border-color: ${accent}88 !important }
120
185
  .sa-send:hover:not(:disabled) { filter: brightness(1.1); transform: scale(1.05) }
121
186
  .sa-send:active:not(:disabled) { transform: scale(.96) }
122
187
  .sa-send:disabled { opacity: .4; cursor: not-allowed }
123
- .sa-copy:hover { opacity: 1 !important; background: rgba(0,0,0,.06) !important }
188
+ .sa-act:hover { opacity: 1 !important; background: rgba(0,0,0,.06) !important }
189
+ .sa-react:hover { transform: scale(1.2) }
124
190
  .sa-scroll::-webkit-scrollbar { width: 4px }
125
191
  .sa-scroll::-webkit-scrollbar-track { background: transparent }
126
192
  .sa-scroll::-webkit-scrollbar-thumb { background: rgba(0,0,0,.12); border-radius: 4px }
@@ -130,7 +196,7 @@ var GLOBAL_CSS = (accent) => `
130
196
  .sa-md td { padding:6px 10px; border-bottom:1px solid rgba(0,0,0,.06); vertical-align:top }
131
197
  .sa-md tr:last-child td { border-bottom:none }
132
198
  .sa-md tr:hover td { background:rgba(0,0,0,.02) }
133
- .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) }
199
+ .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',monospace; border:1px solid rgba(255,255,255,.06) }
134
200
  .sa-md code { background:rgba(0,0,0,.07); padding:2px 6px; border-radius:4px; font-size:12px; font-family:monospace; color:#1e293b }
135
201
  .sa-md pre code { background:none; padding:0; color:inherit; font-size:inherit }
136
202
  .sa-md ul,.sa-md ol { margin:6px 0; padding-left:20px }
@@ -150,10 +216,10 @@ var GLOBAL_CSS = (accent) => `
150
216
  }
151
217
  `;
152
218
  function md(text) {
153
- 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) => {
154
- const ths = hdr.split("|").filter((c) => c.trim()).map((c) => `<th>${c.trim()}</th>`).join("");
155
- const trs = body.trim().split("\n").map((row) => {
156
- const tds = row.split("|").filter((c) => c.trim()).map((c) => `<td>${c.trim()}</td>`).join("");
219
+ let s = text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/```(\w*)\n?([\s\S]*?)```/g, (_, l, c) => `<pre><code class="lang-${l}">${c.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, (_, h, b) => {
220
+ const ths = h.split("|").filter((c) => c.trim()).map((c) => `<th>${c.trim()}</th>`).join("");
221
+ const trs = b.trim().split("\n").map((r) => {
222
+ const tds = r.split("|").filter((c) => c.trim()).map((c) => `<td>${c.trim()}</td>`).join("");
157
223
  return `<tr>${tds}</tr>`;
158
224
  }).join("");
159
225
  return `<div style="overflow-x:auto"><table><thead><tr>${ths}</tr></thead><tbody>${trs}</tbody></table></div>`;
@@ -161,109 +227,58 @@ function md(text) {
161
227
  return `<p>${s}</p>`;
162
228
  }
163
229
  function Dots({ color }) {
164
- 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: {
165
- width: 7,
166
- height: 7,
167
- borderRadius: "50%",
168
- background: color,
169
- display: "inline-block",
170
- animation: "sa-bounce 1.3s infinite",
171
- animationDelay: `${i * 0.18}s`
172
- } }, i)) });
230
+ 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: { width: 7, height: 7, borderRadius: "50%", background: color, display: "inline-block", animation: "sa-bounce 1.3s infinite", animationDelay: `${i * 0.18}s` } }, i)) });
173
231
  }
174
- function Copy({ text }) {
232
+ function CopyBtn({ text }) {
175
233
  const [ok, setOk] = useState2(false);
176
- return /* @__PURE__ */ jsx2("button", { className: "sa-copy", onClick: () => {
234
+ return /* @__PURE__ */ jsx2("button", { className: "sa-act", onClick: () => {
177
235
  navigator.clipboard?.writeText(text).then(() => {
178
236
  setOk(true);
179
237
  setTimeout(() => setOk(false), 1600);
180
238
  });
181
- }, title: "Copy response", style: {
182
- background: "none",
183
- border: "none",
184
- cursor: "pointer",
185
- padding: "3px 7px",
186
- borderRadius: 5,
187
- fontSize: 11,
188
- color: "#94a3b8",
189
- opacity: 0.65,
190
- transition: "all .15s",
191
- display: "flex",
192
- alignItems: "center",
193
- gap: 3
194
- }, children: ok ? /* @__PURE__ */ jsxs(Fragment, { children: [
239
+ }, style: { background: "none", border: "none", cursor: "pointer", padding: "3px 7px", borderRadius: 5, fontSize: 11, color: "#94a3b8", opacity: 0.65, transition: "all .15s", display: "flex", alignItems: "center", gap: 3 }, children: ok ? /* @__PURE__ */ jsxs(Fragment, { children: [
195
240
  /* @__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" }) }),
196
- " Copied"
241
+ "Copied"
197
242
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
198
243
  /* @__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" }) }),
199
- " Copy"
244
+ "Copy"
200
245
  ] }) });
201
246
  }
202
- function Bubble({ role, content, streaming, accent, time }) {
247
+ function Bubble({ role, content, streaming, accent, time, idx, reactions, onReact, onRetry, chartData, hasTable }) {
203
248
  const isUser = role === "user";
204
249
  const t = time.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
205
- return /* @__PURE__ */ jsxs("div", { className: "sa-msg", style: {
206
- display: "flex",
207
- flexDirection: "column",
208
- alignItems: isUser ? "flex-end" : "flex-start",
209
- gap: 4,
210
- maxWidth: "90%",
211
- alignSelf: isUser ? "flex-end" : "flex-start"
212
- }, children: [
213
- /* @__PURE__ */ jsxs("div", { style: {
214
- display: "flex",
215
- alignItems: "center",
216
- gap: 6,
217
- flexDirection: isUser ? "row-reverse" : "row"
218
- }, children: [
219
- /* @__PURE__ */ jsx2("div", { style: {
220
- width: 26,
221
- height: 26,
222
- borderRadius: "50%",
223
- flexShrink: 0,
224
- background: isUser ? `linear-gradient(135deg,${accent},${adj(accent, -25)})` : "linear-gradient(135deg,#6366f1,#8b5cf6)",
225
- display: "flex",
226
- alignItems: "center",
227
- justifyContent: "center",
228
- fontSize: 11,
229
- color: "white",
230
- fontWeight: 700,
231
- boxShadow: isUser ? `0 2px 8px ${accent}44` : "0 2px 8px #6366f144"
232
- }, children: isUser ? "U" : "\u2726" }),
250
+ const cur = reactions[idx];
251
+ const csv = hasTable ? mdTableToCSV(content) : null;
252
+ return /* @__PURE__ */ jsxs("div", { className: "sa-msg", style: { display: "flex", flexDirection: "column", alignItems: isUser ? "flex-end" : "flex-start", gap: 4, maxWidth: "90%", alignSelf: isUser ? "flex-end" : "flex-start" }, children: [
253
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6, flexDirection: isUser ? "row-reverse" : "row" }, children: [
254
+ /* @__PURE__ */ jsx2("div", { style: { width: 26, height: 26, borderRadius: "50%", flexShrink: 0, background: isUser ? `linear-gradient(135deg,${accent},${adj(accent, -25)})` : "linear-gradient(135deg,#6366f1,#8b5cf6)", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 11, color: "white", fontWeight: 700, boxShadow: isUser ? `0 2px 8px ${accent}44` : "0 2px 8px #6366f144" }, children: isUser ? "U" : "\u2726" }),
233
255
  /* @__PURE__ */ jsx2("span", { style: { fontSize: 11.5, fontWeight: 600, color: isUser ? "#475569" : "#6366f1" }, children: isUser ? "You" : "SyncAgent" }),
234
256
  /* @__PURE__ */ jsx2("span", { style: { fontSize: 10, color: "#cbd5e1" }, children: t })
235
257
  ] }),
236
- /* @__PURE__ */ jsxs("div", { style: {
237
- padding: isUser ? "10px 14px" : "12px 16px",
238
- borderRadius: isUser ? "18px 4px 18px 18px" : "4px 18px 18px 18px",
239
- background: isUser ? `linear-gradient(135deg,${accent},${adj(accent, -20)})` : "#ffffff",
240
- color: isUser ? "white" : "#1e293b",
241
- fontSize: 13.5,
242
- lineHeight: 1.65,
243
- wordBreak: "break-word",
244
- border: isUser ? "none" : "1px solid #e8edf3",
245
- boxShadow: isUser ? `0 4px 16px ${accent}33` : "0 2px 12px rgba(0,0,0,0.07)",
246
- maxWidth: "100%"
247
- }, children: [
258
+ /* @__PURE__ */ jsxs("div", { style: { padding: isUser ? "10px 14px" : "12px 16px", borderRadius: isUser ? "18px 4px 18px 18px" : "4px 18px 18px 18px", background: isUser ? `linear-gradient(135deg,${accent},${adj(accent, -20)})` : "#ffffff", color: isUser ? "white" : "#1e293b", fontSize: 13.5, lineHeight: 1.65, wordBreak: "break-word", border: isUser ? "none" : "1px solid #e8edf3", boxShadow: isUser ? `0 4px 16px ${accent}33` : "0 2px 12px rgba(0,0,0,0.07)", maxWidth: "100%" }, children: [
248
259
  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) } }),
249
260
  streaming && content && /* @__PURE__ */ jsx2("span", { className: "sa-cursor", style: { color: accent } })
250
261
  ] }),
251
- !isUser && content && !streaming && /* @__PURE__ */ jsx2("div", { style: { paddingLeft: 32, display: "flex", gap: 2 }, children: /* @__PURE__ */ jsx2(Copy, { text: content }) })
262
+ !isUser && !streaming && chartData && chartData.length > 1 && /* @__PURE__ */ jsx2("div", { style: { paddingLeft: 32, width: "calc(100% - 32px)" }, children: /* @__PURE__ */ jsx2(BarChart, { data: chartData, accent }) }),
263
+ !isUser && content && !streaming && /* @__PURE__ */ jsxs("div", { style: { paddingLeft: 32, display: "flex", gap: 2, alignItems: "center", flexWrap: "wrap" }, children: [
264
+ /* @__PURE__ */ jsx2(CopyBtn, { text: content }),
265
+ csv && /* @__PURE__ */ jsxs("button", { className: "sa-act", onClick: () => downloadCSV(csv), style: { background: "none", border: "none", cursor: "pointer", padding: "3px 7px", borderRadius: 5, fontSize: 11, color: "#94a3b8", opacity: 0.65, transition: "all .15s", display: "flex", alignItems: "center", gap: 3 }, children: [
266
+ /* @__PURE__ */ jsx2("svg", { width: "11", height: "11", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx2("path", { d: "M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z" }) }),
267
+ "CSV"
268
+ ] }),
269
+ ["up", "down"].map((r) => /* @__PURE__ */ jsx2("button", { className: "sa-react", onClick: () => onReact(idx, r), style: { background: cur === r ? r === "up" ? `${accent}18` : "#fef2f2" : "none", border: cur === r ? `1px solid ${r === "up" ? accent + "44" : "#fecaca"}` : "none", cursor: "pointer", padding: "3px 6px", borderRadius: 5, fontSize: 13, transition: "all .15s", opacity: cur && cur !== r ? 0.3 : 0.7 }, children: r === "up" ? "\u{1F44D}" : "\u{1F44E}" }, r)),
270
+ onRetry && /* @__PURE__ */ jsxs("button", { className: "sa-act", onClick: onRetry, style: { background: "none", border: "none", cursor: "pointer", padding: "3px 7px", borderRadius: 5, fontSize: 11, color: "#94a3b8", opacity: 0.65, transition: "all .15s", display: "flex", alignItems: "center", gap: 3 }, children: [
271
+ /* @__PURE__ */ jsx2("svg", { width: "11", height: "11", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx2("path", { d: "M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z" }) }),
272
+ "Retry"
273
+ ] })
274
+ ] })
252
275
  ] });
253
276
  }
254
- function Chips({ items, onPick, accent }) {
255
- 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: {
256
- padding: "5px 13px",
257
- borderRadius: 20,
258
- fontSize: 12,
259
- cursor: "pointer",
260
- border: `1px solid ${accent}33`,
261
- background: `${accent}0a`,
262
- color: accent,
263
- fontWeight: 500,
264
- transition: "all .15s",
265
- whiteSpace: "nowrap"
266
- }, children: s }, s)) });
277
+ function Chips({ items, onPick, accent, onPin, pinned }) {
278
+ return /* @__PURE__ */ jsx2("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, padding: "0 14px 10px" }, children: items.map((s) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 0 }, children: [
279
+ /* @__PURE__ */ jsx2("button", { className: "sa-chip", onClick: () => onPick(s), style: { padding: "5px 10px 5px 13px", borderRadius: onPin ? "20px 0 0 20px" : "20px", fontSize: 12, cursor: "pointer", border: `1px solid ${accent}33`, borderRight: onPin ? "none" : "", background: `${accent}0a`, color: accent, fontWeight: 500, transition: "all .15s", whiteSpace: "nowrap" }, children: s }),
280
+ onPin && /* @__PURE__ */ jsx2("button", { onClick: () => onPin(s), title: pinned?.includes(s) ? "Unpin" : "Pin", style: { padding: "5px 7px", borderRadius: "0 20px 20px 0", fontSize: 10, cursor: "pointer", border: `1px solid ${accent}33`, borderLeft: "none", background: pinned?.includes(s) ? `${accent}18` : `${accent}0a`, color: accent, transition: "all .15s" }, children: pinned?.includes(s) ? "\u{1F4CC}" : "\u{1F4CD}" })
281
+ ] }, s)) });
267
282
  }
268
283
  function ChatInner({
269
284
  mode = "floating",
@@ -276,75 +291,127 @@ function ChatInner({
276
291
  accentColor = "#10b981",
277
292
  className,
278
293
  style: customStyle,
279
- suggestions = ["Show all records", "Count total entries", "Show recent activity"]
294
+ suggestions = ["Show all records", "Count total entries", "Show recent activity"],
295
+ persistKey,
296
+ context,
297
+ onReaction,
298
+ onData
280
299
  }) {
281
- const { messages, isLoading, error, status, sendMessage, stop, reset } = useSyncAgent();
300
+ const { messages, isLoading, error, status, lastData, sendMessage, stop, reset } = useSyncAgent({ context, onData });
282
301
  const [open, setOpen] = useState2(defaultOpen);
283
302
  const [input, setInput] = useState2("");
284
- const [ts] = useState2(() => /* @__PURE__ */ new Map());
303
+ const [ts, setTs] = useState2([]);
304
+ const [reactions, setReactions] = useState2({});
305
+ const [panelH, setPanelH] = useState2(600);
306
+ const [lastUserMsg, setLastUserMsg] = useState2("");
307
+ const [pinnedQueries, setPinnedQueries] = useState2(() => persistKey ? LS.load(`pins_${persistKey}`) || [] : []);
308
+ const [historyIdx, setHistoryIdx] = useState2(-1);
309
+ const [chartDataMap, setChartDataMap] = useState2({});
285
310
  const endRef = useRef2(null);
286
311
  const inputRef = useRef2(null);
287
- const prevLen = useRef2(0);
288
- if (messages.length > prevLen.current) {
289
- for (let i = prevLen.current; i < messages.length; i++)
290
- if (!ts.has(i)) ts.set(i, /* @__PURE__ */ new Date());
291
- prevLen.current = messages.length;
292
- }
312
+ const resizeRef = useRef2(null);
313
+ const PKEY = persistKey || "default";
314
+ useEffect(() => {
315
+ if (messages.length > ts.length) {
316
+ setTs((prev) => {
317
+ const n = [...prev];
318
+ while (n.length < messages.length) n.push(/* @__PURE__ */ new Date());
319
+ return n;
320
+ });
321
+ }
322
+ }, [messages.length]);
323
+ useEffect(() => {
324
+ if (!persistKey || messages.length === 0) return;
325
+ LS.save(`chat_${PKEY}`, { messages, ts: ts.map((t) => t?.toISOString() ?? null) });
326
+ }, [messages, ts, persistKey]);
327
+ useEffect(() => {
328
+ if (!lastData) return;
329
+ const chart = detectChartData(lastData);
330
+ if (chart) {
331
+ const lastAiIdx = [...messages].reverse().findIndex((m) => m.role === "assistant");
332
+ if (lastAiIdx >= 0) {
333
+ const idx = messages.length - 1 - lastAiIdx;
334
+ setChartDataMap((prev) => ({ ...prev, [idx]: chart }));
335
+ }
336
+ }
337
+ }, [lastData]);
293
338
  useEffect(() => {
294
339
  endRef.current?.scrollIntoView({ behavior: "smooth" });
295
340
  }, [messages, isLoading]);
296
341
  useEffect(() => {
297
342
  if (open) setTimeout(() => inputRef.current?.focus(), 120);
298
343
  }, [open]);
299
- const send = useCallback2(() => {
300
- const t = input.trim();
344
+ const send = useCallback2((text) => {
345
+ const t = (text || input).trim();
301
346
  if (!t || isLoading) return;
347
+ setLastUserMsg(t);
348
+ setHistoryIdx(-1);
302
349
  setInput("");
303
350
  sendMessage(t);
304
351
  }, [input, isLoading, sendMessage]);
352
+ const userMessages = messages.filter((m) => m.role === "user").map((m) => m.content);
305
353
  const onKey = (e) => {
306
354
  if (e.key === "Enter" && !e.shiftKey) {
307
355
  e.preventDefault();
308
356
  send();
357
+ return;
358
+ }
359
+ if (e.key === "ArrowUp" && !input.trim()) {
360
+ e.preventDefault();
361
+ const next = Math.min(historyIdx + 1, userMessages.length - 1);
362
+ setHistoryIdx(next);
363
+ setInput(userMessages[userMessages.length - 1 - next] || "");
364
+ }
365
+ if (e.key === "ArrowDown" && historyIdx >= 0) {
366
+ e.preventDefault();
367
+ const next = historyIdx - 1;
368
+ setHistoryIdx(next);
369
+ setInput(next < 0 ? "" : userMessages[userMessages.length - 1 - next] || "");
309
370
  }
310
371
  };
372
+ const handleReact = useCallback2((idx, r) => {
373
+ setReactions((prev) => ({ ...prev, [idx]: prev[idx] === r ? void 0 : r }));
374
+ onReaction?.(idx, r, messages[idx]?.content || "");
375
+ }, [messages, onReaction]);
376
+ const togglePin = useCallback2((q) => {
377
+ setPinnedQueries((prev) => {
378
+ const next = prev.includes(q) ? prev.filter((p) => p !== q) : [...prev, q];
379
+ if (persistKey) LS.save(`pins_${PKEY}`, next);
380
+ return next;
381
+ });
382
+ }, [persistKey]);
383
+ const newConversation = useCallback2(() => {
384
+ if (persistKey) LS.del(`chat_${PKEY}`);
385
+ setTs([]);
386
+ setReactions({});
387
+ setLastUserMsg("");
388
+ setHistoryIdx(-1);
389
+ setChartDataMap({});
390
+ reset();
391
+ }, [reset, persistKey]);
392
+ const onResizeStart = useCallback2((e) => {
393
+ e.preventDefault();
394
+ resizeRef.current = { startY: e.clientY, startH: panelH };
395
+ const onMove = (ev) => {
396
+ if (!resizeRef.current) return;
397
+ setPanelH(Math.min(Math.max(resizeRef.current.startH + (resizeRef.current.startY - ev.clientY), 320), window.innerHeight - 120));
398
+ };
399
+ const onUp = () => {
400
+ resizeRef.current = null;
401
+ window.removeEventListener("mousemove", onMove);
402
+ window.removeEventListener("mouseup", onUp);
403
+ };
404
+ window.addEventListener("mousemove", onMove);
405
+ window.addEventListener("mouseup", onUp);
406
+ }, [panelH]);
311
407
  const noMsgs = messages.length === 0;
312
- const panel = /* @__PURE__ */ jsxs("div", { className: `sa-panel ${className || ""}`, style: {
313
- height: mode === "inline" ? "100%" : 600,
314
- maxHeight: mode === "inline" ? "none" : "calc(100vh - 110px)",
315
- background: "#f8fafc",
316
- borderRadius: mode === "inline" ? 14 : 20,
317
- 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)",
318
- display: "flex",
319
- flexDirection: "column",
320
- overflow: "hidden",
321
- fontFamily: "-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif",
322
- border: "1px solid rgba(0,0,0,.07)",
323
- ...customStyle
324
- }, children: [
325
- /* @__PURE__ */ jsx2("style", { children: GLOBAL_CSS(accentColor) }),
326
- /* @__PURE__ */ jsxs("div", { style: {
327
- padding: "13px 16px",
328
- background: `linear-gradient(135deg,${accentColor},${adj(accentColor, -28)})`,
329
- display: "flex",
330
- alignItems: "center",
331
- justifyContent: "space-between",
332
- flexShrink: 0,
333
- boxShadow: "0 2px 12px rgba(0,0,0,.12)"
334
- }, children: [
408
+ const allChips = [.../* @__PURE__ */ new Set([...pinnedQueries, ...noMsgs ? suggestions : []])];
409
+ const panel = /* @__PURE__ */ jsxs("div", { className: `sa-panel ${className || ""}`, style: { height: mode === "inline" ? "100%" : panelH, maxHeight: mode === "inline" ? "none" : "calc(100vh - 110px)", background: "#f8fafc", borderRadius: mode === "inline" ? 14 : 20, 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)", display: "flex", flexDirection: "column", overflow: "hidden", fontFamily: "-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif", border: "1px solid rgba(0,0,0,.07)", ...customStyle }, children: [
410
+ /* @__PURE__ */ jsx2("style", { children: CSS(accentColor) }),
411
+ mode === "floating" && /* @__PURE__ */ jsx2("div", { onMouseDown: onResizeStart, style: { height: 8, flexShrink: 0, cursor: "ns-resize", background: "transparent", display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsx2("div", { style: { width: 32, height: 3, borderRadius: 2, background: "rgba(0,0,0,.1)" } }) }),
412
+ /* @__PURE__ */ jsxs("div", { style: { padding: "11px 16px", background: `linear-gradient(135deg,${accentColor},${adj(accentColor, -28)})`, display: "flex", alignItems: "center", justifyContent: "space-between", flexShrink: 0, boxShadow: "0 2px 12px rgba(0,0,0,.12)" }, children: [
335
413
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 10 }, children: [
336
- /* @__PURE__ */ jsx2("div", { style: {
337
- width: 36,
338
- height: 36,
339
- borderRadius: "50%",
340
- background: "rgba(255,255,255,.18)",
341
- backdropFilter: "blur(8px)",
342
- display: "flex",
343
- alignItems: "center",
344
- justifyContent: "center",
345
- fontSize: 17,
346
- boxShadow: "0 2px 8px rgba(0,0,0,.15)"
347
- }, children: "\u2726" }),
414
+ /* @__PURE__ */ jsx2("div", { style: { width: 36, height: 36, borderRadius: "50%", background: "rgba(255,255,255,.18)", backdropFilter: "blur(8px)", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 17, boxShadow: "0 2px 8px rgba(0,0,0,.15)" }, children: "\u2726" }),
348
415
  /* @__PURE__ */ jsxs("div", { children: [
349
416
  /* @__PURE__ */ jsx2("div", { style: { color: "white", fontWeight: 700, fontSize: 14, lineHeight: 1.2, letterSpacing: "-0.01em" }, children: title }),
350
417
  /* @__PURE__ */ jsx2("div", { style: { color: "rgba(255,255,255,.72)", fontSize: 11, marginTop: 1 }, children: isLoading ? /* @__PURE__ */ jsxs("span", { style: { animation: "sa-pulse 1.2s infinite" }, children: [
@@ -354,111 +421,60 @@ function ChatInner({
354
421
  ] })
355
422
  ] }),
356
423
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 4 }, children: [
357
- messages.length > 0 && /* @__PURE__ */ jsx2("button", { onClick: reset, style: {
358
- background: "rgba(255,255,255,.15)",
359
- border: "none",
360
- color: "white",
361
- cursor: "pointer",
362
- borderRadius: 7,
363
- padding: "4px 9px",
364
- fontSize: 11,
365
- fontWeight: 500,
366
- backdropFilter: "blur(4px)"
367
- }, children: "Clear" }),
368
- mode === "floating" && /* @__PURE__ */ jsx2("button", { onClick: () => setOpen(false), style: {
369
- background: "rgba(255,255,255,.15)",
370
- border: "none",
371
- color: "white",
372
- cursor: "pointer",
373
- borderRadius: 7,
374
- padding: "4px 9px",
375
- fontSize: 18,
376
- lineHeight: 1,
377
- backdropFilter: "blur(4px)"
378
- }, children: "\xD7" })
424
+ messages.length > 0 && /* @__PURE__ */ jsxs("button", { onClick: newConversation, style: { background: "rgba(255,255,255,.15)", border: "none", color: "white", cursor: "pointer", borderRadius: 7, padding: "4px 9px", fontSize: 11, fontWeight: 500, backdropFilter: "blur(4px)", display: "flex", alignItems: "center", gap: 4 }, children: [
425
+ /* @__PURE__ */ jsx2("svg", { width: "11", height: "11", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx2("path", { d: "M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" }) }),
426
+ "New"
427
+ ] }),
428
+ mode === "floating" && /* @__PURE__ */ jsx2("button", { onClick: () => setOpen(false), style: { background: "rgba(255,255,255,.15)", border: "none", color: "white", cursor: "pointer", borderRadius: 7, padding: "4px 9px", fontSize: 18, lineHeight: 1, backdropFilter: "blur(4px)" }, children: "\xD7" })
379
429
  ] })
380
430
  ] }),
381
- /* @__PURE__ */ jsxs("div", { className: "sa-scroll", style: {
382
- flex: 1,
383
- overflowY: "auto",
384
- padding: "16px 14px 8px",
385
- display: "flex",
386
- flexDirection: "column",
387
- gap: 16,
388
- background: "#f8fafc"
389
- }, children: [
390
- noMsgs && /* @__PURE__ */ jsxs("div", { style: {
391
- flex: 1,
392
- display: "flex",
393
- flexDirection: "column",
394
- alignItems: "center",
395
- justifyContent: "center",
396
- padding: "32px 20px",
397
- textAlign: "center"
398
- }, children: [
399
- /* @__PURE__ */ jsx2("div", { style: {
400
- width: 56,
401
- height: 56,
402
- borderRadius: "50%",
403
- marginBottom: 14,
404
- background: `linear-gradient(135deg,${accentColor}18,${accentColor}35)`,
405
- display: "flex",
406
- alignItems: "center",
407
- justifyContent: "center",
408
- fontSize: 24,
409
- boxShadow: `0 4px 20px ${accentColor}22`
410
- }, children: "\u2726" }),
431
+ /* @__PURE__ */ jsxs("div", { className: "sa-scroll", style: { flex: 1, overflowY: "auto", padding: "16px 14px 8px", display: "flex", flexDirection: "column", gap: 16, background: "#f8fafc" }, children: [
432
+ noMsgs && /* @__PURE__ */ jsxs("div", { style: { flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", padding: "32px 20px", textAlign: "center" }, children: [
433
+ /* @__PURE__ */ jsx2("div", { style: { width: 56, height: 56, borderRadius: "50%", marginBottom: 14, background: `linear-gradient(135deg,${accentColor}18,${accentColor}35)`, display: "flex", alignItems: "center", justifyContent: "center", fontSize: 24, boxShadow: `0 4px 20px ${accentColor}22` }, children: "\u2726" }),
411
434
  /* @__PURE__ */ jsx2("p", { style: { fontSize: 14, color: "#64748b", lineHeight: 1.65, maxWidth: 260, margin: 0 }, children: welcomeMessage })
412
435
  ] }),
413
436
  messages.map((msg, i) => /* @__PURE__ */ jsx2(
414
437
  Bubble,
415
438
  {
439
+ idx: i,
416
440
  role: msg.role,
417
441
  content: msg.content,
418
442
  streaming: isLoading && i === messages.length - 1 && msg.role === "assistant",
419
443
  accent: accentColor,
420
- time: ts.get(i) ?? /* @__PURE__ */ new Date()
444
+ time: ts[i] ?? /* @__PURE__ */ new Date(),
445
+ reactions,
446
+ onReact: handleReact,
447
+ chartData: chartDataMap[i],
448
+ hasTable: msg.content.includes("|"),
449
+ onRetry: !isLoading && i === messages.length - 1 && msg.role === "assistant" && !!error ? () => send(lastUserMsg) : void 0
421
450
  },
422
451
  i
423
452
  )),
424
- error && /* @__PURE__ */ jsxs("div", { style: {
425
- padding: "10px 14px",
426
- borderRadius: 10,
427
- fontSize: 13,
428
- background: "#fef2f2",
429
- color: "#dc2626",
430
- border: "1px solid #fecaca",
431
- alignSelf: "flex-start",
432
- display: "flex",
433
- alignItems: "center",
434
- gap: 6
435
- }, children: [
453
+ error && /* @__PURE__ */ jsxs("div", { style: { padding: "10px 14px", borderRadius: 10, fontSize: 13, background: "#fef2f2", color: "#dc2626", border: "1px solid #fecaca", alignSelf: "flex-start", display: "flex", alignItems: "center", gap: 8 }, children: [
436
454
  /* @__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" }) }),
437
- error.message
455
+ /* @__PURE__ */ jsx2("span", { children: error.message }),
456
+ lastUserMsg && /* @__PURE__ */ jsxs("button", { onClick: () => send(lastUserMsg), style: { marginLeft: 4, padding: "3px 10px", borderRadius: 6, border: "1px solid #fecaca", background: "white", color: "#dc2626", cursor: "pointer", fontSize: 11, fontWeight: 600, display: "flex", alignItems: "center", gap: 3 }, children: [
457
+ /* @__PURE__ */ jsx2("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx2("path", { d: "M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z" }) }),
458
+ "Retry"
459
+ ] })
438
460
  ] }),
439
461
  /* @__PURE__ */ jsx2("div", { ref: endRef })
440
462
  ] }),
441
- noMsgs && suggestions.length > 0 && /* @__PURE__ */ jsx2(Chips, { items: suggestions, onPick: (s) => {
442
- setInput(s);
443
- inputRef.current?.focus();
444
- }, accent: accentColor }),
445
- isLoading && status && status.step !== "done" && /* @__PURE__ */ jsxs("div", { style: {
446
- padding: "7px 14px",
447
- borderTop: "1px solid #f0f4f8",
448
- background: "#fafbfc",
449
- display: "flex",
450
- alignItems: "center",
451
- gap: 8,
452
- flexShrink: 0
453
- }, children: [
454
- /* @__PURE__ */ jsx2("div", { style: { display: "flex", gap: 3 }, children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx2("div", { style: {
455
- width: 5,
456
- height: 5,
457
- borderRadius: "50%",
458
- background: accentColor,
459
- animation: "sa-bounce 1.2s infinite",
460
- animationDelay: `${i * 0.15}s`
461
- } }, i)) }),
463
+ allChips.length > 0 && /* @__PURE__ */ jsx2(
464
+ Chips,
465
+ {
466
+ items: allChips,
467
+ onPick: (s) => {
468
+ setInput(s);
469
+ inputRef.current?.focus();
470
+ },
471
+ accent: accentColor,
472
+ onPin: togglePin,
473
+ pinned: pinnedQueries
474
+ }
475
+ ),
476
+ isLoading && status && status.step !== "done" && /* @__PURE__ */ jsxs("div", { style: { padding: "7px 14px", borderTop: "1px solid #f0f4f8", background: "#fafbfc", display: "flex", alignItems: "center", gap: 8, flexShrink: 0 }, children: [
477
+ /* @__PURE__ */ jsx2("div", { style: { display: "flex", gap: 3 }, children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx2("div", { style: { width: 5, height: 5, borderRadius: "50%", background: accentColor, animation: "sa-bounce 1.2s infinite", animationDelay: `${i * 0.15}s` } }, i)) }),
462
478
  /* @__PURE__ */ jsxs("span", { style: { fontSize: 11.5, color: "#64748b", fontWeight: 500 }, children: [
463
479
  status.step === "connecting" && "\u{1F50C} ",
464
480
  status.step === "schema" && "\u{1F4CB} ",
@@ -468,22 +484,8 @@ function ChatInner({
468
484
  status.label
469
485
  ] })
470
486
  ] }),
471
- /* @__PURE__ */ jsxs("div", { style: {
472
- padding: "10px 12px 12px",
473
- borderTop: "1px solid #e8edf3",
474
- background: "#ffffff",
475
- flexShrink: 0
476
- }, children: [
477
- /* @__PURE__ */ jsxs("div", { style: {
478
- display: "flex",
479
- gap: 8,
480
- alignItems: "flex-end",
481
- background: "#f1f5f9",
482
- borderRadius: 14,
483
- border: "1.5px solid #e2e8f0",
484
- padding: "8px 8px 8px 14px",
485
- transition: "border-color .15s, box-shadow .15s"
486
- }, children: [
487
+ /* @__PURE__ */ jsxs("div", { style: { padding: "10px 12px 12px", borderTop: "1px solid #e8edf3", background: "#ffffff", flexShrink: 0 }, children: [
488
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, alignItems: "flex-end", background: "#f1f5f9", borderRadius: 14, border: "1.5px solid #e2e8f0", padding: "8px 8px 8px 14px", transition: "border-color .15s,box-shadow .15s" }, children: [
487
489
  /* @__PURE__ */ jsx2(
488
490
  "textarea",
489
491
  {
@@ -512,111 +514,32 @@ function ChatInner({
512
514
  placeholder,
513
515
  disabled: isLoading,
514
516
  rows: 1,
515
- style: {
516
- flex: 1,
517
- background: "none",
518
- border: "none",
519
- resize: "none",
520
- fontSize: 13.5,
521
- lineHeight: 1.55,
522
- color: "#1e293b",
523
- fontFamily: "inherit",
524
- padding: 0,
525
- maxHeight: 130,
526
- outline: "none"
527
- }
517
+ style: { flex: 1, background: "none", border: "none", resize: "none", fontSize: 13.5, lineHeight: 1.55, color: "#1e293b", fontFamily: "inherit", padding: 0, maxHeight: 130, outline: "none" }
528
518
  }
529
519
  ),
530
520
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 5, alignItems: "flex-end", flexShrink: 0 }, children: [
531
- isLoading && /* @__PURE__ */ jsxs("button", { onClick: stop, style: {
532
- background: "#fef2f2",
533
- border: "1px solid #fecaca",
534
- color: "#dc2626",
535
- borderRadius: 9,
536
- padding: "6px 11px",
537
- cursor: "pointer",
538
- fontSize: 11.5,
539
- fontWeight: 600,
540
- display: "flex",
541
- alignItems: "center",
542
- gap: 4
543
- }, children: [
521
+ isLoading && /* @__PURE__ */ jsxs("button", { onClick: stop, style: { background: "#fef2f2", border: "1px solid #fecaca", color: "#dc2626", borderRadius: 9, padding: "6px 11px", cursor: "pointer", fontSize: 11.5, fontWeight: 600, display: "flex", alignItems: "center", gap: 4 }, children: [
544
522
  /* @__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" }) }),
545
523
  "Stop"
546
524
  ] }),
547
- /* @__PURE__ */ jsx2("button", { className: "sa-send", onClick: send, disabled: isLoading || !input.trim(), style: {
548
- width: 36,
549
- height: 36,
550
- borderRadius: 10,
551
- border: "none",
552
- background: input.trim() && !isLoading ? `linear-gradient(135deg,${accentColor},${adj(accentColor, -20)})` : "#e2e8f0",
553
- color: input.trim() && !isLoading ? "white" : "#94a3b8",
554
- cursor: "pointer",
555
- display: "flex",
556
- alignItems: "center",
557
- justifyContent: "center",
558
- transition: "all .15s",
559
- flexShrink: 0,
560
- boxShadow: input.trim() && !isLoading ? `0 3px 12px ${accentColor}44` : "none"
561
- }, 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" }) }) })
525
+ /* @__PURE__ */ jsx2("button", { className: "sa-send", onClick: () => send(), disabled: isLoading || !input.trim(), style: { width: 36, height: 36, borderRadius: 10, border: "none", background: input.trim() && !isLoading ? `linear-gradient(135deg,${accentColor},${adj(accentColor, -20)})` : "#e2e8f0", color: input.trim() && !isLoading ? "white" : "#94a3b8", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center", transition: "all .15s", flexShrink: 0, boxShadow: input.trim() && !isLoading ? `0 3px 12px ${accentColor}44` : "none" }, 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" }) }) })
562
526
  ] })
563
527
  ] }),
564
528
  /* @__PURE__ */ jsxs("div", { style: { textAlign: "center", marginTop: 7, fontSize: 10, color: "#c4cdd8", letterSpacing: ".01em" }, children: [
565
529
  "Powered by ",
566
530
  /* @__PURE__ */ jsx2("span", { style: { color: accentColor, fontWeight: 600 }, children: "SyncAgent" }),
567
531
  /* @__PURE__ */ jsx2("span", { style: { margin: "0 5px", opacity: 0.5 }, children: "\xB7" }),
568
- "Enter to send \xB7 Shift+Enter for new line"
532
+ "Enter to send \xB7 \u2191\u2193 history \xB7 Shift+Enter new line"
569
533
  ] })
570
534
  ] })
571
535
  ] });
572
536
  if (mode === "inline") return panel;
573
537
  const left = position === "bottom-left";
574
538
  return /* @__PURE__ */ jsxs(Fragment, { children: [
575
- /* @__PURE__ */ jsx2("div", { className: "sa-panel-float", style: {
576
- position: "fixed",
577
- bottom: 84,
578
- zIndex: 99998,
579
- ...left ? { left: 16 } : { right: 16 },
580
- width: 430,
581
- maxWidth: "calc(100vw - 32px)",
582
- transition: "opacity .28s cubic-bezier(.16,1,.3,1), transform .28s cubic-bezier(.16,1,.3,1)",
583
- opacity: open ? 1 : 0,
584
- transform: open ? "translateY(0) scale(1)" : "translateY(20px) scale(.95)",
585
- pointerEvents: open ? "auto" : "none"
586
- }, children: panel }),
587
- /* @__PURE__ */ jsxs("button", { className: "sa-fab", onClick: () => setOpen((o) => !o), style: {
588
- position: "fixed",
589
- bottom: 16,
590
- zIndex: 99999,
591
- ...left ? { left: 16 } : { right: 16 },
592
- width: 58,
593
- height: 58,
594
- borderRadius: "50%",
595
- border: "none",
596
- background: `linear-gradient(135deg,${accentColor},${adj(accentColor, -28)})`,
597
- cursor: "pointer",
598
- boxShadow: `0 6px 24px ${accentColor}55, 0 2px 8px rgba(0,0,0,.18)`,
599
- display: "flex",
600
- alignItems: "center",
601
- justifyContent: "center"
602
- }, children: [
603
- /* @__PURE__ */ jsx2("div", { style: {
604
- transition: "transform .3s cubic-bezier(.16,1,.3,1), opacity .2s",
605
- transform: open ? "rotate(90deg) scale(.9)" : "rotate(0) scale(1)",
606
- opacity: open ? 0.85 : 1,
607
- display: "flex"
608
- }, 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" }) }) }),
609
- !open && messages.length > 0 && /* @__PURE__ */ jsx2("div", { style: {
610
- position: "absolute",
611
- top: 3,
612
- right: 3,
613
- width: 13,
614
- height: 13,
615
- borderRadius: "50%",
616
- background: "#ef4444",
617
- border: "2.5px solid white",
618
- animation: "sa-pulse 2s infinite"
619
- } })
539
+ /* @__PURE__ */ jsx2("div", { className: "sa-panel-float", style: { position: "fixed", bottom: 84, zIndex: 99998, ...left ? { left: 16 } : { right: 16 }, width: 430, maxWidth: "calc(100vw - 32px)", transition: "opacity .28s cubic-bezier(.16,1,.3,1),transform .28s cubic-bezier(.16,1,.3,1)", opacity: open ? 1 : 0, transform: open ? "translateY(0) scale(1)" : "translateY(20px) scale(.95)", pointerEvents: open ? "auto" : "none" }, children: panel }),
540
+ /* @__PURE__ */ jsxs("button", { className: "sa-fab", onClick: () => setOpen((o) => !o), style: { position: "fixed", bottom: 16, zIndex: 99999, ...left ? { left: 16 } : { right: 16 }, width: 58, height: 58, borderRadius: "50%", border: "none", background: `linear-gradient(135deg,${accentColor},${adj(accentColor, -28)})`, cursor: "pointer", boxShadow: `0 6px 24px ${accentColor}55,0 2px 8px rgba(0,0,0,.18)`, display: "flex", alignItems: "center", justifyContent: "center", transition: "transform .2s,box-shadow .2s" }, children: [
541
+ /* @__PURE__ */ jsx2("div", { style: { transition: "transform .3s cubic-bezier(.16,1,.3,1),opacity .2s", transform: open ? "rotate(90deg) scale(.9)" : "rotate(0) scale(1)", opacity: open ? 0.85 : 1, display: "flex" }, 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" }) }) }),
542
+ !open && messages.length > 0 && /* @__PURE__ */ jsx2("div", { style: { position: "absolute", top: 3, right: 3, width: 13, height: 13, borderRadius: "50%", background: "#ef4444", border: "2.5px solid white", animation: "sa-pulse 2s infinite" } })
620
543
  ] })
621
544
  ] });
622
545
  }