@lukeashford/aurelius 3.8.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -92,6 +92,147 @@ Input.displayName = "Input";
92
92
  import React4, { createContext, useContext } from "react";
93
93
  import { Check } from "lucide-react";
94
94
 
95
+ // src/utils/clipboard.ts
96
+ async function copyToClipboard(text) {
97
+ if (typeof navigator !== "undefined" && navigator.clipboard) {
98
+ try {
99
+ await navigator.clipboard.writeText(text);
100
+ return true;
101
+ } catch {
102
+ }
103
+ }
104
+ if (typeof document === "undefined") {
105
+ return false;
106
+ }
107
+ const textArea = document.createElement("textarea");
108
+ textArea.value = text;
109
+ textArea.setAttribute("readonly", "");
110
+ textArea.setAttribute("aria-hidden", "true");
111
+ textArea.style.position = "fixed";
112
+ textArea.style.opacity = "0";
113
+ textArea.style.pointerEvents = "none";
114
+ document.body.appendChild(textArea);
115
+ try {
116
+ textArea.select();
117
+ return document.execCommand("copy");
118
+ } catch {
119
+ return false;
120
+ } finally {
121
+ document.body.removeChild(textArea);
122
+ }
123
+ }
124
+
125
+ // src/utils/hooks.ts
126
+ import { useCallback, useEffect, useRef, useState } from "react";
127
+ function composeRefs(...refs) {
128
+ return (node) => {
129
+ for (const ref of refs) {
130
+ if (!ref) {
131
+ continue;
132
+ }
133
+ if (typeof ref === "function") {
134
+ ref(node);
135
+ } else {
136
+ ;
137
+ ref.current = node;
138
+ }
139
+ }
140
+ };
141
+ }
142
+ var scrollLockCount = 0;
143
+ var scrollLockOriginalOverflow = null;
144
+ var scrollLockOriginalPaddingRight = null;
145
+ function useScrollLock(isLocked) {
146
+ useEffect(() => {
147
+ if (!isLocked || typeof document === "undefined") {
148
+ return;
149
+ }
150
+ if (scrollLockCount === 0) {
151
+ scrollLockOriginalOverflow = document.body.style.overflow;
152
+ scrollLockOriginalPaddingRight = document.body.style.paddingRight;
153
+ const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
154
+ document.body.style.overflow = "hidden";
155
+ if (scrollbarWidth > 0) {
156
+ document.body.style.paddingRight = `${scrollbarWidth}px`;
157
+ }
158
+ }
159
+ scrollLockCount++;
160
+ return () => {
161
+ scrollLockCount--;
162
+ if (scrollLockCount === 0) {
163
+ document.body.style.overflow = scrollLockOriginalOverflow ?? "";
164
+ document.body.style.paddingRight = scrollLockOriginalPaddingRight ?? "";
165
+ scrollLockOriginalOverflow = null;
166
+ scrollLockOriginalPaddingRight = null;
167
+ }
168
+ };
169
+ }, [isLocked]);
170
+ }
171
+ function useEscapeKey(onEscape, isActive = true) {
172
+ useEffect(() => {
173
+ if (!isActive || typeof window === "undefined") {
174
+ return;
175
+ }
176
+ const handler = (e) => {
177
+ if (e.key === "Escape") {
178
+ onEscape();
179
+ }
180
+ };
181
+ window.addEventListener("keydown", handler);
182
+ return () => window.removeEventListener("keydown", handler);
183
+ }, [onEscape, isActive]);
184
+ }
185
+ function useClickOutside(ref, handler, isActive = true) {
186
+ useEffect(() => {
187
+ if (!isActive || typeof document === "undefined") {
188
+ return;
189
+ }
190
+ const listener = (event) => {
191
+ const node = ref.current;
192
+ if (node && !node.contains(event.target)) {
193
+ handler(event);
194
+ }
195
+ };
196
+ document.addEventListener("mousedown", listener);
197
+ return () => document.removeEventListener("mousedown", listener);
198
+ }, [ref, handler, isActive]);
199
+ }
200
+ function useCopyToClipboard(resetMs = 2e3) {
201
+ const [copied, setCopied] = useState(false);
202
+ const timerRef = useRef(null);
203
+ const mountedRef = useRef(true);
204
+ useEffect(() => {
205
+ mountedRef.current = true;
206
+ return () => {
207
+ mountedRef.current = false;
208
+ if (timerRef.current !== null) {
209
+ clearTimeout(timerRef.current);
210
+ timerRef.current = null;
211
+ }
212
+ };
213
+ }, []);
214
+ const copy = useCallback(async (text) => {
215
+ const ok = await copyToClipboard(text);
216
+ if (!mountedRef.current) {
217
+ return ok;
218
+ }
219
+ if (ok) {
220
+ setCopied(true);
221
+ if (timerRef.current !== null) {
222
+ clearTimeout(timerRef.current);
223
+ }
224
+ timerRef.current = setTimeout(() => {
225
+ timerRef.current = null;
226
+ if (mountedRef.current) {
227
+ setCopied(false);
228
+ }
229
+ }, resetMs);
230
+ }
231
+ return ok;
232
+ }, [resetMs]);
233
+ return { copied, copy };
234
+ }
235
+
95
236
  // src/components/Skeleton.tsx
96
237
  import React3 from "react";
97
238
  var Skeleton = React3.forwardRef(
@@ -116,8 +257,7 @@ function slotLoading(loading, path) {
116
257
  return false;
117
258
  }
118
259
  if (Array.isArray(path)) {
119
- const [section, field] = path;
120
- return !!loading[section]?.[field];
260
+ return !!loading.header?.[path[1]];
121
261
  }
122
262
  return !!loading[path];
123
263
  }
