@syncagent/react 0.1.7 → 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
@@ -131,30 +131,63 @@ function useSyncAgent(options = {}) {
131
131
  // src/chat.tsx
132
132
  var import_react3 = require("react");
133
133
  var import_jsx_runtime2 = require("react/jsx-runtime");
134
- 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) => `
135
164
  @keyframes sa-bounce { 0%,80%,100%{transform:translateY(0);opacity:.35} 40%{transform:translateY(-5px);opacity:1} }
136
165
  @keyframes sa-fadein { from{opacity:0;transform:translateY(8px)} to{opacity:1;transform:translateY(0)} }
137
166
  @keyframes sa-cursor { 0%,100%{opacity:1} 50%{opacity:0} }
138
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)} }
139
169
  .sa-msg { animation: sa-fadein .22s cubic-bezier(.16,1,.3,1) }
140
- .sa-fab { transition: transform .2s, box-shadow .2s }
141
170
  .sa-fab:hover { transform: scale(1.08) }
142
171
  .sa-fab:active { transform: scale(.96) }
143
172
  .sa-chip:hover { background: ${accent}22 !important; border-color: ${accent}88 !important }
144
173
  .sa-send:hover:not(:disabled) { filter: brightness(1.1); transform: scale(1.05) }
145
174
  .sa-send:active:not(:disabled) { transform: scale(.96) }
146
175
  .sa-send:disabled { opacity: .4; cursor: not-allowed }
147
- .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) }
148
179
  .sa-scroll::-webkit-scrollbar { width: 4px }
149
180
  .sa-scroll::-webkit-scrollbar-track { background: transparent }
150
181
  .sa-scroll::-webkit-scrollbar-thumb { background: rgba(0,0,0,.12); border-radius: 4px }
151
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 }
152
185
  .sa-md table { width:100%; border-collapse:collapse; font-size:12.5px; margin:10px 0 }
153
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 }
154
187
  .sa-md td { padding:6px 10px; border-bottom:1px solid rgba(0,0,0,.06); vertical-align:top }
155
188
  .sa-md tr:last-child td { border-bottom:none }
156
189
  .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) }
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) }
158
191
  .sa-md code { background:rgba(0,0,0,.07); padding:2px 6px; border-radius:4px; font-size:12px; font-family:monospace; color:#1e293b }
159
192
  .sa-md pre code { background:none; padding:0; color:inherit; font-size:inherit }
160
193
  .sa-md ul,.sa-md ol { margin:6px 0; padding-left:20px }
@@ -174,10 +207,10 @@ var GLOBAL_CSS = (accent) => `
174
207
  }
175
208
  `;
176
209
  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("");
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("");
181
214
  return `<tr>${tds}</tr>`;
182
215
  }).join("");
183
216
  return `<div style="overflow-x:auto"><table><thead><tr>${ths}</tr></thead><tbody>${trs}</tbody></table></div>`;
@@ -185,109 +218,61 @@ function md(text) {
185
218
  return `<p>${s}</p>`;
186
219
  }
187
220
  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)) });
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)) });
197
222
  }
198
- function Copy({ text }) {
223
+ function CopyBtn({ text }) {
199
224
  const [ok, setOk] = (0, import_react3.useState)(false);
200
- 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: () => {
201
226
  navigator.clipboard?.writeText(text).then(() => {
202
227
  setOk(true);
203
228
  setTimeout(() => setOk(false), 1600);
204
229
  });
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: [
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: [
219
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" }) }),
220
- " Copied"
232
+ "Copied"
221
233
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
222
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" }) }),
223
- " Copy"
235
+ "Copy"
224
236
  ] }) });
225
237
  }
226
- 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 }) {
227
252
  const isUser = role === "user";
228
253
  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" }),
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" }),
257
257
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 11.5, fontWeight: 600, color: isUser ? "#475569" : "#6366f1" }, children: isUser ? "You" : "SyncAgent" }),
258
258
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 10, color: "#cbd5e1" }, children: t })
259
259
  ] }),
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: [
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: [
272
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) } }),
273
262
  streaming && content && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "sa-cursor", style: { color: accent } })
274
263
  ] }),
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 }) })
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
+ ] })
276
272
  ] });
277
273
  }
278
274
  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)) });
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)) });
291
276
  }
292
277
  function ChatInner({
293
278
  mode = "floating",
@@ -300,29 +285,52 @@ function ChatInner({
300
285
  accentColor = "#10b981",
301
286
  className,
302
287
  style: customStyle,
303
- 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
304
291
  }) {
305
292
  const { messages, isLoading, error, status, sendMessage, stop, reset } = useSyncAgent();
306
293
  const [open, setOpen] = (0, import_react3.useState)(defaultOpen);
307
294
  const [input, setInput] = (0, import_react3.useState)("");
308
- 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)("");
309
299
  const endRef = (0, import_react3.useRef)(null);
310
300
  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
- }
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]);
317
324
  (0, import_react3.useEffect)(() => {
318
325
  endRef.current?.scrollIntoView({ behavior: "smooth" });
319
326
  }, [messages, isLoading]);
320
327
  (0, import_react3.useEffect)(() => {
321
328
  if (open) setTimeout(() => inputRef.current?.focus(), 120);
322
329
  }, [open]);
323
- const send = (0, import_react3.useCallback)(() => {
324
- const t = input.trim();
330
+ const send = (0, import_react3.useCallback)((text) => {
331
+ const t = (text || input).trim();
325
332
  if (!t || isLoading) return;
333
+ setLastUserMsg(t);
326
334
  setInput("");
327
335
  sendMessage(t);
328
336
  }, [input, isLoading, sendMessage]);
@@ -332,13 +340,40 @@ function ChatInner({
332
340
  send();
333
341
  }
334
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]);
335
370
  const noMsgs = messages.length === 0;
336
371
  const panel = /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `sa-panel ${className || ""}`, style: {
337
- height: mode === "inline" ? "100%" : 600,
372
+ height: mode === "inline" ? "100%" : panelH,
338
373
  maxHeight: mode === "inline" ? "none" : "calc(100vh - 110px)",
339
374
  background: "#f8fafc",
340
375
  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)",
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)",
342
377
  display: "flex",
343
378
  flexDirection: "column",
344
379
  overflow: "hidden",
@@ -346,29 +381,20 @@ function ChatInner({
346
381
  border: "1px solid rgba(0,0,0,.07)",
347
382
  ...customStyle
348
383
  }, 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)})`,
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",
353
391
  display: "flex",
