@retrivora-ai/rag-engine 1.1.5 → 1.1.6

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
@@ -104,7 +104,7 @@ var import_react2 = __toESM(require("react"));
104
104
  var import_image = __toESM(require("next/image"));
105
105
  var import_lucide_react2 = require("lucide-react");
106
106
  function ProductCard({ product, primaryColor = "#6366f1" }) {
107
- return /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex-shrink-0 w-64 bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-xl overflow-hidden shadow-sm hover:shadow-md transition-all duration-300 group" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "relative h-40 w-full bg-slate-100 dark:bg-white/5 flex items-center justify-center overflow-hidden" }, product.image ? /* @__PURE__ */ import_react2.default.createElement(
107
+ return /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex-shrink-0 w-full bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-xl overflow-hidden shadow-sm hover:shadow-md transition-all duration-300 group" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "relative h-40 w-full bg-slate-100 dark:bg-white/5 flex items-center justify-center overflow-hidden" }, product.image ? /* @__PURE__ */ import_react2.default.createElement(
108
108
  import_image.default,
109
109
  {
110
110
  src: product.image,
@@ -138,25 +138,37 @@ function ProductCarousel({ products, primaryColor = "#6366f1" }) {
138
138
  const scrollRef = (0, import_react3.useRef)(null);
139
139
  const scroll = (direction) => {
140
140
  if (scrollRef.current) {
141
- const scrollAmount = direction === "left" ? -280 : 280;
142
- scrollRef.current.scrollBy({ left: scrollAmount, behavior: "smooth" });
141
+ const firstCard = scrollRef.current.firstElementChild;
142
+ if (firstCard) {
143
+ const cardWidth = firstCard.offsetWidth + 16;
144
+ const scrollAmount = direction === "left" ? -cardWidth : cardWidth;
145
+ scrollRef.current.scrollBy({ left: scrollAmount, behavior: "smooth" });
146
+ }
143
147
  }
144
148
  };
145
149
  if (!products || products.length === 0) return null;
146
- return /* @__PURE__ */ import_react3.default.createElement("div", { className: "relative w-full my-4 group/carousel" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "absolute -left-3 top-1/2 -translate-y-1/2 z-10 opacity-0 group-hover/carousel:opacity-100 transition-opacity" }, /* @__PURE__ */ import_react3.default.createElement(
150
+ return /* @__PURE__ */ import_react3.default.createElement("div", { className: "relative w-full my-4 group/carousel" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "absolute -left-4 top-1/2 -translate-y-1/2 z-20 opacity-100 md:opacity-0 md:group-hover/carousel:opacity-100 transition-opacity" }, /* @__PURE__ */ import_react3.default.createElement(
147
151
  "button",
148
152
  {
149
- onClick: () => scroll("left"),
150
- className: "p-1.5 rounded-full bg-white dark:bg-slate-800 shadow-lg border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/80 hover:text-indigo-500 dark:hover:text-indigo-400 transition-colors"
153
+ onClick: (e) => {
154
+ e.stopPropagation();
155
+ scroll("left");
156
+ },
157
+ className: "p-2 rounded-full bg-white dark:bg-slate-800 shadow-xl border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/80 hover:text-indigo-500 dark:hover:text-indigo-400 transition-all hover:scale-110 active:scale-95",
158
+ "aria-label": "Scroll left"
151
159
  },
152
- /* @__PURE__ */ import_react3.default.createElement(import_lucide_react3.ChevronLeft, { className: "w-4 h-4" })
153
- )), /* @__PURE__ */ import_react3.default.createElement("div", { className: "absolute -right-3 top-1/2 -translate-y-1/2 z-10 opacity-0 group-hover/carousel:opacity-100 transition-opacity" }, /* @__PURE__ */ import_react3.default.createElement(
160
+ /* @__PURE__ */ import_react3.default.createElement(import_lucide_react3.ChevronLeft, { className: "w-5 h-5" })
161
+ )), /* @__PURE__ */ import_react3.default.createElement("div", { className: "absolute -right-4 top-1/2 -translate-y-1/2 z-20 opacity-100 md:opacity-0 md:group-hover/carousel:opacity-100 transition-opacity" }, /* @__PURE__ */ import_react3.default.createElement(
154
162
  "button",
155
163
  {
156
- onClick: () => scroll("right"),
157
- className: "p-1.5 rounded-full bg-white dark:bg-slate-800 shadow-lg border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/80 hover:text-indigo-500 dark:hover:text-indigo-400 transition-colors"
164
+ onClick: (e) => {
165
+ e.stopPropagation();
166
+ scroll("right");
167
+ },
168
+ className: "p-2 rounded-full bg-white dark:bg-slate-800 shadow-xl border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/80 hover:text-indigo-500 dark:hover:text-indigo-400 transition-all hover:scale-110 active:scale-95",
169
+ "aria-label": "Scroll right"
158
170
  },
159
- /* @__PURE__ */ import_react3.default.createElement(import_lucide_react3.ChevronRight, { className: "w-4 h-4" })
171
+ /* @__PURE__ */ import_react3.default.createElement(import_lucide_react3.ChevronRight, { className: "w-5 h-5" })
160
172
  )), /* @__PURE__ */ import_react3.default.createElement(
161
173
  "div",
162
174
  {
@@ -164,7 +176,14 @@ function ProductCarousel({ products, primaryColor = "#6366f1" }) {
164
176
  className: "flex gap-4 overflow-x-auto pb-4 px-1 scrollbar-hide snap-x snap-mandatory",
165
177
  style: { scrollbarWidth: "none", msOverflowStyle: "none" }
166
178
  },
167
- products.map((product) => /* @__PURE__ */ import_react3.default.createElement("div", { key: product.id, className: "snap-start" }, /* @__PURE__ */ import_react3.default.createElement(ProductCard, { product, primaryColor })))
179
+ products.map((product) => /* @__PURE__ */ import_react3.default.createElement(
180
+ "div",
181
+ {
182
+ key: product.id,
183
+ className: "snap-start flex-shrink-0 w-[85%] sm:w-[48%] md:w-[31%] min-w-[200px] max-w-[300px]"
184
+ },
185
+ /* @__PURE__ */ import_react3.default.createElement(ProductCard, { product, primaryColor })
186
+ ))
168
187
  ));
169
188
  }
170
189
 
@@ -240,14 +259,14 @@ function MessageBubble({
240
259
  style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
241
260
  },
242
261
  isUser ? /* @__PURE__ */ import_react4.default.createElement(import_lucide_react4.User, { className: "w-4 h-4 text-white" }) : /* @__PURE__ */ import_react4.default.createElement(import_lucide_react4.Bot, { className: "w-4 h-4" })
243
- ), /* @__PURE__ */ import_react4.default.createElement("div", { className: `flex flex-col gap-1 max-w-[75%] ${isUser ? "items-end" : "items-start"}` }, /* @__PURE__ */ import_react4.default.createElement(
262
+ ), /* @__PURE__ */ import_react4.default.createElement("div", { className: `flex flex-col gap-1 max-w-[90%] ${isUser ? "items-end" : "items-start"}` }, /* @__PURE__ */ import_react4.default.createElement(
244
263
  "div",
245
264
  {
246
265
  className: `relative px-4 py-3 rounded-2xl text-sm leading-relaxed shadow-sm dark:shadow-lg ${isUser ? "text-white rounded-tr-sm" : "bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 text-slate-800 dark:text-white/90 rounded-tl-sm"}`,
247
266
  style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
248
267
  },
249
- isStreaming && !message.content ? /* @__PURE__ */ import_react4.default.createElement("span", { className: "flex items-center gap-1 py-1" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:0ms]" }), /* @__PURE__ */ import_react4.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:150ms]" }), /* @__PURE__ */ import_react4.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:300ms]" })) : /* @__PURE__ */ import_react4.default.createElement("div", { className: `prose prose-sm max-w-none ${isUser ? "prose-invert" : "dark:prose-invert"}` }, /* @__PURE__ */ import_react4.default.createElement(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default] }, cleanContent || message.content), hasProducts && /* @__PURE__ */ import_react4.default.createElement(ProductCarousel, { products: allProducts, primaryColor }), isStreaming && message.content && /* @__PURE__ */ import_react4.default.createElement("span", { className: "inline-block w-1.5 h-3.5 ml-1 bg-emerald-500 animate-pulse align-middle" }))
250
- ), !isUser && sources && sources.length > 0 && /* @__PURE__ */ import_react4.default.createElement("div", { className: "w-full" }, /* @__PURE__ */ import_react4.default.createElement(
268
+ isStreaming && !message.content ? /* @__PURE__ */ import_react4.default.createElement("span", { className: "flex items-center gap-1 py-1" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:0ms]" }), /* @__PURE__ */ import_react4.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:150ms]" }), /* @__PURE__ */ import_react4.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:300ms]" })) : /* @__PURE__ */ import_react4.default.createElement("div", { className: `prose prose-sm max-w-none ${isUser ? "prose-invert" : "dark:prose-invert"}` }, /* @__PURE__ */ import_react4.default.createElement(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default] }, cleanContent || message.content), isStreaming && message.content && /* @__PURE__ */ import_react4.default.createElement("span", { className: "inline-block w-1.5 h-3.5 ml-1 bg-emerald-500 animate-pulse align-middle" }))
269
+ ), !isUser && hasProducts && /* @__PURE__ */ import_react4.default.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ import_react4.default.createElement(ProductCarousel, { products: allProducts, primaryColor })), !isUser && sources && sources.length > 0 && /* @__PURE__ */ import_react4.default.createElement("div", { className: "w-full" }, /* @__PURE__ */ import_react4.default.createElement(
251
270
  "button",
252
271
  {
253
272
  onClick: () => setShowSources((s) => !s),
package/dist/index.mjs CHANGED
@@ -62,7 +62,7 @@ import React2 from "react";
62
62
  import Image from "next/image";
63
63
  import { ShoppingCart, ExternalLink } from "lucide-react";
64
64
  function ProductCard({ product, primaryColor = "#6366f1" }) {
65
- return /* @__PURE__ */ React2.createElement("div", { className: "flex-shrink-0 w-64 bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-xl overflow-hidden shadow-sm hover:shadow-md transition-all duration-300 group" }, /* @__PURE__ */ React2.createElement("div", { className: "relative h-40 w-full bg-slate-100 dark:bg-white/5 flex items-center justify-center overflow-hidden" }, product.image ? /* @__PURE__ */ React2.createElement(
65
+ return /* @__PURE__ */ React2.createElement("div", { className: "flex-shrink-0 w-full bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-xl overflow-hidden shadow-sm hover:shadow-md transition-all duration-300 group" }, /* @__PURE__ */ React2.createElement("div", { className: "relative h-40 w-full bg-slate-100 dark:bg-white/5 flex items-center justify-center overflow-hidden" }, product.image ? /* @__PURE__ */ React2.createElement(
66
66
  Image,
67
67
  {
68
68
  src: product.image,
@@ -96,25 +96,37 @@ function ProductCarousel({ products, primaryColor = "#6366f1" }) {
96
96
  const scrollRef = useRef(null);
97
97
  const scroll = (direction) => {
98
98
  if (scrollRef.current) {
99
- const scrollAmount = direction === "left" ? -280 : 280;
100
- scrollRef.current.scrollBy({ left: scrollAmount, behavior: "smooth" });
99
+ const firstCard = scrollRef.current.firstElementChild;
100
+ if (firstCard) {
101
+ const cardWidth = firstCard.offsetWidth + 16;
102
+ const scrollAmount = direction === "left" ? -cardWidth : cardWidth;
103
+ scrollRef.current.scrollBy({ left: scrollAmount, behavior: "smooth" });
104
+ }
101
105
  }
102
106
  };
103
107
  if (!products || products.length === 0) return null;
104
- return /* @__PURE__ */ React3.createElement("div", { className: "relative w-full my-4 group/carousel" }, /* @__PURE__ */ React3.createElement("div", { className: "absolute -left-3 top-1/2 -translate-y-1/2 z-10 opacity-0 group-hover/carousel:opacity-100 transition-opacity" }, /* @__PURE__ */ React3.createElement(
108
+ return /* @__PURE__ */ React3.createElement("div", { className: "relative w-full my-4 group/carousel" }, /* @__PURE__ */ React3.createElement("div", { className: "absolute -left-4 top-1/2 -translate-y-1/2 z-20 opacity-100 md:opacity-0 md:group-hover/carousel:opacity-100 transition-opacity" }, /* @__PURE__ */ React3.createElement(
105
109
  "button",
106
110
  {
107
- onClick: () => scroll("left"),
108
- className: "p-1.5 rounded-full bg-white dark:bg-slate-800 shadow-lg border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/80 hover:text-indigo-500 dark:hover:text-indigo-400 transition-colors"
111
+ onClick: (e) => {
112
+ e.stopPropagation();
113
+ scroll("left");
114
+ },
115
+ className: "p-2 rounded-full bg-white dark:bg-slate-800 shadow-xl border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/80 hover:text-indigo-500 dark:hover:text-indigo-400 transition-all hover:scale-110 active:scale-95",
116
+ "aria-label": "Scroll left"
109
117
  },
110
- /* @__PURE__ */ React3.createElement(ChevronLeft, { className: "w-4 h-4" })
111
- )), /* @__PURE__ */ React3.createElement("div", { className: "absolute -right-3 top-1/2 -translate-y-1/2 z-10 opacity-0 group-hover/carousel:opacity-100 transition-opacity" }, /* @__PURE__ */ React3.createElement(
118
+ /* @__PURE__ */ React3.createElement(ChevronLeft, { className: "w-5 h-5" })
119
+ )), /* @__PURE__ */ React3.createElement("div", { className: "absolute -right-4 top-1/2 -translate-y-1/2 z-20 opacity-100 md:opacity-0 md:group-hover/carousel:opacity-100 transition-opacity" }, /* @__PURE__ */ React3.createElement(
112
120
  "button",
113
121
  {
114
- onClick: () => scroll("right"),
115
- className: "p-1.5 rounded-full bg-white dark:bg-slate-800 shadow-lg border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/80 hover:text-indigo-500 dark:hover:text-indigo-400 transition-colors"
122
+ onClick: (e) => {
123
+ e.stopPropagation();
124
+ scroll("right");
125
+ },
126
+ className: "p-2 rounded-full bg-white dark:bg-slate-800 shadow-xl border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/80 hover:text-indigo-500 dark:hover:text-indigo-400 transition-all hover:scale-110 active:scale-95",
127
+ "aria-label": "Scroll right"
116
128
  },
117
- /* @__PURE__ */ React3.createElement(ChevronRight, { className: "w-4 h-4" })
129
+ /* @__PURE__ */ React3.createElement(ChevronRight, { className: "w-5 h-5" })
118
130
  )), /* @__PURE__ */ React3.createElement(
119
131
  "div",
120
132
  {
@@ -122,7 +134,14 @@ function ProductCarousel({ products, primaryColor = "#6366f1" }) {
122
134
  className: "flex gap-4 overflow-x-auto pb-4 px-1 scrollbar-hide snap-x snap-mandatory",
123
135
  style: { scrollbarWidth: "none", msOverflowStyle: "none" }
124
136
  },
125
- products.map((product) => /* @__PURE__ */ React3.createElement("div", { key: product.id, className: "snap-start" }, /* @__PURE__ */ React3.createElement(ProductCard, { product, primaryColor })))
137
+ products.map((product) => /* @__PURE__ */ React3.createElement(
138
+ "div",
139
+ {
140
+ key: product.id,
141
+ className: "snap-start flex-shrink-0 w-[85%] sm:w-[48%] md:w-[31%] min-w-[200px] max-w-[300px]"
142
+ },
143
+ /* @__PURE__ */ React3.createElement(ProductCard, { product, primaryColor })
144
+ ))
126
145
  ));
127
146
  }
128
147
 
@@ -198,14 +217,14 @@ function MessageBubble({
198
217
  style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
199
218
  },
200
219
  isUser ? /* @__PURE__ */ React4.createElement(User, { className: "w-4 h-4 text-white" }) : /* @__PURE__ */ React4.createElement(Bot, { className: "w-4 h-4" })
201
- ), /* @__PURE__ */ React4.createElement("div", { className: `flex flex-col gap-1 max-w-[75%] ${isUser ? "items-end" : "items-start"}` }, /* @__PURE__ */ React4.createElement(
220
+ ), /* @__PURE__ */ React4.createElement("div", { className: `flex flex-col gap-1 max-w-[90%] ${isUser ? "items-end" : "items-start"}` }, /* @__PURE__ */ React4.createElement(
202
221
  "div",
203
222
  {
204
223
  className: `relative px-4 py-3 rounded-2xl text-sm leading-relaxed shadow-sm dark:shadow-lg ${isUser ? "text-white rounded-tr-sm" : "bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 text-slate-800 dark:text-white/90 rounded-tl-sm"}`,
205
224
  style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
206
225
  },
207
- isStreaming && !message.content ? /* @__PURE__ */ React4.createElement("span", { className: "flex items-center gap-1 py-1" }, /* @__PURE__ */ React4.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:0ms]" }), /* @__PURE__ */ React4.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:150ms]" }), /* @__PURE__ */ React4.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:300ms]" })) : /* @__PURE__ */ React4.createElement("div", { className: `prose prose-sm max-w-none ${isUser ? "prose-invert" : "dark:prose-invert"}` }, /* @__PURE__ */ React4.createElement(ReactMarkdown, { remarkPlugins: [remarkGfm] }, cleanContent || message.content), hasProducts && /* @__PURE__ */ React4.createElement(ProductCarousel, { products: allProducts, primaryColor }), isStreaming && message.content && /* @__PURE__ */ React4.createElement("span", { className: "inline-block w-1.5 h-3.5 ml-1 bg-emerald-500 animate-pulse align-middle" }))
208
- ), !isUser && sources && sources.length > 0 && /* @__PURE__ */ React4.createElement("div", { className: "w-full" }, /* @__PURE__ */ React4.createElement(
226
+ isStreaming && !message.content ? /* @__PURE__ */ React4.createElement("span", { className: "flex items-center gap-1 py-1" }, /* @__PURE__ */ React4.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:0ms]" }), /* @__PURE__ */ React4.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:150ms]" }), /* @__PURE__ */ React4.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:300ms]" })) : /* @__PURE__ */ React4.createElement("div", { className: `prose prose-sm max-w-none ${isUser ? "prose-invert" : "dark:prose-invert"}` }, /* @__PURE__ */ React4.createElement(ReactMarkdown, { remarkPlugins: [remarkGfm] }, cleanContent || message.content), isStreaming && message.content && /* @__PURE__ */ React4.createElement("span", { className: "inline-block w-1.5 h-3.5 ml-1 bg-emerald-500 animate-pulse align-middle" }))
227
+ ), !isUser && hasProducts && /* @__PURE__ */ React4.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ React4.createElement(ProductCarousel, { products: allProducts, primaryColor })), !isUser && sources && sources.length > 0 && /* @__PURE__ */ React4.createElement("div", { className: "w-full" }, /* @__PURE__ */ React4.createElement(
209
228
  "button",
210
229
  {
211
230
  onClick: () => setShowSources((s) => !s),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retrivora-ai/rag-engine",
3
- "version": "1.1.5",
3
+ "version": "1.1.6",
4
4
  "description": "Retrivora AI is a plug-and-play AI engine for RAG chat experiences — generic vector DB + LLM provider, embeddable or standalone.",
5
5
  "author": "Abhinav Alkuchi",
6
6
  "license": "MIT",
@@ -118,7 +118,7 @@ export function MessageBubble({
118
118
  }
119
119
  </div>
120
120
 
121
- <div className={`flex flex-col gap-1 max-w-[75%] ${isUser ? 'items-end' : 'items-start'}`}>
121
+ <div className={`flex flex-col gap-1 max-w-[90%] ${isUser ? 'items-end' : 'items-start'}`}>
122
122
  {/* Bubble */}
123
123
  <div
124
124
  className={`relative px-4 py-3 rounded-2xl text-sm leading-relaxed shadow-sm dark:shadow-lg ${
@@ -144,11 +144,6 @@ export function MessageBubble({
144
144
  {cleanContent || message.content}
145
145
  </ReactMarkdown>
146
146
 
147
- {/* Product Carousel */}
148
- {hasProducts && (
149
- <ProductCarousel products={allProducts} primaryColor={primaryColor} />
150
- )}
151
-
152
147
  {isStreaming && message.content && (
153
148
  <span className="inline-block w-1.5 h-3.5 ml-1 bg-emerald-500 animate-pulse align-middle" />
154
149
  )}
@@ -156,6 +151,13 @@ export function MessageBubble({
156
151
  )}
157
152
  </div>
158
153
 
154
+ {/* Product Carousel (Outside the bubble) */}
155
+ {!isUser && hasProducts && (
156
+ <div className="w-full mt-1">
157
+ <ProductCarousel products={allProducts} primaryColor={primaryColor} />
158
+ </div>
159
+ )}
160
+
159
161
  {/* Sources toggle */}
160
162
  {!isUser && sources && sources.length > 0 && (
161
163
  <div className="w-full">
@@ -21,7 +21,7 @@ interface ProductCardProps {
21
21
 
22
22
  export function ProductCard({ product, primaryColor = '#6366f1' }: ProductCardProps) {
23
23
  return (
24
- <div className="flex-shrink-0 w-64 bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-xl overflow-hidden shadow-sm hover:shadow-md transition-all duration-300 group">
24
+ <div className="flex-shrink-0 w-full bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-xl overflow-hidden shadow-sm hover:shadow-md transition-all duration-300 group">
25
25
  {/* Product Image */}
26
26
  <div className="relative h-40 w-full bg-slate-100 dark:bg-white/5 flex items-center justify-center overflow-hidden">
27
27
  {product.image ? (
@@ -14,8 +14,13 @@ export function ProductCarousel({ products, primaryColor = '#6366f1' }: ProductC
14
14
 
15
15
  const scroll = (direction: 'left' | 'right') => {
16
16
  if (scrollRef.current) {
17
- const scrollAmount = direction === 'left' ? -280 : 280;
18
- scrollRef.current.scrollBy({ left: scrollAmount, behavior: 'smooth' });
17
+ const firstCard = scrollRef.current.firstElementChild as HTMLElement;
18
+ if (firstCard) {
19
+ // Use actual rendered width + gap
20
+ const cardWidth = firstCard.offsetWidth + 16;
21
+ const scrollAmount = direction === 'left' ? -cardWidth : cardWidth;
22
+ scrollRef.current.scrollBy({ left: scrollAmount, behavior: 'smooth' });
23
+ }
19
24
  }
20
25
  };
21
26
 
@@ -24,21 +29,23 @@ export function ProductCarousel({ products, primaryColor = '#6366f1' }: ProductC
24
29
  return (
25
30
  <div className="relative w-full my-4 group/carousel">
26
31
  {/* Scroll Buttons */}
27
- <div className="absolute -left-3 top-1/2 -translate-y-1/2 z-10 opacity-0 group-hover/carousel:opacity-100 transition-opacity">
32
+ <div className="absolute -left-4 top-1/2 -translate-y-1/2 z-20 opacity-100 md:opacity-0 md:group-hover/carousel:opacity-100 transition-opacity">
28
33
  <button
29
- onClick={() => scroll('left')}
30
- className="p-1.5 rounded-full bg-white dark:bg-slate-800 shadow-lg border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/80 hover:text-indigo-500 dark:hover:text-indigo-400 transition-colors"
34
+ onClick={(e) => { e.stopPropagation(); scroll('left'); }}
35
+ className="p-2 rounded-full bg-white dark:bg-slate-800 shadow-xl border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/80 hover:text-indigo-500 dark:hover:text-indigo-400 transition-all hover:scale-110 active:scale-95"
36
+ aria-label="Scroll left"
31
37
  >
32
- <ChevronLeft className="w-4 h-4" />
38
+ <ChevronLeft className="w-5 h-5" />
33
39
  </button>
34
40
  </div>
35
41
 
36
- <div className="absolute -right-3 top-1/2 -translate-y-1/2 z-10 opacity-0 group-hover/carousel:opacity-100 transition-opacity">
42
+ <div className="absolute -right-4 top-1/2 -translate-y-1/2 z-20 opacity-100 md:opacity-0 md:group-hover/carousel:opacity-100 transition-opacity">
37
43
  <button
38
- onClick={() => scroll('right')}
39
- className="p-1.5 rounded-full bg-white dark:bg-slate-800 shadow-lg border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/80 hover:text-indigo-500 dark:hover:text-indigo-400 transition-colors"
44
+ onClick={(e) => { e.stopPropagation(); scroll('right'); }}
45
+ className="p-2 rounded-full bg-white dark:bg-slate-800 shadow-xl border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/80 hover:text-indigo-500 dark:hover:text-indigo-400 transition-all hover:scale-110 active:scale-95"
46
+ aria-label="Scroll right"
40
47
  >
41
- <ChevronRight className="w-4 h-4" />
48
+ <ChevronRight className="w-5 h-5" />
42
49
  </button>
43
50
  </div>
44
51
 
@@ -49,7 +56,10 @@ export function ProductCarousel({ products, primaryColor = '#6366f1' }: ProductC
49
56
  style={{ scrollbarWidth: 'none', msOverflowStyle: 'none' }}
50
57
  >
51
58
  {products.map((product) => (
52
- <div key={product.id} className="snap-start">
59
+ <div
60
+ key={product.id}
61
+ className="snap-start flex-shrink-0 w-[85%] sm:w-[48%] md:w-[31%] min-w-[200px] max-w-[300px]"
62
+ >
53
63
  <ProductCard product={product} primaryColor={primaryColor} />
54
64
  </div>
55
65
  ))}