@wealthx/shadcn 1.5.37 → 1.5.39

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 (60) hide show
  1. package/.turbo/turbo-build.log +142 -133
  2. package/CHANGELOG.md +12 -0
  3. package/dist/{chunk-LSSIWLYU.mjs → chunk-6XNEHTII.mjs} +1 -1
  4. package/dist/{chunk-ULQ53FRJ.mjs → chunk-7NQKFPXE.mjs} +1 -1
  5. package/dist/{chunk-734FOOJC.mjs → chunk-B5PSUONN.mjs} +25 -58
  6. package/dist/{chunk-DSVKEVX6.mjs → chunk-CZOGJC76.mjs} +1 -1
  7. package/dist/chunk-EFHPSKVF.mjs +192 -0
  8. package/dist/{chunk-JPGL36WQ.mjs → chunk-FL7DEYUA.mjs} +6 -7
  9. package/dist/{chunk-2CHH5QOA.mjs → chunk-FQUT5XD6.mjs} +1 -1
  10. package/dist/chunk-MGIDYXOP.mjs +814 -0
  11. package/dist/{chunk-OG2VM34K.mjs → chunk-MHBQJVHE.mjs} +1 -1
  12. package/dist/{chunk-NB3ZL36B.mjs → chunk-MZI77ZMX.mjs} +17 -2
  13. package/dist/chunk-R7M657QL.mjs +587 -0
  14. package/dist/{chunk-DIH2NZZ3.mjs → chunk-RRROLESJ.mjs} +33 -23
  15. package/dist/components/ui/ai-assistant-drawer.js +269 -121
  16. package/dist/components/ui/ai-assistant-drawer.mjs +2 -1
  17. package/dist/components/ui/ai-conversations/index.js +474 -286
  18. package/dist/components/ui/ai-conversations/index.mjs +2 -1
  19. package/dist/components/ui/chat-input-area.js +429 -0
  20. package/dist/components/ui/chat-input-area.mjs +11 -0
  21. package/dist/components/ui/file-preview-dialog.js +6 -7
  22. package/dist/components/ui/file-preview-dialog.mjs +2 -2
  23. package/dist/components/ui/kanban-column.js +6 -7
  24. package/dist/components/ui/kanban-column.mjs +3 -3
  25. package/dist/components/ui/opportunity-card.js +6 -7
  26. package/dist/components/ui/opportunity-card.mjs +2 -2
  27. package/dist/components/ui/page-top-bar.js +182 -5
  28. package/dist/components/ui/page-top-bar.mjs +3 -1
  29. package/dist/components/ui/pipeline-board.js +6 -7
  30. package/dist/components/ui/pipeline-board.mjs +4 -4
  31. package/dist/components/ui/policy-ai/index.js +1636 -0
  32. package/dist/components/ui/policy-ai/index.mjs +36 -0
  33. package/dist/components/ui/progress.js +6 -7
  34. package/dist/components/ui/progress.mjs +1 -1
  35. package/dist/components/ui/stage-timeline.js +6 -7
  36. package/dist/components/ui/stage-timeline.mjs +2 -2
  37. package/dist/components/ui/support-agent/index.js +1131 -0
  38. package/dist/components/ui/support-agent/index.mjs +27 -0
  39. package/dist/index.js +5609 -4100
  40. package/dist/index.mjs +77 -41
  41. package/dist/styles.css +1 -1
  42. package/package.json +16 -1
  43. package/src/components/index.tsx +54 -0
  44. package/src/components/ui/ai-assistant-drawer.tsx +24 -51
  45. package/src/components/ui/ai-conversations/index.tsx +16 -8
  46. package/src/components/ui/ai-conversations/thread.tsx +38 -27
  47. package/src/components/ui/chat-input-area.tsx +244 -0
  48. package/src/components/ui/page-top-bar.tsx +31 -5
  49. package/src/components/ui/policy-ai/index.tsx +41 -0
  50. package/src/components/ui/policy-ai/policy-ai-panel.tsx +526 -0
  51. package/src/components/ui/policy-ai/policy-ai-primitives.tsx +332 -0
  52. package/src/components/ui/policy-ai/policy-ai-responses.tsx +543 -0
  53. package/src/components/ui/progress.tsx +15 -12
  54. package/src/components/ui/support-agent/index.tsx +25 -0
  55. package/src/components/ui/support-agent/support-agent-fab.tsx +116 -0
  56. package/src/components/ui/support-agent/support-agent-panel.tsx +498 -0
  57. package/src/components/ui/support-agent/support-agent-primitives.tsx +354 -0
  58. package/src/styles/globals.css +1 -0
  59. package/src/styles/styles-css.ts +1 -1
  60. package/tsup.config.ts +3 -0
