@mieweb/ui 0.9.0-dev.0 → 0.9.0-dev.1

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 (31) hide show
  1. package/dist/{chunk-WIAQQNFU.cjs → chunk-2A2FLQJC.cjs} +1138 -12
  2. package/dist/chunk-2A2FLQJC.cjs.map +1 -0
  3. package/dist/{chunk-5BVFVXHN.cjs → chunk-5VXRKOPR.cjs} +16 -525
  4. package/dist/chunk-5VXRKOPR.cjs.map +1 -0
  5. package/dist/{chunk-O6GJ4IYS.js → chunk-IF3X2WZP.js} +16 -521
  6. package/dist/chunk-IF3X2WZP.js.map +1 -0
  7. package/dist/{chunk-22CYDCXA.js → chunk-KATEJG2F.js} +1137 -13
  8. package/dist/chunk-KATEJG2F.js.map +1 -0
  9. package/dist/{chunk-FQBTGS3E.cjs → chunk-O425FKDI.cjs} +563 -55
  10. package/dist/chunk-O425FKDI.cjs.map +1 -0
  11. package/dist/{chunk-MXQ7J4BB.js → chunk-RPG2ZP2T.js} +557 -53
  12. package/dist/chunk-RPG2ZP2T.js.map +1 -0
  13. package/dist/components/SuperChat/index.cjs +36 -17
  14. package/dist/components/SuperChat/index.cjs.map +1 -1
  15. package/dist/components/SuperChat/index.d.cts +16 -3
  16. package/dist/components/SuperChat/index.d.ts +16 -3
  17. package/dist/components/SuperChat/index.js +31 -12
  18. package/dist/components/SuperChat/index.js.map +1 -1
  19. package/dist/components/SuperChat/plugins/index.cjs +3 -3
  20. package/dist/components/SuperChat/plugins/index.js +2 -2
  21. package/dist/index.cjs +820 -1930
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.js +663 -1780
  24. package/dist/index.js.map +1 -1
  25. package/package.json +1 -1
  26. package/dist/chunk-22CYDCXA.js.map +0 -1
  27. package/dist/chunk-5BVFVXHN.cjs.map +0 -1
  28. package/dist/chunk-FQBTGS3E.cjs.map +0 -1
  29. package/dist/chunk-MXQ7J4BB.js.map +0 -1
  30. package/dist/chunk-O6GJ4IYS.js.map +0 -1
  31. package/dist/chunk-WIAQQNFU.cjs.map +0 -1
@@ -1,8 +1,16 @@
1
+ import { useMentionAutocomplete, validateFile, getFileType, generateAttachmentId, AttachmentPreviewItem, MentionMenu, DragDropZone } from './chunk-IF3X2WZP.js';
2
+ import { Dropdown, DropdownItem, DropdownSeparator } from './chunk-KU3XRHWA.js';
1
3
  import { AudioPlayer } from './chunk-VVDE4CHU.js';
4
+ import { AlertTriangle, Paperclip, Plus, Mic, Square, Loader2, ArrowUp, Check, Bot, ChevronUp } from './chunk-BKSPDTX4.js';
2
5
  import { cn } from './chunk-TQONQSJO.js';
3
- import { jsx, jsxs } from 'react/jsx-runtime';
4
- import * as React2 from 'react';
6
+ import { useEscapeKey } from './chunk-T4ME7QCT.js';
7
+ import { useClickOutside } from './chunk-62ZOA6PX.js';
8
+ import { useAnchoredPosition } from './chunk-WSNMQQ4P.js';
9
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
10
+ import * as React3 from 'react';
5
11
  import { cva } from 'class-variance-authority';
12
+ import { createPortal } from 'react-dom';
13
+ import { ChevronUp as ChevronUp$1, Check as Check$1, ChevronRight, ChevronLeft } from 'lucide-react';
6
14
 
