@getnexorai/sdk 0.1.5 → 0.1.7

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.
@@ -277,6 +277,16 @@ interface IsPausedResponse {
277
277
  success: true;
278
278
  paused: boolean;
279
279
  }
280
+ /**
281
+ * A file the AI attached to a chat reply (File Library media). The API resolves
282
+ * the agent's handle(s) server-side and returns only the renderable contract —
283
+ * `image` renders inline, `document` (e.g. PDF) as a download link.
284
+ */
285
+ interface ChatMedia {
286
+ type: "image" | "document";
287
+ url: string;
288
+ filename?: string;
289
+ }
280
290
 
281
291
  /**
282
292
  * Low-level Nexor API client.
@@ -390,6 +400,8 @@ declare class NexorClient {
390
400
  workflow_run_id?: UUID;
391
401
  /** True when the API recognised an existing lead (returning visitor). */
392
402
  matched_existing?: boolean;
403
+ /** File(s) the AI attached to this reply (inline media channels). */
404
+ media?: ChatMedia[];
393
405
  }>;
394
406
  /**
395
407
  * Create (or upsert) a lead from the web chat widget and force its FIRST
@@ -640,4 +652,4 @@ interface ChatHandle {
640
652
  getSessionId(): string;
641
653
  }
642
654
 
643
- export { type AutomationInput as A, type BulkResultEntry as B, type Campaign as C, type ForceFirstMessage as F, type GetLeadHistoryParams as G, type ISODateString as I, type Lead as L, type MeetingNotesInput as M, NexorClient as N, type RequestOptions as R, type SendMessageInput as S, type UUID as U, type WhatsAppTemplate as W, type ChatChannelKey as a, type ChatChannels as b, type ChatHandle as c, type ChatInitOptions as d, type ChatLeadCapture as e, type ClientOptions as f, type CreateLeadBulkResponse as g, type CreateLeadInput as h, type CreateLeadResponse as i, type CreateMeetingInput as j, type GetLeadHistoryResponse as k, type GetLeadResponse as l, type IsPausedResponse as m, type LeadHistoryItem as n, type ListTemplatesParams as o, type ResumeAutomationInput as p, type SyncTagsInput as q, type SyncTagsResponse as r, type Workflow as s, type WorkflowRunStub as t };
655
+ export { type AutomationInput as A, type BulkResultEntry as B, type Campaign as C, type ForceFirstMessage as F, type GetLeadHistoryParams as G, type ISODateString as I, type Lead as L, type MeetingNotesInput as M, NexorClient as N, type RequestOptions as R, type SendMessageInput as S, type UUID as U, type WhatsAppTemplate as W, type ChatChannelKey as a, type ChatChannels as b, type ChatHandle as c, type ChatInitOptions as d, type ChatLeadCapture as e, type ChatMedia as f, type ClientOptions as g, type CreateLeadBulkResponse as h, type CreateLeadInput as i, type CreateLeadResponse as j, type CreateMeetingInput as k, type GetLeadHistoryResponse as l, type GetLeadResponse as m, type IsPausedResponse as n, type LeadHistoryItem as o, type ListTemplatesParams as p, type ResumeAutomationInput as q, type SyncTagsInput as r, type SyncTagsResponse as s, type Workflow as t, type WorkflowRunStub as u };
package/dist/index.cjs CHANGED
@@ -1151,6 +1151,40 @@ var widgetCss = (accent, accentText) => `
1151
1151
  }
1152
1152
  .nexor-chat__msg--bot .nexor-chat__msg-time { text-align: left; }
1153
1153
 
1154
+ /* File Library attachment: the image itself is the bubble (no padding/bg). */
1155
+ .nexor-chat__msg--media {
1156
+ padding: 0;
1157
+ background: transparent;
1158
+ border: none;
1159
+ box-shadow: none;
1160
+ overflow: hidden;
1161
+ }
1162
+ .nexor-chat__media-img {
1163
+ display: block;
1164
+ max-width: 100%;
1165
+ max-height: 260px;
1166
+ width: auto;
1167
+ height: auto;
1168
+ border-radius: 14px;
1169
+ border-bottom-left-radius: 4px;
1170
+ object-fit: cover;
1171
+ }
1172
+ .nexor-chat__media-doc {
1173
+ display: inline-flex;
1174
+ align-items: center;
1175
+ gap: 6px;
1176
+ padding: 8px 12px;
1177
+ border-radius: 14px;
1178
+ border-bottom-left-radius: 4px;
1179
+ background: #fff;
1180
+ border: 1px solid rgba(0,0,0,0.06);
1181
+ color: ${accent};
1182
+ text-decoration: none;
1183
+ font-size: 13px;
1184
+ font-weight: 500;
1185
+ }
1186
+ .nexor-chat__media-doc::before { content: "\\1F4CE"; }
1187
+
1154
1188
  @keyframes nexor-msg-in {
1155
1189
  from { transform: translateY(8px) scale(0.96); opacity: 0; }
1156
1190
  to { transform: translateY(0) scale(1); opacity: 1; }
@@ -1290,7 +1324,8 @@ var widgetCss = (accent, accentText) => `
1290
1324
  transition: border-color 140ms ease, box-shadow 140ms ease;
1291
1325
  }
1292
1326
  /* Composer textarea: auto-grows (height managed in JS) up to ~4 lines, then
1293
- scrolls. resize:none disables the manual drag handle. */
1327
+ scrolls. resize:none disables the manual drag handle. Filled style: neutral
1328
+ pill at rest that "lights up" white on focus \u2014 calmer than a glowing ring. */
1294
1329
  textarea.nexor-chat__input {
1295
1330
  display: block;
1296
1331
  resize: none;
@@ -1299,11 +1334,22 @@ textarea.nexor-chat__input {
1299
1334
  max-height: 98px; /* 4 lines (20\xD74) + 16 padding + 2 border */
1300
1335
  white-space: pre-wrap;
1301
1336
  word-break: break-word;
1337
+ background: #f3f4f6;
1338
+ border-color: transparent;
1339
+ border-radius: 19px;
1340
+ padding: 8px 14px;
1341
+ transition: background 140ms ease, border-color 140ms ease, box-shadow 140ms ease;
1302
1342
  }
1343
+ textarea.nexor-chat__input::placeholder { color: #9ca3af; }
1303
1344
  .nexor-chat__input:focus,
1304
1345
  .nexor-chat__capture input:focus {
1305
1346
  border-color: ${accent};
1306
- box-shadow: 0 0 0 3px ${accent}33;
1347
+ box-shadow: 0 0 0 2px ${accent}1f;
1348
+ }
1349
+ textarea.nexor-chat__input:focus {
1350
+ background: #fff;
1351
+ border-color: ${accent}99;
1352
+ box-shadow: 0 1px 3px rgba(0,0,0,0.06);
1307
1353
  }
1308
1354
  .nexor-chat__input--error,
1309
1355
  .nexor-chat__capture input.nexor-chat__input--error {
@@ -1444,21 +1490,26 @@ textarea.nexor-chat__input {
1444
1490
  background: ${accent};
1445
1491
  color: ${accentText};
1446
1492
  border: none;
1447
- border-radius: 8px;
1448
- padding: 0 14px;
1493
+ border-radius: 9999px; /* circle, matches the pill composer */
1494
+ padding: 0;
1449
1495
  font: inherit;
1450
1496
  font-weight: 600;
1451
1497
  cursor: pointer;
1452
- min-width: 44px;
1498
+ width: 38px;
1453
1499
  height: 38px; /* matches the one-line textarea height */
1454
1500
  flex: none;
1455
1501
  display: flex;
1456
1502
  align-items: center;
1457
1503
  justify-content: center;
1458
- transition: transform 140ms ease, filter 140ms ease;
1504
+ box-shadow: 0 1px 3px rgba(0,0,0,0.12);
1505
+ transition: transform 140ms ease, filter 140ms ease, box-shadow 140ms ease;
1506
+ }
1507
+ .nexor-chat__send:hover {
1508
+ filter: brightness(1.08);
1509
+ box-shadow: 0 2px 6px rgba(0,0,0,0.18);
1510
+ transform: translateY(-1px);
1459
1511
  }
1460
- .nexor-chat__send:hover { filter: brightness(1.08); }
1461
- .nexor-chat__send:active { transform: scale(0.95); }
1512
+ .nexor-chat__send:active { transform: scale(0.93); }
1462
1513
  .nexor-chat__send[disabled] {
1463
1514
  opacity: 0.6;
1464
1515
  cursor: not-allowed;
@@ -1469,7 +1520,7 @@ textarea.nexor-chat__input {
1469
1520
  height: 18px;
1470
1521
  transition: transform 200ms ease;
1471
1522
  }
1472
- .nexor-chat__send:not([disabled]):hover svg { transform: translateX(2px); }
1523
+ .nexor-chat__send:not([disabled]):hover svg { transform: translateY(-1.5px); }
1473
1524
 
1474
1525
  /* Small spinner inside the send button while a turn is in flight */
1475
1526
  .nexor-chat__send--loading svg { display: none; }
@@ -1622,11 +1673,13 @@ textarea.nexor-chat__input {
1622
1673
  // src/chat/dom.ts
1623
1674
  var STYLE_ATTR = "data-nexor-chat-styles";
1624
1675
  function injectStyles(accent, accentText) {
1625
- if (document.querySelector(`style[${STYLE_ATTR}]`)) return;
1626
- const style = document.createElement("style");
1627
- style.setAttribute(STYLE_ATTR, "1");
1676
+ let style = document.querySelector(`style[${STYLE_ATTR}]`);
1677
+ if (!style) {
1678
+ style = document.createElement("style");
1679
+ style.setAttribute(STYLE_ATTR, "1");
1680
+ document.head.appendChild(style);
1681
+ }
1628
1682
  style.textContent = widgetCss(accent, accentText);
1629
- document.head.appendChild(style);
1630
1683
  }
1631
1684
  var CHAT_ICON = `
1632
1685
  <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
@@ -1989,8 +2042,8 @@ function buildComposer(cfg) {
1989
2042
  send.setAttribute("aria-label", "Send message");
1990
2043
  send.innerHTML = `
1991
2044
  <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
1992
- <path d="M3.4 11.2 20.5 4.3c.7-.3 1.4.4 1.1 1.1l-6.9 17.1c-.3.8-1.4.8-1.8 0L10 14l-7.5-3c-.8-.3-.8-1.5 0-1.8Z"
1993
- stroke="currentColor" stroke-width="1.6" stroke-linejoin="round" fill="currentColor"/>
2045
+ <path d="M12 19V5.8M5.8 12 12 5.8 18.2 12"
2046
+ stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/>
1994
2047
  </svg>
1995
2048
  `;
1996
2049
  form.appendChild(input);
@@ -2032,6 +2085,28 @@ function buildMessage(role, text, ts, locale) {
2032
2085
  }
2033
2086
  return el;
2034
2087
  }
2088
+ function buildMediaMessage(item) {
2089
+ const el = document.createElement("div");
2090
+ el.className = "nexor-chat__msg nexor-chat__msg--bot nexor-chat__msg--media";
2091
+ if (item.type === "image") {
2092
+ const img = document.createElement("img");
2093
+ img.className = "nexor-chat__media-img";
2094
+ img.src = item.url;
2095
+ img.alt = item.filename || "";
2096
+ img.loading = "lazy";
2097
+ img.addEventListener("error", () => el.remove());
2098
+ el.appendChild(img);
2099
+ } else {
2100
+ const a = document.createElement("a");
2101
+ a.className = "nexor-chat__media-doc";
2102
+ a.href = item.url;
2103
+ a.target = "_blank";
2104
+ a.rel = "noopener noreferrer";
2105
+ a.textContent = item.filename || "Descargar archivo";
2106
+ el.appendChild(a);
2107
+ }
2108
+ return el;
2109
+ }
2035
2110
  function formatRelativeTime(ts, locale, now = Date.now()) {
2036
2111
  try {
2037
2112
  const diff = Math.max(0, now - ts);
@@ -2209,7 +2284,8 @@ function initChat(client, options) {
2209
2284
  setView("chat");
2210
2285
  renderResumeBanner();
2211
2286
  for (const m of persisted.history) {
2212
- appendBubble(m.role === "user" ? "user" : "bot", m.text, m.ts);
2287
+ if (m.media) appendNode(buildMediaMessage(m.media));
2288
+ else appendBubble(m.role === "user" ? "user" : "bot", m.text, m.ts);
2213
2289
  }
2214
2290
  scrollToBottom();
2215
2291
  }
@@ -2742,9 +2818,12 @@ function initChat(client, options) {
2742
2818
  renderResumeBanner(true);
2743
2819
  }
2744
2820
  const reply = (res.reply ?? "").trim();
2821
+ const hasMedia = Array.isArray(res.media) && res.media.length > 0;
2745
2822
  hideTyping();
2746
2823
  if (reply) {
2747
- appendBotReply(reply);
2824
+ appendBotReply(reply, hasMedia ? () => appendBotMedia(res.media) : void 0);
2825
+ } else if (hasMedia) {
2826
+ appendBotMedia(res.media);
2748
2827
  } else {
2749
2828
  appendSystem(cfg.errorText);
2750
2829
  cfg.onError?.(new Error("Nexor SDK: empty reply from server"));
@@ -2779,10 +2858,21 @@ function initChat(client, options) {
2779
2858
  if (!state.isOpen) bumpUnread();
2780
2859
  cfg.onMessage?.({ role: "bot", text });
2781
2860
  }
2782
- function appendBotReply(text) {
2861
+ function appendBotMedia(items) {
2862
+ if (!Array.isArray(items) || items.length === 0) return;
2863
+ for (const item of items) {
2864
+ if (!item || typeof item.url !== "string" || !item.url) continue;
2865
+ appendNode(buildMediaMessage(item));
2866
+ state.history.push({ role: "bot", text: "", ts: Date.now(), media: item });
2867
+ }
2868
+ saveHistory();
2869
+ if (!state.isOpen) bumpUnread();
2870
+ }
2871
+ function appendBotReply(text, after) {
2783
2872
  const parts = cfg.splitReplies ? splitReply(text) : [text];
2784
2873
  if (parts.length <= 1) {
2785
2874
  appendBot(text);
2875
+ after?.();
2786
2876
  return;
2787
2877
  }
2788
2878
  const [first, ...rest] = parts;
@@ -2790,7 +2880,10 @@ function initChat(client, options) {
2790
2880
  let i = 0;
2791
2881
  const sendNext = () => {
2792
2882
  const part = rest[i];
2793
- if (part === void 0) return;
2883
+ if (part === void 0) {
2884
+ after?.();
2885
+ return;
2886
+ }
2794
2887
  const isLast = i === rest.length - 1;
2795
2888
  i++;
2796
2889
  showTyping();