@@ -0,0 +1,244 @@
1
+ import * as React from "react";
2
+ import { ImagePlus, Paperclip, Send } from "lucide-react";
3
+ import { cn } from "@/lib/utils";
4
+ import { Button } from "@/components/ui/button";
5
+ import { Textarea } from "@/components/ui/textarea";
6
+
7
+ /**
8
+ * ChatInputArea — WealthX Design System
9
+ *
10
+ * General-purpose chat input used across any feature that involves
11
+ * a conversation or messaging interface (Policy AI, Support Agent,
12
+ * AI Conversations, Website Chat Widget, etc.).
13
+ *
14
+ * Features:
15
+ * - Textarea with auto-resize up to `maxHeight`
16
+ * - Enter to send / Shift+Enter for new line
17
+ * - Optional file attachment (Paperclip) — shown only when `onAttachFile` is provided
18
+ * - Optional image upload (ImagePlus) — shown only when `onAttachImage` is provided
19
+ * - Focus ring on outer container via `focus-within`
20
+ * - Fully disabled during streaming / loading states
21
+ *
22
+ * @example
23
+ * <ChatInputArea
24
+ * value={inputValue}
25
+ * onChange={setInputValue}
26
+ * onSend={handleSend}
27
+ * onAttachFile={handleFiles}
28
+ * onAttachImage={handleImages}
29
+ * placeholder="Ask anything…"
30
+ * />
31
+ */
32
+ export interface ChatInputAreaProps {
33
+ /** Controlled text value. */
34
+ value: string;
35
+ /** Called on every keystroke. */
36
+ onChange: (value: string) => void;
37
+ /**
38
+ * Called when the user submits (Enter key or Send button click).
39
+ * Receives the trimmed text. Not called when value is empty or when disabled.
40
+ */
41
+ onSend: (value: string) => void;
42
+ /**
43
+ * When provided, a Paperclip button appears and this callback is fired
44
+ * with the selected FileList. Hidden when omitted.
45
+ */
46
+ onAttachFile?: (files: FileList) => void;
47
+ /**
48
+ * When provided, an ImagePlus button appears and this callback is fired
49
+ * with the selected image FileList. Hidden when omitted.
50
+ */
51
+ onAttachImage?: (files: FileList) => void;
52
+ /** Disables all controls — use while streaming / waiting for a response. */
53
+ disabled?: boolean;
54
+ /** Textarea placeholder text. */
55
+ placeholder?: string;
56
+ /**
57
+ * Hint text rendered below the input box.
58
+ * Pass `false` to hide it entirely.
59
+ * Defaults to "Enter to send · Shift+Enter for new line".
60
+ */
61
+ hint?: string | false;
62
+ /**
63
+ * Maximum textarea height in pixels before scrolling kicks in.
64
+ * @default 160
65
+ */
66
+ maxHeight?: number;
67
+ /** Focus the textarea on mount. */
68
+ autoFocus?: boolean;
69
+ className?: string;
70
+ }
71
+
72
+ const DEFAULT_HINT = "Enter to send · Shift+Enter for new line";
73
+
74
+ export function ChatInputArea({
75
+ value,
76
+ onChange,
77
+ onSend,
78
+ onAttachFile,
79
+ onAttachImage,
80
+ disabled = false,
81
+ placeholder = "Type your message…",
82
+ hint = DEFAULT_HINT,
83
+ maxHeight = 160,
84
+ autoFocus = false,
85
+ className,
86
+ }: ChatInputAreaProps) {
87
+ const textareaRef = React.useRef<HTMLTextAreaElement>(null);
88
+ const fileInputRef = React.useRef<HTMLInputElement>(null);
89
+ const imageInputRef = React.useRef<HTMLInputElement>(null);
90
+
91
+ // Focus on mount when autoFocus is requested
92
+ React.useEffect(() => {
93
+ if (autoFocus) {
94
+ setTimeout(() => textareaRef.current?.focus(), 50);
95
+ }
96
+ }, [autoFocus]);
97
+
98
+ const handleSend = React.useCallback(() => {
99
+ const text = value.trim();
100
+ if (!text || disabled) return;
101
+ onSend(text);
102
+ // Reset textarea height after clearing (caller is responsible for clearing value)
103
+ if (textareaRef.current) {
104
+ textareaRef.current.style.height = "auto";
105
+ }
106
+ }, [value, disabled, onSend]);
107
+
108
+ const handleKeyDown = React.useCallback(
109
+ (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
110
+ if (e.key === "Enter" && !e.shiftKey) {
111
+ e.preventDefault();
112
+ handleSend();
113
+ }
114
+ },
115
+ [handleSend],
116
+ );
117
+
118
+ const handleTextareaChange = React.useCallback(
119
+ (e: React.ChangeEvent<HTMLTextAreaElement>) => {
120
+ onChange(e.target.value);
121
+ // Auto-resize
122
+ const el = e.target;
123
+ el.style.height = "auto";
124
+ el.style.height = `${Math.min(el.scrollHeight, maxHeight)}px`;
125
+ },
126
+ [onChange, maxHeight],
127
+ );
128
+
129
+ const handleFileChange = React.useCallback(
130
+ (e: React.ChangeEvent<HTMLInputElement>) => {
131
+ if (e.target.files?.length) {
132
+ onAttachFile?.(e.target.files);
133
+ e.target.value = "";
134
+ }
135
+ },
136
+ [onAttachFile],
137
+ );
138
+
139
+ const handleImageChange = React.useCallback(
140
+ (e: React.ChangeEvent<HTMLInputElement>) => {
141
+ if (e.target.files?.length) {
142
+ onAttachImage?.(e.target.files);
143
+ e.target.value = "";
144
+ }
145
+ },
146
+ [onAttachImage],
147
+ );
148
+
149
+ const showFileButton = typeof onAttachFile === "function";
150
+ const showImageButton = typeof onAttachImage === "function";
151
+
152
+ return (
153
+ <div
154
+ data-slot="chat-input-area"
155
+ className={cn("flex flex-col gap-1.5", className)}
156
+ >
157
+ {/* Unified input box — textarea + action bar share one border */}
158
+ <div className="border border-border bg-background flex flex-col focus-within:ring-1 focus-within:ring-ring">
159
+ <Textarea
160
+ ref={textareaRef}
161
+ value={value}
162
+ onChange={handleTextareaChange}
163
+ onKeyDown={handleKeyDown}
164
+ placeholder={placeholder}
165
+ disabled={disabled}
166
+ rows={3}
167
+ className="resize-none text-sm border-0 shadow-none focus-visible:ring-0 px-3 pt-3 pb-1 min-h-[72px]"
168
+ aria-label={placeholder}
169
+ />
170
+
171
+ {/* Action bar — attachment buttons on the left, Send on the right */}
172
+ <div className="flex items-center justify-between px-2 pb-2">
173
+ {/* Left: attachment buttons */}
174
+ <div className="flex items-center gap-0.5">
175
+ {showFileButton && (
176
+ <>
177
+ <Button
178
+ variant="ghost"
179
+ size="icon-sm"
180
+ type="button"
181
+ title="Attach file"
182
+ aria-label="Attach file"
183
+ disabled={disabled}
184
+ onClick={() => fileInputRef.current?.click()}
185
+ >
186
+ <Paperclip className="size-3.5" aria-hidden="true" />
187
+ </Button>
188
+ <input
189
+ ref={fileInputRef}
190
+ type="file"
191
+ multiple
192
+ className="sr-only"
193
+ tabIndex={-1}
194
+ onChange={handleFileChange}
195
+ />
196
+ </>
197
+ )}
198
+
199
+ {showImageButton && (
200
+ <>
201
+ <Button
202
+ variant="ghost"
203
+ size="icon-sm"
204
+ type="button"
205
+ title="Upload image"
206
+ aria-label="Upload image"
207
+ disabled={disabled}
208
+ onClick={() => imageInputRef.current?.click()}
209
+ >
210
+ <ImagePlus className="size-3.5" aria-hidden="true" />
211
+ </Button>
212
+ <input
213
+ ref={imageInputRef}
214
+ type="file"
215
+ multiple
216
+ accept="image/*"
217
+ className="sr-only"
218
+ tabIndex={-1}
219
+ onChange={handleImageChange}
220
+ />
221
+ </>
222
+ )}
223
+ </div>
224
+
225
+ {/* Right: Send */}
226
+ <Button
227
+ size="icon-sm"
228
+ type="button"
229
+ aria-label="Send message"
230
+ disabled={!value.trim() || disabled}
231
+ onClick={handleSend}
232
+ >
233
+ <Send className="size-3.5" aria-hidden="true" />
234
+ </Button>
235
+ </div>
236
+ </div>
237
+
238
+ {/* Hint text */}
239
+ {hint !== false && (
240
+ <p className="text-xs text-muted-foreground">{hint}</p>
241
+ )}
242
+ </div>
243
+ );
244
+ }
@@ -1,4 +1,6 @@
1
1
  import * as React from "react";
