@syncagent/react 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -63,6 +63,7 @@ function useSyncAgent(options = {}) {
63
63
  const [messages, setMessages] = (0, import_react2.useState)([]);
64
64
  const [isLoading, setIsLoading] = (0, import_react2.useState)(false);
65
65
  const [error, setError] = (0, import_react2.useState)(null);
66
+ const [status, setStatus] = (0, import_react2.useState)(null);
66
67
  const abortRef = (0, import_react2.useRef)(null);
67
68
  const sendMessage = (0, import_react2.useCallback)(
68
69
  async (content) => {
@@ -72,12 +73,14 @@ function useSyncAgent(options = {}) {
72
73
  setMessages(updated);
73
74
  setIsLoading(true);
74
75
  setError(null);
76
+ setStatus(null);
75
77
  const placeholder = { role: "assistant", content: "" };
76
78
  setMessages([...updated, placeholder]);
77
79
  abortRef.current = new AbortController();
78
80
  try {
79
81
  await client.chat(updated, {
80
82
  signal: abortRef.current.signal,
83
+ onStatus: (step, label) => setStatus({ step, label }),
81
84
  onToken: (token) => {
82
85
  placeholder.content += token;
83
86
  setMessages((prev) => {
@@ -87,6 +90,7 @@ function useSyncAgent(options = {}) {
87
90
  });
88
91
  },
89
92
  onComplete: (text) => {
93
+ setStatus(null);
90
94
  setMessages((prev) => {
91
95
  const next = [...prev];
92
96
  next[next.length - 1] = { role: "assistant", content: text };
@@ -105,6 +109,7 @@ function useSyncAgent(options = {}) {
105
109
  }
106
110
  } finally {
107
111
  setIsLoading(false);
112
+ setStatus(null);
108
113
  abortRef.current = null;
109
114
  }
110
115
  },
@@ -118,37 +123,71 @@ function useSyncAgent(options = {}) {
118
123
  setMessages([]);
119
124
  setError(null);
120
125
  setIsLoading(false);
126
+ setStatus(null);
121
127
  }, []);
122
- return { messages, isLoading, error, sendMessage, stop, reset };
128
+ return { messages, isLoading, error, status, sendMessage, stop, reset };
123
129
  }
124
130
 
125
131
  // src/chat.tsx
126
132
  var import_react3 = require("react");
127
133
  var import_jsx_runtime2 = require("react/jsx-runtime");
128
- var GLOBAL_CSS = (accent) => `
134
+ function loadHistory(key) {
135
+ try {
136
+ const raw = localStorage.getItem(`sa_chat_${key}`);
137
+ if (!raw) return null;
138
+ const parsed = JSON.parse(raw);
139
+ if (parsed.timestamps) {
140
+ parsed.timestamps = parsed.timestamps.map((t) => t ? new Date(t) : null);
141
+ }
142
+ return parsed;
143
+ } catch {
144
+ return null;
145
+ }
146
+ }
147
+ function saveHistory(key, messages, timestamps) {
148
+ try {
149
+ localStorage.setItem(`sa_chat_${key}`, JSON.stringify({
150
+ messages,
151
+ timestamps: timestamps.map((t) => t?.toISOString() ?? null),
152
+ savedAt: (/* @__PURE__ */ new Date()).toISOString()
153
+ }));
154
+ } catch {
155
+ }
156
+ }
157
+ function clearHistory(key) {
158
+ try {
159
+ localStorage.removeItem(`sa_chat_${key}`);
160
+ } catch {
161
+ }
162
+ }
163
+ var CSS = (accent) => `
129
164
  @keyframes sa-bounce { 0%,80%,100%{transform:translateY(0);opacity:.35} 40%{transform:translateY(-5px);opacity:1} }
130
165
  @keyframes sa-fadein { from{opacity:0;transform:translateY(8px)} to{opacity:1;transform:translateY(0)} }
131
166
  @keyframes sa-cursor { 0%,100%{opacity:1} 50%{opacity:0} }
132
167
  @keyframes sa-pulse { 0%,100%{opacity:.6} 50%{opacity:1} }
168
+ @keyframes sa-shake { 0%,100%{transform:translateX(0)} 25%{transform:translateX(-4px)} 75%{transform:translateX(4px)} }
133
169
  .sa-msg { animation: sa-fadein .22s cubic-bezier(.16,1,.3,1) }
134
- .sa-fab { transition: transform .2s, box-shadow .2s }
135
170
  .sa-fab:hover { transform: scale(1.08) }
136
171
  .sa-fab:active { transform: scale(.96) }
137
172
  .sa-chip:hover { background: ${accent}22 !important; border-color: ${accent}88 !important }
138
173
  .sa-send:hover:not(:disabled) { filter: brightness(1.1); transform: scale(1.05) }
139
174
  .sa-send:active:not(:disabled) { transform: scale(.96) }
140
175
  .sa-send:disabled { opacity: .4; cursor: not-allowed }
141
- .sa-copy:hover { opacity: 1 !important; background: rgba(0,0,0,.06) !important }
176
+ .sa-act:hover { opacity: 1 !important; background: rgba(0,0,0,.06) !important }
177
+ .sa-react:hover { transform: scale(1.2) }
178
+ .sa-react.active { transform: scale(1.15) }
142
179
  .sa-scroll::-webkit-scrollbar { width: 4px }
143
180
  .sa-scroll::-webkit-scrollbar-track { background: transparent }
144
181
  .sa-scroll::-webkit-scrollbar-thumb { background: rgba(0,0,0,.12); border-radius: 4px }
145
182
  .sa-cursor { display:inline-block; width:2px; height:1em; background:currentColor; margin-left:1px; vertical-align:text-bottom; animation: sa-cursor .7s infinite }
183
+ .sa-resize { cursor: ns-resize; user-select: none }
184
+ .sa-resize:hover::after { opacity: 1 }
146
185
  .sa-md table { width:100%; border-collapse:collapse; font-size:12.5px; margin:10px 0 }
147
186
  .sa-md th { padding:7px 10px; text-align:left; font-weight:600; background:rgba(0,0,0,.04); border-bottom:2px solid rgba(0,0,0,.1); white-space:nowrap }
148
187
  .sa-md td { padding:6px 10px; border-bottom:1px solid rgba(0,0,0,.06); vertical-align:top }
149
188
  .sa-md tr:last-child td { border-bottom:none }
150
189
  .sa-md tr:hover td { background:rgba(0,0,0,.02) }
151
- .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) }
190
+ .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) }
152
191
  .sa-md code { background:rgba(0,0,0,.07); padding:2px 6px; border-radius:4px; font-size:12px; font-family:monospace; color:#1e293b }
153
192
  .sa-md pre code { background:none; padding:0; color:inherit; font-size:inherit }
154
193
  .sa-md ul,.sa-md ol { margin:6px 0; padding-left:20px }
@@ -168,10 +207,10 @@ var GLOBAL_CSS = (accent) => `
168
207
  }
169
208
  `;
170
209
  function md(text) {
171
- 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) => {
172
- const ths = hdr.split("|").filter((c) => c.trim()).map((c) => `<th>${c.trim()}</th>`).join("");
173
- const trs = body.trim().split("\n").map((row) => {
174
- const tds = row.split("|").filter((c) => c.trim()).map((c) => `<td>${c.trim()}</td>`).join("");
210
+ 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) => {
211
+ const ths = h.split("|").filter((c) => c.trim()).map((c) => `<th>${c.trim()}</th>`).join("");
212
+ const trs = b.trim().split("\n").map((r) => {
213
+ const tds = r.split("|").filter((c) => c.trim()).map((c) => `<td>${c.trim()}</td>`).join("");
175
214
  return `<tr>${tds}</tr>`;
176
215
  }).join("");
177
216
  return `<div style="overflow-x:auto"><table><thead><tr>${ths}</tr></thead><tbody>${trs}</tbody></table></div>`;
@@ -179,109 +218,61 @@ function md(text) {
179
218
  return `<p>${s}</p>`;
180
219
  }
181
220
  function Dots({ color }) {
182
- 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: {
183
- width: 7,
184
- height: 7,
185
- borderRadius: "50%",
186
- background: color,
187
- display: "inline-block",
188
- animation: "sa-bounce 1.3s infinite",
189
- animationDelay: `${i * 0.18}s`
190
- } }, i)) });
221
+ 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)) });
191
222
  }
