@aigentyc/chat-sdk 0.5.1 → 0.5.3

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.
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { useAutoResize, useIsMobile, useLiveChat, usePreviewEmbed } from './chunk-P7UR37HP.js';
3
3
  import { useTheme } from './chunk-VQPOUMBC.js';
4
- import { Conversation, ConversationContent, ConversationEmptyState, MessageAttachments, MessageAttachment, Sources, SourcesTrigger, SourcesContent, Source, MessageFeedback, Loader } from './chunk-CNGIOCLR.js';
4
+ import { Conversation, anchorToLatestTurn, FollowLatestTurn, ConversationContent, ConversationEmptyState, MessageAttachments, MessageAttachment, Sources, SourcesTrigger, SourcesContent, Source, MessageFeedback, LATEST_TURN_ATTR, Loader } from './chunk-O5TPHZRP.js';
5
5
  import { useI18n } from './chunk-LDQVDUUJ.js';
6
6
  import { ToolRenderer } from './chunk-CW6LCVNC.js';
7
7
  import { useChatContext, useSlots, renderSlot } from './chunk-JAEXKKSE.js';
@@ -985,6 +985,16 @@ function ChatInterface(props) {
985
985
  const [cartOpen, setCartOpen] = useState(false);
986
986
  const [storeOrigin, _setStoreOrigin] = useState(null);
987
987
  const isProcessing = status === "streaming";
988
+ const anchorLatestTurn = props.scrollAnchor !== "bottom";
989
+ const latestUserId = useMemo(() => {
990
+ for (let i = messages.length - 1; i >= 0; i--) {
991
+ const m = messages[i];
992
+ if (m.role !== "user") continue;
993
+ if (hideFirstUserMessage && i === 0) continue;
994
+ return m.id;
995
+ }
996
+ return void 0;
997
+ }, [messages, hideFirstUserMessage]);
988
998
  useEffect(() => {
989
999
  if (messages.length > 0 && showWelcome) setShowWelcome(false);
990
1000
  if (messages.length === 0 && !showWelcome) setShowWelcome(true);
@@ -1086,134 +1096,146 @@ function ChatInterface(props) {
1086
1096
  onCartUpdate: (count) => setCartCount(count)
1087
1097
  }
1088
1098
  ),
1089
- /* @__PURE__ */ jsx(Conversation, { className: "flex-1", children: /* @__PURE__ */ jsxs(ConversationContent, { className: "px-[15px] md:px-[40px] py-6 sm:py-8 w-full space-y-6", children: [
1090
- messages.length === 0 && renderSlot("emptyState", {}, ConversationEmptyState, slots),
1091
- messages.map((message, messageIndex) => {
1092
- if (hideFirstUserMessage && messageIndex === 0 && message.role === "user") return null;
1093
- if (message.role === "system") {
1094
- return /* @__PURE__ */ jsx("div", { children: renderSlot("systemMessage", { message: message.content }, SystemMessage, slots) }, message.id);
1095
- }
1096
- return /* @__PURE__ */ jsxs(
1097
- "div",
1098
- {
1099
- className: `group ${message.role === "assistant" ? "is-assistant" : "is-user"}`,
1100
- children: [
1101
- /* @__PURE__ */ jsx(
1102
- MessageAttachments,
1099
+ /* @__PURE__ */ jsxs(
1100
+ Conversation,
1101
+ {
1102
+ className: "flex-1",
1103
+ targetScrollTop: anchorLatestTurn ? anchorToLatestTurn : void 0,
1104
+ ...anchorLatestTurn ? { initial: "instant", resize: "instant" } : {},
1105
+ children: [
1106
+ anchorLatestTurn && /* @__PURE__ */ jsx(FollowLatestTurn, { turnKey: latestUserId }),
1107
+ /* @__PURE__ */ jsxs(ConversationContent, { className: "px-[15px] md:px-[40px] py-6 sm:py-8 w-full space-y-6", children: [
1108
+ messages.length === 0 && renderSlot("emptyState", {}, ConversationEmptyState, slots),
1109
+ messages.map((message, messageIndex) => {
1110
+ if (hideFirstUserMessage && messageIndex === 0 && message.role === "user") return null;
1111
+ if (message.role === "system") {
1112
+ return /* @__PURE__ */ jsx("div", { children: renderSlot("systemMessage", { message: message.content }, SystemMessage, slots) }, message.id);
1113
+ }
1114
+ return /* @__PURE__ */ jsxs(
1115
+ "div",
1103
1116
  {
1104
- className: message.role === "user" ? "mb-2" : "mr-auto ml-0 mb-2",
1105
- children: (message.parts || []).filter((p) => p.type === "file" && p.url).map((p, i) => /* @__PURE__ */ jsx(
1106
- MessageAttachment,
1107
- {
1108
- className: "size-32",
1109
- data: {
1110
- type: "file",
1111
- url: p.url,
1112
- mediaType: p.mediaType ?? "",
1113
- filename: p.filename
1117
+ className: `group ${message.role === "assistant" ? "is-assistant" : "is-user"}`,
1118
+ ...message.id === latestUserId ? { [LATEST_TURN_ATTR]: "" } : {},
1119
+ children: [
1120
+ /* @__PURE__ */ jsx(
1121
+ MessageAttachments,
1122
+ {
1123
+ className: message.role === "user" ? "mb-2" : "mr-auto ml-0 mb-2",
1124
+ children: (message.parts || []).filter((p) => p.type === "file" && p.url).map((p, i) => /* @__PURE__ */ jsx(
1125
+ MessageAttachment,
1126
+ {
1127
+ className: "size-32",
1128
+ data: {
1129
+ type: "file",
1130
+ url: p.url,
1131
+ mediaType: p.mediaType ?? "",
1132
+ filename: p.filename
1133
+ }
1134
+ },
1135
+ `${message.id}-file-${i}`
1136
+ ))
1114
1137
  }
1115
- },
1116
- `${message.id}-file-${i}`
1117
- ))
1118
- }
1119
- ),
1120
- (() => {
1121
- const parts = (message.parts || []).filter(
1122
- (p) => p.type !== "file" && !(p.type === "text" && !(p.text || "").trim())
1123
- );
1124
- return props.messagePartOrder === "stream-order" ? parts : [...parts].sort((a, b) => {
1125
- const textFirst = props.messagePartOrder !== "tools-first";
1126
- if (a.type === "text" && b.type !== "text") return textFirst ? -1 : 1;
1127
- if (a.type !== "text" && b.type === "text") return textFirst ? 1 : -1;
1128
- return 0;
1129
- });
1130
- })().map((part, i) => {
1131
- if (part.type === "text") {
1132
- if (message.role === "assistant") {
1133
- return /* @__PURE__ */ jsx("div", { children: renderSlot("assistantMessage", { message, partIndex: i, text: part.text || "" }, DefaultAssistantMessage, slots) }, `${message.id}-${i}`);
1134
- }
1135
- return /* @__PURE__ */ jsx("div", { children: renderSlot("userMessage", { message, partIndex: i, text: part.text || "" }, DefaultUserMessage, slots) }, `${message.id}-${i}`);
1136
- }
1137
- if (part.type.startsWith("tool-")) {
1138
- const toolName = part.type.replace("tool-", "");
1139
- if (toolName === "requestHumanChat") return null;
1140
- let output = part.output;
1141
- if (typeof output === "string") {
1142
- try {
1143
- output = JSON.parse(output);
1144
- } catch {
1145
- }
1146
- }
1147
- const result = output && typeof output === "object" && !Array.isArray(output) && ("data" in output || "uiRenderMode" in output) ? output : { data: output };
1148
- return /* @__PURE__ */ jsx(
1149
- ChatErrorBoundary,
1150
- {
1151
- label: `tool "${toolName}"`,
1152
- children: /* @__PURE__ */ jsx(
1153
- ToolRenderer,
1154
- {
1155
- toolName,
1156
- state: part.state ?? "output-available",
1157
- args: part.input ?? {},
1158
- result,
1159
- onSendMessage: handleSubmit,
1160
- isProcessing
1138
+ ),
1139
+ (() => {
1140
+ const parts = (message.parts || []).filter(
1141
+ (p) => p.type !== "file" && !(p.type === "text" && !(p.text || "").trim())
1142
+ );
1143
+ return props.messagePartOrder === "stream-order" ? parts : [...parts].sort((a, b) => {
1144
+ const textFirst = props.messagePartOrder !== "tools-first";
1145
+ if (a.type === "text" && b.type !== "text") return textFirst ? -1 : 1;
1146
+ if (a.type !== "text" && b.type === "text") return textFirst ? 1 : -1;
1147
+ return 0;
1148
+ });
1149
+ })().map((part, i) => {
1150
+ if (part.type === "text") {
1151
+ if (message.role === "assistant") {
1152
+ return /* @__PURE__ */ jsx("div", { children: renderSlot("assistantMessage", { message, partIndex: i, text: part.text || "" }, DefaultAssistantMessage, slots) }, `${message.id}-${i}`);
1161
1153
  }
1162
- )
1163
- },
1164
- `${message.id}-tool-${i}`
1165
- );
1166
- }
1167
- return null;
1168
- }),
1169
- showSources && message.role === "assistant" && (() => {
1170
- const kbSources = extractKbSources(message.parts);
1171
- if (kbSources.length === 0) return null;
1172
- return /* @__PURE__ */ jsxs(Sources, { children: [
1173
- /* @__PURE__ */ jsx(SourcesTrigger, { count: kbSources.length }),
1174
- /* @__PURE__ */ jsx(SourcesContent, { children: kbSources.map((s, idx) => /* @__PURE__ */ jsx(
1175
- Source,
1176
- {
1177
- href: s.sourceUrl ?? "#",
1178
- title: s.documentName || s.pageTitle || `Source ${idx + 1}`
1179
- },
1180
- `${message.id}-src-${idx}`
1181
- )) })
1182
- ] });
1183
- })(),
1184
- message.role === "assistant" && renderSlot("feedback", {
1185
- messageId: message.id,
1186
- projectId: config.projectId,
1187
- sessionId,
1188
- apiKey: config.apiKey,
1189
- apiEndpoint: config.apiEndpoint,
1190
- textContent: (message.parts || []).filter((p) => p.type === "text").map((p) => p.text || "").join("\n") || (typeof message.content === "string" ? message.content : "")
1191
- }, MessageFeedback, slots)
1192
- ]
1193
- },
1194
- message.id
1195
- );
1196
- }),
1197
- status === "streaming" && (() => {
1198
- const lastMessage = messages[messages.length - 1];
1199
- if (!lastMessage || lastMessage.role !== "assistant") return true;
1200
- const hasText = (lastMessage.parts || []).some(
1201
- (p) => p.type === "text" && (p.text || "").trim()
1202
- );
1203
- const allToolsComplete = (lastMessage.parts || []).filter((p) => p.type.startsWith("tool-")).every((p) => p.state === "output-available");
1204
- return allToolsComplete && !hasText;
1205
- })() && /* @__PURE__ */ jsx(Loader, { text: t("thinking") }),
1206
- status === "error" && renderSlot(
1207
- "error",
1208
- {
1209
- error,
1210
- onRetry: retryLastTurn,
1211
- onNewConversation: handleNewConversation
1212
- },
1213
- ChatError,
1214
- slots
1215
- )
1216
- ] }) }),
1154
+ return /* @__PURE__ */ jsx("div", { children: renderSlot("userMessage", { message, partIndex: i, text: part.text || "" }, DefaultUserMessage, slots) }, `${message.id}-${i}`);
1155
+ }
1156
+ if (part.type.startsWith("tool-")) {
1157
+ const toolName = part.type.replace("tool-", "");
1158
+ if (toolName === "requestHumanChat") return null;
1159
+ let output = part.output;
1160
+ if (typeof output === "string") {
1161
+ try {
1162
+ output = JSON.parse(output);
1163
+ } catch {
1164
+ }
1165
+ }
1166
+ const result = output && typeof output === "object" && !Array.isArray(output) && ("data" in output || "uiRenderMode" in output) ? output : { data: output };
1167
+ return /* @__PURE__ */ jsx(
1168
+ ChatErrorBoundary,
1169
+ {
1170
+ label: `tool "${toolName}"`,
1171
+ children: /* @__PURE__ */ jsx(
1172
+ ToolRenderer,
1173
+ {
1174
+ toolName,
1175
+ state: part.state ?? "output-available",
1176
+ args: part.input ?? {},
1177
+ result,
1178
+ onSendMessage: handleSubmit,
1179
+ isProcessing
1180
+ }
1181
+ )
1182
+ },
1183
+ `${message.id}-tool-${i}`
1184
+ );
1185
+ }
1186
+ return null;
1187
+ }),
1188
+ showSources && message.role === "assistant" && (() => {
1189
+ const kbSources = extractKbSources(message.parts);
1190
+ if (kbSources.length === 0) return null;
1191
+ return /* @__PURE__ */ jsxs(Sources, { children: [
1192
+ /* @__PURE__ */ jsx(SourcesTrigger, { count: kbSources.length }),
1193
+ /* @__PURE__ */ jsx(SourcesContent, { children: kbSources.map((s, idx) => /* @__PURE__ */ jsx(
1194
+ Source,
1195
+ {
1196
+ href: s.sourceUrl ?? "#",
1197
+ title: s.documentName || s.pageTitle || `Source ${idx + 1}`
1198
+ },
1199
+ `${message.id}-src-${idx}`
1200
+ )) })
1201
+ ] });
1202
+ })(),
1203
+ message.role === "assistant" && renderSlot("feedback", {
1204
+ messageId: message.id,
1205
+ projectId: config.projectId,
1206
+ sessionId,
1207
+ apiKey: config.apiKey,
1208
+ apiEndpoint: config.apiEndpoint,
1209
+ textContent: (message.parts || []).filter((p) => p.type === "text").map((p) => p.text || "").join("\n") || (typeof message.content === "string" ? message.content : "")
1210
+ }, MessageFeedback, slots)
1211
+ ]
1212
+ },
1213
+ message.id
1214
+ );
1215
+ }),
1216
+ status === "streaming" && (() => {
1217
+ const lastMessage = messages[messages.length - 1];
1218
+ if (!lastMessage || lastMessage.role !== "assistant") return true;
1219
+ const hasText = (lastMessage.parts || []).some(
1220
+ (p) => p.type === "text" && (p.text || "").trim()
1221
+ );
1222
+ const allToolsComplete = (lastMessage.parts || []).filter((p) => p.type.startsWith("tool-")).every((p) => p.state === "output-available");
1223
+ return allToolsComplete && !hasText;
1224
+ })() && /* @__PURE__ */ jsx(Loader, { text: t("thinking") }),
1225
+ status === "error" && renderSlot(
1226
+ "error",
1227
+ {
1228
+ error,
1229
+ onRetry: retryLastTurn,
1230
+ onNewConversation: handleNewConversation
1231
+ },
1232
+ ChatError,
1233
+ slots
1234
+ )
1235
+ ] })
1236
+ ]
1237
+ }
1238
+ ),
1217
1239
  /* @__PURE__ */ jsxs("div", { className: "shrink-0 px-[15px] md:px-[40px] pb-2", style: { backgroundColor: "var(--ac-background)" }, children: [
1218
1240
  props.pageQuestions && props.pageQuestions.length > 0 && /* @__PURE__ */ jsx(
1219
1241
  QuickQuestions,
@@ -1247,6 +1269,35 @@ function ChatInterface(props) {
1247
1269
  /* @__PURE__ */ jsx("span", { "data-session-id": sessionId, className: "hidden" })
1248
1270
  ] });
1249
1271
  }
1272
+
1273
+ // src/core/panel-width.ts
1274
+ var DEFAULT_PANEL_WIDTH = 780;
1275
+ var MIN_PANEL_WIDTH = 320;
1276
+ var SIDE_PANEL_GAP_PX = 10;
1277
+ function panelMaxWidth(width) {
1278
+ if (typeof width === "number") {
1279
+ if (!Number.isFinite(width) || width <= 0) return `${DEFAULT_PANEL_WIDTH}px`;
1280
+ return `${Math.max(MIN_PANEL_WIDTH, Math.round(width))}px`;
1281
+ }
1282
+ if (typeof width === "string") {
1283
+ const value = width.trim();
1284
+ if (value && isValidLength(value)) return value;
1285
+ }
1286
+ return `${DEFAULT_PANEL_WIDTH}px`;
1287
+ }
1288
+ function panelRenderedWidth(width) {
1289
+ return `min(100vw, ${panelMaxWidth(width)})`;
1290
+ }
1291
+ function sidePanelOffset(width) {
1292
+ return `calc(${panelRenderedWidth(width)} + ${SIDE_PANEL_GAP_PX}px)`;
1293
+ }
1294
+ function sidePanelWidth(preferredPx, width) {
1295
+ return `min(${preferredPx}px, calc(100vw - ${panelRenderedWidth(width)} - ${SIDE_PANEL_GAP_PX * 2}px))`;
1296
+ }
1297
+ function isValidLength(value) {
1298
+ if (typeof CSS === "undefined" || typeof CSS.supports !== "function") return true;
1299
+ return CSS.supports("max-width", value);
1300
+ }
1250
1301
  function FloatingButton({
1251
1302
  onClick,
1252
1303
  position,
@@ -1410,7 +1461,7 @@ function useLiveChatOrThrow() {
1410
1461
  if (!ctx) throw new Error("LiveChat sub-view rendered outside LiveChatProvider \u2014 this should never happen");
1411
1462
  return ctx;
1412
1463
  }
1413
- function LiveChatPanel({ isWidgetOpen, isRightSide, zIndex }) {
1464
+ function LiveChatPanel({ isWidgetOpen, isRightSide, zIndex, panelWidth }) {
1414
1465
  const lc = useLiveChat();
1415
1466
  if (!lc) return null;
1416
1467
  const isActive = lc.state !== "ai" || lc.showIdentityForm;
@@ -1434,9 +1485,9 @@ function LiveChatPanel({ isWidgetOpen, isRightSide, zIndex }) {
1434
1485
  backgroundColor: "var(--ac-background)",
1435
1486
  borderColor: "var(--ac-border)",
1436
1487
  zIndex,
1437
- width: "min(420px, calc(100vw - 800px))",
1488
+ width: sidePanelWidth(420, panelWidth),
1438
1489
  height: "75vh",
1439
- ...isRightSide ? { right: "790px" } : { left: "790px" },
1490
+ ...isRightSide ? { right: sidePanelOffset(panelWidth) } : { left: sidePanelOffset(panelWidth) },
1440
1491
  transform: showPanel ? "translateY(-50%) translateX(0)" : `translateY(-50%) ${isRightSide ? "translateX(20px)" : "translateX(-20px)"}`,
1441
1492
  opacity: showPanel ? 1 : 0,
1442
1493
  pointerEvents: showPanel ? "auto" : "none",
@@ -1824,7 +1875,7 @@ function EndedView() {
1824
1875
  )
1825
1876
  ] });
1826
1877
  }
1827
- function PreviewEmbedPanel({ isOpen, isRightSide, zIndex }) {
1878
+ function PreviewEmbedPanel({ isOpen, isRightSide, zIndex, panelWidth }) {
1828
1879
  const previewEmbed = usePreviewEmbed();
1829
1880
  if (!previewEmbed) return null;
1830
1881
  const { preview, closePreview } = previewEmbed;
@@ -1837,9 +1888,9 @@ function PreviewEmbedPanel({ isOpen, isRightSide, zIndex }) {
1837
1888
  backgroundColor: "var(--ac-background)",
1838
1889
  borderColor: "var(--ac-border)",
1839
1890
  zIndex,
1840
- width: "min(560px, calc(100vw - 800px))",
1891
+ width: sidePanelWidth(560, panelWidth),
1841
1892
  height: "70vh",
1842
- ...isRightSide ? { right: "790px" } : { left: "790px" },
1893
+ ...isRightSide ? { right: sidePanelOffset(panelWidth) } : { left: sidePanelOffset(panelWidth) },
1843
1894
  transform: showPanel ? "translateY(-50%) translateX(0)" : `translateY(-50%) ${isRightSide ? "translateX(20px)" : "translateX(-20px)"}`,
1844
1895
  opacity: showPanel ? 1 : 0,
1845
1896
  pointerEvents: showPanel ? "auto" : "none",
@@ -1880,6 +1931,6 @@ function PreviewEmbedPanel({ isOpen, isRightSide, zIndex }) {
1880
1931
  );
1881
1932
  }
1882
1933
 
1883
- export { CartDrawer, ChatError, ChatErrorBoundary, ChatFooter, ChatHeader, ChatInput, ChatInterface, FloatingButton, LiveChatPanel, MobileLiveChatDrawer, PreviewEmbedPanel, QuickQuestions, SystemMessage, WelcomePage };
1884
- //# sourceMappingURL=chunk-UTEGHMBN.js.map
1885
- //# sourceMappingURL=chunk-UTEGHMBN.js.map
1934
+ export { CartDrawer, ChatError, ChatErrorBoundary, ChatFooter, ChatHeader, ChatInput, ChatInterface, DEFAULT_PANEL_WIDTH, FloatingButton, LiveChatPanel, MobileLiveChatDrawer, PreviewEmbedPanel, QuickQuestions, SystemMessage, WelcomePage, panelMaxWidth };
1935
+ //# sourceMappingURL=chunk-MQJ6MGTC.js.map
1936
+ //# sourceMappingURL=chunk-MQJ6MGTC.js.map