2
+ import { Bot } from "lucide-react";
3
+ import { Button } from "@/components/ui/button";
2
4
  import { cn } from "@/lib/utils";
3
5
 
4
6
  /**
@@ -10,6 +12,12 @@ import { cn } from "@/lib/utils";
10
12
  *
11
13
  * Used by: Loan CRM, Contact, AI Builder, and other data-heavy pages.
12
14
  * For an in-page title block with a description, use PageHeader instead.
15
+ *
16
+ * Layout:
17
+ * [Title] [page-specific actions…] [Ask Support]
18
+ *
19
+ * "Ask Support" is always pinned to the far right when onAskSupport is
20
+ * provided — it is never passed through the actions slot.
13
21
  */
14
22
 
15
23
  // ---------------------------------------------------------------------------
@@ -19,8 +27,13 @@ import { cn } from "@/lib/utils";
19
27
  export interface PageTopBarProps {
20
28
  /** Page title shown on the left */
21
29
  title: string;
22
- /** Optional action elements rendered to the right */
30
+ /** Page-specific action elements (buttons, dropdowns, etc.) */
23
31
  actions?: React.ReactNode;
32
+ /**
33
+ * When provided, renders the "Ask Support" button pinned to the far right,
34
+ * after all page-specific actions. The handler should open the SupportAgentPanel.
35
+ */
36
+ onAskSupport?: () => void;
24
37
  className?: string;
25
38
  }
