@wealthx/shadcn 1.5.40 → 1.5.42

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 +103 -103
  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-B5PSUONN.mjs → chunk-TF5TOVIM.mjs} +1 -1
  6. package/dist/{chunk-STN5QIWN.mjs → chunk-THOHFAW2.mjs} +119 -46
  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 +218 -44
  17. package/dist/components/ui/support-agent/index.mjs +2 -2
  18. package/dist/index.js +3506 -3329
  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 +153 -46
  30. package/src/styles/styles-css.ts +1 -1
@@ -10,9 +10,109 @@ import {
10
10
 
11
11
  // src/components/ui/chat-input-area.tsx
12
12
  import * as React from "react";
13
- import { ImagePlus, Paperclip, Send } from "lucide-react";
13
+ import { flushSync } from "react-dom";
14
+ import {
15
+ Bold,
16
+ Code,
17
+ Code2,
18
+ ImagePlus,
19
+ Italic,
20
+ Paperclip,
21
+ Send
22
+ } from "lucide-react";
14
23
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
15
24
  var DEFAULT_HINT = "Enter to send \xB7 Shift+Enter for new line";
25
+ var TOOLBAR_ITEMS = [
26
+ {
27
+ type: "button",
28
+ icon: Bold,
29
+ label: "Bold",
30
+ title: "Bold (Ctrl+B)",
31
+ before: "**",
32
+ after: "**",
33
+ placeholder: "bold text"
34
+ },
35
+ {
36
+ type: "button",
37
+ icon: Italic,
38
+ label: "Italic",
39
+ title: "Italic (Ctrl+I)",
40
+ before: "*",
41
+ after: "*",
42
+ placeholder: "italic text"
43
+ },
44
+ {
45
+ type: "button",
46
+ icon: Code,
47
+ label: "Inline code",
48
+ title: "Inline code",
49
+ before: "`",
50
+ after: "`",
51
+ placeholder: "code"
52
+ },
53
+ { type: "divider" },
54
+ {
55
+ type: "button",
56
+ icon: Code2,
57
+ label: "Code block",
58
+ title: "Code block",
59
+ before: "```\n",
60
+ after: "\n```",
61
+ placeholder: "code block"
62
+ }
63
+ ];
64
+ function applyMarkdown(textarea, before, after, placeholder, onChange) {
65
+ const start = textarea.selectionStart;
66
+ const end = textarea.selectionEnd;
67
+ const selected = textarea.value.slice(start, end);
68
+ const insertion = selected || placeholder;
69
+ const next = textarea.value.slice(0, start) + before + insertion + after + textarea.value.slice(end);
70
+ const newStart = start + before.length;
71
+ const newEnd = newStart + insertion.length;
72
+ flushSync(() => onChange(next));
73
+ textarea.focus();
74
+ textarea.setSelectionRange(newStart, newEnd);
75
+ }
76
+ var MarkdownToolbar = React.memo(function MarkdownToolbar2({
77
+ textareaRef,
78
+ onChange,
79
+ disabled
80
+ }) {
81
+ const handleFormat = React.useCallback(
82
+ (e) => {
83
+ if (!textareaRef.current) return;
84
+ const { before, after, placeholder } = e.currentTarget.dataset;
85
+ applyMarkdown(textareaRef.current, before, after, placeholder, onChange);
86
+ },
87
+ [textareaRef, onChange]
88
+ );
89
+ return /* @__PURE__ */ jsx("div", { className: "flex items-center gap-0.5 border-b border-border px-2 py-1", children: TOOLBAR_ITEMS.map(
90
+ (item, i) => item.type === "divider" ? /* @__PURE__ */ jsx(
91
+ "span",
92
+ {
93
+ className: "mx-0.5 h-3.5 w-px bg-border",
94
+ "aria-hidden": "true"
95
+ },
96
+ i
97
+ ) : /* @__PURE__ */ jsx(
98
+ Button,
99
+ {
100
+ variant: "ghost",
101
+ size: "icon-sm",
102
+ type: "button",
103
+ title: item.title,
104
+ "aria-label": item.label,
105
+ disabled,
106
+ "data-before": item.before,
107
+ "data-after": item.after,
108
+ "data-placeholder": item.placeholder,
109
+ onClick: handleFormat,
110
+ children: /* @__PURE__ */ jsx(item.icon, { className: "size-3.5", "aria-hidden": "true" })
111
+ },
112
+ item.label
113
+ )
114
+ ) });
115
+ });
16
116
  function ChatInputArea({
17
117
  value,
18
118
  onChange,
@@ -24,6 +124,7 @@ function ChatInputArea({
24
124
  hint = DEFAULT_HINT,
25
125
  maxHeight = 160,
26
126
  autoFocus = false,
127
+ showMarkdownToolbar = false,
27
128
  className
28
129
  }) {
29
130
  const textareaRef = React.useRef(null);
@@ -92,6 +193,14 @@ function ChatInputArea({
92
193
  className: cn("flex flex-col gap-1.5", className),
93
194
  children: [
94
195
  /* @__PURE__ */ jsxs("div", { className: "border border-border bg-background flex flex-col focus-within:ring-1 focus-within:ring-ring", children: [
196
+ showMarkdownToolbar && /* @__PURE__ */ jsx(
197
+ MarkdownToolbar,
198
+ {
199
+ textareaRef,
200
+ onChange,
201
+ disabled
202
+ }
203
+ ),
95
204
  /* @__PURE__ */ jsx(
96
205
  Textarea,
97
206
  {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ChatInputArea
3
- } from "./chunk-EFHPSKVF.mjs";
3
+ } from "./chunk-EGM4DARZ.mjs";
4
4
  import {
5
5
  Sheet,
6
6
  SheetContent
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-GTAVSBDO.mjs";
4
4
  import {
5
5
  ChatInputArea
6
- } from "./chunk-EFHPSKVF.mjs";
6
+ } from "./chunk-EGM4DARZ.mjs";
7
7
  import {
8
8
  Sheet,
9
9
  SheetContent
@@ -294,7 +294,7 @@ import ReactMarkdown from "react-markdown";
294
294
  import rehypeRaw from "rehype-raw";
295
295
  import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
296
296
  import { Bot, ChevronLeft, ChevronRight, SquarePen, X as X2 } from "lucide-react";
297
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
297
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
298
298
  function SupportTypingIndicator() {
299
299
  return /* @__PURE__ */ jsx3(
300
300
  "span",
@@ -396,6 +396,49 @@ function RichContentRenderer({ richContent }) {
396
396
  }
397
397
  return null;
398
398
  }
399
+ function CloseButton({ onClick }) {
400
+ return /* @__PURE__ */ jsxs3(
401
+ Button,
402
+ {
403
+ variant: "ghost",
404
+ size: "icon",
405
+ className: "size-7 shrink-0",
406
+ onClick,
407
+ title: "Close",
408
+ children: [
409
+ /* @__PURE__ */ jsx3(X2, { className: "size-3.5" }),
410
+ /* @__PURE__ */ jsx3("span", { className: "sr-only", children: "Close" })
411
+ ]
412
+ }
413
+ );
414
+ }
415
+ function ConversationRow({
416
+ conv,
417
+ onClick,
418
+ className
419
+ }) {
420
+ return /* @__PURE__ */ jsxs3(
421
+ "button",
422
+ {
423
+ type: "button",
424
+ onClick,
425
+ className: cn(
426
+ "flex w-full items-center justify-between gap-2 text-left text-sm text-foreground hover:bg-muted/50",
427
+ className
428
+ ),
429
+ children: [
430
+ /* @__PURE__ */ jsx3("span", { className: "flex-1 truncate", children: conv.title }),
431
+ /* @__PURE__ */ jsx3(
432
+ ChevronRight,
433
+ {
434
+ className: "size-3.5 shrink-0 text-muted-foreground",
435
+ "aria-hidden": "true"
436
+ }
437
+ )
438
+ ]
439
+ }
440
+ );
441
+ }
399
442
  function SupportAgentPanel({
400
443
  open,
401
444
  onClose,
@@ -413,13 +456,20 @@ function SupportAgentPanel({
413
456
  onBack,
414
457
  onOpenConversation,
415
458
  onViewAllConversations,
459
+ hasMoreConversations = false,
460
+ onLoadMoreConversations,
461
+ isLoadingMoreConversations = false,
416
462
  className
417
463
  }) {
418
464
  var _a;
419
465
  const [inputValue, setInputValue] = React2.useState("");
466
+ const [showAllConversations, setShowAllConversations] = React2.useState(false);
420
467
  const messagesEndRef = React2.useRef(null);
421
468
  const hasMessages = messages.length > 0;
422
469
  const isChatMode = hasMessages && !!conversationTitle;
470
+ React2.useEffect(() => {
471
+ if (!open) setShowAllConversations(false);
472
+ }, [open]);
423
473
  React2.useEffect(() => {
424
474
  if (!messagesEndRef.current) return;
425
475
  messagesEndRef.current.scrollIntoView({
@@ -441,6 +491,17 @@ function SupportAgentPanel({
441
491
  [onSendMessage]
442
492
  );
443
493
  const hasRecents = !!(recentConversations == null ? void 0 : recentConversations.length);
494
+ const recentsPreview = React2.useMemo(
495
+ () => {
496
+ var _a2;
497
+ return (_a2 = recentConversations == null ? void 0 : recentConversations.slice(0, 3)) != null ? _a2 : [];
498
+ },
499
+ [recentConversations]
500
+ );
501
+ const handleViewAll = React2.useCallback(() => {
502
+ setShowAllConversations(true);
503
+ onViewAllConversations == null ? void 0 : onViewAllConversations();
504
+ }, [onViewAllConversations]);
444
505
  return /* @__PURE__ */ jsx3(Sheet, { open, onOpenChange: (o) => !o && onClose(), children: /* @__PURE__ */ jsxs3(
445
506
  SheetContent,
446
507
  {
@@ -453,38 +514,45 @@ function SupportAgentPanel({
453
514
  "data-slot": "support-agent-panel",
454
515
  children: [
455
516
  /* @__PURE__ */ jsxs3("div", { className: "shrink-0 border-b border-border px-3 py-2.5", children: [
456
- isChatMode ? (
457
- /* Chat mode: [←] Conversation title [✕] */
517
+ showAllConversations ? (
518
+ /* All-conversations mode: [←] All conversations [✕] */
458
519
  /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1", children: [
459
- onBack && /* @__PURE__ */ jsxs3(
520
+ /* @__PURE__ */ jsxs3(
460
521
  Button,
461
522
  {
462
523
  variant: "ghost",
463
524
  size: "icon",
464
525
  className: "size-7 shrink-0",
465
- onClick: onBack,
466
- title: "Back to conversations",
526
+ onClick: () => setShowAllConversations(false),
527
+ title: "Back",
467
528
  children: [
468
529
  /* @__PURE__ */ jsx3(ChevronLeft, { className: "size-3.5" }),
469
530
  /* @__PURE__ */ jsx3("span", { className: "sr-only", children: "Back" })
470
531
  ]
471
532
  }
472
533
  ),
473
- /* @__PURE__ */ jsx3("span", { className: "flex-1 truncate px-1 text-sm font-medium text-foreground", children: conversationTitle }),
474
- /* @__PURE__ */ jsxs3(
534
+ /* @__PURE__ */ jsx3("span", { className: "flex-1 truncate px-1 text-sm font-medium text-foreground", children: "All conversations" }),
535
+ /* @__PURE__ */ jsx3(CloseButton, { onClick: onClose })
536
+ ] })
537
+ ) : isChatMode ? (
538
+ /* Chat mode: [←] Conversation title [✕] */
539
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1", children: [
540
+ onBack && /* @__PURE__ */ jsxs3(
475
541
  Button,
476
542
  {
477
543
  variant: "ghost",
478
544
  size: "icon",
479
545
  className: "size-7 shrink-0",
480
- onClick: onClose,
481
- title: "Close",
546
+ onClick: onBack,
547
+ title: "Back to conversations",
482
548
  children: [
483
- /* @__PURE__ */ jsx3(X2, { className: "size-3.5" }),
484
- /* @__PURE__ */ jsx3("span", { className: "sr-only", children: "Close" })
549
+ /* @__PURE__ */ jsx3(ChevronLeft, { className: "size-3.5" }),
550
+ /* @__PURE__ */ jsx3("span", { className: "sr-only", children: "Back" })
485
551
  ]
486
552
  }
487
- )
553
+ ),
554
+ /* @__PURE__ */ jsx3("span", { className: "flex-1 truncate px-1 text-sm font-medium text-foreground", children: conversationTitle }),
555
+ /* @__PURE__ */ jsx3(CloseButton, { onClick: onClose })
488
556
  ] })
489
557
  ) : (
490
558
  /* Home mode: [Bot icon] Support Assistant [✕] */
@@ -499,23 +567,10 @@ function SupportAgentPanel({
499
567
  ) }),
500
568
  /* @__PURE__ */ jsx3("span", { className: "text-sm font-semibold text-foreground", children: "Support Assistant" })
501
569
  ] }),
502
- /* @__PURE__ */ jsxs3(
503
- Button,
504
- {
505
- variant: "ghost",
506
- size: "icon",
507
- className: "size-7",
508
- onClick: onClose,
509
- title: "Close",
510
- children: [
511
- /* @__PURE__ */ jsx3(X2, { className: "size-3.5" }),
512
- /* @__PURE__ */ jsx3("span", { className: "sr-only", children: "Close" })
513
- ]
514
- }
515
- )
570
+ /* @__PURE__ */ jsx3(CloseButton, { onClick: onClose })
516
571
  ] })
517
572
  ),
518
- context && /* @__PURE__ */ jsx3("div", { className: "mt-2", children: /* @__PURE__ */ jsx3(SupportContextChip, { context }) })
573
+ context && !showAllConversations && /* @__PURE__ */ jsx3("div", { className: "mt-2", children: /* @__PURE__ */ jsx3(SupportContextChip, { context }) })
519
574
  ] }),
520
575
  /* @__PURE__ */ jsx3("div", { className: "flex flex-1 flex-col overflow-y-auto", children: isLoading ? (
521
576
  /* Loading state */
@@ -523,6 +578,33 @@ function SupportAgentPanel({
523
578
  /* @__PURE__ */ jsx3(Spinner, { size: "lg", className: "text-muted-foreground" }),
524
579
  /* @__PURE__ */ jsx3("p", { className: "text-sm text-muted-foreground", children: "Loading\u2026" })
525
580
  ] }) })
581
+ ) : showAllConversations ? (
582
+ /* All conversations — full scrollable list (Jira Rovo pattern) */
583
+ /* @__PURE__ */ jsx3("div", { className: "flex flex-col", children: (recentConversations == null ? void 0 : recentConversations.length) ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
584
+ recentConversations.map((conv) => /* @__PURE__ */ jsx3(
585
+ ConversationRow,
586
+ {
587
+ conv,
588
+ onClick: () => {
589
+ setShowAllConversations(false);
590
+ onOpenConversation == null ? void 0 : onOpenConversation(conv.id);
591
+ },
592
+ className: "border-b border-border px-4 py-3"
593
+ },
594
+ conv.id
595
+ )),
596
+ hasMoreConversations && /* @__PURE__ */ jsx3("div", { className: "border-t border-border p-3", children: /* @__PURE__ */ jsx3(
597
+ Button,
598
+ {
599
+ variant: "outline",
600
+ size: "sm",
601
+ className: "w-full",
602
+ disabled: isLoadingMoreConversations,
603
+ onClick: onLoadMoreConversations,
604
+ children: isLoadingMoreConversations ? "Loading..." : "Load more"
605
+ }
606
+ ) })
607
+ ] }) : /* @__PURE__ */ jsx3("p", { className: "p-4 text-sm text-muted-foreground", children: "No conversations yet." }) })
526
608
  ) : !hasMessages ? (
527
609
  /* Home state — Rovo pattern: New Chat CTA + Recents + Suggested */
528
610
  /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-5 p-4", children: [
@@ -541,31 +623,21 @@ function SupportAgentPanel({
541
623
  ),
542
624
  hasRecents && /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-1", children: [
543
625
  /* @__PURE__ */ jsx3("p", { className: "px-1 text-xs font-medium uppercase tracking-wide text-muted-foreground", children: "Recents" }),
544
- /* @__PURE__ */ jsx3("div", { className: "flex flex-col", children: recentConversations.map((conv) => /* @__PURE__ */ jsxs3(
545
- "button",
626
+ /* @__PURE__ */ jsx3("div", { className: "flex flex-col", children: recentsPreview.map((conv) => /* @__PURE__ */ jsx3(
627
+ ConversationRow,
546
628
  {
547
- type: "button",
629
+ conv,
548
630
  onClick: () => onOpenConversation == null ? void 0 : onOpenConversation(conv.id),
549
- 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",
550
- children: [
551
- /* @__PURE__ */ jsx3("span", { className: "flex-1 truncate", children: conv.title }),
552
- /* @__PURE__ */ jsx3(
553
- ChevronRight,
554
- {
555
- className: "size-3.5 shrink-0 text-muted-foreground",
556
- "aria-hidden": "true"
557
- }
558
- )
559
- ]
631
+ className: "px-1 py-2.5"
560
632
  },
561
633
  conv.id
562
634
  )) }),
563
- onViewAllConversations && /* @__PURE__ */ jsxs3(
635
+ /* @__PURE__ */ jsxs3(
564
636
  Button,
565
637
  {
566
638
  variant: "ghost",
567
639
  size: "sm",
568
- onClick: onViewAllConversations,
640
+ onClick: handleViewAll,
569
641
  className: "h-auto w-fit gap-0.5 px-1 py-1 text-xs text-muted-foreground hover:bg-transparent hover:text-foreground",
570
642
  children: [
571
643
  "View all conversations",
@@ -604,7 +676,8 @@ function SupportAgentPanel({
604
676
  onAttachFile,
605
677
  onAttachImage,
606
678
  disabled: isLoading || isStreaming,
607
- placeholder: "Ask anything\u2026"
679
+ placeholder: "Ask anything\u2026",
680
+ showMarkdownToolbar: true
608
681
  }
609
682
  ) })
610
683
  ]
@@ -33,7 +33,7 @@ import {
33
33
  } from "./chunk-2GIYVERS.mjs";
34
34
  import {
35
35
  ChatInputArea
36
- } from "./chunk-EFHPSKVF.mjs";
36
+ } from "./chunk-EGM4DARZ.mjs";
37
37
  import {
38
38
  Textarea
39
39
  } from "./chunk-BS75ICOO.mjs";
@@ -369,6 +369,7 @@ function Badge(_a) {
369
369
 
370
370
  // src/components/ui/chat-input-area.tsx
371
371
  var React3 = __toESM(require("react"));
372
+ var import_react_dom = require("react-dom");
372
373
  var import_lucide_react3 = require("lucide-react");
373
374
 
374
375
  // src/components/ui/textarea.tsx
@@ -391,6 +392,97 @@ function Textarea(_a) {
391
392
  // src/components/ui/chat-input-area.tsx
392
393
  var import_jsx_runtime6 = require("react/jsx-runtime");
393
394
  var DEFAULT_HINT = "Enter to send \xB7 Shift+Enter for new line";
395
+ var TOOLBAR_ITEMS = [
396
+ {
397
+ type: "button",
398
+ icon: import_lucide_react3.Bold,
399
+ label: "Bold",
400
+ title: "Bold (Ctrl+B)",
401
+ before: "**",
402
+ after: "**",
403
+ placeholder: "bold text"
404
+ },
405
+ {
406
+ type: "button",
407
+ icon: import_lucide_react3.Italic,
408
+ label: "Italic",
409
+ title: "Italic (Ctrl+I)",
410
+ before: "*",
411
+ after: "*",
412
+ placeholder: "italic text"
413
+ },
414
+ {
415
+ type: "button",
416
+ icon: import_lucide_react3.Code,
417
+ label: "Inline code",
418
+ title: "Inline code",
419
+ before: "`",
420
+ after: "`",
421
+ placeholder: "code"
422
+ },
423
+ { type: "divider" },
424
+ {
425
+ type: "button",
426
+ icon: import_lucide_react3.Code2,
427
+ label: "Code block",
428
+ title: "Code block",
429
+ before: "```\n",
430
+ after: "\n```",
431
+ placeholder: "code block"
432
+ }
433
+ ];
434
+ function applyMarkdown(textarea, before, after, placeholder, onChange) {
435
+ const start = textarea.selectionStart;
436
+ const end = textarea.selectionEnd;
437
+ const selected = textarea.value.slice(start, end);
438
+ const insertion = selected || placeholder;
439
+ const next = textarea.value.slice(0, start) + before + insertion + after + textarea.value.slice(end);
440
+ const newStart = start + before.length;
441
+ const newEnd = newStart + insertion.length;
442
+ (0, import_react_dom.flushSync)(() => onChange(next));
443
+ textarea.focus();
444
+ textarea.setSelectionRange(newStart, newEnd);
445
+ }
446
+ var MarkdownToolbar = React3.memo(function MarkdownToolbar2({
447
+ textareaRef,
448
+ onChange,
449
+ disabled
450
+ }) {
451
+ const handleFormat = React3.useCallback(
452
+ (e) => {
453
+ if (!textareaRef.current) return;
454
+ const { before, after, placeholder } = e.currentTarget.dataset;
455
+ applyMarkdown(textareaRef.current, before, after, placeholder, onChange);
456
+ },
457
+ [textareaRef, onChange]
458
+ );
459
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex items-center gap-0.5 border-b border-border px-2 py-1", children: TOOLBAR_ITEMS.map(
460
+ (item, i) => item.type === "divider" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
461
+ "span",
462
+ {
463
+ className: "mx-0.5 h-3.5 w-px bg-border",
464
+ "aria-hidden": "true"
465
+ },
466
+ i
467
+ ) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
468
+ Button,
469
+ {
470
+ variant: "ghost",
471
+ size: "icon-sm",
472
+ type: "button",
473
+ title: item.title,
474
+ "aria-label": item.label,
475
+ disabled,
476
+ "data-before": item.before,
477
+ "data-after": item.after,
478
+ "data-placeholder": item.placeholder,
479
+ onClick: handleFormat,
480
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(item.icon, { className: "size-3.5", "aria-hidden": "true" })
481
+ },
482
+ item.label
483
+ )
484
+ ) });
485
+ });
394
486
  function ChatInputArea({
395
487
  value,
396
488
  onChange,
@@ -402,6 +494,7 @@ function ChatInputArea({
402
494
  hint = DEFAULT_HINT,
403
495
  maxHeight = 160,
404
496
  autoFocus = false,
497
+ showMarkdownToolbar = false,
405
498
  className
406
499
  }) {
407
500
  const textareaRef = React3.useRef(null);
@@ -470,6 +563,14 @@ function ChatInputArea({
470
563
  className: cn("flex flex-col gap-1.5", className),
471
564
  children: [
472
565
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "border border-border bg-background flex flex-col focus-within:ring-1 focus-within:ring-ring", children: [
566
+ showMarkdownToolbar && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
567
+ MarkdownToolbar,
568
+ {
569
+ textareaRef,
570
+ onChange,
571
+ disabled
572
+ }
573
+ ),
473
574
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
474
575
  Textarea,
475
576
  {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  AiAssistantDrawer
3
- } from "../../chunk-B5PSUONN.mjs";
4
- import "../../chunk-EFHPSKVF.mjs";
3
+ } from "../../chunk-TF5TOVIM.mjs";
4
+ import "../../chunk-EGM4DARZ.mjs";
5
5
  import "../../chunk-H3PTREG6.mjs";
6
6
  import "../../chunk-BS75ICOO.mjs";
7
7
  import "../../chunk-JVMXMFBB.mjs";
@@ -1196,6 +1196,7 @@ function Separator(_a2) {
1196
1196
 
1197
1197
  // src/components/ui/chat-input-area.tsx
1198
1198
  var React5 = __toESM(require("react"));
1199
+ var import_react_dom = require("react-dom");
1199
1200
  var import_lucide_react5 = require("lucide-react");
1200
1201
 
1201
1202
  // src/components/ui/textarea.tsx
@@ -1218,6 +1219,97 @@ function Textarea(_a2) {
1218
1219
  // src/components/ui/chat-input-area.tsx
1219
1220
  var import_jsx_runtime17 = require("react/jsx-runtime");
1220
1221
  var DEFAULT_HINT = "Enter to send \xB7 Shift+Enter for new line";
1222
+ var TOOLBAR_ITEMS = [
1223
+ {
1224
+ type: "button",
1225
+ icon: import_lucide_react5.Bold,
1226
+ label: "Bold",
1227
+ title: "Bold (Ctrl+B)",
1228
+ before: "**",
1229
+ after: "**",
1230
+ placeholder: "bold text"
1231
+ },
1232
+ {
1233
+ type: "button",
1234
+ icon: import_lucide_react5.Italic,
1235
+ label: "Italic",
1236
+ title: "Italic (Ctrl+I)",
1237
+ before: "*",
1238
+ after: "*",
1239
+ placeholder: "italic text"
1240
+ },
1241
+ {
1242
+ type: "button",
1243
+ icon: import_lucide_react5.Code,
1244
+ label: "Inline code",
1245
+ title: "Inline code",
1246
+ before: "`",
1247
+ after: "`",
1248
+ placeholder: "code"
1249
+ },
1250
+ { type: "divider" },
1251
+ {
1252
+ type: "button",
1253
+ icon: import_lucide_react5.Code2,
1254
+ label: "Code block",
1255
+ title: "Code block",
1256
+ before: "```\n",
1257
+ after: "\n```",
1258
+ placeholder: "code block"
1259
+ }
1260
+ ];
1261
+ function applyMarkdown(textarea, before, after, placeholder, onChange) {
1262
+ const start = textarea.selectionStart;
1263
+ const end = textarea.selectionEnd;
1264
+ const selected = textarea.value.slice(start, end);
1265
+ const insertion = selected || placeholder;
1266
+ const next = textarea.value.slice(0, start) + before + insertion + after + textarea.value.slice(end);
1267
+ const newStart = start + before.length;
1268
+ const newEnd = newStart + insertion.length;
1269
+ (0, import_react_dom.flushSync)(() => onChange(next));
1270
+ textarea.focus();
1271
+ textarea.setSelectionRange(newStart, newEnd);
1272
+ }
1273
+ var MarkdownToolbar = React5.memo(function MarkdownToolbar2({
1274
+ textareaRef,
1275
+ onChange,
1276
+ disabled
1277
+ }) {
1278
+ const handleFormat = React5.useCallback(
1279
+ (e) => {
1280
+ if (!textareaRef.current) return;
1281
+ const { before, after, placeholder } = e.currentTarget.dataset;
1282
+ applyMarkdown(textareaRef.current, before, after, placeholder, onChange);
1283
+ },
1284
+ [textareaRef, onChange]
1285
+ );
1286
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex items-center gap-0.5 border-b border-border px-2 py-1", children: TOOLBAR_ITEMS.map(
1287
+ (item, i) => item.type === "divider" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1288
+ "span",
1289
+ {
1290
+ className: "mx-0.5 h-3.5 w-px bg-border",
1291
+ "aria-hidden": "true"
1292
+ },
1293
+ i
1294
+ ) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1295
+ Button,
1296
+ {
1297
+ variant: "ghost",
1298
+ size: "icon-sm",
1299
+ type: "button",
1300
+ title: item.title,
1301
+ "aria-label": item.label,
1302
+ disabled,
1303
+ "data-before": item.before,
1304
+ "data-after": item.after,
1305
+ "data-placeholder": item.placeholder,
1306
+ onClick: handleFormat,
1307
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(item.icon, { className: "size-3.5", "aria-hidden": "true" })
1308
+ },
1309
+ item.label
1310
+ )
1311
+ ) });
1312
+ });
1221
1313
  function ChatInputArea({
1222
1314
  value,
1223
1315
  onChange,
@@ -1229,6 +1321,7 @@ function ChatInputArea({
1229
1321
  hint = DEFAULT_HINT,
1230
1322
  maxHeight = 160,
1231
1323
  autoFocus = false,
1324
+ showMarkdownToolbar = false,
1232
1325
  className
1233
1326
  }) {
1234
1327
  const textareaRef = React5.useRef(null);
@@ -1297,6 +1390,14 @@ function ChatInputArea({
1297
1390
  className: cn("flex flex-col gap-1.5", className),
1298
1391
  children: [
1299
1392
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "border border-border bg-background flex flex-col focus-within:ring-1 focus-within:ring-ring", children: [
1393
+ showMarkdownToolbar && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1394
+ MarkdownToolbar,
1395
+ {
1396
+ textareaRef,
1397
+ onChange,
1398
+ disabled
1399
+ }
1400
+ ),
1300
1401
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1301
1402
  Textarea,
1302
1403
  {