354
392
  alignItems: "center",
355
- justifyContent: "space-between",
356
- flexShrink: 0,
357
- boxShadow: "0 2px 12px rgba(0,0,0,.12)"
358
- }, 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: [
359
396
  /* @__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" }),
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" }),
372
398
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
373
399
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { color: "white", fontWeight: 700, fontSize: 14, lineHeight: 1.2, letterSpacing: "-0.01em" }, children: title }),
374
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: [
@@ -378,87 +404,41 @@ function ChatInner({
378
404
  ] })
379
405
  ] }),
380
406
  /* @__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" })
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" })
403
412
  ] })
404
413
  ] }),
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" }),
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" }),
435
417
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { fontSize: 14, color: "#64748b", lineHeight: 1.65, maxWidth: 260, margin: 0 }, children: welcomeMessage })
436
418
  ] }),
437
419
  messages.map((msg, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
438
420
  Bubble,
439
421
  {
422
+ idx: i,
440
423
  role: msg.role,
441
424
  content: msg.content,
442
425
  streaming: isLoading && i === messages.length - 1 && msg.role === "assistant",
443
426
  accent: accentColor,
444
- 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"
445
432
  },
446
433
  i
447
434
  )),
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: [
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: [
460
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" }) }),
461
- 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
+ ] })
462
442
  ] }),
463
443
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref: endRef })
464
444
  ] }),
@@ -466,23 +446,8 @@ function ChatInner({
466
446
  setInput(s);
467
447
  inputRef.current?.focus();
468
448
  }, 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)) }),
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)) }),
486
451
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { fontSize: 11.5, color: "#64748b", fontWeight: 500 }, children: [
487
452
  status.step === "connecting" && "\u{1F50C} ",
488
453
  status.step === "schema" && "\u{1F4CB} ",
@@ -492,22 +457,8 @@ function ChatInner({
492
457
  status.label
493
458
  ] })
494
459
  ] }),
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: [
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: [
511
462
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
512
463
  "textarea",
513
464
  {
@@ -536,53 +487,15 @@ function ChatInner({
536
487
  placeholder,
537
488
  disabled: isLoading,
538
489
  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
- }
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" }
552
491
  }
553
492
  ),
554
493
  /* @__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: [
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: [
568
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" }) }),
569
496
  "Stop"
570
497
  ] }),
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" }) }) })
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" }) }) })
586
499
  ] })
587
500
  ] }),
588
501
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { textAlign: "center", marginTop: 7, fontSize: 10, color: "#c4cdd8", letterSpacing: ".01em" }, children: [
@@ -596,51 +509,10 @@ function ChatInner({
596
509
  if (mode === "inline") return panel;
597
510
  const left = position === "bottom-left";
598
511
  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
- } })
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" } })
644
516
  ] })
645
517
  ] });
646
518
  }