26
39
 
@@ -28,21 +41,34 @@ export interface PageTopBarProps {
28
41
  // Component
29
42
  // ---------------------------------------------------------------------------
30
43
 
31
- export function PageTopBar({ title, actions, className }: PageTopBarProps) {
44
+ export function PageTopBar({
45
+ title,
46
+ actions,
47
+ onAskSupport,
48
+ className,
49
+ }: PageTopBarProps) {
32
50
  return (
33
51
  <div
34
52
  data-slot="page-top-bar"
35
53
  className={cn(
36
54
  "flex shrink-0 items-center justify-between gap-4",
37
55
  "border-b border-border bg-background px-6 py-3",
38
- className,
56
+ className
39
57
  )}
40
58
  >
41
59
  {/* Title */}
42
60
  <h1 className="text-lg font-semibold leading-tight">{title}</h1>
43
61
 
44
- {/* Optional actions */}
45
- {actions && <div className="flex items-center gap-2">{actions}</div>}
62
+ {/* Right side: page actions + Ask Support (always last) */}
63
+ <div className="flex items-center gap-2">
64
+ {actions}
65
+ {onAskSupport && (
66
+ <Button variant="outline" size="sm" onClick={onAskSupport}>
67
+ <Bot className="size-3.5" aria-hidden="true" />
68
+ Ask Support
69
+ </Button>
70
+ )}
71
+ </div>
46
72
  </div>
47
73
  );
48
74
  }
@@ -0,0 +1,41 @@
1
+ // Policy AI — domain barrel
2
+ // Exports all components, hooks, and types for the Policy AI response UI.
3
+
4
+ export {
5
+ PolicyQueryChip,
6
+ PolicyVerdictBadge,
7
+ PolicyCitationPanel,
8
+ } from "./policy-ai-primitives";
9
+
10
+ export type {
11
+ PolicyType,
12
+ PolicyQueryType,
13
+ PolicyVerdict,
14
+ PolicyQueryContext,
15
+ PolicyCitationItem,
16
+ PolicyBankVerdict,
17
+ PolicyRankedBankItem,
18
+ PolicyResponseContent,
19
+ PolicyConversationItem,
20
+ PolicyAIMessage,
21
+ PolicyQueryChipProps,
22
+ PolicyVerdictBadgeProps,
23
+ PolicyCitationPanelProps,
24
+ } from "./policy-ai-primitives";
25
+
26
+ export {
27
+ PolicySingleBankAnswer,
28
+ PolicyComparisonTable,
29
+ PolicyRankedList,
30
+ } from "./policy-ai-responses";
31
+
32
+ export type {
33
+ PolicySummaryCounts,
34
+ PolicySingleBankAnswerProps,
35
+ PolicyComparisonTableProps,
36
+ PolicyRankedListProps,
37
+ } from "./policy-ai-responses";
38
+
39
+ export { PolicyAIFAB, PolicyAIPanel } from "./policy-ai-panel";
40
+
41
+ export type { PolicyAIFABProps, PolicyAIPanelProps } from "./policy-ai-panel";