@lukeashford/aurelius 4.2.0 → 4.3.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
@@ -1320,42 +1320,84 @@ function getFileIcon(type) {
1320
1320
  }
1321
1321
  return File;
1322
1322
  }
1323
- var statusStyles = {
1323
+ var statusBorderClass = {
1324
1324
  pending: "border-silver/30",
1325
1325
  uploading: "border-gold/50",
1326
- complete: "border-success/50",
1327
- error: "border-error/50"
1326
+ uploaded: "border-info/50",
1327
+ analyzing: "border-info/50",
1328
+ analyzed: "border-success/50",
1329
+ upload_failed: "border-error/50",
1330
+ analysis_failed: "border-error/50"
1328
1331
  };
1332
+ var statusHoverLabel = {
1333
+ pending: null,
1334
+ uploading: "Uploading...",
1335
+ uploaded: "Upload complete. Analyzing...",
1336
+ analyzing: "Upload complete. Analyzing...",
1337
+ analyzed: null,
1338
+ upload_failed: "Upload failed. Remove and try again.",
1339
+ analysis_failed: "Analysis failed. Provide a description in your next message."
1340
+ };
1341
+ function isErrorStatus(status) {
1342
+ return status === "upload_failed" || status === "analysis_failed";
1343
+ }
1329
1344
  var FileChip = React15.forwardRef(
1330
1345
  ({
1331
1346
  name,
1332
1347
  size,
1333
1348
  type,
1334
- status = "complete",
1349
+ status = "analyzed",
1335
1350
  previewUrl,
1336
1351
  onRemove,
1337
1352
  removable = true,
1338
1353
  error,
1354
+ artifactId,
1355
+ onOpen,
1339
1356
  className,
1357
+ title,
1340
1358
  ...rest
1341
1359
  }, ref) => {
1342
1360
  const Icon = getFileIcon(type);
1343
1361
  const isImage = type?.startsWith("image/");
1344
1362
  const showPreview = isImage && previewUrl;
1363
+ const clickable = !!(artifactId && onOpen);
1364
+ const hoverLabel = statusHoverLabel[status];
1365
+ const tooltip = title ?? hoverLabel ?? name;
1366
+ const showError = isErrorStatus(status);
1367
+ const handleClick = () => {
1368
+ if (clickable) {
1369
+ onOpen(artifactId);
1370
+ }
1371
+ };
1372
+ const handleKeyDown = (e) => {
1373
+ if (!clickable) {
1374
+ return;
1375
+ }
1376
+ if (e.key === "Enter" || e.key === " ") {
1377
+ e.preventDefault();
1378
+ onOpen(artifactId);
1379
+ }
1380
+ };
1345
1381
  return /* @__PURE__ */ React15.createElement(
1346
1382
  "div",
1347
1383
  {
1384
+ ...rest,
1348
1385
  ref,
1349
1386
  className: cx(
1350
1387
  "group relative inline-flex items-center gap-2 px-2 py-1.5",
1351
1388
  "bg-charcoal border text-sm text-white",
1352
1389
  "transition-colors duration-150",
1353
- statusStyles[status],
1354
- status === "error" && "bg-error/10",
1390
+ statusBorderClass[status],
1391
+ showError && "bg-error/10",
1392
+ clickable && "cursor-pointer hover:bg-graphite",
1355
1393
  className
1356
1394
  ),
1357
- role: "listitem",
1358
- ...rest
1395
+ role: clickable ? "button" : "listitem",
1396
+ tabIndex: clickable ? 0 : void 0,
1397
+ onClick: clickable ? handleClick : void 0,
1398
+ onKeyDown: clickable ? handleKeyDown : void 0,
1399
+ title: tooltip,
1400
+ "aria-label": hoverLabel ? `${name}: ${hoverLabel}` : name
1359
1401
  },
1360
1402
  showPreview ? /* @__PURE__ */ React15.createElement("div", { className: "w-8 h-8 flex-shrink-0 overflow-hidden bg-slate" }, /* @__PURE__ */ React15.createElement(
1361
1403
  "img",
@@ -1366,10 +1408,11 @@ var FileChip = React15.forwardRef(
1366
1408
  }
1367
1409
  )) : /* @__PURE__ */ React15.createElement(Icon, { className: cx(
1368
1410
  "w-4 h-4 flex-shrink-0",
1369
- status === "error" ? "text-error" : "text-silver"
1411
+ showError ? "text-error" : "text-silver"
1370
1412
  ) }),
1371
- /* @__PURE__ */ React15.createElement("div", { className: "flex flex-col min-w-0 flex-1" }, /* @__PURE__ */ React15.createElement("span", { className: "truncate max-w-40", title: name }, name), size !== void 0 && status !== "error" && /* @__PURE__ */ React15.createElement("span", { className: "text-xs text-silver/60" }, formatBytes(size)), status === "error" && error && /* @__PURE__ */ React15.createElement("span", { className: "text-xs text-error truncate", title: error }, error)),
1413
+ /* @__PURE__ */ React15.createElement("div", { className: "flex flex-col min-w-0 flex-1" }, /* @__PURE__ */ React15.createElement("span", { className: "truncate max-w-40", title: name }, name), size !== void 0 && !showError && /* @__PURE__ */ React15.createElement("span", { className: "text-xs text-silver/60" }, formatBytes(size)), showError && error && /* @__PURE__ */ React15.createElement("span", { className: "text-xs text-error truncate", title: error }, error)),
1372
1414
  status === "uploading" && /* @__PURE__ */ React15.createElement(Loader2, { className: "w-3.5 h-3.5 text-gold animate-spin flex-shrink-0" }),
1415
+ (status === "uploaded" || status === "analyzing") && /* @__PURE__ */ React15.createElement(Loader2, { className: "w-3.5 h-3.5 text-info animate-spin flex-shrink-0" }),
1373
1416
  status === "pending" && /* @__PURE__ */ React15.createElement("div", { className: "w-2 h-2 rounded-full bg-silver/50 flex-shrink-0" }),
1374
1417
  removable && onRemove && /* @__PURE__ */ React15.createElement(
1375
1418
  "button",
@@ -1402,6 +1445,7 @@ var AttachmentPreview = React16.forwardRef(
1402
1445
  onRemove,
1403
1446
  removable = true,
1404
1447
  maxVisible,
1448
+ onOpen,
1405
1449
  className,
1406
1450
  ...rest
1407
1451
  }, ref) => {
@@ -1430,7 +1474,9 @@ var AttachmentPreview = React16.forwardRef(
1430
1474
  previewUrl: attachment.previewUrl,
1431
1475
  error: attachment.error,
1432
1476
  removable,
1433
- onRemove: onRemove ? () => onRemove(attachment.id) : void 0
1477
+ onRemove: onRemove ? () => onRemove(attachment.id) : void 0,
1478
+ artifactId: attachment.artifactId,
1479
+ onOpen
1434
1480
  }
1435
1481
  )),
1436
1482
  hiddenCount > 0 && /* @__PURE__ */ React16.createElement(
@@ -3890,6 +3936,8 @@ var Message = React55.forwardRef(
3890
3936
  branchInfo,
3891
3937
  actions,
3892
3938
  hideActions,
3939
+ attachments,
3940
+ onAttachmentOpen,
3893
3941
  ...rest
3894
3942
  }, ref) => {
3895
3943
  const isUser = variant === "user";
@@ -3957,6 +4005,14 @@ var Message = React55.forwardRef(
3957
4005
  ),
3958
4006
  ...rest
3959
4007
  },
4008
+ attachments && attachments.length > 0 && /* @__PURE__ */ React55.createElement("div", { className: cx("mb-1.5", isUser ? "self-end" : "self-start") }, /* @__PURE__ */ React55.createElement(
4009
+ AttachmentPreview,
4010
+ {
4011
+ attachments,
4012
+ removable: false,
4013
+ onOpen: onAttachmentOpen
4014
+ }
4015
+ )),
3960
4016
  isUser && isEditing ? /* @__PURE__ */ React55.createElement("div", { className: "w-full max-w-11/12" }, /* @__PURE__ */ React55.createElement("div", { className: "relative bg-gold" }, /* @__PURE__ */ React55.createElement(
3961
4017
  "textarea",
3962
4018
  {
@@ -4207,6 +4263,7 @@ var ThinkingIndicator = React57.forwardRef(
4207
4263
  isVisible = true,
4208
4264
  phraseInterval = 2500,
4209
4265
  phrases = THINKING_PHRASES,
4266
+ manualLabel,
4210
4267
  className,
4211
4268
  ...rest
4212
4269
  }, ref) => {
@@ -4214,8 +4271,9 @@ var ThinkingIndicator = React57.forwardRef(
4214
4271
  () => Math.floor(Math.random() * phrases.length)
4215
4272
  );
4216
4273
  const [isTransitioning, setIsTransitioning] = useState13(false);
4274
+ const isManual = manualLabel !== void 0;
4217
4275
  useEffect8(() => {
4218
- if (!isVisible || phrases.length <= 1) {
4276
+ if (!isVisible || isManual || phrases.length <= 1) {
4219
4277
  return;
4220
4278
  }
4221
4279
  let fadeTimeout = null;
@@ -4233,7 +4291,7 @@ var ThinkingIndicator = React57.forwardRef(
4233
4291
  clearTimeout(fadeTimeout);
4234
4292
  }
4235
4293
  };
4236
- }, [isVisible, phrases.length, phraseInterval]);
4294
+ }, [isVisible, isManual, phrases.length, phraseInterval]);
4237
4295
  if (!isVisible) {
4238
4296
  return null;
4239
4297
  }
@@ -4270,7 +4328,7 @@ var ThinkingIndicator = React57.forwardRef(
4270
4328
  style: { animationDelay: "300ms" }
4271
4329
  }
4272
4330
  )),
4273
- /* @__PURE__ */ React57.createElement(
4331
+ isManual ? /* @__PURE__ */ React57.createElement("span", { className: "text-sm italic" }, manualLabel) : /* @__PURE__ */ React57.createElement(
4274
4332
  "span",
4275
4333
  {
4276
4334
  className: cx(
@@ -4294,19 +4352,22 @@ import {
4294
4352
  GitBranch as GitBranch2,
4295
4353
  GitCommitVertical,
4296
4354
  GitMerge,
4297
- PencilLine
4355
+ PencilLine,
4356
+ Upload
4298
4357
  } from "lucide-react";
4299
4358
  var KIND_ICONS = {
4300
4359
  task: GitBranch2,
4301
4360
  submit: GitMerge,
4302
4361
  rename: PencilLine,
4303
- init: GitCommitVertical
4362
+ init: GitCommitVertical,
4363
+ ingest: Upload
4304
4364
  };
4305
4365
  var KIND_ARIA_LABELS = {
4306
4366
  task: "Task checkpoint",
4307
4367
  submit: "Submit checkpoint",
4308
4368
  rename: "Rename checkpoint",
4309
- init: "Project head checkpoint"
4369
+ init: "Project head checkpoint",
4370
+ ingest: "Upload batch checkpoint"
4310
4371
  };
4311
4372
  var Checkpoint = React58.forwardRef(
4312
4373
  function Checkpoint2({ name, executionKind, status = "completed", isActive, muted, branchInfo, onJumpHere }, ref) {
@@ -4455,7 +4516,16 @@ GreyedDivider.displayName = "GreyedDivider";
4455
4516
 
4456
4517
  // src/components/chat/ChatView.tsx
4457
4518
  var ChatView = React60.forwardRef(
4458
- function ChatView2({ items, latestUserMessageIndex, isStreaming, isThinking, onScroll, className, ...rest }, ref) {
4519
+ function ChatView2({
4520
+ items,
4521
+ latestUserMessageIndex,
4522
+ isStreaming,
4523
+ isThinking,
4524
+ thinkingLabel,
4525
+ onScroll,
4526
+ className,
4527
+ ...rest
4528
+ }, ref) {
4459
4529
  const { containerRef, anchorRef, scrollToAnchor } = useScrollAnchor({
4460
4530
  behavior: "smooth",
4461
4531
  block: "start"
@@ -4538,7 +4608,7 @@ var ChatView = React60.forwardRef(
4538
4608
  }
4539
4609
  )
4540
4610
  );
4541
- }), showThinking && /* @__PURE__ */ React60.createElement(ThinkingIndicator, { isVisible: true })),
4611
+ }), showThinking && /* @__PURE__ */ React60.createElement(ThinkingIndicator, { isVisible: true, manualLabel: thinkingLabel })),
4542
4612
  /* @__PURE__ */ React60.createElement(
4543
4613
  "div",
4544
4614
  {
@@ -4723,8 +4793,8 @@ var ChatInput = React61.forwardRef(
4723
4793
  );
4724
4794
  const isCentered = position === "centered";
4725
4795
  const hasAttachments = attachments.length > 0;
4726
- const isUploading = attachments.some((a) => a.status === "uploading");
4727
- const canSubmit = value.trim() && !disabled && !isStreaming && !isUploading;
4796
+ const isUploadIncomplete = attachments.some((a) => a.status === "pending" || a.status === "uploading" || a.status === "upload_failed");
4797
+ const canSubmit = value.trim() && !disabled && !isStreaming && !isUploadIncomplete;
4728
4798
  return /* @__PURE__ */ React61.createElement(
4729
4799
  "div",
4730
4800
  {
@@ -4861,7 +4931,7 @@ var ChatInput = React61.forwardRef(
4861
4931
  ChatInput.displayName = "ChatInput";
4862
4932
 
4863
4933
  // src/components/chat/ArtifactsPanel.tsx
4864
- import React71, { useCallback as useCallback17, useEffect as useEffect12, useRef as useRef11, useState as useState17 } from "react";
4934
+ import React71, { useCallback as useCallback18, useEffect as useEffect13, useRef as useRef12, useState as useState18 } from "react";
4865
4935
  import { Image } from "lucide-react";
4866
4936
 
4867
4937
  // src/components/ArtifactCard.tsx
@@ -5519,10 +5589,69 @@ var ArtifactVariantStack = React70.forwardRef(
5519
5589
  );
5520
5590
  ArtifactVariantStack.displayName = "ArtifactVariantStack";
5521
5591
 
5592
+ // src/components/chat/hooks/useResizable.ts
5593
+ import { useCallback as useCallback16, useEffect as useEffect12, useRef as useRef11, useState as useState16 } from "react";
5594
+ function useResizable({
5595
+ initialWidthPercent,
5596
+ minWidthPercent,
5597
+ maxWidthPercent,
5598
+ direction
5599
+ }) {
5600
+ const [widthPercent, setWidthPercent] = useState16(initialWidthPercent);
5601
+ const [isResizing, setIsResizing] = useState16(false);
5602
+ const lastX = useRef11(null);
5603
+ const startResizing = useCallback16((e) => {
5604
+ e.preventDefault();
5605
+ setIsResizing(true);
5606
+ lastX.current = e.clientX;
5607
+ }, []);
5608
+ const stopResizing = useCallback16(() => {
5609
+ setIsResizing(false);
5610
+ lastX.current = null;
5611
+ }, []);
5612
+ const resize = useCallback16(
5613
+ (e) => {
5614
+ if (!isResizing || lastX.current === null) {
5615
+ return;
5616
+ }
5617
+ const deltaX = e.clientX - lastX.current;
5618
+ const factor = direction === "right" ? 1 : -1;
5619
+ const deltaPercent = deltaX / window.innerWidth * 100;
5620
+ setWidthPercent((prevPercent) => {
5621
+ const newPercent = prevPercent + deltaPercent * factor;
5622
+ return Math.min(Math.max(newPercent, minWidthPercent), maxWidthPercent);
5623
+ });
5624
+ lastX.current = e.clientX;
5625
+ },
5626
+ [isResizing, direction, minWidthPercent, maxWidthPercent]
5627
+ );
5628
+ useEffect12(() => {
5629
+ if (isResizing) {
5630
+ window.addEventListener("mousemove", resize);
5631
+ window.addEventListener("mouseup", stopResizing);
5632
+ document.body.style.cursor = "col-resize";
5633
+ document.body.style.userSelect = "none";
5634
+ } else {
5635
+ window.removeEventListener("mousemove", resize);
5636
+ window.removeEventListener("mouseup", stopResizing);
5637
+ document.body.style.cursor = "";
5638
+ document.body.style.userSelect = "";
5639
+ }
5640
+ return () => {
5641
+ window.removeEventListener("mousemove", resize);
5642
+ window.removeEventListener("mouseup", stopResizing);
5643
+ document.body.style.cursor = "";
5644
+ document.body.style.userSelect = "";
5645
+ };
5646
+ }, [isResizing, resize, stopResizing]);
5647
+ const width = `${widthPercent}vw`;
5648
+ return { width, widthPercent, isResizing, startResizing };
5649
+ }
5650
+
5522
5651
  // src/components/chat/hooks/useArtifactTreeNavigation.ts
5523
- import { useCallback as useCallback16, useMemo as useMemo2, useState as useState16 } from "react";
5652
+ import { useCallback as useCallback17, useMemo as useMemo2, useState as useState17 } from "react";
5524
5653
  function useArtifactTreeNavigation(rootNodes) {
5525
- const [stack, setStack] = useState16([]);
5654
+ const [stack, setStack] = useState17([]);
5526
5655
  const currentNodes = useMemo2(() => {
5527
5656
  if (stack.length === 0) return rootNodes;
5528
5657
  return stack[stack.length - 1].children;
@@ -5535,13 +5664,13 @@ function useArtifactTreeNavigation(rootNodes) {
5535
5664
  return entries;
5536
5665
  }, [stack]);
5537
5666
  const isAtRoot = stack.length === 0;
5538
- const navigateInto = useCallback16((node) => {
5667
+ const navigateInto = useCallback17((node) => {
5539
5668
  setStack((prev) => [...prev, node]);
5540
5669
  }, []);
5541
- const navigateTo = useCallback16((index) => {
5670
+ const navigateTo = useCallback17((index) => {
5542
5671
  setStack((prev) => prev.slice(0, index));
5543
5672
  }, []);
5544
- const navigateBack = useCallback16(() => {
5673
+ const navigateBack = useCallback17(() => {
5545
5674
  setStack((prev) => prev.slice(0, -1));
5546
5675
  }, []);
5547
5676
  return {
@@ -5561,7 +5690,7 @@ function ArtifactModal({
5561
5690
  onClose
5562
5691
  }) {
5563
5692
  useEscapeKey(onClose);
5564
- const handleBackdropClick = useCallback17((e) => {
5693
+ const handleBackdropClick = useCallback18((e) => {
5565
5694
  if (e.target === e.currentTarget) {
5566
5695
  onClose();
5567
5696
  }
@@ -5642,6 +5771,20 @@ function ArtifactModal({
5642
5771
  )
5643
5772
  );
5644
5773
  }
5774
+ function findArtifactInNodes(nodes, artifactId) {
5775
+ for (const node of nodes) {
5776
+ if (node.type === "ARTIFACT" && node.artifact?.id === artifactId) {
5777
+ return node.artifact;
5778
+ }
5779
+ if (node.children && node.children.length > 0) {
5780
+ const found = findArtifactInNodes(node.children, artifactId);
5781
+ if (found) {
5782
+ return found;
5783
+ }
5784
+ }
5785
+ }
5786
+ return null;
5787
+ }
5645
5788
  function NodeRenderer({
5646
5789
  node,
5647
5790
  loading,
@@ -5677,31 +5820,48 @@ var ArtifactsPanel = React71.forwardRef(
5677
5820
  ({
5678
5821
  nodes,
5679
5822
  loading,
5823
+ openArtifactId,
5824
+ onArtifactClosed,
5680
5825
  className,
5681
5826
  ...rest
5682
5827
  }, ref) => {
5683
- const [expandedArtifact, setExpandedArtifact] = useState17(null);
5684
- const [zoomIndex, setZoomIndex] = useState17(ZOOM_LEVELS.length - 1);
5828
+ const [expandedArtifact, setExpandedArtifact] = useState18(null);
5829
+ const [zoomIndex, setZoomIndex] = useState18(ZOOM_LEVELS.length - 1);
5685
5830
  const treeNav = useArtifactTreeNavigation(nodes || []);
5686
5831
  const hasNodes = !!nodes && nodes.length > 0;
5687
- const handleExpandArtifact = useCallback17((artifact) => {
5832
+ const handleExpandArtifact = useCallback18((artifact) => {
5688
5833
  setExpandedArtifact(artifact);
5689
5834
  }, []);
5690
- const handleGroupClick = useCallback17((node) => {
5835
+ const handleGroupClick = useCallback18((node) => {
5691
5836
  treeNav.navigateInto(node);
5692
5837
  }, [treeNav]);
5693
- const zoomIn = useCallback17(() => {
5838
+ useEffect13(() => {
5839
+ if (!openArtifactId || !nodes) {
5840
+ return;
5841
+ }
5842
+ const found = findArtifactInNodes(nodes, openArtifactId);
5843
+ if (found) {
5844
+ setExpandedArtifact(found);
5845
+ }
5846
+ }, [openArtifactId, nodes]);
5847
+ const handleModalClose = useCallback18(() => {
5848
+ setExpandedArtifact(null);
5849
+ onArtifactClosed?.();
5850
+ }, [onArtifactClosed]);
5851
+ const zoomIn = useCallback18(() => {
5694
5852
  setZoomIndex((prev) => Math.min(prev + 1, ZOOM_LEVELS.length - 1));
5695
5853
  }, []);
5696
- const zoomOut = useCallback17(() => {
5854
+ const zoomOut = useCallback18(() => {
5697
5855
  setZoomIndex((prev) => Math.max(prev - 1, 0));
5698
5856
  }, []);
5699
5857
  const currentZoom = ZOOM_LEVELS[zoomIndex];
5700
- const contentRef = useRef11(null);
5701
- const [contentHeight, setContentHeight] = useState17(void 0);
5702
- useEffect12(() => {
5858
+ const contentRef = useRef12(null);
5859
+ const [contentHeight, setContentHeight] = useState18(void 0);
5860
+ useEffect13(() => {
5703
5861
  const el = contentRef.current;
5704
- if (!el) return;
5862
+ if (!el) {
5863
+ return;
5864
+ }
5705
5865
  const observer = new ResizeObserver(([entry]) => {
5706
5866
  setContentHeight(entry.contentRect.height);
5707
5867
  });
@@ -5745,7 +5905,15 @@ var ArtifactsPanel = React71.forwardRef(
5745
5905
  },
5746
5906
  "\u2212"
5747
5907
  ),
5748
- /* @__PURE__ */ React71.createElement("span", { className: "text-xs text-silver w-8 text-center tabular-nums", "data-testid": "zoom-level" }, Math.round(currentZoom * 100), "%"),
5908
+ /* @__PURE__ */ React71.createElement(
5909
+ "span",
5910
+ {
5911
+ className: "text-xs text-silver w-8 text-center tabular-nums",
5912
+ "data-testid": "zoom-level"
5913
+ },
5914
+ Math.round(currentZoom * 100),
5915
+ "%"
5916
+ ),
5749
5917
  /* @__PURE__ */ React71.createElement(
5750
5918
  "button",
5751
5919
  {
@@ -5820,7 +5988,7 @@ var ArtifactsPanel = React71.forwardRef(
5820
5988
  ArtifactModal,
5821
5989
  {
5822
5990
  artifact: expandedArtifact,
5823
- onClose: () => setExpandedArtifact(null)
5991
+ onClose: handleModalClose
5824
5992
  }
5825
5993
  ));
5826
5994
  }
@@ -5857,7 +6025,7 @@ var ArtifactsPanelToggle = React71.forwardRef(({ artifactCount = 0, onExpand, cl
5857
6025
  ArtifactsPanelToggle.displayName = "ArtifactsPanelToggle";
5858
6026
 
5859
6027
  // src/components/chat/HistoryPanel.tsx
5860
- import React72, { useCallback as useCallback18, useEffect as useEffect13, useMemo as useMemo3, useRef as useRef12, useState as useState18 } from "react";
6028
+ import React72, { useCallback as useCallback19, useEffect as useEffect14, useMemo as useMemo3, useRef as useRef13, useState as useState19 } from "react";
5861
6029
  import { ChevronDown as ChevronDown2, Pencil as Pencil2 } from "lucide-react";
5862
6030
  function parseTimestamp(ts) {
5863
6031
  if (ts == null) {
@@ -5900,9 +6068,9 @@ function ProjectFilter({
5900
6068
  onChange,
5901
6069
  className
5902
6070
  }) {
5903
- const [open, setOpen] = useState18(false);
5904
- const ref = useRef12(null);
5905
- const closeFilter = useCallback18(() => setOpen(false), []);
6071
+ const [open, setOpen] = useState19(false);
6072
+ const ref = useRef13(null);
6073
+ const closeFilter = useCallback19(() => setOpen(false), []);
5906
6074
  useClickOutside(ref, closeFilter, open);
5907
6075
  const label = value ?? "All projects";
5908
6076
  return /* @__PURE__ */ React72.createElement("div", { className: cx("relative min-w-0", className), ref }, /* @__PURE__ */ React72.createElement(
@@ -5977,28 +6145,28 @@ function ConversationRow({
5977
6145
  onSelect,
5978
6146
  onRename
5979
6147
  }) {
5980
- const [isEditing, setIsEditing] = useState18(false);
5981
- const [draft, setDraft] = useState18(conversation.title);
5982
- const inputRef = useRef12(null);
5983
- useEffect13(() => {
6148
+ const [isEditing, setIsEditing] = useState19(false);
6149
+ const [draft, setDraft] = useState19(conversation.title);
6150
+ const inputRef = useRef13(null);
6151
+ useEffect14(() => {
5984
6152
  if (isEditing && inputRef.current) {
5985
6153
  inputRef.current.focus();
5986
6154
  inputRef.current.select();
5987
6155
  }
5988
6156
  }, [isEditing]);
5989
- const startEdit = useCallback18((e) => {
6157
+ const startEdit = useCallback19((e) => {
5990
6158
  e.stopPropagation();
5991
6159
  setDraft(conversation.title);
5992
6160
  setIsEditing(true);
5993
6161
  }, [conversation.title]);
5994
- const commit = useCallback18(() => {
6162
+ const commit = useCallback19(() => {
5995
6163
  const trimmed = draft.trim();
5996
6164
  if (trimmed && trimmed !== conversation.title) {
5997
6165
  onRename?.(conversation.id, trimmed);
5998
6166
  }
5999
6167
  setIsEditing(false);
6000
6168
  }, [draft, conversation.id, conversation.title, onRename]);
6001
- const cancel = useCallback18(() => {
6169
+ const cancel = useCallback19(() => {
6002
6170
  setDraft(conversation.title);
6003
6171
  setIsEditing(false);
6004
6172
  }, [conversation.title]);
@@ -6082,7 +6250,7 @@ function HistoryPanel({
6082
6250
  onNewChat,
6083
6251
  onRenameConversation
6084
6252
  }) {
6085
- const [projectFilter, setProjectFilter] = useState18(null);
6253
+ const [projectFilter, setProjectFilter] = useState19(null);
6086
6254
  const projects = useMemo3(() => {
6087
6255
  const set = /* @__PURE__ */ new Set();
6088
6256
  for (const c of conversations) {
@@ -6092,7 +6260,7 @@ function HistoryPanel({
6092
6260
  }
6093
6261
  return Array.from(set).sort((a, b) => a.localeCompare(b));
6094
6262
  }, [conversations]);
6095
- useEffect13(() => {
6263
+ useEffect14(() => {
6096
6264
  if (projectFilter && !projects.includes(projectFilter)) {
6097
6265
  setProjectFilter(null);
6098
6266
  }
@@ -6148,7 +6316,7 @@ function HistoryPanel({
6148
6316
  }
6149
6317
 
6150
6318
  // src/components/chat/TodosList.tsx
6151
- import React73, { useCallback as useCallback19, useMemo as useMemo4, useState as useState19 } from "react";
6319
+ import React73, { useCallback as useCallback20, useMemo as useMemo4, useState as useState20 } from "react";
6152
6320
  import { Loader2 as Loader22, Square as Square2 } from "lucide-react";
6153
6321
  var TASK_STATUSES = {
6154
6322
  PENDING: "pending",
@@ -6227,8 +6395,8 @@ function hasInProgressTask(tasks) {
6227
6395
  var TodosList = React73.forwardRef(
6228
6396
  ({ tasks, title = "Tasks", onStopAllTasks, className, ...rest }, ref) => {
6229
6397
  const sortedTasks = useMemo4(() => sortTasks(tasks), [tasks]);
6230
- const [isStopping, setIsStopping] = useState19(false);
6231
- const handleStopClick = useCallback19(async () => {
6398
+ const [isStopping, setIsStopping] = useState20(false);
6399
+ const handleStopClick = useCallback20(async () => {
6232
6400
  if (!onStopAllTasks || isStopping) {
6233
6401
  return;
6234
6402
  }
@@ -6353,7 +6521,7 @@ var ToolSidebar = React74.forwardRef(
6353
6521
  ToolSidebar.displayName = "ToolSidebar";
6354
6522
 
6355
6523
  // src/components/chat/ToolPanelContainer.tsx
6356
- import React75, { useCallback as useCallback20, useEffect as useEffect14, useRef as useRef13, useState as useState20 } from "react";
6524
+ import React75, { useCallback as useCallback21, useEffect as useEffect15, useRef as useRef14, useState as useState21 } from "react";
6357
6525
  var ToolPanelContainer = React75.forwardRef(
6358
6526
  ({
6359
6527
  topContent,
@@ -6365,21 +6533,21 @@ var ToolPanelContainer = React75.forwardRef(
6365
6533
  initialTopPercent = 60,
6366
6534
  ...rest
6367
6535
  }, ref) => {
6368
- const [topPercent, setTopPercent] = useState20(initialTopPercent);
6369
- const [isResizingHeight, setIsResizingHeight] = useState20(false);
6370
- const containerRef = useRef13(null);
6371
- const lastY = useRef13(null);
6536
+ const [topPercent, setTopPercent] = useState21(initialTopPercent);
6537
+ const [isResizingHeight, setIsResizingHeight] = useState21(false);
6538
+ const containerRef = useRef14(null);
6539
+ const lastY = useRef14(null);
6372
6540
  const hasBoth = topContent !== null && bottomContent !== null;
6373
- const startHeightResize = useCallback20((e) => {
6541
+ const startHeightResize = useCallback21((e) => {
6374
6542
  e.preventDefault();
6375
6543
  setIsResizingHeight(true);
6376
6544
  lastY.current = e.clientY;
6377
6545
  }, []);
6378
- const stopHeightResize = useCallback20(() => {
6546
+ const stopHeightResize = useCallback21(() => {
6379
6547
  setIsResizingHeight(false);
6380
6548
  lastY.current = null;
6381
6549
  }, []);
6382
- const resizeHeight = useCallback20(
6550
+ const resizeHeight = useCallback21(
6383
6551
  (e) => {
6384
6552
  if (!isResizingHeight || lastY.current === null || !containerRef.current) {
6385
6553
  return;
@@ -6398,7 +6566,7 @@ var ToolPanelContainer = React75.forwardRef(
6398
6566
  },
6399
6567
  [isResizingHeight]
6400
6568
  );
6401
- useEffect14(() => {
6569
+ useEffect15(() => {
6402
6570
  if (isResizingHeight) {
6403
6571
  window.addEventListener("mousemove", resizeHeight);
6404
6572
  window.addEventListener("mouseup", stopHeightResize);
@@ -6473,65 +6641,6 @@ var ToolPanelContainer = React75.forwardRef(
6473
6641
  );
6474
6642
  ToolPanelContainer.displayName = "ToolPanelContainer";
6475
6643
 
6476
- // src/components/chat/hooks/useResizable.ts
6477
- import { useCallback as useCallback21, useEffect as useEffect15, useRef as useRef14, useState as useState21 } from "react";
6478
- function useResizable({
6479
- initialWidthPercent,
6480
- minWidthPercent,
6481
- maxWidthPercent,
6482
- direction
6483
- }) {
6484
- const [widthPercent, setWidthPercent] = useState21(initialWidthPercent);
6485
- const [isResizing, setIsResizing] = useState21(false);
6486
- const lastX = useRef14(null);
6487
- const startResizing = useCallback21((e) => {
6488
- e.preventDefault();
6489
- setIsResizing(true);
6490
- lastX.current = e.clientX;
6491
- }, []);
6492
- const stopResizing = useCallback21(() => {
6493
- setIsResizing(false);
6494
- lastX.current = null;
6495
- }, []);
6496
- const resize = useCallback21(
6497
- (e) => {
6498
- if (!isResizing || lastX.current === null) {
6499
- return;
6500
- }
6501
- const deltaX = e.clientX - lastX.current;
6502
- const factor = direction === "right" ? 1 : -1;
6503
- const deltaPercent = deltaX / window.innerWidth * 100;
6504
- setWidthPercent((prevPercent) => {
6505
- const newPercent = prevPercent + deltaPercent * factor;
6506
- return Math.min(Math.max(newPercent, minWidthPercent), maxWidthPercent);
6507
- });
6508
- lastX.current = e.clientX;
6509
- },
6510
- [isResizing, direction, minWidthPercent, maxWidthPercent]
6511
- );
6512
- useEffect15(() => {
6513
- if (isResizing) {
6514
- window.addEventListener("mousemove", resize);
6515
- window.addEventListener("mouseup", stopResizing);
6516
- document.body.style.cursor = "col-resize";
6517
- document.body.style.userSelect = "none";
6518
- } else {
6519
- window.removeEventListener("mousemove", resize);
6520
- window.removeEventListener("mouseup", stopResizing);
6521
- document.body.style.cursor = "";
6522
- document.body.style.userSelect = "";
6523
- }
6524
- return () => {
6525
- window.removeEventListener("mousemove", resize);
6526
- window.removeEventListener("mouseup", stopResizing);
6527
- document.body.style.cursor = "";
6528
- document.body.style.userSelect = "";
6529
- };
6530
- }, [isResizing, resize, stopResizing]);
6531
- const width = `${widthPercent}vw`;
6532
- return { width, widthPercent, isResizing, startResizing };
6533
- }
6534
-
6535
6644
  // src/components/chat/tree.ts
6536
6645
  function createEmptyTree() {
6537
6646
  return { nodes: {}, rootIds: [], activeLeafId: null, lastLeafId: null };
@@ -6692,6 +6801,7 @@ var ChatInterface = React76.forwardRef(
6692
6801
  onRenameConversation,
6693
6802
  isStreaming = false,
6694
6803
  isThinking = false,
6804
+ thinkingLabel,
6695
6805
  placeholder = "Send a message...",
6696
6806
  emptyStateHelper = "Let's talk.",
6697
6807
  emptyState,
@@ -6700,6 +6810,7 @@ var ChatInterface = React76.forwardRef(
6700
6810
  attachments: propsAttachments,
6701
6811
  onAttachmentsChange,
6702
6812
  onAttachmentRemove,
6813
+ onAttachmentOpen,
6703
6814
  artifactNodes,
6704
6815
  isArtifactsPanelOpen,
6705
6816
  onArtifactsPanelOpenChange,
@@ -6716,6 +6827,14 @@ var ChatInterface = React76.forwardRef(
6716
6827
  }, ref) => {
6717
6828
  const prevArtifactNodesRef = useRef15([]);
6718
6829
  const prevTasksRef = useRef15([]);
6830
+ const [panelOpenArtifactId, setPanelOpenArtifactId] = useState22(null);
6831
+ const handleAttachmentOpen = useCallback22((artifactId) => {
6832
+ setPanelOpenArtifactId(artifactId);
6833
+ onAttachmentOpen?.(artifactId);
6834
+ }, [onAttachmentOpen]);
6835
+ const handleArtifactPanelClosed = useCallback22(() => {
6836
+ setPanelOpenArtifactId(null);
6837
+ }, []);
6719
6838
  const [internalTools, setInternalTools] = useState22({
6720
6839
  "top-left": "history",
6721
6840
  "bottom-left": null,
@@ -6903,7 +7022,15 @@ var ChatInterface = React76.forwardRef(
6903
7022
  isStreaming: node.isStreaming,
6904
7023
  muted: opts.muted,
6905
7024
  branchInfo,
6906
- actions
7025
+ actions,
7026
+ attachments: node.attachments ? node.attachments.map((a) => ({
7027
+ id: a.id,
7028
+ file: { name: a.name, size: a.size ?? 0, type: a.type },
7029
+ previewUrl: a.previewUrl,
7030
+ artifactId: a.artifactId,
7031
+ status: a.status ?? "analyzed"
7032
+ })) : void 0,
7033
+ onAttachmentOpen: handleAttachmentOpen
6907
7034
  };
6908
7035
  },
6909
7036
  [
@@ -6913,7 +7040,8 @@ var ChatInterface = React76.forwardRef(
6913
7040
  onEditMessage,
6914
7041
  onRetryMessage,
6915
7042
  handleBranchSwitch,
6916
- handleJumpToCheckpoint
7043
+ handleJumpToCheckpoint,
7044
+ handleAttachmentOpen
6917
7045
  ]
6918
7046
  );
6919
7047
  const displayItems = useMemo5(() => {
@@ -6982,6 +7110,8 @@ var ChatInterface = React76.forwardRef(
6982
7110
  ArtifactsPanel,
6983
7111
  {
6984
7112
  nodes: artifactNodes,
7113
+ openArtifactId: panelOpenArtifactId,
7114
+ onArtifactClosed: handleArtifactPanelClosed,
6985
7115
  className: "h-full"
6986
7116
  }
6987
7117
  );
@@ -7044,6 +7174,7 @@ var ChatInterface = React76.forwardRef(
7044
7174
  latestUserMessageIndex,
7045
7175
  isStreaming,
7046
7176
  isThinking,
7177
+ thinkingLabel,
7047
7178
  className: "flex-1"
7048
7179
  }
7049
7180
  )), /* @__PURE__ */ React76.createElement("div", { className: cx(