7
15
  var sizeClasses = {
8
16
  sm: "h-4 w-4",
@@ -215,9 +223,9 @@ function CollapsiblePill({
215
223
  children
216
224
  }) {
217
225
  const hasContent = Boolean(children);
218
- const [isOpen, setIsOpen] = React2.useState(defaultOpen);
219
- const contentId = React2.useId();
220
- React2.useEffect(() => {
226
+ const [isOpen, setIsOpen] = React3.useState(defaultOpen);
227
+ const contentId = React3.useId();
228
+ React3.useEffect(() => {
221
229
  setIsOpen(defaultOpen);
222
230
  }, [defaultOpen]);
223
231
  const toggle = () => {
@@ -1074,10 +1082,10 @@ function ThinkingBlock({
1074
1082
  streaming,
1075
1083
  defaultCollapsed = false
1076
1084
  }) {
1077
- const startedAt = React2.useRef(Date.now());
1078
- const [elapsed, setElapsed] = React2.useState(null);
1079
- const prevStreaming = React2.useRef(streaming);
1080
- React2.useEffect(() => {
1085
+ const startedAt = React3.useRef(Date.now());
1086
+ const [elapsed, setElapsed] = React3.useState(null);
1087
+ const prevStreaming = React3.useRef(streaming);
1088
+ React3.useEffect(() => {
1081
1089
  if (prevStreaming.current && !streaming) {
1082
1090
  setElapsed(Math.round((Date.now() - startedAt.current) / 1e3));
1083
1091
  }
@@ -1281,7 +1289,7 @@ var bubbleVariants = cva("rounded-2xl px-4 py-2.5 w-fit max-w-[85%]", {
1281
1289
  variant: "assistant"
1282
1290
  }
1283
1291
  });
1284
- var ChatBubble = React2.forwardRef(
1292
+ var ChatBubble = React3.forwardRef(
1285
1293
  ({ className, variant, hasError, accent, style, children, ...props }, ref) => {
1286
1294
  return /* @__PURE__ */ jsx(
1287
1295
  "div",
@@ -1397,7 +1405,1123 @@ function AIMessageDisplay({
1397
1405
  }
1398
1406
  );
1399
1407
  }
1408
+ var MENU_OFFSET_PX = 6;
1409
+ var MENU_MAX_HEIGHT_PX = 320;
1410
+ function optionKey(option) {
1411
+ return `${option.provider}\0${option.model}`;
1412
+ }
1413
+ function renderedOptionKey(option, index) {
1414
+ return `${option.id ?? optionKey(option)}\0${index}`;
1415
+ }
1416
+ function groupByProvider(models) {
1417
+ const groups = /* @__PURE__ */ new Map();
1418
+ for (const model of models) {
1419
+ const group = groups.get(model.provider);
1420
+ if (group) {
1421
+ group.push(model);
1422
+ } else {
1423
+ groups.set(model.provider, [model]);
1424
+ }
1425
+ }
1426
+ return Array.from(groups, ([provider, options]) => ({ provider, options }));
1427
+ }
1428
+ function getProviderLabel(models, provider) {
1429
+ return models.find((model) => model.provider === provider && model.providerLabel)?.providerLabel ?? provider;
1430
+ }
1431
+ function ComposerModelSelector({
1432
+ models,
1433
+ value,
1434
+ providerFilter,
1435
+ onProviderFilterChange,
1436
+ onChange,
1437
+ disabled = false,
1438
+ variant = "default",
1439
+ className,
1440
+ boundaryRef,
1441
+ placeholder = "Model",
1442
+ anyLabel = "Any",
1443
+ emptyLabel = "No models",
1444
+ ariaLabel = "Model",
1445
+ effortOptions,
1446
+ effort = null,
1447
+ defaultEffort,
1448
+ onEffortChange,
1449
+ effortLabel = "Effort",
1450
+ effortHint,
1451
+ defaultBadgeLabel = "Default",
1452
+ backLabel = "Back"
1453
+ }) {
1454
+ const [open, setOpen] = React3.useState(false);
1455
+ const [view, setView] = React3.useState("models");
1456
+ const [effortHighlight, setEffortHighlight] = React3.useState(0);
1457
+ const [internalProviderFilter, setInternalProviderFilter] = React3.useState(null);
1458
+ const [highlightedIndex, setHighlightedIndex] = React3.useState(0);
1459
+ const listRef = React3.useRef(null);
1460
+ const effortListRef = React3.useRef(null);
1461
+ const menuId = React3.useId();
1462
+ const {
1463
+ anchorRef: triggerRef,
1464
+ floatingRef: menuRef,
1465
+ style: menuStyle
1466
+ } = useAnchoredPosition({
1467
+ open,
1468
+ placement: "top-start",
1469
+ offset: MENU_OFFSET_PX,
1470
+ matchMinWidth: true,
1471
+ maxHeight: MENU_MAX_HEIGHT_PX,
1472
+ allowFlip: false,
1473
+ boundaryRef
1474
+ });
1475
+ const activeProviderFilter = providerFilter === void 0 ? internalProviderFilter : providerFilter;
1476
+ const selectedKey = value ? optionKey(value) : null;
1477
+ const selectedOption = models.find(
1478
+ (model) => optionKey(model) === selectedKey
1479
+ );
1480
+ const providers = React3.useMemo(
1481
+ () => Array.from(new Set(models.map((model) => model.provider))),
1482
+ [models]
1483
+ );
1484
+ const providerLabels = React3.useMemo(
1485
+ () => new Map(
1486
+ providers.map((provider) => [
1487
+ provider,
1488
+ getProviderLabel(models, provider)
1489
+ ])
1490
+ ),
1491
+ [models, providers]
1492
+ );
1493
+ const filteredModels = React3.useMemo(
1494
+ () => activeProviderFilter === null ? models : models.filter((model) => model.provider === activeProviderFilter),
1495
+ [activeProviderFilter, models]
1496
+ );
1497
+ const groupedModels = React3.useMemo(
1498
+ () => groupByProvider(filteredModels),
1499
+ [filteredModels]
1500
+ );
1501
+ const groupedRenderedModels = React3.useMemo(() => {
1502
+ let index = 0;
1503
+ return groupedModels.map((group) => ({
1504
+ provider: group.provider,
1505
+ options: group.options.map((option) => {
1506
+ const rendered = {
1507
+ option,
1508
+ index,
1509
+ key: renderedOptionKey(option, index)
1510
+ };
1511
+ index += 1;
1512
+ return rendered;
1513
+ })
1514
+ }));
1515
+ }, [groupedModels]);
1516
+ const renderedModels = React3.useMemo(
1517
+ () => groupedRenderedModels.flatMap((group) => group.options),
1518
+ [groupedRenderedModels]
1519
+ );
1520
+ const getOptionId = React3.useCallback(
1521
+ (index) => `${menuId}-option-${index}`,
1522
+ [menuId]
1523
+ );
1524
+ const getEffortOptionId = React3.useCallback(
1525
+ (index) => `${menuId}-effort-option-${index}`,
1526
+ [menuId]
1527
+ );
1528
+ const activeOptionId = open && renderedModels[highlightedIndex] ? getOptionId(highlightedIndex) : void 0;
1529
+ const selectedRenderedIndex = React3.useMemo(
1530
+ () => selectedKey ? renderedModels.findIndex(
1531
+ (renderedModel) => optionKey(renderedModel.option) === selectedKey
1532
+ ) : -1,
1533
+ [renderedModels, selectedKey]
1534
+ );
1535
+ const setProvider = React3.useCallback(
1536
+ (provider) => {
1537
+ if (providerFilter === void 0) {
1538
+ setInternalProviderFilter(provider);
1539
+ }
1540
+ onProviderFilterChange?.(provider);
1541
+ },
1542
+ [onProviderFilterChange, providerFilter]
1543
+ );
1544
+ const close = React3.useCallback(
1545
+ (restoreFocus = true) => {
1546
+ setOpen(false);
1547
+ if (restoreFocus) {
1548
+ triggerRef.current?.focus({ preventScroll: true });
1549
+ }
1550
+ },
1551
+ [triggerRef]
1552
+ );
1553
+ const selectModel = React3.useCallback(
1554
+ (model) => {
1555
+ onChange({ provider: model.provider, model: model.model });
1556
+ close();
1557
+ },
1558
+ [close, onChange]
1559
+ );
1560
+ const efforts = React3.useMemo(() => effortOptions ?? [], [effortOptions]);
1561
+ const activeEffortOptionId = open && efforts[effortHighlight] ? getEffortOptionId(effortHighlight) : void 0;
1562
+ const selectedEffortOption = efforts.find(
1563
+ (option) => option.value === effort
1564
+ );
1565
+ const selectEffort = React3.useCallback(
1566
+ (value2) => {
1567
+ onEffortChange?.(value2);
1568
+ close();
1569
+ },
1570
+ [close, onEffortChange]
1571
+ );
1572
+ const handleEffortKeyDown = (event) => {
1573
+ if (efforts.length === 0) return;
1574
+ if (event.key === "ArrowDown") {
1575
+ event.preventDefault();
1576
+ setEffortHighlight((current) => (current + 1) % efforts.length);
1577
+ }
1578
+ if (event.key === "ArrowUp") {
1579
+ event.preventDefault();
1580
+ setEffortHighlight(
1581
+ (current) => (current - 1 + efforts.length) % efforts.length
1582
+ );
1583
+ }
1584
+ if (event.key === "Home") {
1585
+ event.preventDefault();
1586
+ setEffortHighlight(0);
1587
+ }
1588
+ if (event.key === "End") {
1589
+ event.preventDefault();
1590
+ setEffortHighlight(efforts.length - 1);
1591
+ }
1592
+ if (event.key === "ArrowLeft" || event.key === "Backspace") {
1593
+ event.preventDefault();
1594
+ setView("models");
1595
+ }
1596
+ if (event.key === "Enter" || event.key === " ") {
1597
+ event.preventDefault();
1598
+ const highlighted = efforts[effortHighlight];
1599
+ if (highlighted) selectEffort(highlighted.value);
1600
+ }
1601
+ };
1602
+ const outsideRefs = React3.useMemo(
1603
+ () => [triggerRef, menuRef],
1604
+ [triggerRef, menuRef]
1605
+ );
1606
+ useClickOutside(outsideRefs, () => close(false), open);
1607
+ useEscapeKey(close, open);
1608
+ React3.useEffect(() => {
1609
+ if (!open) return;
1610
+ setHighlightedIndex(selectedRenderedIndex >= 0 ? selectedRenderedIndex : 0);
1611
+ }, [activeProviderFilter, open, selectedRenderedIndex]);
1612
+ React3.useEffect(() => {
1613
+ if (!open) return;
1614
+ setView("models");
1615
+ }, [open]);
1616
+ React3.useEffect(() => {
1617
+ if (!open) return;
1618
+ const current = efforts.findIndex((option) => option.value === effort);
1619
+ setEffortHighlight(current >= 0 ? current : 0);
1620
+ }, [effort, efforts, open]);
1621
+ React3.useEffect(() => {
1622
+ if (!open) return;
1623
+ setHighlightedIndex(
1624
+ (current) => Math.min(current, Math.max(renderedModels.length - 1, 0))
1625
+ );
1626
+ }, [open, renderedModels.length]);
1627
+ React3.useEffect(() => {
1628
+ if (!open) return;
1629
+ requestAnimationFrame(() => {
1630
+ const target = view === "effort" ? effortListRef.current : listRef.current;
1631
+ target?.focus({ preventScroll: true });
1632
+ });
1633
+ }, [open, view]);
1634
+ const handleTriggerKeyDown = (event) => {
1635
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
1636
+ event.preventDefault();
1637
+ setOpen(true);
1638
+ }
1639
+ };
1640
+ const handleMenuKeyDown = (event) => {
1641
+ if (renderedModels.length === 0) return;
1642
+ if (event.key === "ArrowDown") {
1643
+ event.preventDefault();
1644
+ setHighlightedIndex((current) => (current + 1) % renderedModels.length);
1645
+ }
1646
+ if (event.key === "ArrowUp") {
1647
+ event.preventDefault();
1648
+ setHighlightedIndex(
1649
+ (current) => (current - 1 + renderedModels.length) % renderedModels.length
1650
+ );
1651
+ }
1652
+ if (event.key === "Home") {
1653
+ event.preventDefault();
1654
+ setHighlightedIndex(0);
1655
+ }
1656
+ if (event.key === "End") {
1657
+ event.preventDefault();
1658
+ setHighlightedIndex(renderedModels.length - 1);
1659
+ }
1660
+ if (event.key === "Enter" || event.key === " ") {
1661
+ event.preventDefault();
1662
+ const highlightedModel = renderedModels[highlightedIndex]?.option;
1663
+ if (highlightedModel) {
1664
+ selectModel(highlightedModel);
1665
+ }
1666
+ }
1667
+ };
1668
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1669
+ /* @__PURE__ */ jsxs(
1670
+ "button",
1671
+ {
1672
+ ref: triggerRef,
1673
+ type: "button",
1674
+ disabled,
1675
+ "aria-haspopup": "listbox",
1676
+ "aria-expanded": open,
1677
+ "aria-controls": open ? menuId : void 0,
1678
+ onClick: () => setOpen((current) => !current),
1679
+ onKeyDown: handleTriggerKeyDown,
1680
+ "data-slot": "composer-model-selector-trigger",
1681
+ className: cn(
1682
+ variant === "ghost" ? [
1683
+ "inline-flex max-w-full min-w-0 items-center gap-1 rounded-md px-2 py-1 text-xs",
1684
+ "text-neutral-600 dark:text-neutral-400",
1685
+ "transition-colors duration-150",
1686
+ "hover:bg-neutral-100 hover:text-neutral-900 dark:hover:bg-neutral-700 dark:hover:text-white",
1687
+ "focus-visible:ring-primary-500 focus-visible:ring-2 focus-visible:outline-none",
1688
+ "disabled:pointer-events-none disabled:opacity-40"
1689
+ ] : [
1690
+ "inline-flex h-8 max-w-full items-center gap-1.5 rounded-full border px-2.5 text-sm font-medium",
1691
+ "border-border bg-background text-foreground shadow-sm",
1692
+ "hover:bg-muted/50",
1693
+ "focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none",
1694
+ "disabled:cursor-not-allowed disabled:opacity-50"
1695
+ ],
1696
+ className
1697
+ ),
1698
+ children: [
1699
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: selectedOption?.label ?? selectedOption?.model ?? placeholder }),
1700
+ selectedEffortOption && /* @__PURE__ */ jsx(
1701
+ "span",
1702
+ {
1703
+ "data-slot": "composer-model-selector-trigger-effort",
1704
+ className: "text-muted-foreground min-w-0 truncate font-normal",
1705
+ children: selectedEffortOption.label
1706
+ }
1707
+ ),
1708
+ /* @__PURE__ */ jsx(
1709
+ ChevronUp$1,
1710
+ {
1711
+ "aria-hidden": "true",
1712
+ className: cn(
1713
+ "shrink-0",
1714
+ variant === "ghost" ? "h-3 w-3" : "h-3.5 w-3.5"
1715
+ )
1716
+ }
1717
+ )
1718
+ ]
1719
+ }
1720
+ ),
1721
+ open && createPortal(
1722
+ /* @__PURE__ */ jsxs(
1723
+ "div",
1724
+ {
1725
+ ref: menuRef,
1726
+ style: menuStyle,
1727
+ "data-slot": "composer-model-selector-menu",
1728
+ className: cn(
1729
+ "border-border bg-card text-card-foreground flex w-80 max-w-full flex-col rounded-lg border shadow-lg",
1730
+ "animate-in fade-in overflow-hidden duration-100"
1731
+ ),
1732
+ children: [
1733
+ view === "models" && /* @__PURE__ */ jsxs(Fragment, { children: [
1734
+ /* @__PURE__ */ jsx(
1735
+ "div",
1736
+ {
1737
+ "data-slot": "composer-model-selector-provider-filter",
1738
+ className: "border-border flex min-h-9 items-center gap-1 overflow-x-auto overflow-y-hidden border-b p-2",
1739
+ children: [null, ...providers].map((provider) => {
1740
+ const selected = activeProviderFilter === provider;
1741
+ return /* @__PURE__ */ jsx(
1742
+ "button",
1743
+ {
1744
+ type: "button",
1745
+ "aria-pressed": selected,
1746
+ onMouseDown: (event) => event.preventDefault(),
1747
+ onClick: () => setProvider(provider),
1748
+ className: cn(
1749
+ "inline-flex h-6 shrink-0 items-center justify-center rounded-full px-2.5 text-xs font-medium whitespace-nowrap transition-colors",
1750
+ "focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none",
1751
+ selected ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted hover:text-foreground"
1752
+ ),
1753
+ children: provider === null ? anyLabel : providerLabels.get(provider) ?? provider
1754
+ },
1755
+ provider ?? "all-providers"
1756
+ );
1757
+ })
1758
+ }
1759
+ ),
1760
+ /* @__PURE__ */ jsx(
1761
+ "div",
1762
+ {
1763
+ ref: listRef,
1764
+ id: menuId,
1765
+ role: "listbox",
1766
+ "aria-label": ariaLabel,
1767
+ "aria-activedescendant": activeOptionId,
1768
+ tabIndex: -1,
1769
+ onKeyDown: handleMenuKeyDown,
1770
+ className: "min-h-0 flex-1 overflow-y-auto p-1",
1771
+ children: groupedModels.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-muted-foreground px-3 py-4 text-center text-sm", children: emptyLabel }) : groupedRenderedModels.map((group) => /* @__PURE__ */ jsxs(
1772
+ "div",
1773
+ {
1774
+ role: "group",
1775
+ "aria-label": providerLabels.get(group.provider) ?? group.provider,
1776
+ children: [
1777
+ /* @__PURE__ */ jsx("div", { className: "text-muted-foreground px-3 py-1.5 text-xs font-semibold uppercase", children: providerLabels.get(group.provider) ?? group.provider }),
1778
+ group.options.map((renderedModel) => {
1779
+ const { index, key, option: model } = renderedModel;
1780
+ const selected = optionKey(model) === selectedKey;
1781
+ const highlighted = index === highlightedIndex;
1782
+ return /* @__PURE__ */ jsxs(
1783
+ "button",
1784
+ {
1785
+ id: getOptionId(index),
1786
+ type: "button",
1787
+ role: "option",
1788
+ "aria-selected": selected,
1789
+ onMouseDown: (event) => event.preventDefault(),
1790
+ onClick: () => selectModel(model),
1791
+ onMouseEnter: () => setHighlightedIndex(index),
1792
+ className: cn(
1793
+ "flex w-full items-center gap-2 rounded-md px-3 py-2 text-start text-sm",
1794
+ "focus-visible:ring-ring transition-colors focus-visible:ring-2 focus-visible:outline-none",
1795
+ highlighted && "bg-muted",
1796
+ selected && "bg-primary/10 text-primary"
1797
+ ),
1798
+ children: [
1799
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: model.label ?? model.model }),
1800
+ selected && /* @__PURE__ */ jsx(
1801
+ Check$1,
1802
+ {
1803
+ "aria-hidden": "true",
1804
+ className: "text-primary h-4 w-4 shrink-0"
1805
+ }
1806
+ )
1807
+ ]
1808
+ },
1809
+ key
1810
+ );
1811
+ })
1812
+ ]
1813
+ },
1814
+ group.provider
1815
+ ))
1816
+ }
1817
+ ),
1818
+ efforts.length > 0 && /* @__PURE__ */ jsx("div", { className: "border-border border-t p-1", children: /* @__PURE__ */ jsxs(
1819
+ "button",
1820
+ {
1821
+ type: "button",
1822
+ "data-slot": "composer-model-selector-effort-row",
1823
+ "aria-haspopup": "listbox",
1824
+ onMouseDown: (event) => event.preventDefault(),
1825
+ onClick: () => setView("effort"),
1826
+ className: cn(
1827
+ "flex w-full items-center gap-2 rounded-md px-3 py-2 text-start text-sm",
1828
+ "hover:bg-muted transition-colors",
1829
+ "focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none"
1830
+ ),
1831
+ children: [
1832
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: effortLabel }),
1833
+ selectedEffortOption && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground min-w-0 truncate", children: selectedEffortOption.label }),
1834
+ /* @__PURE__ */ jsx(
1835
+ ChevronRight,
1836
+ {
1837
+ "aria-hidden": "true",
1838
+ className: "text-muted-foreground h-4 w-4 shrink-0 rtl:-scale-x-100"
1839
+ }
1840
+ )
1841
+ ]
1842
+ }
1843
+ ) })
1844
+ ] }),
1845
+ view === "effort" && /* @__PURE__ */ jsxs(Fragment, { children: [
1846
+ /* @__PURE__ */ jsx("div", { className: "border-border flex items-center gap-1 border-b p-2", children: /* @__PURE__ */ jsxs(
1847
+ "button",
1848
+ {
1849
+ type: "button",
1850
+ "data-slot": "composer-model-selector-effort-back",
1851
+ "aria-label": backLabel,
1852
+ onMouseDown: (event) => event.preventDefault(),
1853
+ onClick: () => setView("models"),
1854
+ className: cn(
1855
+ "inline-flex h-6 shrink-0 items-center gap-1 rounded-full px-2 text-xs font-medium",
1856
+ "text-muted-foreground hover:bg-muted hover:text-foreground transition-colors",
1857
+ "focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none"
1858
+ ),
1859
+ children: [
1860
+ /* @__PURE__ */ jsx(
1861
+ ChevronLeft,
1862
+ {
1863
+ "aria-hidden": "true",
1864
+ className: "h-3.5 w-3.5 rtl:-scale-x-100"
1865
+ }
1866
+ ),
1867
+ effortLabel
1868
+ ]
1869
+ }
1870
+ ) }),
1871
+ effortHint && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground px-3 pt-2 text-xs", children: effortHint }),
1872
+ /* @__PURE__ */ jsx(
1873
+ "div",
1874
+ {
1875
+ ref: effortListRef,
1876
+ id: menuId,
1877
+ role: "listbox",
1878
+ "aria-label": effortLabel,
1879
+ "aria-activedescendant": activeEffortOptionId,
1880
+ tabIndex: -1,
1881
+ onKeyDown: handleEffortKeyDown,
1882
+ className: "min-h-0 flex-1 overflow-y-auto p-1",
1883
+ children: efforts.map((option, index) => {
1884
+ const selected = option.value === effort;
1885
+ const highlighted = index === effortHighlight;
1886
+ return /* @__PURE__ */ jsxs(
1887
+ "button",
1888
+ {
1889
+ id: getEffortOptionId(index),
1890
+ type: "button",
1891
+ role: "option",
1892
+ "aria-selected": selected,
1893
+ onMouseDown: (event) => event.preventDefault(),
1894
+ onClick: () => selectEffort(option.value),
1895
+ onMouseEnter: () => setEffortHighlight(index),
1896
+ className: cn(
1897
+ "flex w-full items-center gap-2 rounded-md px-3 py-2 text-start text-sm",
1898
+ "focus-visible:ring-ring transition-colors focus-visible:ring-2 focus-visible:outline-none",
1899
+ highlighted && "bg-muted",
1900
+ selected && "bg-primary/10 text-primary"
1901
+ ),
1902
+ children: [
1903
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
1904
+ /* @__PURE__ */ jsx("span", { className: "block truncate", children: option.label }),
1905
+ option.description && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground block truncate text-xs", children: option.description })
1906
+ ] }),
1907
+ option.value === defaultEffort && /* @__PURE__ */ jsx("span", { className: "bg-muted text-muted-foreground shrink-0 rounded px-1.5 py-0.5 text-xs", children: defaultBadgeLabel }),
1908
+ selected && /* @__PURE__ */ jsx(
1909
+ Check$1,
1910
+ {
1911
+ "aria-hidden": "true",
1912
+ className: "text-primary h-4 w-4 shrink-0"
1913
+ }
1914
+ )
1915
+ ]
1916
+ },
1917
+ option.value
1918
+ );
1919
+ })
1920
+ }
1921
+ )
1922
+ ] })
1923
+ ]
1924
+ }
1925
+ ),
1926
+ document.body
1927
+ )
1928
+ ] });
1929
+ }
1930
+ ComposerModelSelector.displayName = "ComposerModelSelector";
1931
+ var iconButtonClasses = cn(
1932
+ "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg",
1933
+ "text-neutral-500 dark:text-neutral-400",
1934
+ "transition-colors duration-150",
1935
+ "hover:bg-neutral-100 hover:text-neutral-900 dark:hover:bg-neutral-700 dark:hover:text-white",
1936
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500",
1937
+ "disabled:pointer-events-none disabled:opacity-40"
1938
+ );
1939
+ var selectorTriggerClasses = cn(
1940
+ "flex min-w-0 items-center gap-1 rounded-md px-2 py-1 text-xs",
1941
+ "text-neutral-600 dark:text-neutral-400",
1942
+ "transition-colors duration-150",
1943
+ "hover:bg-neutral-100 hover:text-neutral-900 dark:hover:bg-neutral-700 dark:hover:text-white",
1944
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500",
1945
+ "disabled:pointer-events-none disabled:opacity-40"
1946
+ );
1947
+ var MAX_INPUT_HEIGHT = 160;
1948
+ var ChatComposer = React3.forwardRef(function ChatComposer2({
1949
+ onSend,
1950
+ value: controlledValue,
1951
+ onValueChange,
1952
+ placeholder = "Type a message\u2026",
1953
+ disabled = false,
1954
+ isSending = false,
1955
+ autoFocus = false,
1956
+ maxLength,
1957
+ showCharacterCount = false,
1958
+ canSendWhenEmpty = false,
1959
+ maxHeight = MAX_INPUT_HEIGHT,
1960
+ textareaProps,
1961
+ addMenuItems,
1962
+ allowAttachments = true,
1963
+ acceptedFileTypes,
1964
+ maxFileSize,
1965
+ maxAttachments = 10,
1966
+ mentionOptions,
1967
+ showMic = false,
1968
+ onMicClick,
1969
+ micSlot,
1970
+ micBehavior = "always",
1971
+ isStreaming = false,
1972
+ onStop,
1973
+ showAgentSelector = false,
1974
+ agents = [],
1975
+ selectedAgent = null,
1976
+ onAgentChange,
1977
+ showModelSelector = false,
1978
+ modelSelectorProps,
1979
+ readOnly = false,
1980
+ readOnlyMessage = "You have read-only access and cannot send messages.",
1981
+ onError,
1982
+ attachmentLimitLabel,
1983
+ sendFailedLabel = "Failed to send message",
1984
+ sendingLabel = "Sending message\u2026",
1985
+ inputLabel = "Message input",
1986
+ addMenuLabel = "Add to message",
1987
+ attachFilesLabel = "Attach files",
1988
+ micLabel = "Start voice input",
1989
+ sendLabel = "Send message",
1990
+ stopLabel = "Stop generating",
1991
+ agentSelectorLabel = "Select agent",
1992
+ mentionListLabel = "Mention",
1993
+ dropFilesLabel = "Drop files here",
1994
+ className
1995
+ }, ref) {
1996
+ const [internalValue, setInternalValue] = React3.useState("");
1997
+ const isControlled = controlledValue !== void 0;
1998
+ const value = isControlled ? controlledValue : internalValue;
1999
+ const [attachments, setAttachments] = React3.useState([]);
2000
+ const attachmentsRef = React3.useRef(attachments);
2001
+ attachmentsRef.current = attachments;
2002
+ const textareaRef = React3.useRef(null);
2003
+ const fileInputRef = React3.useRef(null);
2004
+ const [addMenuOpen, setAddMenuOpen] = React3.useState(false);
2005
+ const [agentMenuOpen, setAgentMenuOpen] = React3.useState(false);
2006
+ const hasText = value.trim().length > 0;
2007
+ const hasContent = hasText || attachments.length > 0;
2008
+ const isOverLimit = maxLength !== void 0 && value.length > maxLength;
2009
+ const canSend = (hasContent || canSendWhenEmpty) && onSend !== void 0 && !disabled && !isSending && !isStreaming && !isOverLimit;
2010
+ const setValue = React3.useCallback(
2011
+ (next) => {
2012
+ if (!isControlled) setInternalValue(next);
2013
+ onValueChange?.(next);
2014
+ },
2015
+ [isControlled, onValueChange]
2016
+ );
2017
+ const mention = useMentionAutocomplete({
2018
+ options: mentionOptions,
2019
+ value,
2020
+ setValue,
2021
+ textareaRef
2022
+ });
2023
+ const resizeTextarea = React3.useCallback(() => {
2024
+ const textarea = textareaRef.current;
2025
+ if (!textarea) return;
2026
+ textarea.style.height = "auto";
2027
+ if (textarea.scrollHeight === 0) return;
2028
+ textarea.style.height = `${textarea.scrollHeight}px`;
2029
+ }, []);
2030
+ React3.useLayoutEffect(resizeTextarea, [resizeTextarea, value]);
2031
+ React3.useEffect(() => {
2032
+ const textarea = textareaRef.current;
2033
+ if (!textarea || typeof ResizeObserver === "undefined") return;
2034
+ let frame = 0;
2035
+ const observer = new ResizeObserver(() => {
2036
+ cancelAnimationFrame(frame);
2037
+ frame = requestAnimationFrame(resizeTextarea);
2038
+ });
2039
+ observer.observe(textarea);
2040
+ return () => {
2041
+ cancelAnimationFrame(frame);
2042
+ observer.disconnect();
2043
+ };
2044
+ }, [resizeTextarea]);
2045
+ const addFiles = React3.useCallback(
2046
+ (files) => {
2047
+ if (!allowAttachments || files.length === 0) return;
2048
+ const limitMessage = attachmentLimitLabel ?? `Attachment limit reached (max ${maxAttachments})`;
2049
+ const room = maxAttachments - attachmentsRef.current.length;
2050
+ if (files.length > room) {
2051
+ onError?.(limitMessage, { reason: "attachment-limit" });
2052
+ if (room <= 0) return;
2053
+ }
2054
+ const staged = [];
2055
+ for (const file of files.slice(0, Math.max(room, 0))) {
2056
+ const typeCheck = validateFile(file, acceptedFileTypes, void 0);
2057
+ if (!typeCheck.valid) {
2058
+ onError?.(`${file.name}: ${typeCheck.error}`, {
2059
+ reason: "file-type",
2060
+ file
2061
+ });
2062
+ continue;
2063
+ }
2064
+ const sizeCheck = validateFile(file, void 0, maxFileSize);
2065
+ if (!sizeCheck.valid) {
2066
+ onError?.(`${file.name}: ${sizeCheck.error}`, {
2067
+ reason: "file-size",
2068
+ file
2069
+ });
2070
+ continue;
2071
+ }
2072
+ const type = getFileType(file.type);
2073
+ staged.push({
2074
+ id: generateAttachmentId(),
2075
+ file,
2076
+ type,
2077
+ previewUrl: type === "image" || type === "video" ? URL.createObjectURL(file) : void 0
2078
+ });
2079
+ }
2080
+ if (staged.length > 0) {
2081
+ attachmentsRef.current = [...attachmentsRef.current, ...staged];
2082
+ setAttachments((current) => [...current, ...staged]);
2083
+ }
2084
+ },
2085
+ [
2086
+ allowAttachments,
2087
+ maxAttachments,
2088
+ acceptedFileTypes,
2089
+ maxFileSize,
2090
+ onError,
2091
+ attachmentLimitLabel
2092
+ ]
2093
+ );
2094
+ const removeAttachment = React3.useCallback((id) => {
2095
+ const target = attachmentsRef.current.find(
2096
+ (attachment) => attachment.id === id
2097
+ );
2098
+ if (target?.previewUrl) URL.revokeObjectURL(target.previewUrl);
2099
+ attachmentsRef.current = attachmentsRef.current.filter(
2100
+ (attachment) => attachment.id !== id
2101
+ );
2102
+ setAttachments(
2103
+ (current) => current.filter((attachment) => attachment.id !== id)
2104
+ );
2105
+ }, []);
2106
+ React3.useEffect(() => {
2107
+ return () => {
2108
+ for (const attachment of attachmentsRef.current) {
2109
+ if (attachment.previewUrl) URL.revokeObjectURL(attachment.previewUrl);
2110
+ }
2111
+ };
2112
+ }, []);
2113
+ const handleFileInputChange = (event) => {
2114
+ const files = Array.from(event.target.files ?? []);
2115
+ addFiles(files);
2116
+ event.target.value = "";
2117
+ };
2118
+ const handlePaste = (event) => {
2119
+ if (!allowAttachments) return;
2120
+ const files = Array.from(event.clipboardData.files);
2121
+ if (files.length > 0) {
2122
+ event.preventDefault();
2123
+ addFiles(files);
2124
+ }
2125
+ };
2126
+ React3.useImperativeHandle(
2127
+ ref,
2128
+ () => ({
2129
+ addFiles,
2130
+ focus: () => textareaRef.current?.focus(),
2131
+ getTextarea: () => textareaRef.current
2132
+ }),
2133
+ [addFiles]
2134
+ );
2135
+ const handleSend = () => {
2136
+ if (!canSend || !onSend) return;
2137
+ const message = {
2138
+ content: value.trim(),
2139
+ attachments: attachments.map((attachment) => attachment.file)
2140
+ };
2141
+ for (const attachment of attachments) {
2142
+ if (attachment.previewUrl) URL.revokeObjectURL(attachment.previewUrl);
2143
+ }
2144
+ attachmentsRef.current = [];
2145
+ setAttachments([]);
2146
+ setValue("");
2147
+ try {
2148
+ Promise.resolve(onSend(message)).catch(() => {
2149
+ onError?.(sendFailedLabel, { reason: "send-failed" });
2150
+ });
2151
+ } catch {
2152
+ onError?.(sendFailedLabel, { reason: "send-failed" });
2153
+ }
2154
+ textareaRef.current?.focus();
2155
+ };
2156
+ const handleKeyDown = (event) => {
2157
+ if (mention.handleKeyDown(event)) return;
2158
+ if (event.key === "Enter" && !event.shiftKey) {
2159
+ event.preventDefault();
2160
+ handleSend();
2161
+ }
2162
+ };
2163
+ if (readOnly) {
2164
+ return /* @__PURE__ */ jsxs(
2165
+ "div",
2166
+ {
2167
+ "data-slot": "chat-composer",
2168
+ role: "status",
2169
+ className: cn(
2170
+ "flex items-center gap-2 rounded-xl border px-4 py-3 text-sm",
2171
+ "border-amber-300 bg-amber-50 text-amber-900",
2172
+ "dark:border-amber-700 dark:bg-amber-950 dark:text-amber-200",
2173
+ className
2174
+ ),
2175
+ children: [
2176
+ /* @__PURE__ */ jsx(AlertTriangle, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
2177
+ /* @__PURE__ */ jsx("span", { "data-slot": "chat-composer-readonly", children: readOnlyMessage })
2178
+ ]
2179
+ }
2180
+ );
2181
+ }
2182
+ const showAddMenu = allowAttachments || addMenuItems && addMenuItems.length > 0;
2183
+ const micVisible = (showMic || onMicClick !== void 0 || micSlot !== void 0) && (micBehavior === "always" || !hasContent);
2184
+ const showSelectorRow = showAgentSelector || showModelSelector;
2185
+ const selectedAgentOption = agents.find((agent) => agent.id === selectedAgent) ?? null;
2186
+ const cells = {
2187
+ input: "col-span-3 col-start-1 row-start-1 px-1 pt-1 md:col-span-1 md:col-start-2 md:self-end md:px-0 md:py-1.5",
2188
+ textarea: "px-2 pt-2 pb-1 md:py-1.5",
2189
+ add: "col-start-1 row-start-2 pb-2 ps-2 md:row-start-1 md:self-end md:p-1.5 md:pe-0.5",
2190
+ actions: "col-start-3 row-start-2 flex items-center gap-1 pb-2 pe-2 md:row-start-1 md:self-end md:gap-0.5 md:p-1.5 md:ps-0.5"
2191
+ };
2192
+ const composerCard = /* @__PURE__ */ jsxs(Fragment, { children: [
2193
+ attachments.length > 0 && /* @__PURE__ */ jsx(
2194
+ "div",
2195
+ {
2196
+ "data-slot": "chat-composer-attachments",
2197
+ className: "flex flex-wrap gap-2 px-3 pt-3",
2198
+ children: attachments.map((attachment) => /* @__PURE__ */ jsx(
2199
+ AttachmentPreviewItem,
2200
+ {
2201
+ attachment: { ...attachment, state: "pending" },
2202
+ onRemove: () => removeAttachment(attachment.id),
2203
+ disabled
2204
+ },
2205
+ attachment.id
2206
+ ))
2207
+ }
2208
+ ),
2209
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[auto_minmax(0,1fr)_auto]", children: [
2210
+ /* @__PURE__ */ jsxs("div", { className: cells.input, ref: mention.anchorRef, children: [
2211
+ /* @__PURE__ */ jsx(
2212
+ MentionMenu,
2213
+ {
2214
+ mention,
2215
+ label: mentionListLabel,
2216
+ dataSlot: "chat-composer-mention-list"
2217
+ }
2218
+ ),
2219
+ /* @__PURE__ */ jsx(
2220
+ "textarea",
2221
+ {
2222
+ ...textareaProps,
2223
+ ref: textareaRef,
2224
+ "data-slot": "chat-composer-input",
2225
+ value,
2226
+ onChange: (event) => {
2227
+ textareaProps?.onChange?.(event);
2228
+ setValue(event.target.value);
2229
+ mention.sync(
2230
+ event.target.value,
2231
+ event.target.selectionStart ?? event.target.value.length
2232
+ );
2233
+ },
2234
+ onKeyDown: (event) => {
2235
+ textareaProps?.onKeyDown?.(event);
2236
+ if (event.defaultPrevented) return;
2237
+ handleKeyDown(event);
2238
+ },
2239
+ onClick: (event) => {
2240
+ textareaProps?.onClick?.(event);
2241
+ if (event.defaultPrevented) return;
2242
+ const el = event.currentTarget;
2243
+ mention.sync(el.value, el.selectionStart ?? el.value.length);
2244
+ },
2245
+ onPaste: (event) => {
2246
+ textareaProps?.onPaste?.(event);
2247
+ if (event.defaultPrevented) return;
2248
+ handlePaste(event);
2249
+ },
2250
+ placeholder,
2251
+ disabled,
2252
+ autoFocus,
2253
+ rows: 1,
2254
+ "aria-label": inputLabel,
2255
+ ...mention.inputProps,
2256
+ style: {
2257
+ ...textareaProps?.style,
2258
+ maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight
2259
+ },
2260
+ className: cn(
2261
+ "block w-full resize-none bg-transparent",
2262
+ "rounded-lg text-sm",
2263
+ cells.textarea,
2264
+ "text-neutral-900 placeholder:text-neutral-400 dark:text-white dark:placeholder:text-neutral-500",
2265
+ // Ring the input itself on focus rather than the whole shell.
2266
+ "focus:ring-primary-500 focus:ring-1 focus:outline-none",
2267
+ "disabled:cursor-not-allowed",
2268
+ textareaProps?.className
2269
+ )
2270
+ }
2271
+ )
2272
+ ] }),
2273
+ showAddMenu && /* @__PURE__ */ jsx("div", { className: cells.add, children: /* @__PURE__ */ jsxs(
2274
+ Dropdown,
2275
+ {
2276
+ placement: "top-start",
2277
+ open: addMenuOpen,
2278
+ onOpenChange: setAddMenuOpen,
2279
+ trigger: /* @__PURE__ */ jsx(
2280
+ "button",
2281
+ {
2282
+ type: "button",
2283
+ "data-slot": "chat-composer-add-button",
2284
+ "aria-label": addMenuLabel,
2285
+ disabled,
2286
+ className: iconButtonClasses,
2287
+ children: /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4", "aria-hidden": "true" })
2288
+ }
2289
+ ),
2290
+ children: [
2291
+ allowAttachments && /* @__PURE__ */ jsx(
2292
+ DropdownItem,
2293
+ {
2294
+ icon: /* @__PURE__ */ jsx(Paperclip, { className: "h-4 w-4", "aria-hidden": "true" }),
2295
+ onClick: () => {
2296
+ setAddMenuOpen(false);
2297
+ fileInputRef.current?.click();
2298
+ },
2299
+ children: attachFilesLabel
2300
+ }
2301
+ ),
2302
+ allowAttachments && addMenuItems && addMenuItems.length > 0 && /* @__PURE__ */ jsx(DropdownSeparator, {}),
2303
+ addMenuItems?.map((item) => /* @__PURE__ */ jsx(
2304
+ DropdownItem,
2305
+ {
2306
+ icon: item.icon,
2307
+ disabled: item.disabled,
2308
+ variant: item.variant,
2309
+ checked: item.checked,
2310
+ onClick: () => {
2311
+ setAddMenuOpen(false);
2312
+ item.onSelect?.();
2313
+ },
2314
+ children: /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: item.label })
2315
+ },
2316
+ item.id
2317
+ ))
2318
+ ]
2319
+ }
2320
+ ) }),
2321
+ allowAttachments && /* @__PURE__ */ jsx(
2322
+ "input",
2323
+ {
2324
+ ref: fileInputRef,
2325
+ type: "file",
2326
+ multiple: true,
2327
+ accept: acceptedFileTypes?.join(","),
2328
+ onChange: handleFileInputChange,
2329
+ className: "hidden",
2330
+ "aria-hidden": "true",
2331
+ tabIndex: -1
2332
+ }
2333
+ ),
2334
+ /* @__PURE__ */ jsxs("div", { className: cells.actions, children: [
2335
+ showCharacterCount && maxLength !== void 0 && /* @__PURE__ */ jsxs(
2336
+ "span",
2337
+ {
2338
+ "data-slot": "chat-composer-char-count",
2339
+ "aria-live": "polite",
2340
+ className: cn(
2341
+ "pe-1 text-xs tabular-nums",
2342
+ isOverLimit ? "font-medium text-red-700 dark:text-red-400" : "text-neutral-500 dark:text-neutral-400"
2343
+ ),
2344
+ children: [
2345
+ value.length,
2346
+ "/",
2347
+ maxLength
2348
+ ]
2349
+ }
2350
+ ),
2351
+ micVisible && (micSlot != null ? (
2352
+ // Normalize slot content (e.g. a RecordButton) to the 32px
2353
+ // control row: taller content overflow-centers instead of
2354
+ // inflating the pill and pushing the other cells apart. A div
2355
+ // so block-level slot content stays valid HTML.
2356
+ /* @__PURE__ */ jsx(
2357
+ "div",
2358
+ {
2359
+ "data-slot": "chat-composer-mic-slot",
2360
+ className: "flex h-8 shrink-0 items-center",
2361
+ children: micSlot
2362
+ }
2363
+ )
2364
+ ) : /* @__PURE__ */ jsx(
2365
+ "button",
2366
+ {
2367
+ type: "button",
2368
+ "data-slot": "chat-composer-mic-button",
2369
+ "aria-label": micLabel,
2370
+ disabled,
2371
+ onClick: onMicClick,
2372
+ className: iconButtonClasses,
2373
+ children: /* @__PURE__ */ jsx(Mic, { className: "h-4 w-4", "aria-hidden": "true" })
2374
+ }
2375
+ )),
2376
+ isStreaming && onStop ? /* @__PURE__ */ jsx(
2377
+ "button",
2378
+ {
2379
+ type: "button",
2380
+ "data-slot": "chat-composer-stop-button",
2381
+ "aria-label": stopLabel,
2382
+ disabled,
2383
+ onClick: onStop,
2384
+ className: cn(
2385
+ iconButtonClasses,
2386
+ "text-neutral-700 dark:text-neutral-200"
2387
+ ),
2388
+ children: /* @__PURE__ */ jsx(
2389
+ Square,
2390
+ {
2391
+ className: "h-3.5 w-3.5 fill-current",
2392
+ "aria-hidden": "true"
2393
+ }
2394
+ )
2395
+ }
2396
+ ) : /* @__PURE__ */ jsx(
2397
+ "button",
2398
+ {
2399
+ type: "button",
2400
+ "data-slot": "chat-composer-send-button",
2401
+ "aria-label": isSending ? sendingLabel : sendLabel,
2402
+ "aria-busy": isSending || void 0,
2403
+ disabled: !canSend,
2404
+ onClick: handleSend,
2405
+ className: cn(
2406
+ iconButtonClasses,
2407
+ canSend && "bg-primary-800 hover:bg-primary-700 dark:bg-primary-600 dark:hover:bg-primary-500 text-white hover:text-white dark:hover:text-white"
2408
+ ),
2409
+ children: isSending ? /* @__PURE__ */ jsx(
2410
+ Loader2,
2411
+ {
2412
+ className: "h-4 w-4 animate-spin",
2413
+ "aria-hidden": "true"
2414
+ }
2415
+ ) : /* @__PURE__ */ jsx(ArrowUp, { className: "h-4 w-4", "aria-hidden": "true" })
2416
+ }
2417
+ )
2418
+ ] })
2419
+ ] })
2420
+ ] });
2421
+ const selectorsRow = showSelectorRow && /* @__PURE__ */ jsxs(
2422
+ "div",
2423
+ {
2424
+ "data-slot": "chat-composer-selectors",
2425
+ className: "flex items-center justify-between gap-2 px-2 pt-1",
2426
+ children: [
2427
+ showAgentSelector ? /* @__PURE__ */ jsx(
2428
+ Dropdown,
2429
+ {
2430
+ placement: "top-start",
2431
+ open: agentMenuOpen,
2432
+ onOpenChange: setAgentMenuOpen,
2433
+ trigger: /* @__PURE__ */ jsxs(
2434
+ "button",
2435
+ {
2436
+ type: "button",
2437
+ "data-slot": "chat-composer-agent-trigger",
2438
+ "aria-label": selectedAgentOption ? `${agentSelectorLabel}: ${selectedAgentOption.label}` : agentSelectorLabel,
2439
+ disabled,
2440
+ className: selectorTriggerClasses,
2441
+ children: [
2442
+ selectedAgentOption?.icon ?? /* @__PURE__ */ jsx(Bot, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
2443
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: selectedAgentOption?.label ?? agentSelectorLabel }),
2444
+ /* @__PURE__ */ jsx(ChevronUp, { className: "h-3 w-3 shrink-0", "aria-hidden": "true" })
2445
+ ]
2446
+ }
2447
+ ),
2448
+ children: agents.map((agent) => /* @__PURE__ */ jsx(
2449
+ DropdownItem,
2450
+ {
2451
+ icon: agent.icon,
2452
+ role: "menuitemradio",
2453
+ "aria-checked": agent.id === selectedAgent,
2454
+ onClick: () => {
2455
+ setAgentMenuOpen(false);
2456
+ onAgentChange?.(agent.id);
2457
+ },
2458
+ children: /* @__PURE__ */ jsxs("span", { className: "flex w-full items-center justify-between gap-2", children: [
2459
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
2460
+ /* @__PURE__ */ jsx("span", { className: "block truncate", children: agent.label }),
2461
+ agent.description && /* @__PURE__ */ jsx("span", { className: "block truncate text-xs text-neutral-500 dark:text-neutral-400", children: agent.description })
2462
+ ] }),
2463
+ agent.id === selectedAgent && /* @__PURE__ */ jsx(
2464
+ Check,
2465
+ {
2466
+ className: "text-primary-600 dark:text-primary-400 h-4 w-4 shrink-0",
2467
+ "aria-hidden": "true"
2468
+ }
2469
+ )
2470
+ ] })
2471
+ },
2472
+ agent.id
2473
+ ))
2474
+ }
2475
+ ) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true" }),
2476
+ showModelSelector && modelSelectorProps && /* @__PURE__ */ jsx(
2477
+ ComposerModelSelector,
2478
+ {
2479
+ variant: "ghost",
2480
+ ...modelSelectorProps,
2481
+ disabled: disabled || modelSelectorProps.disabled
2482
+ }
2483
+ )
2484
+ ]
2485
+ }
2486
+ );
2487
+ const card = /* @__PURE__ */ jsx(
2488
+ "div",
2489
+ {
2490
+ "data-slot": "chat-composer-card",
2491
+ className: cn(
2492
+ "rounded-2xl border border-neutral-200 bg-white shadow-sm",
2493
+ "dark:border-[#2e2e30] dark:bg-[#1c1c1e]"
2494
+ ),
2495
+ children: composerCard
2496
+ }
2497
+ );
2498
+ return /* @__PURE__ */ jsxs(
2499
+ "div",
2500
+ {
2501
+ "data-slot": "chat-composer",
2502
+ className: cn(
2503
+ "flex w-full flex-col",
2504
+ disabled && "opacity-60",
2505
+ className
2506
+ ),
2507
+ children: [
2508
+ allowAttachments ? /* @__PURE__ */ jsx(
2509
+ DragDropZone,
2510
+ {
2511
+ onFilesDropped: addFiles,
2512
+ maxFiles: Number.MAX_SAFE_INTEGER,
2513
+ disabled,
2514
+ overlayLabel: dropFilesLabel,
2515
+ children: card
2516
+ }
2517
+ ) : card,
2518
+ selectorsRow
2519
+ ]
2520
+ }
2521
+ );
2522
+ });
2523
+ ChatComposer.displayName = "ChatComposer";
1400
2524
 
1401
- export { AILogoIcon, AIMessageDisplay, AITypingIndicator, ChatBubble, ChevronIcon, CloseIcon, CollapsiblePill, MCPToolCallDisplay, MessageAvatar, RefreshIcon, ResourceLink, SendIcon, SparklesIcon, SpinnerIcon, ToolStatusIcon, getToolIcon };
1402
- //# sourceMappingURL=chunk-22CYDCXA.js.map
1403
- //# sourceMappingURL=chunk-22CYDCXA.js.map
2525
+ export { AILogoIcon, AIMessageDisplay, AITypingIndicator, ChatBubble, ChatComposer, ChevronIcon, CloseIcon, CollapsiblePill, ComposerModelSelector, MCPToolCallDisplay, MessageAvatar, RefreshIcon, ResourceLink, SendIcon, SparklesIcon, SpinnerIcon, ToolStatusIcon, getToolIcon };
2526
+ //# sourceMappingURL=chunk-KATEJG2F.js.map
2527
+ //# sourceMappingURL=chunk-KATEJG2F.js.map