@wealthx/shadcn 1.5.39 → 1.5.41

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.
Files changed (30) hide show
  1. package/.turbo/turbo-build.log +118 -118
  2. package/CHANGELOG.md +12 -0
  3. package/dist/{chunk-MGIDYXOP.mjs → chunk-DWNLBUDC.mjs} +459 -67
  4. package/dist/{chunk-EFHPSKVF.mjs → chunk-EGM4DARZ.mjs} +110 -1
  5. package/dist/{chunk-R7M657QL.mjs → chunk-GIQGZFP6.mjs} +138 -46
  6. package/dist/{chunk-B5PSUONN.mjs → chunk-TF5TOVIM.mjs} +1 -1
  7. package/dist/{chunk-RRROLESJ.mjs → chunk-XHZONBL4.mjs} +1 -1
  8. package/dist/components/ui/ai-assistant-drawer.js +101 -0
  9. package/dist/components/ui/ai-assistant-drawer.mjs +2 -2
  10. package/dist/components/ui/ai-conversations/index.js +101 -0
  11. package/dist/components/ui/ai-conversations/index.mjs +2 -2
  12. package/dist/components/ui/chat-input-area.js +101 -0
  13. package/dist/components/ui/chat-input-area.mjs +1 -1
  14. package/dist/components/ui/policy-ai/index.js +818 -261
  15. package/dist/components/ui/policy-ai/index.mjs +11 -2
  16. package/dist/components/ui/support-agent/index.js +233 -45
  17. package/dist/components/ui/support-agent/index.mjs +2 -2
  18. package/dist/index.js +3521 -3330
  19. package/dist/index.mjs +5 -5
  20. package/dist/styles.css +1 -1
  21. package/package.json +1 -1
  22. package/src/components/ui/chat-input-area.tsx +181 -2
  23. package/src/components/ui/policy-ai/index.tsx +12 -0
  24. package/src/components/ui/policy-ai/policy-ai-context-sidebar.tsx +231 -0
  25. package/src/components/ui/policy-ai/policy-ai-history-panel.tsx +175 -0
  26. package/src/components/ui/policy-ai/policy-ai-page.tsx +243 -0
  27. package/src/components/ui/policy-ai/policy-ai-panel.tsx +64 -57
  28. package/src/components/ui/policy-ai/policy-ai-responses.tsx +8 -12
  29. package/src/components/ui/support-agent/support-agent-panel.tsx +170 -48
  30. package/src/styles/styles-css.ts +1 -1
@@ -1,4 +1,7 @@
1
1
  import * as React from "react";
2
+ import ReactMarkdown from "react-markdown";
3
+ import rehypeRaw from "rehype-raw";
4
+ import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
2
5
  import { Bot, ChevronLeft, ChevronRight, SquarePen, X } from "lucide-react";
3
6
  import { cn } from "@/lib/utils";
4
7
  import { Sheet, SheetContent } from "@/components/ui/sheet";
@@ -66,6 +69,12 @@ export interface SupportAgentMessage {
66
69
  richContent?: SupportAgentRichContent;
67
70
  /** True while the assistant response is streaming in. */
68
71
  isStreaming?: boolean;
72
+ /**
73
+ * Status text shown beside the animated dots while the response streams
74
+ * (e.g. "Checking the help center"). Rendered only while the bubble is empty
75
+ * and streaming. The trailing "…" animates on its own — do not include dots here.
76
+ */
77
+ streamingLabel?: string;
69
78
  /** True if the message errored. */
70
79
  isErrored?: boolean;
71
80
  }
@@ -153,6 +162,28 @@ function SupportTypingIndicator() {
153
162
  );
154
163
  }
155
164
 
