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