192
- function Copy({ text }) {
223
+ function CopyBtn({ text }) {
193
224
  const [ok, setOk] = (0, import_react3.useState)(false);
194
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "sa-copy", onClick: () => {
225
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "sa-act", onClick: () => {
195
226
  navigator.clipboard?.writeText(text).then(() => {
196
227
  setOk(true);
197
228
  setTimeout(() => setOk(false), 1600);
198
229
  });
199
- }, title: "Copy response", style: {
200
- background: "none",
201
- border: "none",
202
- cursor: "pointer",
203
- padding: "3px 7px",
204
- borderRadius: 5,
205
- fontSize: 11,
206
- color: "#94a3b8",
207
- opacity: 0.65,
208
- transition: "all .15s",
209
- display: "flex",
210
- alignItems: "center",
211
- gap: 3
212
- }, children: ok ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
230
+ }, 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: [
213
231
  /* @__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" }) }),
214
- " Copied"
232
+ "Copied"
215
233
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
216
234
  /* @__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" }) }),
217
- " Copy"
235
+ "Copy"
218
236
  ] }) });
219
237
  }
220
- function Bubble({ role, content, streaming, accent, time }) {
238
+ function ReactionBtns({ idx, reactions, onReact, accent }) {
239
+ const cur = reactions[idx];
240
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", gap: 2 }, children: ["up", "down"].map((r) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: `sa-react${cur === r ? " active" : ""}`, onClick: () => onReact(idx, r), title: r === "up" ? "Helpful" : "Not helpful", style: {
241
+ background: cur === r ? r === "up" ? `${accent}18` : "#fef2f2" : "none",
242
+ border: cur === r ? `1px solid ${r === "up" ? accent + "44" : "#fecaca"}` : "none",
243
+ cursor: "pointer",
244
+ padding: "3px 6px",
245
+ borderRadius: 5,
246
+ fontSize: 13,
247
+ transition: "all .15s",
248
+ opacity: cur && cur !== r ? 0.3 : 0.7
249
+ }, children: r === "up" ? "\u{1F44D}" : "\u{1F44E}" }, r)) });
250
+ }
251
+ function Bubble({ role, content, streaming, accent, time, idx, reactions, onReact, onRetry, hasError }) {
221
252
  const isUser = role === "user";
222
253
  const t = time.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
223
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "sa-msg", style: {
224
- display: "flex",
225
- flexDirection: "column",
226
- alignItems: isUser ? "flex-end" : "flex-start",
227
- gap: 4,
228
- maxWidth: "90%",
229
- alignSelf: isUser ? "flex-end" : "flex-start"
230
- }, children: [
231
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
232
- display: "flex",
233
- alignItems: "center",
234
- gap: 6,
235
- flexDirection: isUser ? "row-reverse" : "row"
236
- }, children: [
237
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
238
- width: 26,
239
- height: 26,
240
- borderRadius: "50%",
241
- flexShrink: 0,
242
- background: isUser ? `linear-gradient(135deg,${accent},${adj(accent, -25)})` : "linear-gradient(135deg,#6366f1,#8b5cf6)",
243
- display: "flex",
244
- alignItems: "center",
245
- justifyContent: "center",
246
- fontSize: 11,
247
- color: "white",
248
- fontWeight: 700,
249
- boxShadow: isUser ? `0 2px 8px ${accent}44` : "0 2px 8px #6366f144"
250
- }, children: isUser ? "U" : "\u2726" }),
254
+ 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: [
255
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6, flexDirection: isUser ? "row-reverse" : "row" }, children: [
256
+ /* @__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" }),
251
257
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 11.5, fontWeight: 600, color: isUser ? "#475569" : "#6366f1" }, children: isUser ? "You" : "SyncAgent" }),
252
258
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 10, color: "#cbd5e1" }, children: t })
253
259
  ] }),
254
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
255
- padding: isUser ? "10px 14px" : "12px 16px",
256
- borderRadius: isUser ? "18px 4px 18px 18px" : "4px 18px 18px 18px",
257
- background: isUser ? `linear-gradient(135deg,${accent},${adj(accent, -20)})` : "#ffffff",
258
- color: isUser ? "white" : "#1e293b",
259
- fontSize: 13.5,
260
- lineHeight: 1.65,
261
- wordBreak: "break-word",
262
- border: isUser ? "none" : "1px solid #e8edf3",
263
- boxShadow: isUser ? `0 4px 16px ${accent}33` : "0 2px 12px rgba(0,0,0,0.07)",
264
- maxWidth: "100%"
265
- }, children: [
260
+ /* @__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%", animation: hasError ? "sa-shake .3s ease" : "none" }, children: [
266
261
  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) } }),
267
262
  streaming && content && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "sa-cursor", style: { color: accent } })
268
263
  ] }),
269
- !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 }) })
264
+ !isUser && content && !streaming && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { paddingLeft: 32, display: "flex", gap: 2, alignItems: "center" }, children: [
265
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CopyBtn, { text: content }),
266
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ReactionBtns, { idx, reactions, onReact, accent }),
267
+ 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: [
268
+ /* @__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" }) }),
269
+ "Retry"
270
+ ] })
271
+ ] })
270
272
  ] });
271
273
  }
272
274
  function Chips({ items, onPick, accent }) {
273
- 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: {
274
- padding: "5px 13px",
275
- borderRadius: 20,
276
- fontSize: 12,
277
- cursor: "pointer",
278
- border: `1px solid ${accent}33`,
279
- background: `${accent}0a`,
280
- color: accent,
281
- fontWeight: 500,
282
- transition: "all .15s",
283
- whiteSpace: "nowrap"
284
- }, children: s }, s)) });
275
+ 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: { padding: "5px 13px", borderRadius: 20, fontSize: 12, cursor: "pointer", border: `1px solid ${accent}33`, background: `${accent}0a`, color: accent, fontWeight: 500, transition: "all .15s", whiteSpace: "nowrap" }, children: s }, s)) });
285
276
  }
286
277
  function ChatInner({
287
278
  mode = "floating",
@@ -294,29 +285,52 @@ function ChatInner({
294
285
  accentColor = "#10b981",
295
286
  className,
296
287
  style: customStyle,
297
- suggestions = ["Show all records", "Count total entries", "Show recent activity"]
288
+ suggestions = ["Show all records", "Count total entries", "Show recent activity"],
289
+ persistKey,
290
+ onReaction
298
291
  }) {
299
- const { messages, isLoading, error, sendMessage, stop, reset } = useSyncAgent();
292
+ const { messages, isLoading, error, status, sendMessage, stop, reset } = useSyncAgent();
300
293
  const [open, setOpen] = (0, import_react3.useState)(defaultOpen);
301
294
  const [input, setInput] = (0, import_react3.useState)("");
302
- const [ts] = (0, import_react3.useState)(() => /* @__PURE__ */ new Map());
295
+ const [ts, setTs] = (0, import_react3.useState)([]);
296
+ const [reactions, setReactions] = (0, import_react3.useState)({});
297
+ const [panelH, setPanelH] = (0, import_react3.useState)(600);
298
+ const [lastUserMsg, setLastUserMsg] = (0, import_react3.useState)("");
303
299
  const endRef = (0, import_react3.useRef)(null);
304
300
  const inputRef = (0, import_react3.useRef)(null);
305
- const prevLen = (0, import_react3.useRef)(0);
306
- if (messages.length > prevLen.current) {
307
- for (let i = prevLen.current; i < messages.length; i++)
308
- if (!ts.has(i)) ts.set(i, /* @__PURE__ */ new Date());
309
- prevLen.current = messages.length;
310
- }
301
+ const resizeRef = (0, import_react3.useRef)(null);
302
+ const STORAGE_KEY = persistKey || "default";
303
+ const loaded = (0, import_react3.useRef)(false);
304
+ (0, import_react3.useEffect)(() => {
305
+ if (loaded.current || !persistKey) return;
306
+ loaded.current = true;
307
+ const saved = loadHistory(STORAGE_KEY);
308
+ if (saved?.messages?.length) {
309
+ }
310
+ }, []);
311
+ (0, import_react3.useEffect)(() => {
312
+ if (!persistKey || messages.length === 0) return;
313
+ saveHistory(STORAGE_KEY, messages, ts);
314
+ }, [messages, ts, persistKey]);
315
+ (0, import_react3.useEffect)(() => {
316
+ if (messages.length > ts.length) {
317
+ setTs((prev) => {
318
+ const next = [...prev];
319
+ while (next.length < messages.length) next.push(/* @__PURE__ */ new Date());
320
+ return next;
321
+ });
322
+ }
323
+ }, [messages.length]);
311
324
  (0, import_react3.useEffect)(() => {
312
325
  endRef.current?.scrollIntoView({ behavior: "smooth" });
313
326
  }, [messages, isLoading]);
314
327
  (0, import_react3.useEffect)(() => {
315
328
  if (open) setTimeout(() => inputRef.current?.focus(), 120);
316
329
  }, [open]);
317
- const send = (0, import_react3.useCallback)(() => {
318
- const t = input.trim();
330
+ const send = (0, import_react3.useCallback)((text) => {
331
+ const t = (text || input).trim();
319
332
  if (!t || isLoading) return;
333
+ setLastUserMsg(t);
320
334
  setInput("");
321
335
  sendMessage(t);
322
336
  }, [input, isLoading, sendMessage]);
@@ -326,13 +340,40 @@ function ChatInner({
326
340
  send();
327
341
  }
328
342
  };
343
+ const handleReact = (0, import_react3.useCallback)((idx, r) => {
344
+ setReactions((prev) => ({ ...prev, [idx]: prev[idx] === r ? void 0 : r }));
345
+ onReaction?.(idx, r, messages[idx]?.content || "");
346
+ }, [messages, onReaction]);
347
+ const newConversation = (0, import_react3.useCallback)(() => {
348
+ if (persistKey) clearHistory(STORAGE_KEY);
349
+ setTs([]);
350
+ setReactions({});
351
+ setLastUserMsg("");
352
+ reset();
353
+ }, [reset, persistKey]);
354
+ const onResizeStart = (0, import_react3.useCallback)((e) => {
355
+ e.preventDefault();
356
+ resizeRef.current = { startY: e.clientY, startH: panelH };
357
+ const onMove = (ev) => {
358
+ if (!resizeRef.current) return;
359
+ const delta = resizeRef.current.startY - ev.clientY;
360
+ setPanelH(Math.min(Math.max(resizeRef.current.startH + delta, 320), window.innerHeight - 120));
361
+ };
362
+ const onUp = () => {
363
+ resizeRef.current = null;
364
+ window.removeEventListener("mousemove", onMove);
365
+ window.removeEventListener("mouseup", onUp);
366
+ };
367
+ window.addEventListener("mousemove", onMove);
368
+ window.addEventListener("mouseup", onUp);
369
+ }, [panelH]);
329
370
  const noMsgs = messages.length === 0;
330
371
  const panel = /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `sa-panel ${className || ""}`, style: {
331
- height: mode === "inline" ? "100%" : 600,
372
+ height: mode === "inline" ? "100%" : panelH,
332
373
  maxHeight: mode === "inline" ? "none" : "calc(100vh - 110px)",
333
374
  background: "#f8fafc",
334
375
  borderRadius: mode === "inline" ? 14 : 20,
335
- 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)",
376
+ 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)",
336
377
  display: "flex",
337
378
  flexDirection: "column",
338
379
  overflow: "hidden",
@@ -340,116 +381,64 @@ function ChatInner({
340
381
  border: "1px solid rgba(0,0,0,.07)",
341
382
  ...customStyle
342
383
  }, children: [
343
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: GLOBAL_CSS(accentColor) }),
344
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
345
- padding: "13px 16px",
346
- background: `linear-gradient(135deg,${accentColor},${adj(accentColor, -28)})`,
384
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: CSS(accentColor) }),
385
+ mode === "floating" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "sa-resize", onMouseDown: onResizeStart, style: {
386
+ height: 6,
387
+ flexShrink: 0,
388
+ cursor: "ns-resize",
389
+ background: "transparent",
390
+ position: "relative",
347
391
  display: "flex",
348
392
  alignItems: "center",
349
- justifyContent: "space-between",
350
- flexShrink: 0,
351
- boxShadow: "0 2px 12px rgba(0,0,0,.12)"
352
- }, children: [
393
+ justifyContent: "center"
394
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { width: 32, height: 3, borderRadius: 2, background: "rgba(0,0,0,.12)" } }) }),
395
+ /* @__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: [
353
396
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10 }, children: [
354
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
355
- width: 36,
356
- height: 36,
357
- borderRadius: "50%",
358
- background: "rgba(255,255,255,.18)",
359
- backdropFilter: "blur(8px)",
360
- display: "flex",
361
- alignItems: "center",
362
- justifyContent: "center",
363
- fontSize: 17,
364
- boxShadow: "0 2px 8px rgba(0,0,0,.15)"
365
- }, children: "\u2726" }),
397
+ /* @__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" }),
366
398
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
367
399
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { color: "white", fontWeight: 700, fontSize: 14, lineHeight: 1.2, letterSpacing: "-0.01em" }, children: title }),
368
- /* @__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.jsx)("span", { style: { animation: "sa-pulse 1.2s infinite" }, children: "\u25CF Thinking..." }) : subtitle })
400
+ /* @__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: [
401
+ "\u25CF ",
402
+ status?.label || "Thinking..."
403
+ ] }) : subtitle })
369
404
  ] })
370
405
  ] }),
371
406
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: 4 }, children: [
372
- messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: reset, style: {
373
- background: "rgba(255,255,255,.15)",
374
- border: "none",
375
- color: "white",
376
- cursor: "pointer",
377
- borderRadius: 7,
378
- padding: "4px 9px",
379
- fontSize: 11,
380
- fontWeight: 500,
381
- backdropFilter: "blur(4px)"
382
- }, children: "Clear" }),
383
- mode === "floating" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: () => setOpen(false), style: {
384
- background: "rgba(255,255,255,.15)",
385
- border: "none",
386
- color: "white",
387
- cursor: "pointer",
388
- borderRadius: 7,
389
- padding: "4px 9px",
390
- fontSize: 18,
391
- lineHeight: 1,
392
- backdropFilter: "blur(4px)"
393
- }, children: "\xD7" })
407
+ messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { onClick: newConversation, title: "New conversation", 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: [
408
+ /* @__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" }) }),
409
+ "New"
410
+ ] }),
411
+ 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" })
394
412
  ] })
395
413
  ] }),
396
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "sa-scroll", style: {
397
- flex: 1,
398
- overflowY: "auto",
399
- padding: "16px 14px 8px",
400
- display: "flex",
401
- flexDirection: "column",
402
- gap: 16,
403
- background: "#f8fafc"
404
- }, children: [
405
- noMsgs && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
406
- flex: 1,
407
- display: "flex",
408
- flexDirection: "column",
409
- alignItems: "center",
410
- justifyContent: "center",
411
- padding: "32px 20px",
412
- textAlign: "center"
413
- }, children: [
414
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
415
- width: 56,
416
- height: 56,
417
- borderRadius: "50%",
418
- marginBottom: 14,
419
- background: `linear-gradient(135deg,${accentColor}18,${accentColor}35)`,
420
- display: "flex",
421
- alignItems: "center",
422
- justifyContent: "center",
423
- fontSize: 24,
424
- boxShadow: `0 4px 20px ${accentColor}22`
425
- }, children: "\u2726" }),
414
+ /* @__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: [
415
+ 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: [
416
+ /* @__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" }),
426
417
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { fontSize: 14, color: "#64748b", lineHeight: 1.65, maxWidth: 260, margin: 0 }, children: welcomeMessage })
427
418
  ] }),
428
419
  messages.map((msg, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
429
420
  Bubble,
430
421
  {
422
+ idx: i,
431
423
  role: msg.role,
432
424
  content: msg.content,
433
425
  streaming: isLoading && i === messages.length - 1 && msg.role === "assistant",
434
426
  accent: accentColor,
435
- time: ts.get(i) ?? /* @__PURE__ */ new Date()
427
+ time: ts[i] ?? /* @__PURE__ */ new Date(),
428
+ reactions,
429
+ onReact: handleReact,
430
+ onRetry: !isLoading && i === messages.length - 1 && msg.role === "assistant" && !!error ? () => send(lastUserMsg) : void 0,
431
+ hasError: !!error && i === messages.length - 1 && msg.role === "assistant"
436
432
  },