165
+ // ---------------------------------------------------------------------------
166
+ // StreamingStatus — optional status label with an animated "…" ellipsis
167
+ // (e.g. "Checking the help center" + animated dots), shown while streaming.
168
+ // ---------------------------------------------------------------------------
169
+
170
+ function StreamingStatus({ label }: { label?: string }) {
171
+ // No label → the standard DS typing indicator (3 bouncing dots).
172
+ if (!label) return <SupportTypingIndicator />;
173
+
174
+ // With label → status text + the same DS typing dots: "Checking the help center • • •"
175
+ return (
176
+ <span
177
+ className="flex items-center gap-1.5 text-sm text-muted-foreground"
178
+ role="status"
179
+ aria-label={`${label}…`}
180
+ >
181
+ <span>{label}</span>
182
+ <SupportTypingIndicator />
183
+ </span>
184
+ );
185
+ }
186
+
156
187
  // ---------------------------------------------------------------------------
157
188
  // MessageBubble — renders a single message with optional rich content
158
189
  // ---------------------------------------------------------------------------
@@ -185,11 +216,24 @@ function MessageBubble({ message }: MessageBubbleProps) {
185
216
  data-role={message.role}
186
217
  >
187
218
  {isEmpty && message.isStreaming ? (
188
- <SupportTypingIndicator />
189
- ) : (
219
+ <StreamingStatus label={message.streamingLabel} />
220
+ ) : isUser ? (
190
221
  <span className="whitespace-pre-wrap break-words leading-relaxed">
191
222
  {message.content}
192
223
  </span>
224
+ ) : (
225
+ <div className="break-words text-sm leading-relaxed [&_a]:text-primary [&_a]:underline [&_p]:m-0 [&_p:not(:last-child)]:mb-2 [&_ul]:my-1 [&_ul]:list-disc [&_ul]:pl-4 [&_ol]:my-1 [&_ol]:list-decimal [&_ol]:pl-4 [&_li]:mb-0.5">
226
+ <ReactMarkdown
227
+ rehypePlugins={[rehypeRaw, [rehypeSanitize, defaultSchema]]}
228
+ components={{
229
+ a: ({ node, ...props }) => (
230
+ <a {...props} target="_blank" rel="noopener noreferrer" />
231
+ ),
232
+ }}
233
+ >
234
+ {message.content}
235
+ </ReactMarkdown>
236
+ </div>
193
237
  )}
194
238
  {message.isErrored && (
195
239
  <p className="mt-1 text-xs opacity-70">
@@ -240,6 +284,54 @@ function RichContentRenderer({ richContent }: RichContentRendererProps) {
240
284
  return null;
241
285
  }
242
286
 
287
+ // ---------------------------------------------------------------------------
288
+ // Local sub-components
289
+ // ---------------------------------------------------------------------------
290
+
291
+ /** Reusable close button — consistent across all panel header variants. */
292
+ function CloseButton({ onClick }: { onClick: () => void }) {
293
+ return (
294
+ <Button
295
+ variant="ghost"
296
+ size="icon"
297
+ className="size-7 shrink-0"
298
+ onClick={onClick}
299
+ title="Close"
300
+ >
301
+ <X className="size-3.5" />
302
+ <span className="sr-only">Close</span>
303
+ </Button>
304
+ );
305
+ }
306
+
307
+ /** Single conversation row — used in both home recents and all-conversations list. */
308
+ function ConversationRow({
309
+ conv,
310
+ onClick,
311
+ className,
312
+ }: {
313
+ conv: SupportAgentRecentConversation;
314
+ onClick: () => void;
315
+ className?: string;
316
+ }) {
317
+ return (
318
+ <button
319
+ type="button"
320
+ onClick={onClick}
321
+ className={cn(
322
+ "flex w-full items-center justify-between gap-2 text-left text-sm text-foreground hover:bg-muted/50",
323
+ className,
324
+ )}
325
+ >
326
+ <span className="flex-1 truncate">{conv.title}</span>
327
+ <ChevronRight
328
+ className="size-3.5 shrink-0 text-muted-foreground"
329
+ aria-hidden="true"
330
+ />
331
+ </button>
332
+ );
333
+ }
334
+
243
335
  // ---------------------------------------------------------------------------
244
336
  // SupportAgentPanel
245
337
  // ---------------------------------------------------------------------------
@@ -264,6 +356,7 @@ export function SupportAgentPanel({
264
356
  className,
265
357
  }: SupportAgentPanelProps) {
266
358
  const [inputValue, setInputValue] = React.useState("");
359
+ const [showAllConversations, setShowAllConversations] = React.useState(false);
267
360
  const messagesEndRef = React.useRef<HTMLDivElement>(null);
268
361
 
269
362
  const hasMessages = messages.length > 0;
@@ -271,6 +364,11 @@ export function SupportAgentPanel({
271
364
  // Chat header mode: has messages AND a conversation title
272
365
  const isChatMode = hasMessages && !!conversationTitle;
273
366
 
367
+ // Reset "all conversations" view when panel closes
368
+ React.useEffect(() => {
369
+ if (!open) setShowAllConversations(false);
370
+ }, [open]);
371
+
274
372
  // Auto-scroll to latest message
275
373
  React.useEffect(() => {
276
374
  if (!messagesEndRef.current) return;
@@ -296,6 +394,16 @@ export function SupportAgentPanel({
296
394
  );
297
395
 
298
396
  const hasRecents = !!recentConversations?.length;
397
+ // Home state shows only the 3 most recent; "View all" expands to the full list.
398
+ const recentsPreview = React.useMemo(
399
+ () => recentConversations?.slice(0, 3) ?? [],
400
+ [recentConversations],
401
+ );
402
+
403
+ const handleViewAll = React.useCallback(() => {
404
+ setShowAllConversations(true);
405
+ onViewAllConversations?.(); // optional analytics callback
406
+ }, [onViewAllConversations]);
299
407
 
300
408
  return (
301
409
  <Sheet open={open} onOpenChange={(o) => !o && onClose()}>
@@ -310,7 +418,25 @@ export function SupportAgentPanel({
310
418
  >
311
419
  {/* ── Header ── */}
312
420
  <div className="shrink-0 border-b border-border px-3 py-2.5">
313
- {isChatMode ? (
421
+ {showAllConversations ? (
422
+ /* All-conversations mode: [←] All conversations [✕] */
423
+ <div className="flex items-center gap-1">
424
+ <Button
425
+ variant="ghost"
426
+ size="icon"
427
+ className="size-7 shrink-0"
428
+ onClick={() => setShowAllConversations(false)}
429
+ title="Back"
430
+ >
431
+ <ChevronLeft className="size-3.5" />
432
+ <span className="sr-only">Back</span>
433
+ </Button>
434
+ <span className="flex-1 truncate px-1 text-sm font-medium text-foreground">
435
+ All conversations
436
+ </span>
437
+ <CloseButton onClick={onClose} />
438
+ </div>
439
+ ) : isChatMode ? (
314
440
  /* Chat mode: [←] Conversation title [✕] */
315
441
  <div className="flex items-center gap-1">
316
442
  {onBack && (
@@ -328,16 +454,7 @@ export function SupportAgentPanel({
328
454
  <span className="flex-1 truncate px-1 text-sm font-medium text-foreground">
329
455
  {conversationTitle}
330
456
  </span>
331
- <Button
332
- variant="ghost"
333
- size="icon"
334
- className="size-7 shrink-0"
335
- onClick={onClose}
336
- title="Close"
337
- >
338
- <X className="size-3.5" />
339
- <span className="sr-only">Close</span>
340
- </Button>
457
+ <CloseButton onClick={onClose} />
341
458
  </div>
342
459
  ) : (
343
460
  /* Home mode: [Bot icon] Support Assistant [✕] */
@@ -353,20 +470,11 @@ export function SupportAgentPanel({
353
470
  Support Assistant
354
471
  </span>
355
472
  </div>
356
- <Button
357
- variant="ghost"
358
- size="icon"
359
- className="size-7"
360
- onClick={onClose}
361
- title="Close"
362
- >
363
- <X className="size-3.5" />
364
- <span className="sr-only">Close</span>
365
- </Button>
473
+ <CloseButton onClick={onClose} />
366
474
  </div>
367
475
  )}
368
- {/* Context chip — shown below header title in both modes */}
369
- {context && (
476
+ {/* Context chip — shown below header title in home and chat modes only */}
477
+ {context && !showAllConversations && (
370
478
  <div className="mt-2">
371
479
  <SupportContextChip context={context} />
372
480
  </div>
@@ -383,6 +491,27 @@ export function SupportAgentPanel({
383
491
  <p className="text-sm text-muted-foreground">Loading…</p>
384
492
  </div>
385
493
  </div>
494
+ ) : showAllConversations ? (
495
+ /* All conversations — full scrollable list (Jira Rovo pattern) */
496
+ <div className="flex flex-col">
497
+ {recentConversations?.length ? (
498
+ recentConversations.map((conv) => (
499
+ <ConversationRow
500
+ key={conv.id}
501
+ conv={conv}
502
+ onClick={() => {
503
+ setShowAllConversations(false);
504
+ onOpenConversation?.(conv.id);
505
+ }}
506
+ className="border-b border-border px-4 py-3"
507
+ />
508
+ ))
509
+ ) : (
510
+ <p className="p-4 text-sm text-muted-foreground">
511
+ No conversations yet.
512
+ </p>
513
+ )}
514
+ </div>
386
515
  ) : !hasMessages ? (
387
516
  /* Home state — Rovo pattern: New Chat CTA + Recents + Suggested */
388
517
  <div className="flex flex-col gap-5 p-4">
@@ -399,39 +528,31 @@ export function SupportAgentPanel({
399
528
  </Button>
400
529
  )}
401
530
 
402
- {/* Recents — previous conversations */}
531
+ {/* Recents — 3 most recent conversations */}
403
532
  {hasRecents && (
404
533
  <div className="flex flex-col gap-1">
405
534
  <p className="px-1 text-xs font-medium uppercase tracking-wide text-muted-foreground">
406
535
  Recents
407
536
  </p>
408
537
  <div className="flex flex-col">
409
- {recentConversations!.map((conv) => (
410
- <button
538
+ {recentsPreview.map((conv) => (
539
+ <ConversationRow
411
540
  key={conv.id}
412
- type="button"
541
+ conv={conv}
413
542
  onClick={() => onOpenConversation?.(conv.id)}
414
- className="flex w-full items-center justify-between gap-2 px-1 py-2.5 text-left text-sm text-foreground hover:bg-muted/50"
415
- >
416
- <span className="flex-1 truncate">{conv.title}</span>
417
- <ChevronRight
418
- className="size-3.5 shrink-0 text-muted-foreground"
419
- aria-hidden="true"
420
- />
421
- </button>
543
+ className="px-1 py-2.5"
544
+ />
422
545
  ))}
423
546
  </div>
424
- {onViewAllConversations && (
425
- <Button
426
- variant="ghost"
427
- size="sm"
428
- onClick={onViewAllConversations}
429
- className="h-auto w-fit gap-0.5 px-1 py-1 text-xs text-muted-foreground hover:bg-transparent hover:text-foreground"
430
- >
431
- View all conversations
432
- <ChevronRight className="size-3.5" aria-hidden="true" />
433
- </Button>
434
- )}
547
+ <Button
548
+ variant="ghost"
549
+ size="sm"
550
+ onClick={handleViewAll}
551
+ className="h-auto w-fit gap-0.5 px-1 py-1 text-xs text-muted-foreground hover:bg-transparent hover:text-foreground"
552
+ >
553
+ View all conversations
554
+ <ChevronRight className="size-3.5" aria-hidden="true" />
555
+ </Button>
435
556
  </div>
436
557
  )}
437
558
 
@@ -490,6 +611,7 @@ export function SupportAgentPanel({
490
611
  onAttachImage={onAttachImage}
491
612
  disabled={isLoading || isStreaming}
492
613
  placeholder="Ask anything…"
614
+ showMarkdownToolbar
493
615
  />
494
616
  </div>
495
617
  </SheetContent>