@@ -1393,29 +1533,28 @@ var Select = React20.forwardRef(
1393
1533
  Select.displayName = "Select";
1394
1534
 
1395
1535
  // src/components/Checkbox.tsx
1396
- import React21, { useCallback } from "react";
1536
+ import React21, { useCallback as useCallback2, useId } from "react";
1397
1537
  var checkmarkSvg = `url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='%231A1A1A' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e")`;
1398
1538
  var Checkbox = React21.forwardRef(
1399
- ({ className, label, id, ...rest }, ref) => {
1400
- const inputId = id || rest.name || Math.random().toString(36).substr(2, 9);
1401
- const setRef = useCallback((node) => {
1402
- if (node) {
1403
- if (node.checked) {
1404
- node.style.backgroundImage = checkmarkSvg;
1405
- }
1406
- }
1407
- if (typeof ref === "function") {
1408
- ref(node);
1409
- } else if (ref) {
1410
- ref.current = node;
1539
+ ({ className, label, id, onChange, ...rest }, ref) => {
1540
+ const generatedId = useId();
1541
+ const inputId = id || rest.name || generatedId;
1542
+ const initBackground = useCallback2((node) => {
1543
+ if (node && node.checked) {
1544
+ node.style.backgroundImage = checkmarkSvg;
1411
1545
  }
1412
- }, [ref]);
1546
+ }, []);
1547
+ const handleChange = useCallback2((e) => {
1548
+ e.currentTarget.style.backgroundImage = e.currentTarget.checked ? checkmarkSvg : "none";
1549
+ onChange?.(e);
1550
+ }, [onChange]);
1413
1551
  return /* @__PURE__ */ React21.createElement("div", { className: "flex items-center" }, /* @__PURE__ */ React21.createElement(
1414
1552
  "input",
1415
1553
  {
1554
+ ...rest,
1416
1555
  type: "checkbox",
1417
1556
  id: inputId,
1418
- ref: setRef,
1557
+ ref: composeRefs(initBackground, ref),
1419
1558
  className: cx(
1420
1559
  "appearance-none h-4 w-4 border border-ash bg-graphite",
1421
1560
  "checked:bg-gold checked:border-gold",
@@ -1429,16 +1568,7 @@ var Checkbox = React21.forwardRef(
1429
1568
  backgroundSize: "contain",
1430
1569
  backgroundRepeat: "no-repeat"
1431
1570
  },
1432
- onChange: (e) => {
1433
- const input = e.currentTarget;
1434
- if (input.checked) {
1435
- input.style.backgroundImage = checkmarkSvg;
1436
- } else {
1437
- input.style.backgroundImage = "none";
1438
- }
1439
- rest.onChange?.(e);
1440
- },
1441
- ...rest
1571
+ onChange: handleChange
1442
1572
  }
1443
1573
  ), label && /* @__PURE__ */ React21.createElement(
1444
1574
  "label",
@@ -1453,29 +1583,44 @@ var Checkbox = React21.forwardRef(
1453
1583
  Checkbox.displayName = "Checkbox";
1454
1584
 
1455
1585
  // src/components/Radio.tsx
1456
- import React22, { useCallback as useCallback2 } from "react";
1586
+ import React22, { useCallback as useCallback3, useId as useId2 } from "react";
1457
1587
  var radioDotSvg = `url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='%231A1A1A' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e")`;
1458
1588
  var Radio = React22.forwardRef(
1459
- ({ className, label, id, ...rest }, ref) => {
1460
- const inputId = id || rest.name || Math.random().toString(36).substr(2, 9);
1461
- const setRef = useCallback2((node) => {
1462
- if (node) {
1463
- if (node.checked) {
1464
- node.style.backgroundImage = radioDotSvg;
1465
- }
1589
+ ({ className, label, id, onChange, ...rest }, ref) => {
1590
+ const generatedId = useId2();
1591
+ const inputId = id || rest.name || generatedId;
1592
+ const initBackground = useCallback3((node) => {
1593
+ if (node && node.checked) {
1594
+ node.style.backgroundImage = radioDotSvg;
1466
1595
  }
1467
- if (typeof ref === "function") {
1468
- ref(node);
1469
- } else if (ref) {
1470
- ref.current = node;
1596
+ }, []);
1597
+ const handleChange = useCallback3((e) => {
1598
+ const input = e.currentTarget;
1599
+ if (input.checked) {
1600
+ input.style.backgroundImage = radioDotSvg;
1601
+ if (input.name) {
1602
+ const escaped = typeof CSS !== "undefined" && typeof CSS.escape === "function" ? CSS.escape(input.name) : input.name.replace(/["\\\]]/g, "\\$&");
1603
+ const radios = input.ownerDocument.querySelectorAll(
1604
+ `input[type="radio"][name="${escaped}"]`
1605
+ );
1606
+ radios.forEach((radio) => {
1607
+ if (radio !== input) {
1608
+ radio.style.backgroundImage = "none";
1609
+ }
1610
+ });
1611
+ }
1612
+ } else {
1613
+ input.style.backgroundImage = "none";
1471
1614
  }
1472
- }, [ref]);
1615
+ onChange?.(e);
1616
+ }, [onChange]);
1473
1617
  return /* @__PURE__ */ React22.createElement("div", { className: "flex items-center" }, /* @__PURE__ */ React22.createElement(
1474
1618
  "input",
1475
1619
  {
1620
+ ...rest,
1476
1621
  type: "radio",
1477
1622
  id: inputId,
1478
- ref: setRef,
1623
+ ref: composeRefs(initBackground, ref),
1479
1624
  className: cx(
1480
1625
  "appearance-none h-4 w-4 border border-ash rounded-full bg-graphite",
1481
1626
  "checked:bg-gold checked:border-gold",
@@ -1489,26 +1634,7 @@ var Radio = React22.forwardRef(
1489
1634
  backgroundSize: "contain",
1490
1635
  backgroundRepeat: "no-repeat"
1491
1636
  },
1492
- onChange: (e) => {
1493
- const input = e.currentTarget;
1494
- if (input.checked) {
1495
- input.style.backgroundImage = radioDotSvg;
1496
- if (input.name) {
1497
- const radios = document.querySelectorAll(
1498
- `input[type="radio"][name="${input.name}"]`
1499
- );
1500
- radios.forEach((radio) => {
1501
- if (radio !== input) {
1502
- radio.style.backgroundImage = "none";
1503
- }
1504
- });
1505
- }
1506
- } else {
1507
- input.style.backgroundImage = "none";
1508
- }
1509
- rest.onChange?.(e);
1510
- },
1511
- ...rest
1637
+ onChange: handleChange
1512
1638
  }
1513
1639
  ), label && /* @__PURE__ */ React22.createElement(
1514
1640
  "label",
@@ -1523,7 +1649,7 @@ var Radio = React22.forwardRef(
1523
1649
  Radio.displayName = "Radio";
1524
1650
 
1525
1651
  // src/components/Switch.tsx
1526
- import React23, { useCallback as useCallback3, useRef, useState } from "react";
1652
+ import React23, { useCallback as useCallback4, useRef as useRef2, useState as useState2 } from "react";
1527
1653
  var Switch = React23.forwardRef(
1528
1654
  ({
1529
1655
  checked: controlledChecked,
@@ -1534,11 +1660,11 @@ var Switch = React23.forwardRef(
1534
1660
  label,
1535
1661
  ...rest
1536
1662
  }, ref) => {
1537
- const [internalChecked, setInternalChecked] = useState(defaultChecked);
1663
+ const [internalChecked, setInternalChecked] = useState2(defaultChecked);
1538
1664
  const isControlled = controlledChecked !== void 0;
1539
1665
  const checked = isControlled ? controlledChecked : internalChecked;
1540
- const buttonRef = useRef(null);
1541
- const setRefs = useCallback3(
1666
+ const buttonRef = useRef2(null);
1667
+ const setRefs = useCallback4(
1542
1668
  (node) => {
1543
1669
  buttonRef.current = node;
1544
1670
  if (typeof ref === "function") {
@@ -1608,7 +1734,7 @@ var Switch = React23.forwardRef(
1608
1734
  Switch.displayName = "Switch";
1609
1735
 
1610
1736
  // src/components/Slider.tsx
1611
- import React24, { useCallback as useCallback4, useRef as useRef2, useState as useState2 } from "react";
1737
+ import React24, { useCallback as useCallback5, useRef as useRef3, useState as useState3 } from "react";
1612
1738
  var SIZE_TRACK = {
1613
1739
  sm: "h-1",
1614
1740
  md: "h-2",
@@ -1635,13 +1761,13 @@ var Slider = React24.forwardRef(
1635
1761
  className,
1636
1762
  ...props
1637
1763
  }, ref) => {
1638
- const [internalValue, setInternalValue] = useState2(defaultValue);
1639
- const [isDragging, setIsDragging] = useState2(false);
1640
- const trackRef = useRef2(null);
1764
+ const [internalValue, setInternalValue] = useState3(defaultValue);
1765
+ const [isDragging, setIsDragging] = useState3(false);
1766
+ const trackRef = useRef3(null);
1641
1767
  const isControlled = controlledValue !== void 0;
1642
1768
  const value = isControlled ? controlledValue : internalValue;
1643
1769
  const percentage = (value - min) / (max - min) * 100;
1644
- const updateValue = useCallback4(
1770
+ const updateValue = useCallback5(
1645
1771
  (clientX) => {
1646
1772
  if (!trackRef.current || disabled) {
1647
1773
  return;
@@ -1997,16 +2123,21 @@ var Progress = React28.forwardRef(
1997
2123
  Progress.displayName = "Progress";
1998
2124
 
1999
2125
  // src/components/Toast.tsx
2000
- import React29, { createContext as createContext2, useCallback as useCallback5, useContext as useContext2, useEffect, useState as useState3 } from "react";
2126
+ import React29, { createContext as createContext2, useCallback as useCallback6, useContext as useContext2, useEffect as useEffect2, useState as useState4 } from "react";
2001
2127
  import { createPortal } from "react-dom";
2002
2128
  import { AlertCircle, AlertTriangle as AlertTriangle2, CheckCircle as CheckCircle2, Info as Info2, X as X2 } from "lucide-react";
2129
+ var toastCounter = 0;
2130
+ function createToastId() {
2131
+ toastCounter += 1;
2132
+ return `toast-${Date.now().toString(36)}-${toastCounter}`;
2133
+ }
2003
2134
  var ToastContext = createContext2(null);
2004
2135
  function useToast() {
2005
2136
  const context = useContext2(ToastContext);
2006
2137
  if (!context) {
2007
2138
  throw new Error("useToast must be used within a ToastProvider");
2008
2139
  }
2009
- const toast = useCallback5(
2140
+ const toast = useCallback6(
2010
2141
  (options) => {
2011
2142
  return context.addToast(options);
2012
2143
  },
@@ -2022,14 +2153,14 @@ var ToastProvider = ({
2022
2153
  position = "bottom-right",
2023
2154
  defaultDuration = 5e3
2024
2155
  }) => {
2025
- const [toasts, setToasts] = useState3([]);
2026
- const [mounted, setMounted] = useState3(false);
2027
- useEffect(() => {
2156
+ const [toasts, setToasts] = useState4([]);
2157
+ const [mounted, setMounted] = useState4(false);
2158
+ useEffect2(() => {
2028
2159
  setMounted(true);
2029
2160
  }, []);
2030
- const addToast = useCallback5(
2161
+ const addToast = useCallback6(
2031
2162
  (toast) => {
2032
- const id = Math.random().toString(36).substr(2, 9);
2163
+ const id = createToastId();
2033
2164
  const newToast = {
2034
2165
  ...toast,
2035
2166
  id,
@@ -2040,33 +2171,33 @@ var ToastProvider = ({
2040
2171
  },
2041
2172
  [defaultDuration]
2042
2173
  );
2043
- const removeToast = useCallback5((id) => {
2174
+ const removeToast = useCallback6((id) => {
2044
2175
  setToasts((prev) => prev.filter((t) => t.id !== id));
2045
2176
  }, []);
2046
2177
  return /* @__PURE__ */ React29.createElement(ToastContext.Provider, { value: { toasts, addToast, removeToast, position } }, children, mounted && /* @__PURE__ */ React29.createElement(ToastViewport, null));
2047
2178
  };
2048
2179
  ToastProvider.displayName = "ToastProvider";
2180
+ var POSITION_CLASSES = {
2181
+ "top-right": "top-4 right-4",
2182
+ "top-left": "top-4 left-4",
2183
+ "bottom-right": "bottom-4 right-4",
2184
+ "bottom-left": "bottom-4 left-4",
2185
+ "top-center": "top-4 left-1/2 -translate-x-1/2",
2186
+ "bottom-center": "bottom-4 left-1/2 -translate-x-1/2"
2187
+ };
2049
2188
  var ToastViewport = () => {
2050
2189
  const context = useContext2(ToastContext);
2051
2190
  if (!context) {
2052
2191
  return null;
2053
2192
  }
2054
2193
  const { toasts, position } = context;
2055
- const positionClasses = {
2056
- "top-right": "top-4 right-4",
2057
- "top-left": "top-4 left-4",
2058
- "bottom-right": "bottom-4 right-4",
2059
- "bottom-left": "bottom-4 left-4",
2060
- "top-center": "top-4 left-1/2 -translate-x-1/2",
2061
- "bottom-center": "bottom-4 left-1/2 -translate-x-1/2"
2062
- };
2063
2194
  return createPortal(
2064
2195
  /* @__PURE__ */ React29.createElement(
2065
2196
  "div",
2066
2197
  {
2067
2198
  className: cx(
2068
2199
  "fixed z-50 flex flex-col gap-2 pointer-events-none",
2069
- positionClasses[position]
2200
+ POSITION_CLASSES[position]
2070
2201
  )
2071
2202
  },
2072
2203
  toasts.map((toast) => /* @__PURE__ */ React29.createElement(Toast, { key: toast.id, ...toast }))
@@ -2097,7 +2228,7 @@ var Toast = ({
2097
2228
  action
2098
2229
  }) => {
2099
2230
  const context = useContext2(ToastContext);
2100
- useEffect(() => {
2231
+ useEffect2(() => {
2101
2232
  if (duration && duration > 0) {
2102
2233
  const timer = setTimeout(() => {
2103
2234
  context?.removeToast(id);
@@ -2129,41 +2260,17 @@ var Toast = ({
2129
2260
  Toast.displayName = "Toast";
2130
2261
 
2131
2262
  // src/components/Modal.tsx
2132
- import React30, { useEffect as useEffect2, useState as useState4 } from "react";
2263
+ import React30, { useEffect as useEffect3, useState as useState5 } from "react";
2133
2264
  import { createPortal as createPortal2 } from "react-dom";
2134
2265
  import { X as X3 } from "lucide-react";
2135
2266
  var Modal = ({ isOpen, onClose, title, children, className }) => {
2136
- const [mounted, setMounted] = useState4(false);
2137
- useEffect2(() => {
2267
+ const [mounted, setMounted] = useState5(false);
2268
+ useEffect3(() => {
2138
2269
  setMounted(true);
2139
2270
  }, []);
2140
- useEffect2(() => {
2141
- if (isOpen) {
2142
- const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
2143
- document.body.style.overflow = "hidden";
2144
- document.body.style.paddingRight = `${scrollbarWidth}px`;
2145
- } else {
2146
- document.body.style.overflow = "unset";
2147
- document.body.style.paddingRight = "0px";
2148
- }
2149
- return () => {
2150
- document.body.style.overflow = "unset";
2151
- document.body.style.paddingRight = "0px";
2152
- };
2153
- }, [isOpen]);
2154
- useEffect2(() => {
2155
- const handleEsc = (e) => {
2156
- if (e.key === "Escape") {
2157
- onClose();
2158
- }
2159
- };
2160
- window.addEventListener("keydown", handleEsc);
2161
- return () => window.removeEventListener("keydown", handleEsc);
2162
- }, [onClose]);
2163
- if (!mounted) {
2164
- return null;
2165
- }
2166
- if (!isOpen) {
2271
+ useScrollLock(isOpen);
2272
+ useEscapeKey(onClose, isOpen);
2273
+ if (!mounted || !isOpen) {
2167
2274
  return null;
2168
2275
  }
2169
2276
  const content = /* @__PURE__ */ React30.createElement(
@@ -2202,7 +2309,7 @@ var Modal = ({ isOpen, onClose, title, children, className }) => {
2202
2309
  Modal.displayName = "Modal";
2203
2310
 
2204
2311
  // src/components/Drawer.tsx
2205
- import React31, { useEffect as useEffect3, useState as useState5 } from "react";
2312
+ import React31, { useEffect as useEffect4, useState as useState6 } from "react";
2206
2313
  import { createPortal as createPortal3 } from "react-dom";
2207
2314
  import { X as X4 } from "lucide-react";
2208
2315
  var SIZE_MAP2 = {
@@ -2237,7 +2344,7 @@ var SIZE_MAP2 = {
2237
2344
  bottom: "h-full"
2238
2345
  }
2239
2346
  };
2240
- var POSITION_CLASSES = {
2347
+ var POSITION_CLASSES2 = {
2241
2348
  left: "left-0 top-0 h-full",
2242
2349
  right: "right-0 top-0 h-full",
2243
2350
  top: "top-0 left-0 w-full",
@@ -2258,33 +2365,12 @@ var Drawer = ({
2258
2365
  children,
2259
2366
  className
2260
2367
  }) => {
2261
- const [mounted, setMounted] = useState5(false);
2262
- useEffect3(() => {
2368
+ const [mounted, setMounted] = useState6(false);
2369
+ useEffect4(() => {
2263
2370
  setMounted(true);
2264
2371
  }, []);
2265
- useEffect3(() => {
2266
- if (isOpen) {
2267
- const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
2268
- document.body.style.overflow = "hidden";
2269
- document.body.style.paddingRight = `${scrollbarWidth}px`;
2270
- } else {
2271
- document.body.style.overflow = "unset";
2272
- document.body.style.paddingRight = "0px";
2273
- }
2274
- return () => {
2275
- document.body.style.overflow = "unset";
2276
- document.body.style.paddingRight = "0px";
2277
- };
2278
- }, [isOpen]);
2279
- useEffect3(() => {
2280
- const handleEsc = (e) => {
2281
- if (e.key === "Escape") {
2282
- onClose();
2283
- }
2284
- };
2285
- window.addEventListener("keydown", handleEsc);
2286
- return () => window.removeEventListener("keydown", handleEsc);
2287
- }, [onClose]);
2372
+ useScrollLock(isOpen);
2373
+ useEscapeKey(onClose, isOpen);
2288
2374
  if (!mounted) {
2289
2375
  return null;
2290
2376
  }
@@ -2315,7 +2401,7 @@ var Drawer = ({
2315
2401
  className: cx(
2316
2402
  "fixed bg-charcoal border-ash shadow-2xl flex flex-col",
2317
2403
  "transition-transform duration-300 ease-out",
2318
- POSITION_CLASSES[position],
2404
+ POSITION_CLASSES2[position],
2319
2405
  SIZE_MAP2[size][position],
2320
2406
  position === "left" && "border-r",
2321
2407
  position === "right" && "border-l",
@@ -2342,8 +2428,8 @@ var Drawer = ({
2342
2428
  Drawer.displayName = "Drawer";
2343
2429
 
2344
2430
  // src/components/Popover.tsx
2345
- import React32, { useCallback as useCallback6, useEffect as useEffect4, useId, useRef as useRef3, useState as useState6 } from "react";
2346
- var POSITION_CLASSES2 = {
2431
+ import React32, { useCallback as useCallback7, useId as useId3, useRef as useRef4, useState as useState7 } from "react";
2432
+ var POSITION_CLASSES3 = {
2347
2433
  top: {
2348
2434
  start: "bottom-full left-0 mb-2",
2349
2435
  center: "bottom-full left-1/2 -translate-x-1/2 mb-2",
@@ -2374,12 +2460,12 @@ var Popover = ({
2374
2460
  onOpenChange,
2375
2461
  closeOnClickOutside = true
2376
2462
  }) => {
2377
- const [internalOpen, setInternalOpen] = useState6(false);
2463
+ const [internalOpen, setInternalOpen] = useState7(false);
2378
2464
  const isControlled = controlledOpen !== void 0;
2379
2465
  const isOpen = isControlled ? controlledOpen : internalOpen;
2380
- const containerRef = useRef3(null);
2381
- const baseId = useId();
2382
- const setIsOpen = useCallback6(
2466
+ const containerRef = useRef4(null);
2467
+ const baseId = useId3();
2468
+ const setIsOpen = useCallback7(
2383
2469
  (newOpen) => {
2384
2470
  if (!isControlled) {
2385
2471
  setInternalOpen(newOpen);
@@ -2388,30 +2474,9 @@ var Popover = ({
2388
2474
  },
2389
2475
  [isControlled, onOpenChange]
2390
2476
  );
2391
- useEffect4(() => {
2392
- if (!isOpen || !closeOnClickOutside) {
2393
- return;
2394
- }
2395
- const handleClickOutside = (e) => {
2396
- if (containerRef.current && !containerRef.current.contains(e.target)) {
2397
- setIsOpen(false);
2398
- }
2399
- };
2400
- document.addEventListener("mousedown", handleClickOutside);
2401
- return () => document.removeEventListener("mousedown", handleClickOutside);
2402
- }, [isOpen, closeOnClickOutside, setIsOpen]);
2403
- useEffect4(() => {
2404
- if (!isOpen) {
2405
- return;
2406
- }
2407
- const handleEscape = (e) => {
2408
- if (e.key === "Escape") {
2409
- setIsOpen(false);
2410
- }
2411
- };
2412
- document.addEventListener("keydown", handleEscape);
2413
- return () => document.removeEventListener("keydown", handleEscape);
2414
- }, [isOpen, setIsOpen]);
2477
+ const close = useCallback7(() => setIsOpen(false), [setIsOpen]);
2478
+ useClickOutside(containerRef, close, isOpen && closeOnClickOutside);
2479
+ useEscapeKey(close, isOpen);
2415
2480
  const handleTriggerClick = () => {
2416
2481
  setIsOpen(!isOpen);
2417
2482
  };
@@ -2432,7 +2497,7 @@ var Popover = ({
2432
2497
  "absolute z-50 min-w-48 p-4",
2433
2498
  "bg-charcoal border border-ash shadow-lg",
2434
2499
  "animate-fade-in",
2435
- POSITION_CLASSES2[position][align]
2500
+ POSITION_CLASSES3[position][align]
2436
2501
  )
2437
2502
  },
2438
2503
  children
@@ -2441,7 +2506,7 @@ var Popover = ({
2441
2506
  Popover.displayName = "Popover";
2442
2507
 
2443
2508
  // src/components/Dialog.tsx
2444
- import React33, { useCallback as useCallback7 } from "react";
2509
+ import React33, { useCallback as useCallback8 } from "react";
2445
2510
  var ConfirmDialog = ({
2446
2511
  title = "Confirm",
2447
2512
  description,
@@ -2454,11 +2519,11 @@ var ConfirmDialog = ({
2454
2519
  isLoading = false,
2455
2520
  ...props
2456
2521
  }) => {
2457
- const handleCancel = useCallback7(() => {
2522
+ const handleCancel = useCallback8(() => {
2458
2523
  onCancel?.();
2459
2524
  onClose();
2460
2525
  }, [onCancel, onClose]);
2461
- const handleConfirm = useCallback7(async () => {
2526
+ const handleConfirm = useCallback8(async () => {
2462
2527
  await onConfirm();
2463
2528
  onClose();
2464
2529
  }, [onConfirm, onClose]);
@@ -2502,11 +2567,11 @@ var PromptDialog = ({
2502
2567
  ...props
2503
2568
  }) => {
2504
2569
  const [value, setValue] = React33.useState(defaultValue);
2505
- const handleCancel = useCallback7(() => {
2570
+ const handleCancel = useCallback8(() => {
2506
2571
  onCancel?.();
2507
2572
  onClose();
2508
2573
  }, [onCancel, onClose]);
2509
- const handleSubmit = useCallback7(
2574
+ const handleSubmit = useCallback8(
2510
2575
  async (e) => {
2511
2576
  e.preventDefault();
2512
2577
  await onSubmit(value);
@@ -2543,7 +2608,7 @@ var PromptDialog = ({
2543
2608
  PromptDialog.displayName = "PromptDialog";
2544
2609
 
2545
2610
  // src/components/Tabs.tsx
2546
- import React34, { createContext as createContext3, useCallback as useCallback8, useContext as useContext3, useId as useId2, useState as useState7 } from "react";
2611
+ import React34, { createContext as createContext3, useCallback as useCallback9, useContext as useContext3, useId as useId4, useState as useState8 } from "react";
2547
2612
  var TabsContext = createContext3(null);
2548
2613
  function useTabsContext() {
2549
2614
  const context = useContext3(TabsContext);
@@ -2554,11 +2619,11 @@ function useTabsContext() {
2554
2619
  }
2555
2620
  var Tabs = React34.forwardRef(
2556
2621
  ({ defaultValue, value, onValueChange, children, className, ...props }, ref) => {
2557
- const [internalValue, setInternalValue] = useState7(defaultValue ?? "");
2622
+ const [internalValue, setInternalValue] = useState8(defaultValue ?? "");
2558
2623
  const isControlled = value !== void 0;
2559
2624
  const activeTab = isControlled ? value : internalValue;
2560
- const baseId = useId2();
2561
- const setActiveTab = useCallback8(
2625
+ const baseId = useId4();
2626
+ const setActiveTab = useCallback9(
2562
2627
  (id) => {
2563
2628
  if (!isControlled) {
2564
2629
  setInternalValue(id);
@@ -2654,7 +2719,7 @@ var TabPanel = React34.forwardRef(
2654
2719
  TabPanel.displayName = "TabPanel";
2655
2720
 
2656
2721
  // src/components/Accordion.tsx
2657
- import React35, { createContext as createContext4, useCallback as useCallback9, useContext as useContext4, useId as useId3, useState as useState8 } from "react";
2722
+ import React35, { createContext as createContext4, useCallback as useCallback10, useContext as useContext4, useId as useId5, useState as useState9 } from "react";
2658
2723
  import { ChevronDown } from "lucide-react";
2659
2724
  var AccordionContext = createContext4(null);
2660
2725
  function useAccordionContext() {
@@ -2666,7 +2731,7 @@ function useAccordionContext() {
2666
2731
  }
2667
2732
  var Accordion = React35.forwardRef(
2668
2733
  ({ type = "single", defaultValue, value, onValueChange, children, className, ...props }, ref) => {
2669
- const [internalValue, setInternalValue] = useState8(() => {
2734
+ const [internalValue, setInternalValue] = useState9(() => {
2670
2735
  if (defaultValue) {
2671
2736
  return new Set(Array.isArray(defaultValue) ? defaultValue : [defaultValue]);
2672
2737
  }
@@ -2674,7 +2739,7 @@ var Accordion = React35.forwardRef(
2674
2739
  });
2675
2740
  const isControlled = value !== void 0;
2676
2741
  const expandedItems = isControlled ? new Set(Array.isArray(value) ? value : [value]) : internalValue;
2677
- const toggleItem = useCallback9(
2742
+ const toggleItem = useCallback10(
2678
2743
  (id) => {
2679
2744
  const newSet = new Set(expandedItems);
2680
2745
  if (newSet.has(id)) {
@@ -2725,7 +2790,7 @@ var AccordionTrigger = React35.forwardRef(
2725
2790
  ({ children, className, ...props }, ref) => {
2726
2791
  const { expandedItems, toggleItem } = useAccordionContext();
2727
2792
  const itemContext = useContext4(AccordionItemContext);
2728
- const baseId = useId3();
2793
+ const baseId = useId5();
2729
2794
  if (!itemContext) {
2730
2795
  throw new Error("AccordionTrigger must be used within an AccordionItem");
2731
2796
  }
@@ -2770,7 +2835,7 @@ var AccordionContent = React35.forwardRef(
2770
2835
  ({ children, className, ...props }, ref) => {
2771
2836
  const { expandedItems } = useAccordionContext();
2772
2837
  const itemContext = useContext4(AccordionItemContext);
2773
- const baseId = useId3();
2838
+ const baseId = useId5();
2774
2839
  if (!itemContext) {
2775
2840
  throw new Error("AccordionContent must be used within an AccordionItem");
2776
2841
  }
@@ -2800,12 +2865,12 @@ AccordionContent.displayName = "AccordionContent";
2800
2865
  // src/components/Menu.tsx
2801
2866
  import React36, {
2802
2867
  createContext as createContext5,
2803
- useCallback as useCallback10,
2868
+ useCallback as useCallback11,
2804
2869
  useContext as useContext5,
2805
2870
  useEffect as useEffect5,
2806
- useId as useId4,
2807
- useRef as useRef4,
2808
- useState as useState9
2871
+ useId as useId6,
2872
+ useRef as useRef5,
2873
+ useState as useState10
2809
2874
  } from "react";
2810
2875
  var MenuContext = createContext5(null);
2811
2876
  function useMenuContext() {
@@ -2816,11 +2881,11 @@ function useMenuContext() {
2816
2881
  return context;
2817
2882
  }
2818
2883
  var Menu = ({ children, open, onOpenChange }) => {
2819
- const [internalOpen, setInternalOpen] = useState9(false);
2884
+ const [internalOpen, setInternalOpen] = useState10(false);
2820
2885
  const isControlled = open !== void 0;
2821
2886
  const isOpen = isControlled ? open : internalOpen;
2822
- const baseId = useId4();
2823
- const setIsOpen = useCallback10(
2887
+ const baseId = useId6();
2888
+ const setIsOpen = useCallback11(
2824
2889
  (newOpen) => {
2825
2890
  if (!isControlled) {
2826
2891
  setInternalOpen(newOpen);
@@ -2872,55 +2937,42 @@ var MenuTrigger = React36.forwardRef(
2872
2937
  }
2873
2938
  );
2874
2939
  MenuTrigger.displayName = "MenuTrigger";
2940
+ var MENU_ALIGN_CLASSES = {
2941
+ start: "left-0",
2942
+ center: "left-1/2 -translate-x-1/2",
2943
+ end: "right-0"
2944
+ };
2945
+ var MENU_SIDE_CLASSES = {
2946
+ top: "bottom-full mb-1",
2947
+ bottom: "top-full mt-1"
2948
+ };
2875
2949
  var MenuContent = React36.forwardRef(
2876
2950
  ({ children, className, align = "start", side = "bottom", ...props }, ref) => {
2877
2951
  const { isOpen, setIsOpen, triggerId, menuId } = useMenuContext();
2878
- const menuRef = useRef4(null);
2952
+ const menuRef = useRef5(null);
2953
+ const close = useCallback11(() => setIsOpen(false), [setIsOpen]);
2954
+ useEscapeKey(close, isOpen);
2879
2955
  useEffect5(() => {
2880
2956
  if (!isOpen) {
2881
2957
  return;
2882
2958
  }
2883
2959
  const handleClickOutside = (e) => {
2960
+ const target = e.target;
2884
2961
  const trigger = document.getElementById(triggerId);
2885
- if (menuRef.current && !menuRef.current.contains(e.target) && trigger && !trigger.contains(e.target)) {
2886
- setIsOpen(false);
2887
- }
2888
- };
2889
- const handleEscape = (e) => {
2890
- if (e.key === "Escape") {
2962
+ if (menuRef.current && !menuRef.current.contains(target) && !trigger?.contains(target)) {
2891
2963
  setIsOpen(false);
2892
2964
  }
2893
2965
  };
2894
2966
  document.addEventListener("mousedown", handleClickOutside);
2895
- document.addEventListener("keydown", handleEscape);
2896
- return () => {
2897
- document.removeEventListener("mousedown", handleClickOutside);
2898
- document.removeEventListener("keydown", handleEscape);
2899
- };
2967
+ return () => document.removeEventListener("mousedown", handleClickOutside);
2900
2968
  }, [isOpen, setIsOpen, triggerId]);
2901
2969
  if (!isOpen) {
2902
2970
  return null;
2903
2971
  }
2904
- const alignmentClasses = {
2905
- start: "left-0",
2906
- center: "left-1/2 -translate-x-1/2",
2907
- end: "right-0"
2908
- };
2909
- const sideClasses = {
2910
- top: "bottom-full mb-1",
2911
- bottom: "top-full mt-1"
2912
- };
2913
2972
  return /* @__PURE__ */ React36.createElement(
2914
2973
  "div",
2915
2974
  {
2916
- ref: (node) => {
2917
- menuRef.current = node;
2918
- if (typeof ref === "function") {
2919
- ref(node);
2920
- } else if (ref) {
2921
- ref.current = node;
2922
- }
2923
- },
2975
+ ref: composeRefs(menuRef, ref),
2924
2976
  id: menuId,
2925
2977
  role: "menu",
2926
2978
  "aria-labelledby": triggerId,
@@ -2928,8 +2980,8 @@ var MenuContent = React36.forwardRef(
2928
2980
  "absolute z-50 min-w-40 py-1",
2929
2981
  "bg-charcoal border border-ash shadow-lg",
2930
2982
  "animate-fade-in",
2931
- alignmentClasses[align],
2932
- sideClasses[side],
2983
+ MENU_ALIGN_CLASSES[align],
2984
+ MENU_SIDE_CLASSES[side],
2933
2985
  className
2934
2986
  ),
2935
2987
  ...props
@@ -3653,12 +3705,19 @@ function SquareLoaderIcon({ className, ...props }) {
3653
3705
  }
3654
3706
 
3655
3707
  // src/components/Message.tsx
3656
- import React55, { useEffect as useEffect6, useRef as useRef5, useState as useState10 } from "react";
3708
+ import React55, { useCallback as useCallback12, useEffect as useEffect6, useRef as useRef6, useState as useState11 } from "react";
3709
+ import { Check as Check3, ChevronLeft as ChevronLeft2, ChevronRight as ChevronRight3, Copy, GitBranch, Pencil, RotateCcw, Send, X as X5 } from "lucide-react";
3657
3710
 
3658
3711
  // src/components/MarkdownContent.tsx
3659
3712
  import React54, { useMemo } from "react";
3660
3713
  import DOMPurify from "dompurify";
3661
3714
  import { marked } from "marked";
3715
+ DOMPurify.addHook("afterSanitizeAttributes", (node) => {
3716
+ if (node.tagName === "A") {
3717
+ node.setAttribute("target", "_blank");
3718
+ node.setAttribute("rel", "noopener noreferrer");
3719
+ }
3720
+ });
3662
3721
  var DEFAULT_SANITIZE_CONFIG = {
3663
3722
  ALLOWED_TAGS: [
3664
3723
  "h1",
@@ -3726,16 +3785,6 @@ var DEFAULT_SANITIZE_CONFIG = {
3726
3785
  ADD_ATTR: ["target", "rel"],
3727
3786
  ALLOWED_URI_REGEXP: /^(?:(?:https?|mailto|tel):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i
3728
3787
  };
3729
- function useDOMPurifySetup() {
3730
- useMemo(() => {
3731
- DOMPurify.addHook("afterSanitizeAttributes", (node) => {
3732
- if (node.tagName === "A") {
3733
- node.setAttribute("target", "_blank");
3734
- node.setAttribute("rel", "noopener noreferrer");
3735
- }
3736
- });
3737
- }, []);
3738
- }
3739
3788
  var CURSOR_BASE_CLASSES = "inline-block bg-current animate-cursor-blink w-0.5 h-cursor translate-y-cursor-offset";
3740
3789
  function injectStreamingCursor(html, cursorClassName) {
3741
3790
  if (!html.trim()) {
@@ -3765,7 +3814,6 @@ function injectStreamingCursor(html, cursorClassName) {
3765
3814
  }
3766
3815
  var MarkdownContent = React54.forwardRef(
3767
3816
  ({ className, content, isMarkdown = true, sanitizeConfig, isStreaming, cursorClassName, ...rest }, ref) => {
3768
- useDOMPurifySetup();
3769
3817
  const sanitizedHtml = useMemo(() => {
3770
3818
  if (!content && !isStreaming) {
3771
3819
  return "";
@@ -3787,7 +3835,7 @@ var MarkdownContent = React54.forwardRef(
3787
3835
  return injectStreamingCursor(sanitized, cursorClassName);
3788
3836
  }
3789
3837
  return sanitized;
3790
- }, [content, sanitizeConfig, isStreaming, cursorClassName]);
3838
+ }, [content, isMarkdown, sanitizeConfig, isStreaming, cursorClassName]);
3791
3839
  return /* @__PURE__ */ React54.createElement(
3792
3840
  "div",
3793
3841
  {
@@ -3802,162 +3850,32 @@ var MarkdownContent = React54.forwardRef(
3802
3850
  MarkdownContent.displayName = "MarkdownContent";
3803
3851
 
3804
3852
  // src/components/Message.tsx
3805
- var variantStyles2 = {
3853
+ var VARIANT_STYLES3 = {
3806
3854
  user: "bg-gold text-obsidian ml-auto",
3807
3855
  assistant: "bg-charcoal border border-ash text-white mr-auto"
3808
3856
  };
3809
- var ActionButton = ({ onClick, label, children, className, disabled }) => /* @__PURE__ */ React55.createElement(
3810
- "button",
3811
- {
3812
- type: "button",
3813
- onClick,
3814
- disabled,
3815
- className: cx(
3816
- "p-1.5 text-silver/60 hover:text-silver transition-colors duration-150",
3817
- "hover:bg-white/5",
3818
- "disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent",
3819
- className
3820
- ),
3821
- "aria-label": label
3822
- },
3823
- children
3824
- );
3825
- var CopyIcon = () => /* @__PURE__ */ React55.createElement(
3826
- "svg",
3827
- {
3828
- xmlns: "http://www.w3.org/2000/svg",
3829
- viewBox: "0 0 24 24",
3830
- fill: "none",
3831
- stroke: "currentColor",
3832
- strokeWidth: "2",
3833
- strokeLinecap: "round",
3834
- strokeLinejoin: "round",
3835
- className: "w-3.5 h-3.5"
3836
- },
3837
- /* @__PURE__ */ React55.createElement("rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2" }),
3838
- /* @__PURE__ */ React55.createElement("path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" })
3839
- );
3840
- var CheckIcon = () => /* @__PURE__ */ React55.createElement(
3841
- "svg",
3842
- {
3843
- xmlns: "http://www.w3.org/2000/svg",
3844
- viewBox: "0 0 24 24",
3845
- fill: "none",
3846
- stroke: "currentColor",
3847
- strokeWidth: "2",
3848
- strokeLinecap: "round",
3849
- strokeLinejoin: "round",
3850
- className: "w-3.5 h-3.5 text-success"
3851
- },
3852
- /* @__PURE__ */ React55.createElement("polyline", { points: "20 6 9 17 4 12" })
3853
- );
3854
- var PencilIcon = () => /* @__PURE__ */ React55.createElement(
3855
- "svg",
3856
- {
3857
- xmlns: "http://www.w3.org/2000/svg",
3858
- viewBox: "0 0 24 24",
3859
- fill: "none",
3860
- stroke: "currentColor",
3861
- strokeWidth: "2",
3862
- strokeLinecap: "round",
3863
- strokeLinejoin: "round",
3864
- className: "w-3.5 h-3.5"
3865
- },
3866
- /* @__PURE__ */ React55.createElement("path", { d: "M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" }),
3867
- /* @__PURE__ */ React55.createElement("path", { d: "m15 5 4 4" })
3868
- );
3869
- var RetryIcon = () => /* @__PURE__ */ React55.createElement(
3870
- "svg",
3871
- {
3872
- xmlns: "http://www.w3.org/2000/svg",
3873
- viewBox: "0 0 24 24",
3874
- fill: "none",
3875
- stroke: "currentColor",
3876
- strokeWidth: "2",
3877
- strokeLinecap: "round",
3878
- strokeLinejoin: "round",
3879
- className: "w-3.5 h-3.5"
3880
- },
3881
- /* @__PURE__ */ React55.createElement("path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" }),
3882
- /* @__PURE__ */ React55.createElement("path", { d: "M21 3v5h-5" }),
3883
- /* @__PURE__ */ React55.createElement("path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" }),
3884
- /* @__PURE__ */ React55.createElement("path", { d: "M8 16H3v5" })
3885
- );
3886
- var ChevronLeftIcon2 = () => /* @__PURE__ */ React55.createElement(
3887
- "svg",
3888
- {
3889
- xmlns: "http://www.w3.org/2000/svg",
3890
- viewBox: "0 0 24 24",
3891
- fill: "none",
3892
- stroke: "currentColor",
3893
- strokeWidth: "2",
3894
- strokeLinecap: "round",
3895
- strokeLinejoin: "round",
3896
- className: "w-3 h-3"
3897
- },
3898
- /* @__PURE__ */ React55.createElement("path", { d: "m15 18-6-6 6-6" })
3899
- );
3900
- var ChevronRightIcon2 = () => /* @__PURE__ */ React55.createElement(
3901
- "svg",
3902
- {
3903
- xmlns: "http://www.w3.org/2000/svg",
3904
- viewBox: "0 0 24 24",
3905
- fill: "none",
3906
- stroke: "currentColor",
3907
- strokeWidth: "2",
3908
- strokeLinecap: "round",
3909
- strokeLinejoin: "round",
3910
- className: "w-3 h-3"
3911
- },
3912
- /* @__PURE__ */ React55.createElement("path", { d: "m9 18 6-6-6-6" })
3913
- );
3914
- var GitBranchIcon = () => /* @__PURE__ */ React55.createElement(
3915
- "svg",
3916
- {
3917
- xmlns: "http://www.w3.org/2000/svg",
3918
- viewBox: "0 0 24 24",
3919
- fill: "none",
3920
- stroke: "currentColor",
3921
- strokeWidth: "2",
3922
- strokeLinecap: "round",
3923
- strokeLinejoin: "round",
3924
- className: "w-3 h-3 mr-0.5 text-silver/50"
3925
- },
3926
- /* @__PURE__ */ React55.createElement("line", { x1: "6", x2: "6", y1: "3", y2: "15" }),
3927
- /* @__PURE__ */ React55.createElement("circle", { cx: "18", cy: "6", r: "3" }),
3928
- /* @__PURE__ */ React55.createElement("circle", { cx: "6", cy: "18", r: "3" }),
3929
- /* @__PURE__ */ React55.createElement("path", { d: "M18 9a9 9 0 0 1-9 9" })
3857
+ var ACTION_BUTTON_CLASSES = cx(
3858
+ "p-1.5 text-silver/60 hover:text-silver transition-colors duration-150",
3859
+ "hover:bg-white/5",
3860
+ "disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent"
3930
3861
  );
3931
- var XIcon = () => /* @__PURE__ */ React55.createElement(
3932
- "svg",
3933
- {
3934
- xmlns: "http://www.w3.org/2000/svg",
3935
- viewBox: "0 0 24 24",
3936
- fill: "none",
3937
- stroke: "currentColor",
3938
- strokeWidth: "2",
3939
- strokeLinecap: "round",
3940
- strokeLinejoin: "round",
3941
- className: "w-4 h-4"
3942
- },
3943
- /* @__PURE__ */ React55.createElement("path", { d: "M18 6 6 18" }),
3944
- /* @__PURE__ */ React55.createElement("path", { d: "m6 6 12 12" })
3945
- );
3946
- var SendIcon = () => /* @__PURE__ */ React55.createElement(
3947
- "svg",
3948
- {
3949
- xmlns: "http://www.w3.org/2000/svg",
3950
- viewBox: "0 0 24 24",
3951
- fill: "none",
3952
- stroke: "currentColor",
3953
- strokeWidth: "2",
3954
- strokeLinecap: "round",
3955
- strokeLinejoin: "round",
3956
- className: "w-4 h-4"
3957
- },
3958
- /* @__PURE__ */ React55.createElement("path", { d: "m22 2-7 20-4-9-9-4Z" }),
3959
- /* @__PURE__ */ React55.createElement("path", { d: "M22 2 11 13" })
3862
+ var BRANCH_BUTTON_CLASSES = cx(
3863
+ "p-0.5 hover:text-white hover:bg-white/10 transition-colors",
3864
+ "disabled:opacity-30 disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:hover:text-silver/70"
3960
3865
  );
3866
+ function ActionButton({ onClick, label, children, disabled }) {
3867
+ return /* @__PURE__ */ React55.createElement(
3868
+ "button",
3869
+ {
3870
+ type: "button",
3871
+ onClick,
3872
+ disabled,
3873
+ className: ACTION_BUTTON_CLASSES,
3874
+ "aria-label": label
3875
+ },
3876
+ children
3877
+ );
3878
+ }
3961
3879
  var Message = React55.forwardRef(
3962
3880
  ({
3963
3881
  variant = "assistant",
@@ -3970,10 +3888,10 @@ var Message = React55.forwardRef(
3970
3888
  ...rest
3971
3889
  }, ref) => {
3972
3890
  const isUser = variant === "user";
3973
- const [copied, setCopied] = useState10(false);
3974
- const [isEditing, setIsEditing] = useState10(false);
3975
- const [editValue, setEditValue] = useState10(typeof content === "string" ? content : "");
3976
- const textareaRef = useRef5(null);
3891
+ const { copied, copy } = useCopyToClipboard();
3892
+ const [isEditing, setIsEditing] = useState11(false);
3893
+ const [editValue, setEditValue] = useState11(typeof content === "string" ? content : "");
3894
+ const textareaRef = useRef6(null);
3977
3895
  const showBranchNav = branchInfo && branchInfo.total > 1;
3978
3896
  const showActions = actions && !hideActions && !isStreaming;
3979
3897
  useEffect6(() => {
@@ -3985,25 +3903,11 @@ var Message = React55.forwardRef(
3985
3903
  textarea.setSelectionRange(textarea.value.length, textarea.value.length);
3986
3904
  }
3987
3905
  }, [isEditing]);
3988
- const handleCopy = async () => {
3989
- if (typeof content !== "string") {
3990
- return;
3991
- }
3992
- try {
3993
- await navigator.clipboard.writeText(content);
3994
- setCopied(true);
3995
- setTimeout(() => setCopied(false), 2e3);
3996
- } catch {
3997
- const textArea = document.createElement("textarea");
3998
- textArea.value = content;
3999
- document.body.appendChild(textArea);
4000
- textArea.select();
4001
- document.execCommand("copy");
4002
- document.body.removeChild(textArea);
4003
- setCopied(true);
4004
- setTimeout(() => setCopied(false), 2e3);
3906
+ const handleCopy = useCallback12(() => {
3907
+ if (typeof content === "string") {
3908
+ void copy(content);
4005
3909
  }
4006
- };
3910
+ }, [copy, content]);
4007
3911
  const handleStartEdit = () => {
4008
3912
  if (typeof content === "string") {
4009
3913
  setEditValue(content);
@@ -4066,7 +3970,7 @@ var Message = React55.forwardRef(
4066
3970
  className: "p-1.5 text-obsidian/60 hover:text-obsidian transition-colors",
4067
3971
  "aria-label": "Cancel edit"
4068
3972
  },
4069
- /* @__PURE__ */ React55.createElement(XIcon, null)
3973
+ /* @__PURE__ */ React55.createElement(X5, { className: "w-4 h-4" })
4070
3974
  ), /* @__PURE__ */ React55.createElement(
4071
3975
  "button",
4072
3976
  {
@@ -4076,13 +3980,13 @@ var Message = React55.forwardRef(
4076
3980
  className: "p-1.5 text-obsidian/60 hover:text-obsidian transition-colors disabled:opacity-30",
4077
3981
  "aria-label": "Submit edit"
4078
3982
  },
4079
- /* @__PURE__ */ React55.createElement(SendIcon, null)
3983
+ /* @__PURE__ */ React55.createElement(Send, { className: "w-4 h-4" })
4080
3984
  )))) : /* @__PURE__ */ React55.createElement(
4081
3985
  "div",
4082
3986
  {
4083
3987
  className: cx(
4084
3988
  "px-3 py-2 w-fit max-w-11/12",
4085
- variantStyles2[variant]
3989
+ VARIANT_STYLES3[variant]
4086
3990
  )
4087
3991
  },
4088
3992
  typeof content === "string" ? /* @__PURE__ */ React55.createElement(
@@ -4104,33 +4008,27 @@ var Message = React55.forwardRef(
4104
4008
  onClick: handleCopy,
4105
4009
  label: copied ? "Copied!" : "Copy message"
4106
4010
  },
4107
- copied ? /* @__PURE__ */ React55.createElement(CheckIcon, null) : /* @__PURE__ */ React55.createElement(CopyIcon, null)
4108
- ), isUser && actions.onEdit && typeof content === "string" && /* @__PURE__ */ React55.createElement(ActionButton, { onClick: handleStartEdit, label: "Edit message" }, /* @__PURE__ */ React55.createElement(PencilIcon, null)), !isUser && actions.onRetry && /* @__PURE__ */ React55.createElement(ActionButton, { onClick: actions.onRetry, label: "Regenerate response" }, /* @__PURE__ */ React55.createElement(RetryIcon, null)), showBranchNav && /* @__PURE__ */ React55.createElement(React55.Fragment, null, /* @__PURE__ */ React55.createElement("div", { className: "w-px h-4 bg-ash/40 mx-1" }), /* @__PURE__ */ React55.createElement("div", { className: "flex items-center gap-0.5 text-silver/70" }, /* @__PURE__ */ React55.createElement(GitBranchIcon, null), /* @__PURE__ */ React55.createElement(
4011
+ copied ? /* @__PURE__ */ React55.createElement(Check3, { className: "w-3.5 h-3.5 text-success" }) : /* @__PURE__ */ React55.createElement(Copy, { className: "w-3.5 h-3.5" })
4012
+ ), isUser && actions.onEdit && typeof content === "string" && /* @__PURE__ */ React55.createElement(ActionButton, { onClick: handleStartEdit, label: "Edit message" }, /* @__PURE__ */ React55.createElement(Pencil, { className: "w-3.5 h-3.5" })), !isUser && actions.onRetry && /* @__PURE__ */ React55.createElement(ActionButton, { onClick: actions.onRetry, label: "Regenerate response" }, /* @__PURE__ */ React55.createElement(RotateCcw, { className: "w-3.5 h-3.5" })), showBranchNav && /* @__PURE__ */ React55.createElement(React55.Fragment, null, /* @__PURE__ */ React55.createElement("div", { className: "w-px h-4 bg-ash/40 mx-1" }), /* @__PURE__ */ React55.createElement("div", { className: "flex items-center gap-0.5 text-silver/70" }, /* @__PURE__ */ React55.createElement(GitBranch, { className: "w-3 h-3 mr-0.5 text-silver/50" }), /* @__PURE__ */ React55.createElement(
4109
4013
  "button",
4110
4014
  {
4111
4015
  type: "button",
4112
4016
  onClick: branchInfo.onPrevious,
4113
4017
  disabled: branchInfo.current <= 1,
4114
- className: cx(
4115
- "p-0.5 hover:text-white hover:bg-white/10 transition-colors",
4116
- "disabled:opacity-30 disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:hover:text-silver/70"
4117
- ),
4018
+ className: BRANCH_BUTTON_CLASSES,
4118
4019
  "aria-label": "Previous branch"
4119
4020
  },
4120
- /* @__PURE__ */ React55.createElement(ChevronLeftIcon2, null)
4021
+ /* @__PURE__ */ React55.createElement(ChevronLeft2, { className: "w-3 h-3" })
4121
4022
  ), /* @__PURE__ */ React55.createElement("span", { className: "text-xs tabular-nums min-w-6 text-center" }, branchInfo.current, "/", branchInfo.total), /* @__PURE__ */ React55.createElement(
4122
4023
  "button",
4123
4024
  {
4124
4025
  type: "button",
4125
4026
  onClick: branchInfo.onNext,
4126
4027
  disabled: branchInfo.current >= branchInfo.total,
4127
- className: cx(
4128
- "p-0.5 hover:text-white hover:bg-white/10 transition-colors",
4129
- "disabled:opacity-30 disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:hover:text-silver/70"
4130
- ),
4028
+ className: BRANCH_BUTTON_CLASSES,
4131
4029
  "aria-label": "Next branch"
4132
4030
  },
4133
- /* @__PURE__ */ React55.createElement(ChevronRightIcon2, null)
4031
+ /* @__PURE__ */ React55.createElement(ChevronRight3, { className: "w-3 h-3" })
4134
4032
  ))))
4135
4033
  );
4136
4034
  }
@@ -4141,7 +4039,7 @@ Message.displayName = "Message";
4141
4039
  import React56 from "react";
4142
4040
  var StreamingCursor = React56.forwardRef(
4143
4041
  ({ className, variant = "line", ...rest }, ref) => {
4144
- const variantStyles3 = {
4042
+ const variantStyles2 = {
4145
4043
  block: "w-2.5 h-cursor translate-y-cursor-offset",
4146
4044
  line: "w-0.5 h-cursor translate-y-cursor-offset",
4147
4045
  underscore: "w-2.5 h-0.5 self-end mb-0.5"
@@ -4152,7 +4050,7 @@ var StreamingCursor = React56.forwardRef(
4152
4050
  ref,
4153
4051
  className: cx(
4154
4052
  "inline-block bg-current animate-cursor-blink",
4155
- variantStyles3[variant],
4053
+ variantStyles2[variant],
4156
4054
  className
4157
4055
  ),
4158
4056
  "aria-hidden": "true",
@@ -4164,18 +4062,18 @@ var StreamingCursor = React56.forwardRef(
4164
4062
  StreamingCursor.displayName = "StreamingCursor";
4165
4063
 
4166
4064
  // src/components/chat/ChatInterface.tsx
4167
- import React74, { useCallback as useCallback20, useEffect as useEffect16, useMemo as useMemo5, useRef as useRef14, useState as useState21 } from "react";
4065
+ import React74, { useCallback as useCallback22, useEffect as useEffect16, useMemo as useMemo5, useRef as useRef15, useState as useState22 } from "react";
4168
4066
 
4169
4067
  // src/components/chat/ChatView.tsx
4170
4068
  import React58, { useEffect as useEffect9 } from "react";
4171
4069
 
4172
4070
  // src/components/chat/hooks/useScrollAnchor.ts
4173
- import { useCallback as useCallback11, useRef as useRef6 } from "react";
4071
+ import { useCallback as useCallback13, useRef as useRef7 } from "react";
4174
4072
  function useScrollAnchor(options = {}) {
4175
4073
  const { behavior = "smooth", block = "start" } = options;
4176
- const containerRef = useRef6(null);
4177
- const anchorRef = useRef6(null);
4178
- const scrollToAnchor = useCallback11(() => {
4074
+ const containerRef = useRef7(null);
4075
+ const anchorRef = useRef7(null);
4076
+ const scrollToAnchor = useCallback13(() => {
4179
4077
  const el = anchorRef.current;
4180
4078
  if (!el) {
4181
4079
  return;
@@ -4186,7 +4084,7 @@ function useScrollAnchor(options = {}) {
4186
4084
  });
4187
4085
  });
4188
4086
  }, [behavior, block]);
4189
- const scrollToBottom = useCallback11(() => {
4087
+ const scrollToBottom = useCallback13(() => {
4190
4088
  const container = containerRef.current;
4191
4089
  if (!container) {
4192
4090
  return;
@@ -4197,7 +4095,7 @@ function useScrollAnchor(options = {}) {
4197
4095
  container.scrollTop = container.scrollHeight;
4198
4096
  }
4199
4097
  }, [behavior]);
4200
- const isScrolledToBottom = useCallback11(() => {
4098
+ const isScrolledToBottom = useCallback13(() => {
4201
4099
  const container = containerRef.current;
4202
4100
  if (!container) {
4203
4101
  return true;
@@ -4216,15 +4114,15 @@ function useScrollAnchor(options = {}) {
4216
4114
  }
4217
4115
 
4218
4116
  // src/components/chat/hooks/useAdaptiveSpacer.ts
4219
- import { useCallback as useCallback12, useEffect as useEffect7, useRef as useRef7, useState as useState11 } from "react";
4117
+ import { useCallback as useCallback14, useEffect as useEffect7, useRef as useRef8, useState as useState12 } from "react";
4220
4118
  function useAdaptiveSpacer(options = {}) {
4221
4119
  const { minHeight = 0, containerRef: externalContainerRef, anchorRef } = options;
4222
- const internalContainerRef = useRef7(null);
4120
+ const internalContainerRef = useRef8(null);
4223
4121
  const containerRef = externalContainerRef ?? internalContainerRef;
4224
- const contentRef = useRef7(null);
4225
- const spacerRef = useRef7(null);
4226
- const [spacerHeight, setSpacerHeight] = useState11(0);
4227
- const recalculate = useCallback12(() => {
4122
+ const contentRef = useRef8(null);
4123
+ const spacerRef = useRef8(null);
4124
+ const [spacerHeight, setSpacerHeight] = useState12(0);
4125
+ const recalculate = useCallback14(() => {
4228
4126
  const container = containerRef.current;
4229
4127
  const content = contentRef.current;
4230
4128
  if (!container || !content) {
@@ -4283,7 +4181,7 @@ function useAdaptiveSpacer(options = {}) {
4283
4181
  }
4284
4182
 
4285
4183
  // src/components/chat/ThinkingIndicator.tsx
4286
- import React57, { useEffect as useEffect8, useState as useState12 } from "react";
4184
+ import React57, { useEffect as useEffect8, useState as useState13 } from "react";
4287
4185
  var THINKING_PHRASES = [
4288
4186
  "Consulting the ancient tomes...",
4289
4187
  "Parsing the ineffable...",
@@ -4307,22 +4205,29 @@ var ThinkingIndicator = React57.forwardRef(
4307
4205
  className,
4308
4206
  ...rest
4309
4207
  }, ref) => {
4310
- const [currentIndex, setCurrentIndex] = useState12(
4208
+ const [currentIndex, setCurrentIndex] = useState13(
4311
4209
  () => Math.floor(Math.random() * phrases.length)
4312
4210
  );
4313
- const [isTransitioning, setIsTransitioning] = useState12(false);
4211
+ const [isTransitioning, setIsTransitioning] = useState13(false);
4314
4212
  useEffect8(() => {
4315
4213
  if (!isVisible || phrases.length <= 1) {
4316
4214
  return;
4317
4215
  }
4216
+ let fadeTimeout = null;
4318
4217
  const interval = setInterval(() => {
4319
4218
  setIsTransitioning(true);
4320
- setTimeout(() => {
4219
+ fadeTimeout = setTimeout(() => {
4321
4220
  setCurrentIndex((prev) => (prev + 1) % phrases.length);
4322
4221
  setIsTransitioning(false);
4222
+ fadeTimeout = null;
4323
4223
  }, 200);
4324
4224
  }, phraseInterval);
4325
- return () => clearInterval(interval);
4225
+ return () => {
4226
+ clearInterval(interval);
4227
+ if (fadeTimeout !== null) {
4228
+ clearTimeout(fadeTimeout);
4229
+ }
4230
+ };
4326
4231
  }, [isVisible, phrases.length, phraseInterval]);
4327
4232
  if (!isVisible) {
4328
4233
  return null;
@@ -4401,15 +4306,7 @@ var ChatView = React58.forwardRef(
4401
4306
  return /* @__PURE__ */ React58.createElement(
4402
4307
  "div",
4403
4308
  {
4404
- ref: (node) => {
4405
- ;
4406
- containerRef.current = node;
4407
- if (typeof ref === "function") {
4408
- ref(node);
4409
- } else if (ref) {
4410
- ref.current = node;
4411
- }
4412
- },
4309
+ ref: composeRefs(containerRef, ref),
4413
4310
  onScroll,
4414
4311
  className: cx(
4415
4312
  "flex flex-col w-full h-full overflow-y-auto scroll-smooth",
@@ -4467,8 +4364,8 @@ var ChatView = React58.forwardRef(
4467
4364
  ChatView.displayName = "ChatView";
4468
4365
 
4469
4366
  // src/components/chat/ChatInput.tsx
4470
- import React59, { useCallback as useCallback13, useEffect as useEffect10, useRef as useRef8, useState as useState13 } from "react";
4471
- import { Paperclip, Send, Square, X as X5 } from "lucide-react";
4367
+ import React59, { useCallback as useCallback15, useEffect as useEffect10, useRef as useRef9, useState as useState14 } from "react";
4368
+ import { Paperclip, Send as Send2, Square, X as X6 } from "lucide-react";
4472
4369
 
4473
4370
  // src/components/chat/types.ts
4474
4371
  function isImageFile(file) {
@@ -4480,13 +4377,11 @@ function createPreviewUrl(file) {
4480
4377
  }
4481
4378
  return void 0;
4482
4379
  }
4483
- function revokePreviewUrl(url) {
4484
- if (url) {
4485
- URL.revokeObjectURL(url);
4486
- }
4487
- }
4488
4380
  function generateId() {
4489
- return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
4381
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
4382
+ return crypto.randomUUID();
4383
+ }
4384
+ return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
4490
4385
  }
4491
4386
  function createEmptyTree() {
4492
4387
  return {
@@ -4646,6 +4541,7 @@ var ChatInput = React59.forwardRef(
4646
4541
  onStop,
4647
4542
  attachments: controlledAttachments,
4648
4543
  onAttachmentsChange,
4544
+ onAttachmentRemove,
4649
4545
  showAttachmentButton = true,
4650
4546
  acceptedFileTypes,
4651
4547
  notice,
@@ -4655,13 +4551,13 @@ var ChatInput = React59.forwardRef(
4655
4551
  className,
4656
4552
  ...rest
4657
4553
  }, ref) => {
4658
- const [value, setValue] = useState13(initialInputValue);
4659
- const [localAttachments, setLocalAttachments] = useState13([]);
4660
- const [isDragOver, setIsDragOver] = useState13(false);
4661
- const textareaRef = useRef8(null);
4662
- const fileInputRef = useRef8(null);
4554
+ const [value, setValue] = useState14(initialInputValue);
4555
+ const [localAttachments, setLocalAttachments] = useState14([]);
4556
+ const [isDragOver, setIsDragOver] = useState14(false);
4557
+ const textareaRef = useRef9(null);
4558
+ const fileInputRef = useRef9(null);
4663
4559
  const attachments = controlledAttachments ?? localAttachments;
4664
- const setAttachments = useCallback13(
4560
+ const setAttachments = useCallback15(
4665
4561
  (newAttachments) => {
4666
4562
  if (onAttachmentsChange) {
4667
4563
  if (typeof newAttachments === "function") {
@@ -4675,7 +4571,7 @@ var ChatInput = React59.forwardRef(
4675
4571
  },
4676
4572
  [attachments, onAttachmentsChange]
4677
4573
  );
4678
- const handleSubmit = useCallback13(() => {
4574
+ const handleSubmit = useCallback15(() => {
4679
4575
  const trimmed = value.trim();
4680
4576
  if (!trimmed || disabled || isStreaming) {
4681
4577
  return;
@@ -4687,7 +4583,7 @@ var ChatInput = React59.forwardRef(
4687
4583
  textareaRef.current.style.height = "auto";
4688
4584
  }
4689
4585
  }, [value, disabled, isStreaming, onSubmit, attachments, setAttachments]);
4690
- const handleKeyDown = useCallback13(
4586
+ const handleKeyDown = useCallback15(
4691
4587
  (e) => {
4692
4588
  if (e.key === "Enter" && !e.shiftKey) {
4693
4589
  e.preventDefault();
@@ -4696,7 +4592,7 @@ var ChatInput = React59.forwardRef(
4696
4592
  },
4697
4593
  [handleSubmit]
4698
4594
  );
4699
- const handleChange = useCallback13((e) => {
4595
+ const handleChange = useCallback15((e) => {
4700
4596
  setValue(e.target.value);
4701
4597
  onInputChange?.(e.target.value);
4702
4598
  const textarea = e.target;
@@ -4708,7 +4604,7 @@ var ChatInput = React59.forwardRef(
4708
4604
  textareaRef.current.focus();
4709
4605
  }
4710
4606
  }, [disabled, isStreaming, autoFocus]);
4711
- const addFiles = useCallback13(
4607
+ const addFiles = useCallback15(
4712
4608
  (files) => {
4713
4609
  const newAttachments = Array.from(files).map((file) => ({
4714
4610
  id: generateId(),
@@ -4720,7 +4616,7 @@ var ChatInput = React59.forwardRef(
4720
4616
  },
4721
4617
  [setAttachments]
4722
4618
  );
4723
- const handleFileSelect = useCallback13(
4619
+ const handleFileSelect = useCallback15(
4724
4620
  (e) => {
4725
4621
  const files = e.target.files;
4726
4622
  if (files && files.length > 0) {
@@ -4730,35 +4626,39 @@ var ChatInput = React59.forwardRef(
4730
4626
  },
4731
4627
  [addFiles]
4732
4628
  );
4733
- const handleRemoveAttachment = useCallback13(
4629
+ const handleRemoveAttachment = useCallback15(
4734
4630
  (id) => {
4631
+ const attachment = attachments.find((a) => a.id === id);
4632
+ if (attachment && onAttachmentRemove) {
4633
+ onAttachmentRemove(attachment);
4634
+ }
4735
4635
  setAttachments((prev) => {
4736
- const attachment = prev.find((a) => a.id === id);
4737
- if (attachment?.previewUrl) {
4738
- URL.revokeObjectURL(attachment.previewUrl);
4636
+ const attachmentToRemove = prev.find((a) => a.id === id);
4637
+ if (attachmentToRemove?.previewUrl) {
4638
+ URL.revokeObjectURL(attachmentToRemove.previewUrl);
4739
4639
  }
4740
4640
  return prev.filter((a) => a.id !== id);
4741
4641
  });
4742
4642
  },
4743
- [setAttachments]
4643
+ [attachments, onAttachmentRemove, setAttachments]
4744
4644
  );
4745
- const handleDragEnter = useCallback13((e) => {
4645
+ const handleDragEnter = useCallback15((e) => {
4746
4646
  e.preventDefault();
4747
4647
  e.stopPropagation();
4748
4648
  setIsDragOver(true);
4749
4649
  }, []);
4750
- const handleDragLeave = useCallback13((e) => {
4650
+ const handleDragLeave = useCallback15((e) => {
4751
4651
  e.preventDefault();
4752
4652
  e.stopPropagation();
4753
4653
  if (!e.currentTarget.contains(e.relatedTarget)) {
4754
4654
  setIsDragOver(false);
4755
4655
  }
4756
4656
  }, []);
4757
- const handleDragOver = useCallback13((e) => {
4657
+ const handleDragOver = useCallback15((e) => {
4758
4658
  e.preventDefault();
4759
4659
  e.stopPropagation();
4760
4660
  }, []);
4761
- const handleDrop = useCallback13(
4661
+ const handleDrop = useCallback15(
4762
4662
  (e) => {
4763
4663
  e.preventDefault();
4764
4664
  e.stopPropagation();
@@ -4772,7 +4672,8 @@ var ChatInput = React59.forwardRef(
4772
4672
  );
4773
4673
  const isCentered = position === "centered";
4774
4674
  const hasAttachments = attachments.length > 0;
4775
- const canSubmit = value.trim() && !disabled && !isStreaming;
4675
+ const isUploading = attachments.some((a) => a.status === "uploading");
4676
+ const canSubmit = value.trim() && !disabled && !isStreaming && !isUploading;
4776
4677
  return /* @__PURE__ */ React59.createElement(
4777
4678
  "div",
4778
4679
  {
@@ -4801,7 +4702,7 @@ var ChatInput = React59.forwardRef(
4801
4702
  notice.variant === "warning" ? "text-gold" : "text-error"
4802
4703
  )
4803
4704
  },
4804
- /* @__PURE__ */ React59.createElement(X5, { className: "w-3 h-3" })
4705
+ /* @__PURE__ */ React59.createElement(X6, { className: "w-3 h-3" })
4805
4706
  )),
4806
4707
  /* @__PURE__ */ React59.createElement(
4807
4708
  "div",
@@ -4900,7 +4801,7 @@ var ChatInput = React59.forwardRef(
4900
4801
  ),
4901
4802
  "aria-label": "Send message"
4902
4803
  },
4903
- /* @__PURE__ */ React59.createElement(Send, { className: "w-5 h-5" })
4804
+ /* @__PURE__ */ React59.createElement(Send2, { className: "w-5 h-5" })
4904
4805
  ))
4905
4806
  )
4906
4807
  );
@@ -4909,7 +4810,8 @@ var ChatInput = React59.forwardRef(
4909
4810
  ChatInput.displayName = "ChatInput";
4910
4811
 
4911
4812
  // src/components/chat/ArtifactsPanel.tsx
4912
- import React69, { useCallback as useCallback15, useEffect as useEffect12, useRef as useRef10, useState as useState16 } from "react";
4813
+ import React69, { useCallback as useCallback17, useEffect as useEffect12, useRef as useRef11, useState as useState17 } from "react";
4814
+ import { Image } from "lucide-react";
4913
4815
 
4914
4816
  // src/components/ArtifactCard.tsx
4915
4817
  import React66 from "react";
@@ -5113,15 +5015,7 @@ var AudioCard = React62.forwardRef(
5113
5015
  loop,
5114
5016
  width: "100%",
5115
5017
  height,
5116
- style: { backgroundColor: "transparent" },
5117
- config: {
5118
- file: {
5119
- forceAudio: true,
5120
- attributes: {
5121
- style: { width: "100%", height }
5122
- }
5123
- }
5124
- },
5018
+ style: { backgroundColor: "transparent", width: "100%", height },
5125
5019
  ...playerProps
5126
5020
  }
5127
5021
  ))),
@@ -5405,8 +5299,10 @@ var ArtifactCard = React66.forwardRef(
5405
5299
  )
5406
5300
  }
5407
5301
  );
5408
- default:
5409
- return null;
5302
+ default: {
5303
+ const _exhaustive = artifact.type;
5304
+ return _exhaustive;
5305
+ }
5410
5306
  }
5411
5307
  };
5412
5308
  const isCardExpandable = !!onExpand && (artifact.type === "IMAGE" || artifact.type === "PDF" || artifact.type === "SCRIPT" || artifact.type === "TEXT");
@@ -5443,7 +5339,7 @@ var ArtifactCard = React66.forwardRef(
5443
5339
  ArtifactCard.displayName = "ArtifactCard";
5444
5340
 
5445
5341
  // src/components/ArtifactGroup.tsx
5446
- import React67, { useEffect as useEffect11, useRef as useRef9, useState as useState14 } from "react";
5342
+ import React67, { useEffect as useEffect11, useRef as useRef10, useState as useState15 } from "react";
5447
5343
  var LAYER_OFFSET = "8px";
5448
5344
  var LAYER_OFFSET_2X = "16px";
5449
5345
  var ArtifactGroup = React67.forwardRef(
@@ -5451,8 +5347,8 @@ var ArtifactGroup = React67.forwardRef(
5451
5347
  const children = node.children;
5452
5348
  const count = children.length;
5453
5349
  const frontChild = children[0];
5454
- const prevCountRef = useRef9(count);
5455
- const [badgePing, setBadgePing] = useState14(false);
5350
+ const prevCountRef = useRef10(count);
5351
+ const [badgePing, setBadgePing] = useState15(false);
5456
5352
  useEffect11(() => {
5457
5353
  if (count !== prevCountRef.current) {
5458
5354
  prevCountRef.current = count;
@@ -5573,9 +5469,9 @@ var ArtifactVariantStack = React68.forwardRef(
5573
5469
  ArtifactVariantStack.displayName = "ArtifactVariantStack";
5574
5470
 
5575
5471
  // src/components/chat/hooks/useArtifactTreeNavigation.ts
5576
- import { useCallback as useCallback14, useMemo as useMemo2, useState as useState15 } from "react";
5472
+ import { useCallback as useCallback16, useMemo as useMemo2, useState as useState16 } from "react";
5577
5473
  function useArtifactTreeNavigation(rootNodes) {
5578
- const [stack, setStack] = useState15([]);
5474
+ const [stack, setStack] = useState16([]);
5579
5475
  const currentNodes = useMemo2(() => {
5580
5476
  if (stack.length === 0) return rootNodes;
5581
5477
  return stack[stack.length - 1].children;
@@ -5588,13 +5484,13 @@ function useArtifactTreeNavigation(rootNodes) {
5588
5484
  return entries;
5589
5485
  }, [stack]);
5590
5486
  const isAtRoot = stack.length === 0;
5591
- const navigateInto = useCallback14((node) => {
5487
+ const navigateInto = useCallback16((node) => {
5592
5488
  setStack((prev) => [...prev, node]);
5593
5489
  }, []);
5594
- const navigateTo = useCallback14((index) => {
5490
+ const navigateTo = useCallback16((index) => {
5595
5491
  setStack((prev) => prev.slice(0, index));
5596
5492
  }, []);
5597
- const navigateBack = useCallback14(() => {
5493
+ const navigateBack = useCallback16(() => {
5598
5494
  setStack((prev) => prev.slice(0, -1));
5599
5495
  }, []);
5600
5496
  return {
@@ -5613,16 +5509,8 @@ function ArtifactModal({
5613
5509
  artifact,
5614
5510
  onClose
5615
5511
  }) {
5616
- useEffect12(() => {
5617
- const handleKeyDown = (e) => {
5618
- if (e.key === "Escape") {
5619
- onClose();
5620
- }
5621
- };
5622
- document.addEventListener("keydown", handleKeyDown);
5623
- return () => document.removeEventListener("keydown", handleKeyDown);
5624
- }, [onClose]);
5625
- const handleBackdropClick = useCallback15((e) => {
5512
+ useEscapeKey(onClose);
5513
+ const handleBackdropClick = useCallback17((e) => {
5626
5514
  if (e.target === e.currentTarget) {
5627
5515
  onClose();
5628
5516
  }
@@ -5741,25 +5629,25 @@ var ArtifactsPanel = React69.forwardRef(
5741
5629
  className,
5742
5630
  ...rest
5743
5631
  }, ref) => {
5744
- const [expandedArtifact, setExpandedArtifact] = useState16(null);
5745
- const [zoomIndex, setZoomIndex] = useState16(ZOOM_LEVELS.length - 1);
5632
+ const [expandedArtifact, setExpandedArtifact] = useState17(null);
5633
+ const [zoomIndex, setZoomIndex] = useState17(ZOOM_LEVELS.length - 1);
5746
5634
  const treeNav = useArtifactTreeNavigation(nodes || []);
5747
5635
  const hasNodes = !!nodes && nodes.length > 0;
5748
- const handleExpandArtifact = useCallback15((artifact) => {
5636
+ const handleExpandArtifact = useCallback17((artifact) => {
5749
5637
  setExpandedArtifact(artifact);
5750
5638
  }, []);
5751
- const handleGroupClick = useCallback15((node) => {
5639
+ const handleGroupClick = useCallback17((node) => {
5752
5640
  treeNav.navigateInto(node);
5753
5641
  }, [treeNav]);
5754
- const zoomIn = useCallback15(() => {
5642
+ const zoomIn = useCallback17(() => {
5755
5643
  setZoomIndex((prev) => Math.min(prev + 1, ZOOM_LEVELS.length - 1));
5756
5644
  }, []);
5757
- const zoomOut = useCallback15(() => {
5645
+ const zoomOut = useCallback17(() => {
5758
5646
  setZoomIndex((prev) => Math.max(prev - 1, 0));
5759
5647
  }, []);
5760
5648
  const currentZoom = ZOOM_LEVELS[zoomIndex];
5761
- const contentRef = useRef10(null);
5762
- const [contentHeight, setContentHeight] = useState16(void 0);
5649
+ const contentRef = useRef11(null);
5650
+ const [contentHeight, setContentHeight] = useState17(void 0);
5763
5651
  useEffect12(() => {
5764
5652
  const el = contentRef.current;
5765
5653
  if (!el) return;
@@ -5905,23 +5793,7 @@ var ArtifactsPanelToggle = React69.forwardRef(({ artifactCount = 0, onExpand, cl
5905
5793
  "aria-label": "Expand artifacts panel",
5906
5794
  ...rest
5907
5795
  },
5908
- /* @__PURE__ */ React69.createElement(
5909
- "svg",
5910
- {
5911
- xmlns: "http://www.w3.org/2000/svg",
5912
- viewBox: "0 0 20 20",
5913
- fill: "currentColor",
5914
- className: "w-5 h-5"
5915
- },
5916
- /* @__PURE__ */ React69.createElement(
5917
- "path",
5918
- {
5919
- fillRule: "evenodd",
5920
- d: "M2 4.5A1.5 1.5 0 013.5 3h13A1.5 1.5 0 0118 4.5v11a1.5 1.5 0 01-1.5 1.5h-13A1.5 1.5 0 012 15.5v-11zM4 5v1h1V5H4zm2 0v1h1V5H6zm7 0v1h1V5h-1zm2 0v1h1V5h-1zM4 14v1h1v-1H4zm2 0v1h1v-1H6zm7 0v1h1v-1h-1zm2 0v1h1v-1h-1zM8 8.118a.5.5 0 01.757-.429l4 2.382a.5.5 0 010 .858l-4 2.382A.5.5 0 018 12.882V8.118z",
5921
- clipRule: "evenodd"
5922
- }
5923
- )
5924
- ),
5796
+ /* @__PURE__ */ React69.createElement(Image, { className: "w-5 h-5", "aria-hidden": true }),
5925
5797
  artifactCount > 0 && /* @__PURE__ */ React69.createElement(
5926
5798
  "span",
5927
5799
  {
@@ -5934,7 +5806,8 @@ var ArtifactsPanelToggle = React69.forwardRef(({ artifactCount = 0, onExpand, cl
5934
5806
  ArtifactsPanelToggle.displayName = "ArtifactsPanelToggle";
5935
5807
 
5936
5808
  // src/components/chat/HistoryPanel.tsx
5937
- import React70, { useCallback as useCallback16, useEffect as useEffect13, useMemo as useMemo3, useRef as useRef11, useState as useState17 } from "react";
5809
+ import React70, { useCallback as useCallback18, useEffect as useEffect13, useMemo as useMemo3, useRef as useRef12, useState as useState18 } from "react";
5810
+ import { ChevronDown as ChevronDown2, Pencil as Pencil2 } from "lucide-react";
5938
5811
  function parseTimestamp(ts) {
5939
5812
  if (ts == null) {
5940
5813
  return null;
@@ -5970,64 +5843,16 @@ function groupConversations(conversations) {
5970
5843
  { key: "older", label: "Older", conversations: olderList }
5971
5844
  ].filter((g) => g.conversations.length > 0);
5972
5845
  }
5973
- function ChevronDownIcon({ className }) {
5974
- return /* @__PURE__ */ React70.createElement(
5975
- "svg",
5976
- {
5977
- xmlns: "http://www.w3.org/2000/svg",
5978
- viewBox: "0 0 20 20",
5979
- fill: "currentColor",
5980
- className,
5981
- "aria-hidden": "true"
5982
- },
5983
- /* @__PURE__ */ React70.createElement(
5984
- "path",
5985
- {
5986
- fillRule: "evenodd",
5987
- d: "M5.23 7.21a.75.75 0 011.06.02L10 11.06l3.71-3.83a.75.75 0 111.08 1.04l-4.25 4.39a.75.75 0 01-1.08 0L5.21 8.27a.75.75 0 01.02-1.06z",
5988
- clipRule: "evenodd"
5989
- }
5990
- )
5991
- );
5992
- }
5993
- function PencilIcon2({ className }) {
5994
- return /* @__PURE__ */ React70.createElement(
5995
- "svg",
5996
- {
5997
- xmlns: "http://www.w3.org/2000/svg",
5998
- viewBox: "0 0 20 20",
5999
- fill: "currentColor",
6000
- className,
6001
- "aria-hidden": "true"
6002
- },
6003
- /* @__PURE__ */ React70.createElement(
6004
- "path",
6005
- {
6006
- d: "M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z"
6007
- }
6008
- )
6009
- );
6010
- }
6011
5846
  function ProjectFilter({
6012
5847
  projects,
6013
5848
  value,
6014
5849
  onChange,
6015
5850
  className
6016
5851
  }) {
6017
- const [open, setOpen] = useState17(false);
6018
- const ref = useRef11(null);
6019
- useEffect13(() => {
6020
- if (!open) {
6021
- return;
6022
- }
6023
- const handler = (e) => {
6024
- if (ref.current && !ref.current.contains(e.target)) {
6025
- setOpen(false);
6026
- }
6027
- };
6028
- document.addEventListener("mousedown", handler);
6029
- return () => document.removeEventListener("mousedown", handler);
6030
- }, [open]);
5852
+ const [open, setOpen] = useState18(false);
5853
+ const ref = useRef12(null);
5854
+ const closeFilter = useCallback18(() => setOpen(false), []);
5855
+ useClickOutside(ref, closeFilter, open);
6031
5856
  const label = value ?? "All projects";
6032
5857
  return /* @__PURE__ */ React70.createElement("div", { className: cx("relative min-w-0", className), ref }, /* @__PURE__ */ React70.createElement(
6033
5858
  "button",
@@ -6046,7 +5871,7 @@ function ProjectFilter({
6046
5871
  )
6047
5872
  },
6048
5873
  /* @__PURE__ */ React70.createElement("span", { className: "truncate" }, label),
6049
- /* @__PURE__ */ React70.createElement(ChevronDownIcon, { className: "w-3 h-3 shrink-0" })
5874
+ /* @__PURE__ */ React70.createElement(ChevronDown2, { className: "w-3 h-3 shrink-0", "aria-hidden": true })
6050
5875
  ), open && /* @__PURE__ */ React70.createElement(
6051
5876
  "div",
6052
5877
  {
@@ -6101,28 +5926,28 @@ function ConversationRow({
6101
5926
  onSelect,
6102
5927
  onRename
6103
5928
  }) {
6104
- const [isEditing, setIsEditing] = useState17(false);
6105
- const [draft, setDraft] = useState17(conversation.title);
6106
- const inputRef = useRef11(null);
5929
+ const [isEditing, setIsEditing] = useState18(false);
5930
+ const [draft, setDraft] = useState18(conversation.title);
5931
+ const inputRef = useRef12(null);
6107
5932
  useEffect13(() => {
6108
5933
  if (isEditing && inputRef.current) {
6109
5934
  inputRef.current.focus();
6110
5935
  inputRef.current.select();
6111
5936
  }
6112
5937
  }, [isEditing]);
6113
- const startEdit = useCallback16((e) => {
5938
+ const startEdit = useCallback18((e) => {
6114
5939
  e.stopPropagation();
6115
5940
  setDraft(conversation.title);
6116
5941
  setIsEditing(true);
6117
5942
  }, [conversation.title]);
6118
- const commit = useCallback16(() => {
5943
+ const commit = useCallback18(() => {
6119
5944
  const trimmed = draft.trim();
6120
5945
  if (trimmed && trimmed !== conversation.title) {
6121
5946
  onRename?.(conversation.id, trimmed);
6122
5947
  }
6123
5948
  setIsEditing(false);
6124
5949
  }, [draft, conversation.id, conversation.title, onRename]);
6125
- const cancel = useCallback16(() => {
5950
+ const cancel = useCallback18(() => {
6126
5951
  setDraft(conversation.title);
6127
5952
  setIsEditing(false);
6128
5953
  }, [conversation.title]);
@@ -6197,7 +6022,7 @@ function ConversationRow({
6197
6022
  "transition-opacity duration-150"
6198
6023
  )
6199
6024
  },
6200
- /* @__PURE__ */ React70.createElement(PencilIcon2, { className: "w-3.5 h-3.5" })
6025
+ /* @__PURE__ */ React70.createElement(Pencil2, { className: "w-3.5 h-3.5", "aria-hidden": true })
6201
6026
  ));
6202
6027
  }
6203
6028
  function HistoryPanel({
@@ -6206,7 +6031,7 @@ function HistoryPanel({
6206
6031
  onNewChat,
6207
6032
  onRenameConversation
6208
6033
  }) {
6209
- const [projectFilter, setProjectFilter] = useState17(null);
6034
+ const [projectFilter, setProjectFilter] = useState18(null);
6210
6035
  const projects = useMemo3(() => {
6211
6036
  const set = /* @__PURE__ */ new Set();
6212
6037
  for (const c of conversations) {
@@ -6272,7 +6097,7 @@ function HistoryPanel({
6272
6097
  }
6273
6098
 
6274
6099
  // src/components/chat/TodosList.tsx
6275
- import React71, { useCallback as useCallback17, useMemo as useMemo4, useState as useState18 } from "react";
6100
+ import React71, { useCallback as useCallback19, useMemo as useMemo4, useState as useState19 } from "react";
6276
6101
  import { Loader2 as Loader22, Square as Square2 } from "lucide-react";
6277
6102
  var TASK_STATUSES = {
6278
6103
  PENDING: "pending",
@@ -6351,8 +6176,8 @@ function hasInProgressTask(tasks) {
6351
6176
  var TodosList = React71.forwardRef(
6352
6177
  ({ tasks, title = "Tasks", onStopAllTasks, className, ...rest }, ref) => {
6353
6178
  const sortedTasks = useMemo4(() => sortTasks(tasks), [tasks]);
6354
- const [isStopping, setIsStopping] = useState18(false);
6355
- const handleStopClick = useCallback17(async () => {
6179
+ const [isStopping, setIsStopping] = useState19(false);
6180
+ const handleStopClick = useCallback19(async () => {
6356
6181
  if (!onStopAllTasks || isStopping) {
6357
6182
  return;
6358
6183
  }
@@ -6477,7 +6302,7 @@ var ToolSidebar = React72.forwardRef(
6477
6302
  ToolSidebar.displayName = "ToolSidebar";
6478
6303
 
6479
6304
  // src/components/chat/ToolPanelContainer.tsx
6480
- import React73, { useCallback as useCallback18, useEffect as useEffect14, useRef as useRef12, useState as useState19 } from "react";
6305
+ import React73, { useCallback as useCallback20, useEffect as useEffect14, useRef as useRef13, useState as useState20 } from "react";
6481
6306
  var ToolPanelContainer = React73.forwardRef(
6482
6307
  ({
6483
6308
  topContent,
@@ -6489,21 +6314,21 @@ var ToolPanelContainer = React73.forwardRef(
6489
6314
  initialTopPercent = 60,
6490
6315
  ...rest
6491
6316
  }, ref) => {
6492
- const [topPercent, setTopPercent] = useState19(initialTopPercent);
6493
- const [isResizingHeight, setIsResizingHeight] = useState19(false);
6494
- const containerRef = useRef12(null);
6495
- const lastY = useRef12(null);
6317
+ const [topPercent, setTopPercent] = useState20(initialTopPercent);
6318
+ const [isResizingHeight, setIsResizingHeight] = useState20(false);
6319
+ const containerRef = useRef13(null);
6320
+ const lastY = useRef13(null);
6496
6321
  const hasBoth = topContent !== null && bottomContent !== null;
6497
- const startHeightResize = useCallback18((e) => {
6322
+ const startHeightResize = useCallback20((e) => {
6498
6323
  e.preventDefault();
6499
6324
  setIsResizingHeight(true);
6500
6325
  lastY.current = e.clientY;
6501
6326
  }, []);
6502
- const stopHeightResize = useCallback18(() => {
6327
+ const stopHeightResize = useCallback20(() => {
6503
6328
  setIsResizingHeight(false);
6504
6329
  lastY.current = null;
6505
6330
  }, []);
6506
- const resizeHeight = useCallback18(
6331
+ const resizeHeight = useCallback20(
6507
6332
  (e) => {
6508
6333
  if (!isResizingHeight || lastY.current === null || !containerRef.current) {
6509
6334
  return;
@@ -6544,14 +6369,7 @@ var ToolPanelContainer = React73.forwardRef(
6544
6369
  return /* @__PURE__ */ React73.createElement(
6545
6370
  "div",
6546
6371
  {
6547
- ref: (node) => {
6548
- containerRef.current = node;
6549
- if (typeof ref === "function") {
6550
- ref(node);
6551
- } else if (ref) {
6552
- ref.current = node;
6553
- }
6554
- },
6372
+ ref: composeRefs(containerRef, ref),
6555
6373
  className: cx(
6556
6374
  "h-full bg-charcoal/50 flex flex-col relative shrink-0",
6557
6375
  side === "left" ? "border-r border-ash/40" : "border-l border-ash/40",
@@ -6605,26 +6423,26 @@ var ToolPanelContainer = React73.forwardRef(
6605
6423
  ToolPanelContainer.displayName = "ToolPanelContainer";
6606
6424
 
6607
6425
  // src/components/chat/hooks/useResizable.ts
6608
- import { useCallback as useCallback19, useEffect as useEffect15, useRef as useRef13, useState as useState20 } from "react";
6426
+ import { useCallback as useCallback21, useEffect as useEffect15, useRef as useRef14, useState as useState21 } from "react";
6609
6427
  function useResizable({
6610
6428
  initialWidthPercent,
6611
6429
  minWidthPercent,
6612
6430
  maxWidthPercent,
6613
6431
  direction
6614
6432
  }) {
6615
- const [widthPercent, setWidthPercent] = useState20(initialWidthPercent);
6616
- const [isResizing, setIsResizing] = useState20(false);
6617
- const lastX = useRef13(null);
6618
- const startResizing = useCallback19((e) => {
6433
+ const [widthPercent, setWidthPercent] = useState21(initialWidthPercent);
6434
+ const [isResizing, setIsResizing] = useState21(false);
6435
+ const lastX = useRef14(null);
6436
+ const startResizing = useCallback21((e) => {
6619
6437
  e.preventDefault();
6620
6438
  setIsResizing(true);
6621
6439
  lastX.current = e.clientX;
6622
6440
  }, []);
6623
- const stopResizing = useCallback19(() => {
6441
+ const stopResizing = useCallback21(() => {
6624
6442
  setIsResizing(false);
6625
6443
  lastX.current = null;
6626
6444
  }, []);
6627
- const resize = useCallback19(
6445
+ const resize = useCallback21(
6628
6446
  (e) => {
6629
6447
  if (!isResizing || lastX.current === null) {
6630
6448
  return;
@@ -6686,6 +6504,7 @@ var ChatInterface = React74.forwardRef(
6686
6504
  enableMessageActions = true,
6687
6505
  attachments: propsAttachments,
6688
6506
  onAttachmentsChange,
6507
+ onAttachmentRemove,
6689
6508
  artifactNodes,
6690
6509
  isArtifactsPanelOpen,
6691
6510
  onArtifactsPanelOpenChange,
@@ -6700,15 +6519,15 @@ var ChatInterface = React74.forwardRef(
6700
6519
  className,
6701
6520
  ...rest
6702
6521
  }, ref) => {
6703
- const prevArtifactNodesRef = useRef14([]);
6704
- const prevTasksRef = useRef14([]);
6705
- const [internalTools, setInternalTools] = useState21({
6522
+ const prevArtifactNodesRef = useRef15([]);
6523
+ const prevTasksRef = useRef15([]);
6524
+ const [internalTools, setInternalTools] = useState22({
6706
6525
  "top-left": "history",
6707
6526
  "bottom-left": null,
6708
6527
  "top-right": null,
6709
6528
  "bottom-right": null
6710
6529
  });
6711
- const dismissedToolsRef = useRef14(/* @__PURE__ */ new Set());
6530
+ const dismissedToolsRef = useRef15(/* @__PURE__ */ new Set());
6712
6531
  const isPanelControlled = isArtifactsPanelOpen !== void 0;
6713
6532
  const activeTools = useMemo5(() => {
6714
6533
  if (isPanelControlled) {
@@ -6754,7 +6573,7 @@ var ChatInterface = React74.forwardRef(
6754
6573
  const external = externalTools.map(({ content: _content, ...def }) => def);
6755
6574
  return [...builtIn, ...external];
6756
6575
  }, [allSettled, externalTools]);
6757
- const toggleTool = useCallback20((toolId) => {
6576
+ const toggleTool = useCallback22((toolId) => {
6758
6577
  const toolDef = allToolDefinitions.find((t) => t.id === toolId);
6759
6578
  if (!toolDef) {
6760
6579
  return;
@@ -6823,7 +6642,7 @@ var ChatInterface = React74.forwardRef(
6823
6642
  prevArtifactNodesRef.current = nodes;
6824
6643
  prevTasksRef.current = tasks;
6825
6644
  }, [artifactNodes, tasks, isPanelControlled]);
6826
- const handleBranchSwitch = useCallback20(
6645
+ const handleBranchSwitch = useCallback22(
6827
6646
  (nodeId, direction) => {
6828
6647
  if (!isTreeMode || !conversationTree || !onTreeChange) {
6829
6648
  return;
@@ -6876,7 +6695,7 @@ var ChatInterface = React74.forwardRef(
6876
6695
  onRetryMessage,
6877
6696
  handleBranchSwitch
6878
6697
  ]);
6879
- const handleSubmit = useCallback20(
6698
+ const handleSubmit = useCallback22(
6880
6699
  (message, attachments) => {
6881
6700
  onMessageSubmit?.(message, attachments);
6882
6701
  },
@@ -6995,6 +6814,7 @@ var ChatInterface = React74.forwardRef(
6995
6814
  showAttachmentButton,
6996
6815
  attachments: propsAttachments,
6997
6816
  onAttachmentsChange,
6817
+ onAttachmentRemove,
6998
6818
  notice: inputNotice,
6999
6819
  onInputChange,
7000
6820
  initialInputValue,
@@ -7030,8 +6850,8 @@ var ChatInterface = React74.forwardRef(
7030
6850
  ChatInterface.displayName = "ChatInterface";
7031
6851
 
7032
6852
  // src/components/chat/MessageActions.tsx
7033
- import React75, { useCallback as useCallback21, useState as useState22 } from "react";
7034
- import { Check as Check3, Copy, Pencil, RotateCcw, Send as Send2, X as X6 } from "lucide-react";
6853
+ import React75, { useCallback as useCallback23, useState as useState23 } from "react";
6854
+ import { Check as Check4, Copy as Copy2, Pencil as Pencil3, RotateCcw as RotateCcw2, Send as Send3, X as X7 } from "lucide-react";
7035
6855
  var ActionButton2 = ({ onClick, label, children, className, disabled }) => /* @__PURE__ */ React75.createElement(
7036
6856
  "button",
7037
6857
  {
@@ -7060,12 +6880,12 @@ var MessageActions = React75.forwardRef(
7060
6880
  className,
7061
6881
  ...rest
7062
6882
  }, ref) => {
7063
- const [localIsEditing, setLocalIsEditing] = useState22(false);
7064
- const [localEditValue, setLocalEditValue] = useState22(content);
7065
- const [copied, setCopied] = useState22(false);
6883
+ const [localIsEditing, setLocalIsEditing] = useState23(false);
6884
+ const [localEditValue, setLocalEditValue] = useState23(content);
6885
+ const { copied, copy } = useCopyToClipboard();
7066
6886
  const isEditing = controlledIsEditing ?? localIsEditing;
7067
6887
  const editValue = controlledEditValue ?? localEditValue;
7068
- const setIsEditing = useCallback21(
6888
+ const setIsEditing = useCallback23(
7069
6889
  (value) => {
7070
6890
  if (onEditingChange) {
7071
6891
  onEditingChange(value);
@@ -7075,41 +6895,28 @@ var MessageActions = React75.forwardRef(
7075
6895
  },
7076
6896
  [onEditingChange]
7077
6897
  );
7078
- const setEditValue = useCallback21((value) => {
6898
+ const setEditValue = useCallback23((value) => {
7079
6899
  setLocalEditValue(value);
7080
6900
  }, []);
7081
- const handleCopy = useCallback21(async () => {
7082
- try {
7083
- await navigator.clipboard.writeText(content);
7084
- setCopied(true);
7085
- setTimeout(() => setCopied(false), 2e3);
7086
- } catch {
7087
- const textArea = document.createElement("textarea");
7088
- textArea.value = content;
7089
- document.body.appendChild(textArea);
7090
- textArea.select();
7091
- document.execCommand("copy");
7092
- document.body.removeChild(textArea);
7093
- setCopied(true);
7094
- setTimeout(() => setCopied(false), 2e3);
7095
- }
7096
- }, [content]);
7097
- const handleStartEdit = useCallback21(() => {
6901
+ const handleCopy = useCallback23(() => {
6902
+ void copy(content);
6903
+ }, [copy, content]);
6904
+ const handleStartEdit = useCallback23(() => {
7098
6905
  setLocalEditValue(content);
7099
6906
  setIsEditing(true);
7100
6907
  }, [content, setIsEditing]);
7101
- const handleCancelEdit = useCallback21(() => {
6908
+ const handleCancelEdit = useCallback23(() => {
7102
6909
  setIsEditing(false);
7103
6910
  setLocalEditValue(content);
7104
6911
  }, [content, setIsEditing]);
7105
- const handleSubmitEdit = useCallback21(() => {
6912
+ const handleSubmitEdit = useCallback23(() => {
7106
6913
  const trimmed = editValue.trim();
7107
6914
  if (trimmed && trimmed !== content) {
7108
6915
  onEdit?.(trimmed);
7109
6916
  }
7110
6917
  setIsEditing(false);
7111
6918
  }, [editValue, content, onEdit, setIsEditing]);
7112
- const handleEditKeyDown = useCallback21(
6919
+ const handleEditKeyDown = useCallback23(
7113
6920
  (e) => {
7114
6921
  if (e.key === "Enter" && !e.shiftKey) {
7115
6922
  e.preventDefault();
@@ -7152,7 +6959,7 @@ var MessageActions = React75.forwardRef(
7152
6959
  label: "Cancel edit",
7153
6960
  className: "text-silver/60 hover:text-error"
7154
6961
  },
7155
- /* @__PURE__ */ React75.createElement(X6, { className: "w-4 h-4" })
6962
+ /* @__PURE__ */ React75.createElement(X7, { className: "w-4 h-4" })
7156
6963
  ), /* @__PURE__ */ React75.createElement(
7157
6964
  ActionButton2,
7158
6965
  {
@@ -7161,7 +6968,7 @@ var MessageActions = React75.forwardRef(
7161
6968
  className: "text-silver/60 hover:text-gold",
7162
6969
  disabled: !editValue.trim() || editValue.trim() === content
7163
6970
  },
7164
- /* @__PURE__ */ React75.createElement(Send2, { className: "w-4 h-4" })
6971
+ /* @__PURE__ */ React75.createElement(Send3, { className: "w-4 h-4" })
7165
6972
  ))
7166
6973
  ),
7167
6974
  /* @__PURE__ */ React75.createElement("p", { className: "text-xs text-silver/50 mt-1" }, "Press Enter to submit, Esc to cancel. This will create a new branch.")
@@ -7178,9 +6985,9 @@ var MessageActions = React75.forwardRef(
7178
6985
  ),
7179
6986
  ...rest
7180
6987
  },
7181
- /* @__PURE__ */ React75.createElement(ActionButton2, { onClick: handleCopy, label: copied ? "Copied!" : "Copy message" }, copied ? /* @__PURE__ */ React75.createElement(Check3, { className: "w-3.5 h-3.5 text-success" }) : /* @__PURE__ */ React75.createElement(Copy, { className: "w-3.5 h-3.5" })),
7182
- isUser && onEdit && /* @__PURE__ */ React75.createElement(ActionButton2, { onClick: handleStartEdit, label: "Edit message" }, /* @__PURE__ */ React75.createElement(Pencil, { className: "w-3.5 h-3.5" })),
7183
- !isUser && onRetry && /* @__PURE__ */ React75.createElement(ActionButton2, { onClick: onRetry, label: "Regenerate response" }, /* @__PURE__ */ React75.createElement(RotateCcw, { className: "w-3.5 h-3.5" }))
6988
+ /* @__PURE__ */ React75.createElement(ActionButton2, { onClick: handleCopy, label: copied ? "Copied!" : "Copy message" }, copied ? /* @__PURE__ */ React75.createElement(Check4, { className: "w-3.5 h-3.5 text-success" }) : /* @__PURE__ */ React75.createElement(Copy2, { className: "w-3.5 h-3.5" })),
6989
+ isUser && onEdit && /* @__PURE__ */ React75.createElement(ActionButton2, { onClick: handleStartEdit, label: "Edit message" }, /* @__PURE__ */ React75.createElement(Pencil3, { className: "w-3.5 h-3.5" })),
6990
+ !isUser && onRetry && /* @__PURE__ */ React75.createElement(ActionButton2, { onClick: onRetry, label: "Regenerate response" }, /* @__PURE__ */ React75.createElement(RotateCcw2, { className: "w-3.5 h-3.5" }))
7184
6991
  );
7185
6992
  }
7186
6993
  );
@@ -7188,7 +6995,7 @@ MessageActions.displayName = "MessageActions";
7188
6995
 
7189
6996
  // src/components/chat/BranchNavigator.tsx
7190
6997
  import React76 from "react";
7191
- import { ChevronLeft as ChevronLeft2, ChevronRight as ChevronRight3, GitBranch } from "lucide-react";
6998
+ import { ChevronLeft as ChevronLeft3, ChevronRight as ChevronRight4, GitBranch as GitBranch2 } from "lucide-react";
7192
6999
  var BranchNavigator = React76.forwardRef(
7193
7000
  ({
7194
7001
  current,
@@ -7220,7 +7027,7 @@ var BranchNavigator = React76.forwardRef(
7220
7027
  "aria-label": "Branch navigation",
7221
7028
  ...rest
7222
7029
  },
7223
- showIcon && /* @__PURE__ */ React76.createElement(GitBranch, { className: cx(iconSize, "mr-0.5 text-silver/50"), "aria-hidden": "true" }),
7030
+ showIcon && /* @__PURE__ */ React76.createElement(GitBranch2, { className: cx(iconSize, "mr-0.5 text-silver/50"), "aria-hidden": "true" }),
7224
7031
  /* @__PURE__ */ React76.createElement(
7225
7032
  "button",
7226
7033
  {
@@ -7234,7 +7041,7 @@ var BranchNavigator = React76.forwardRef(
7234
7041
  ),
7235
7042
  "aria-label": "Previous branch"
7236
7043
  },
7237
- /* @__PURE__ */ React76.createElement(ChevronLeft2, { className: iconSize })
7044
+ /* @__PURE__ */ React76.createElement(ChevronLeft3, { className: iconSize })
7238
7045
  ),
7239
7046
  /* @__PURE__ */ React76.createElement("span", { className: cx(textSize, "tabular-nums min-w-6 text-center") }, current, "/", total),
7240
7047
  /* @__PURE__ */ React76.createElement(
@@ -7250,7 +7057,7 @@ var BranchNavigator = React76.forwardRef(
7250
7057
  ),
7251
7058
  "aria-label": "Next branch"
7252
7059
  },
7253
- /* @__PURE__ */ React76.createElement(ChevronRight3, { className: iconSize })
7060
+ /* @__PURE__ */ React76.createElement(ChevronRight4, { className: iconSize })
7254
7061
  )
7255
7062
  );
7256
7063
  }
@@ -7468,14 +7275,11 @@ export {
7468
7275
  addMessageToTree,
7469
7276
  areAllTasksSettled,
7470
7277
  createEmptyTree,
7471
- createPreviewUrl,
7472
7278
  generateId,
7473
7279
  getActivePathMessages,
7474
7280
  getSiblingInfo,
7475
7281
  isBranchPoint,
7476
- isImageFile,
7477
7282
  messagesToTree,
7478
- revokePreviewUrl,
7479
7283
  switchBranch,
7480
7284
  updateNodeContent,
7481
7285
  useArtifactTreeNavigation,