437
433
  i
438
434
  )),
439
- error && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
440
- padding: "10px 14px",
441
- borderRadius: 10,
442
- fontSize: 13,
443
- background: "#fef2f2",
444
- color: "#dc2626",
445
- border: "1px solid #fecaca",
446
- alignSelf: "flex-start",
447
- display: "flex",
448
- alignItems: "center",
449
- gap: 6
450
- }, children: [
435
+ 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: [
451
436
  /* @__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" }) }),
452
- error.message
437
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: error.message }),
438
+ 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: [
439
+ /* @__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" }) }),
440
+ "Retry"
441
+ ] })
453
442
  ] }),
454
443
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref: endRef })
455
444
  ] }),
@@ -457,22 +446,19 @@ function ChatInner({
457
446
  setInput(s);
458
447
  inputRef.current?.focus();
459
448
  }, accent: accentColor }),
460
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
461
- padding: "10px 12px 12px",
462
- borderTop: "1px solid #e8edf3",
463
- background: "#ffffff",
464
- flexShrink: 0
465
- }, children: [
466
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
467
- display: "flex",
468
- gap: 8,
469
- alignItems: "flex-end",
470
- background: "#f1f5f9",
471
- borderRadius: 14,
472
- border: "1.5px solid #e2e8f0",
473
- padding: "8px 8px 8px 14px",
474
- transition: "border-color .15s, box-shadow .15s"
475
- }, children: [
449
+ 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: [
450
+ /* @__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)) }),
451
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { fontSize: 11.5, color: "#64748b", fontWeight: 500 }, children: [
452
+ status.step === "connecting" && "\u{1F50C} ",
453
+ status.step === "schema" && "\u{1F4CB} ",
454
+ status.step === "thinking" && "\u{1F9E0} ",
455
+ status.step === "querying" && "\u{1F50D} ",
456
+ status.step === "writing" && "\u270F\uFE0F ",
457
+ status.label
458
+ ] })
459
+ ] }),
460
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { padding: "10px 12px 12px", borderTop: "1px solid #e8edf3", background: "#ffffff", flexShrink: 0 }, children: [
461
+ /* @__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: [
476
462
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
477
463
  "textarea",
478
464
  {
@@ -501,53 +487,15 @@ function ChatInner({
501
487
  placeholder,
502
488
  disabled: isLoading,
503
489
  rows: 1,
504
- style: {
505
- flex: 1,
506
- background: "none",
507
- border: "none",
508
- resize: "none",
509
- fontSize: 13.5,
510
- lineHeight: 1.55,
511
- color: "#1e293b",
512
- fontFamily: "inherit",
513
- padding: 0,
514
- maxHeight: 130,
515
- outline: "none"
516
- }
490
+ 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" }
517
491
  }
518
492
  ),
519
493
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: 5, alignItems: "flex-end", flexShrink: 0 }, children: [
520
- isLoading && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { onClick: stop, style: {
521
- background: "#fef2f2",
522
- border: "1px solid #fecaca",
523
- color: "#dc2626",
524
- borderRadius: 9,
525
- padding: "6px 11px",
526
- cursor: "pointer",
527
- fontSize: 11.5,
528
- fontWeight: 600,
529
- display: "flex",
530
- alignItems: "center",
531
- gap: 4
532
- }, children: [
494
+ 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: [
533
495
  /* @__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" }) }),
534
496
  "Stop"
535
497
  ] }),
536
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "sa-send", onClick: send, disabled: isLoading || !input.trim(), style: {
537
- width: 36,
538
- height: 36,
539
- borderRadius: 10,
540
- border: "none",
541
- background: input.trim() && !isLoading ? `linear-gradient(135deg,${accentColor},${adj(accentColor, -20)})` : "#e2e8f0",
542
- color: input.trim() && !isLoading ? "white" : "#94a3b8",
543
- cursor: "pointer",
544
- display: "flex",
545
- alignItems: "center",
546
- justifyContent: "center",
547
- transition: "all .15s",
548
- flexShrink: 0,
549
- boxShadow: input.trim() && !isLoading ? `0 3px 12px ${accentColor}44` : "none"
550
- }, 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" }) }) })
498
+ /* @__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" }) }) })
551
499
  ] })
552
500
  ] }),
553
501
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { textAlign: "center", marginTop: 7, fontSize: 10, color: "#c4cdd8", letterSpacing: ".01em" }, children: [
@@ -561,51 +509,10 @@ function ChatInner({
561
509
  if (mode === "inline") return panel;
562
510
  const left = position === "bottom-left";
563
511
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
564
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "sa-panel-float", style: {
565
- position: "fixed",
566
- bottom: 84,
567
- zIndex: 99998,
568
- ...left ? { left: 16 } : { right: 16 },
569
- width: 430,
570
- maxWidth: "calc(100vw - 32px)",
571
- transition: "opacity .28s cubic-bezier(.16,1,.3,1), transform .28s cubic-bezier(.16,1,.3,1)",
572
- opacity: open ? 1 : 0,
573
- transform: open ? "translateY(0) scale(1)" : "translateY(20px) scale(.95)",
574
- pointerEvents: open ? "auto" : "none"
575
- }, children: panel }),
576
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { className: "sa-fab", onClick: () => setOpen((o) => !o), style: {
577
- position: "fixed",
578
- bottom: 16,
579
- zIndex: 99999,
580
- ...left ? { left: 16 } : { right: 16 },
581
- width: 58,
582
- height: 58,
583
- borderRadius: "50%",
584
- border: "none",
585
- background: `linear-gradient(135deg,${accentColor},${adj(accentColor, -28)})`,
586
- cursor: "pointer",
587
- boxShadow: `0 6px 24px ${accentColor}55, 0 2px 8px rgba(0,0,0,.18)`,
588
- display: "flex",
589
- alignItems: "center",
590
- justifyContent: "center"
591
- }, children: [
592
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
593
- transition: "transform .3s cubic-bezier(.16,1,.3,1), opacity .2s",
594
- transform: open ? "rotate(90deg) scale(.9)" : "rotate(0) scale(1)",
595
- opacity: open ? 0.85 : 1,
596
- display: "flex"
597
- }, 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" }) }) }),
598
- !open && messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
599
- position: "absolute",
600
- top: 3,
601
- right: 3,
602
- width: 13,
603
- height: 13,
604
- borderRadius: "50%",
605
- background: "#ef4444",
606
- border: "2.5px solid white",
607
- animation: "sa-pulse 2s infinite"
608
- } })
512
+ /* @__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 }),
513
+ /* @__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: [
514
+ /* @__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" }) }) }),
515
+ !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" } })
609
516
  ] })
610
517
  ] });
611
518
  }