'use strict';

var chunkTBSHZO5R_cjs = require('./chunk-TBSHZO5R.cjs');
var chunk7IYXZUJO_cjs = require('./chunk-7IYXZUJO.cjs');
var chunkOLISEQHS_cjs = require('./chunk-OLISEQHS.cjs');
var react = require('react');
var lib = require('@djangocfg/ui-core/lib');
var lucideReact = require('lucide-react');
var components = require('@djangocfg/ui-core/components');
var jsxRuntime = require('react/jsx-runtime');
var reactVirtuoso = require('react-virtuoso');
var hooks = require('@djangocfg/ui-core/hooks');

function useAutoFocusOnStreamEnd(options = {}) {
  const { isStreaming: isStreamingProp, targetRef, enabled = true, delayMs = 0 } = options;
  const ctx = chunkTBSHZO5R_cjs.useChatContextOptional();
  const isStreaming = isStreamingProp ?? ctx?.isStreaming ?? false;
  const composerHandleRef = react.useRef(null);
  composerHandleRef.current = ctx?.composer ?? null;
  const prevStreamingRef = react.useRef(isStreaming);
  react.useEffect(() => {
    const wasStreaming = prevStreamingRef.current;
    prevStreamingRef.current = isStreaming;
    if (!enabled) return;
    if (!(wasStreaming && !isStreaming)) return;
    const focusNow = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => {
      const explicit = targetRef?.current;
      const target = explicit ?? composerHandleRef.current;
      target?.focus();
    }, "focusNow");
    if (delayMs > 0) {
      const id = window.setTimeout(focusNow, delayMs);
      return () => window.clearTimeout(id);
    }
    const raf = requestAnimationFrame(focusNow);
    return () => cancelAnimationFrame(raf);
  }, [isStreaming, enabled, delayMs, targetRef]);
}
chunkOLISEQHS_cjs.__name(useAutoFocusOnStreamEnd, "useAutoFocusOnStreamEnd");
function useRegisterComposer(handle) {
  const ctx = chunkTBSHZO5R_cjs.useChatContextOptional();
  const register = ctx?.registerComposer;
  const focus = handle.focus;
  const moveCursorToEnd = handle.moveCursorToEnd;
  react.useEffect(() => {
    if (!register) return;
    register({ focus, moveCursorToEnd });
    return () => register(null);
  }, [register, focus, moveCursorToEnd]);
}
chunkOLISEQHS_cjs.__name(useRegisterComposer, "useRegisterComposer");

// src/tools/Chat/utils/sanitizeDraft.ts
function sanitizeDraft(input) {
  if (!input) return "";
  let s = input.replace(/[​‌‍﻿]/g, "");
  s = s.replace(/\r\n?/g, "\n");
  s = s.trim();
  return s;
}
chunkOLISEQHS_cjs.__name(sanitizeDraft, "sanitizeDraft");
function isSubmittableDraft(input) {
  return sanitizeDraft(input).length > 0;
}
chunkOLISEQHS_cjs.__name(isSubmittableDraft, "isSubmittableDraft");

// src/tools/Chat/hooks/useChatComposer.ts
var MAX_TEXTAREA_HEIGHT = 240;
function useChatComposer(options) {
  const {
    onSubmit,
    initialValue = "",
    maxLength = chunkTBSHZO5R_cjs.LIMITS.messageMaxLength,
    maxAttachments = chunkTBSHZO5R_cjs.LIMITS.attachmentsMax,
    disabled = false,
    submitOn = "enter",
    history = { enabled: true, size: chunkTBSHZO5R_cjs.LIMITS.composerHistorySize },
    onPasteFiles,
    persistKey,
    preserveExactValue = false
  } = options;
  const initialFromStorage = (() => {
    if (!persistKey || typeof window === "undefined") return initialValue;
    try {
      const stored = window.sessionStorage.getItem(`chat:draft:${persistKey}`);
      return stored && stored.length > 0 ? stored : initialValue;
    } catch {
      return initialValue;
    }
  })();
  const [value, setValueState] = react.useState(initialFromStorage);
  react.useEffect(() => {
    if (!persistKey || typeof window === "undefined") return;
    try {
      const k = `chat:draft:${persistKey}`;
      if (value.length > 0) window.sessionStorage.setItem(k, value);
      else window.sessionStorage.removeItem(k);
    } catch {
    }
  }, [value, persistKey]);
  const [attachments, setAttachments] = react.useState([]);
  const [isSubmitting, setIsSubmitting] = react.useState(false);
  const textareaRef = react.useRef(null);
  const historyRef = react.useRef({ items: [], index: -1 });
  const setValue = react.useCallback(
    (next) => {
      setValueState(next.length > maxLength ? next.slice(0, maxLength) : next);
    },
    [maxLength]
  );
  react.useEffect(() => {
    const el = textareaRef.current;
    if (!el) return;
    el.style.height = "auto";
    el.style.height = `${Math.min(el.scrollHeight, MAX_TEXTAREA_HEIGHT)}px`;
  }, [value]);
  const reset = react.useCallback(() => {
    setValueState("");
    setAttachments([]);
    historyRef.current.index = -1;
  }, []);
  const focus = react.useCallback(() => {
    requestAnimationFrame(() => textareaRef.current?.focus());
  }, []);
  const submit = react.useCallback(async () => {
    const cleaned = preserveExactValue ? value : sanitizeDraft(value);
    if (!cleaned && attachments.length === 0 || isSubmitting || disabled) return;
    setIsSubmitting(true);
    try {
      if (history.enabled !== false && cleaned) {
        const buf = historyRef.current.items;
        if (buf[buf.length - 1] !== cleaned) {
          buf.push(cleaned);
          if (buf.length > (history.size ?? chunkTBSHZO5R_cjs.LIMITS.composerHistorySize)) buf.shift();
        }
        historyRef.current.index = -1;
      }
      const snapshot = [...attachments];
      reset();
      await onSubmit(cleaned, snapshot);
    } finally {
      setIsSubmitting(false);
    }
  }, [value, attachments, isSubmitting, disabled, history, onSubmit, reset, preserveExactValue]);
  const addAttachment = react.useCallback(
    (a) => {
      setAttachments((prev) => {
        if (prev.some((p) => p.id === a.id)) {
          return prev.map((p) => p.id === a.id ? a : p);
        }
        if (prev.length >= maxAttachments) return prev;
        return [...prev, a];
      });
    },
    [maxAttachments]
  );
  const removeAttachment = react.useCallback((id) => {
    setAttachments((prev) => prev.filter((a) => a.id !== id));
  }, []);
  const recallPrevious = react.useCallback(() => {
    const { items } = historyRef.current;
    if (!items.length) return;
    const next = historyRef.current.index < 0 ? items.length - 1 : Math.max(0, historyRef.current.index - 1);
    historyRef.current.index = next;
    setValueState(items[next]);
  }, []);
  const recallNext = react.useCallback(() => {
    const { items } = historyRef.current;
    if (!items.length || historyRef.current.index < 0) return;
    const next = historyRef.current.index + 1;
    if (next >= items.length) {
      historyRef.current.index = -1;
      setValueState("");
      return;
    }
    historyRef.current.index = next;
    setValueState(items[next]);
  }, []);
  const onChange = react.useCallback(
    (e) => {
      setValue(e.target.value);
    },
    [setValue]
  );
  const onKeyDown = react.useCallback(
    (e) => {
      if (e.key === "Enter") {
        const isCmd = e.metaKey || e.ctrlKey;
        const shouldSend = submitOn === "cmd+enter" ? isCmd : !e.shiftKey;
        if (shouldSend) {
          e.preventDefault();
          void submit();
        }
        return;
      }
      if (history.enabled !== false && value === "" && attachments.length === 0) {
        if (e.key === "ArrowUp") {
          e.preventDefault();
          recallPrevious();
        } else if (e.key === "ArrowDown" && historyRef.current.index >= 0) {
          e.preventDefault();
          recallNext();
        }
      }
    },
    [submitOn, submit, history, value, attachments.length, recallPrevious, recallNext]
  );
  const onPaste = react.useCallback(
    (e) => {
      const files = Array.from(e.clipboardData?.files ?? []);
      if (files.length && onPasteFiles) {
        e.preventDefault();
        onPasteFiles(files);
      }
    },
    [onPasteFiles]
  );
  const canSubmit = !disabled && !isSubmitting && ((preserveExactValue ? value.trim().length : sanitizeDraft(value).length) > 0 || attachments.length > 0);
  return {
    value,
    setValue,
    attachments,
    addAttachment,
    removeAttachment,
    isSubmitting,
    canSubmit,
    submit,
    reset,
    focus,
    textareaRef,
    textareaProps: {
      ref: textareaRef,
      value,
      disabled,
      onChange,
      onKeyDown,
      onPaste
    },
    recallPrevious,
    recallNext
  };
}
chunkOLISEQHS_cjs.__name(useChatComposer, "useChatComposer");
function useFocusOnEmptyClick(options = {}) {
  const { targetRef, enabled = true, skipWhileStreaming = true } = options;
  const ctx = chunkTBSHZO5R_cjs.useChatContextOptional();
  const composerHandleRef = react.useRef(null);
  composerHandleRef.current = ctx?.composer ?? null;
  const isStreamingRef = react.useRef(false);
  isStreamingRef.current = ctx?.isStreaming ?? false;
  return react.useCallback(
    (event) => {
      if (!enabled) return;
      if (skipWhileStreaming && isStreamingRef.current) return;
      const pointerType = event.nativeEvent.pointerType;
      if (pointerType && pointerType !== "mouse") return;
      const selection = typeof window !== "undefined" ? window.getSelection?.() : null;
      if (selection && !selection.isCollapsed && selection.toString().length > 0) {
        return;
      }
      const target = event.target;
      if (!target) return;
      if (isInteractive(target)) return;
      const explicit = targetRef?.current;
      if (explicit) {
        explicit.focus?.();
        return;
      }
      composerHandleRef.current?.focus?.();
    },
    [enabled, skipWhileStreaming, targetRef]
  );
}
chunkOLISEQHS_cjs.__name(useFocusOnEmptyClick, "useFocusOnEmptyClick");
var INTERACTIVE_SELECTORS = [
  "a[href]",
  "button",
  "input",
  "textarea",
  "select",
  "label",
  "summary",
  '[role="button"]',
  '[role="link"]',
  '[role="menuitem"]',
  '[role="tab"]',
  '[contenteditable]:not([contenteditable="false"])',
  "[data-no-autofocus]"
].join(",");
function isInteractive(el) {
  if (!el) return false;
  return !!el.closest(INTERACTIVE_SELECTORS);
}
chunkOLISEQHS_cjs.__name(isInteractive, "isInteractive");
function useChatBubbleStyles(role, isError) {
  return react.useMemo(() => {
    const isUser = role === "user";
    const variant = isUser ? "user" : isError ? "error" : "assistant";
    return {
      surface: chunk7IYXZUJO_cjs.BUBBLE_SURFACE[variant],
      anchor: isUser ? chunk7IYXZUJO_cjs.ANCHOR.user : chunk7IYXZUJO_cjs.ANCHOR.assistant,
      toggle: isUser ? chunk7IYXZUJO_cjs.TOGGLE.user : chunk7IYXZUJO_cjs.TOGGLE.assistant
    };
  }, [role, isError]);
}
chunkOLISEQHS_cjs.__name(useChatBubbleStyles, "useChatBubbleStyles");
function useChatRoleStyles(isUser) {
  return react.useMemo(
    () => ({
      anchor: isUser ? chunk7IYXZUJO_cjs.ANCHOR.user : chunk7IYXZUJO_cjs.ANCHOR.assistant,
      toggle: isUser ? chunk7IYXZUJO_cjs.TOGGLE.user : chunk7IYXZUJO_cjs.TOGGLE.assistant
    }),
    [isUser]
  );
}
chunkOLISEQHS_cjs.__name(useChatRoleStyles, "useChatRoleStyles");
function useChatDestructiveStyles() {
  return DESTRUCTIVE_STYLES;
}
chunkOLISEQHS_cjs.__name(useChatDestructiveStyles, "useChatDestructiveStyles");
var DESTRUCTIVE_STYLES = {
  banner: chunk7IYXZUJO_cjs.DESTRUCTIVE_SURFACE.banner,
  hover: chunk7IYXZUJO_cjs.DESTRUCTIVE_SURFACE.hover,
  hoverStrong: chunk7IYXZUJO_cjs.DESTRUCTIVE_SURFACE.hoverStrong,
  text: chunk7IYXZUJO_cjs.DESTRUCTIVE_SURFACE.text,
  menuItem: chunk7IYXZUJO_cjs.DESTRUCTIVE_SURFACE.menuItem,
  toolErrorText: chunk7IYXZUJO_cjs.TOOL_CALL.errorText
};
function AttachmentsGrid({
  attachments,
  maxVisible,
  onClick,
  onRemove,
  isInComposer = false,
  layout = "wrap",
  className
}) {
  if (!attachments?.length) return null;
  const visible = maxVisible ? attachments.slice(0, maxVisible) : attachments;
  return /* @__PURE__ */ jsxRuntime.jsx(
    "div",
    {
      className: lib.cn(
        layout === "grid" ? "grid grid-cols-3 gap-2" : "flex flex-wrap gap-2",
        className
      ),
      children: visible.map((a) => /* @__PURE__ */ jsxRuntime.jsx(
        AttachmentTile,
        {
          attachment: a,
          isInComposer,
          onClick: onClick ? () => onClick(a) : void 0,
          onRemove: onRemove ? () => onRemove(a) : void 0
        },
        a.id
      ))
    }
  );
}
chunkOLISEQHS_cjs.__name(AttachmentsGrid, "AttachmentsGrid");
function AttachmentsList({
  attachments,
  maxVisible,
  onClick,
  onRemove,
  renderers,
  isInComposer = false,
  className
}) {
  if (!attachments?.length) return null;
  const visible = maxVisible ? attachments.slice(0, maxVisible) : attachments;
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: lib.cn("flex w-full flex-col gap-2", className), children: visible.map((a) => {
    const renderer = renderers?.[a.type] ?? renderers?.default;
    const args = {
      attachment: a,
      isInComposer,
      onClick: onClick ? () => onClick(a) : void 0,
      onRemove: onRemove ? () => onRemove(a) : void 0
    };
    if (renderer) {
      return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full min-w-0", children: [
        renderer(args),
        args.onRemove ? /* @__PURE__ */ jsxRuntime.jsx(RemoveBtn, { onRemove: args.onRemove }) : null
      ] }, a.id);
    }
    return /* @__PURE__ */ jsxRuntime.jsx(AttachmentTile, { ...args }, a.id);
  }) });
}
chunkOLISEQHS_cjs.__name(AttachmentsList, "AttachmentsList");
function Attachments(props) {
  const { renderers, layout, ...rest } = props;
  if (renderers) {
    return /* @__PURE__ */ jsxRuntime.jsx(AttachmentsList, { ...rest, renderers });
  }
  return /* @__PURE__ */ jsxRuntime.jsx(AttachmentsGrid, { ...rest, layout: layout === "grid" ? "grid" : "wrap" });
}
chunkOLISEQHS_cjs.__name(Attachments, "Attachments");
function AttachmentTile({ attachment, onClick, onRemove }) {
  const isImage = attachment.type === "image";
  const isUploading = attachment.status === "uploading";
  const inner = isImage ? /* @__PURE__ */ jsxRuntime.jsx(
    "img",
    {
      src: attachment.thumbnailUrl ?? attachment.url,
      alt: attachment.name ?? "attachment",
      className: "h-16 w-16 rounded-md object-cover",
      loading: "lazy"
    }
  ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex max-w-44 items-center gap-2 rounded-md border border-border bg-background/60 px-2 py-1.5 text-xs", children: [
    /* @__PURE__ */ jsxRuntime.jsx(lucideReact.File, { "aria-hidden": true, className: "size-4 shrink-0 text-muted-foreground" }),
    /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: attachment.name ?? "file" })
  ] });
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
    onClick ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick, className: "block", children: inner }) : inner,
    isUploading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center rounded-md bg-background/70 text-[10px] font-medium", children: attachment.progress != null ? `${Math.round(attachment.progress * 100)}%` : "\u2026" }) : null,
    onRemove ? /* @__PURE__ */ jsxRuntime.jsx(RemoveBtn, { onRemove }) : null
  ] });
}
chunkOLISEQHS_cjs.__name(AttachmentTile, "AttachmentTile");
function RemoveBtn({ onRemove }) {
  const styles = useChatDestructiveStyles();
  return /* @__PURE__ */ jsxRuntime.jsx(
    "button",
    {
      type: "button",
      "aria-label": "Remove attachment",
      onClick: onRemove,
      className: lib.cn(
        "absolute -right-1.5 -top-1.5 grid h-4 w-4 place-items-center rounded-full border border-border bg-background text-muted-foreground",
        styles.hoverStrong
      ),
      children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { "aria-hidden": true, className: "size-2.5" })
    }
  );
}
chunkOLISEQHS_cjs.__name(RemoveBtn, "RemoveBtn");
var SIZE_CLASSES = {
  sm: {
    slot: "[&>:not(textarea)]:h-8",
    button: "h-8 w-8",
    iconButton: "size-3.5",
    textarea: "min-h-8 max-h-48 px-3 py-1.5",
    text: "text-sm",
    padding: "gap-1.5",
    containerPadding: "px-2 pt-1.5 pb-[max(0.375rem,env(safe-area-inset-bottom))]"
  },
  md: {
    slot: "[&>:not(textarea)]:h-9",
    button: "h-9 w-9",
    iconButton: "size-4",
    textarea: "min-h-9 max-h-60 px-3.5 py-2",
    text: "text-base sm:text-sm",
    padding: "gap-1.5",
    containerPadding: "px-2.5 pt-2 pb-[max(0.5rem,env(safe-area-inset-bottom))]"
  },
  lg: {
    slot: "[&>:not(textarea)]:h-12",
    button: "h-12 w-12",
    iconButton: "size-5",
    textarea: "min-h-12 max-h-72 px-4 py-3",
    text: "text-base",
    padding: "gap-2",
    containerPadding: "px-3.5 pt-3 pb-[max(0.875rem,env(safe-area-inset-bottom))]"
  }
};
var Composer = react.forwardRef(/* @__PURE__ */ chunkOLISEQHS_cjs.__name(function Composer2({
  composer,
  placeholder = "Type a message...",
  disabled,
  showAttachmentButton = false,
  onPickFiles,
  toolbarStart,
  toolbarEnd,
  attachmentTray,
  className,
  textareaClassName,
  size = "md",
  isStreaming: isStreamingProp,
  onCancel: onCancelProp
}, ref) {
  const ctx = chunkTBSHZO5R_cjs.useChatContextOptional();
  const isStreaming = isStreamingProp ?? ctx?.isStreaming ?? false;
  const onCancel = onCancelProp ?? ctx?.cancelStream;
  const isDisabled = disabled ?? isStreaming;
  const sz = SIZE_CLASSES[size];
  const register = ctx?.registerComposer;
  const composerFocus = composer.focus;
  const composerSetValue = composer.setValue;
  const textareaRef = composer.textareaRef;
  const getValueRef = react.useRef(() => composer.value);
  getValueRef.current = () => composer.value;
  react.useEffect(() => {
    if (!register) return;
    register({
      focus: composerFocus,
      moveCursorToEnd: /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => {
        const el = textareaRef.current;
        if (!el) return;
        const end = el.value.length;
        el.setSelectionRange(end, end);
      }, "moveCursorToEnd"),
      getValue: /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => getValueRef.current(), "getValue"),
      setValue: composerSetValue
    });
    return () => register(null);
  }, [register, composerFocus, composerSetValue, textareaRef]);
  return /* @__PURE__ */ jsxRuntime.jsxs(
    "div",
    {
      ref,
      className: lib.cn(
        "border-t border-border bg-background/95",
        sz.containerPadding,
        className
      ),
      children: [
        composer.attachments.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-1.5", children: attachmentTray ?? /* @__PURE__ */ jsxRuntime.jsx(
          Attachments,
          {
            attachments: composer.attachments,
            onRemove: (a) => composer.removeAttachment(a.id)
          }
        ) }) : null,
        /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex items-end [&>:not(textarea)]:shrink-0", sz.padding, sz.slot), children: [
          showAttachmentButton ? /* @__PURE__ */ jsxRuntime.jsx(
            components.Button,
            {
              type: "button",
              variant: "ghost",
              size: "icon",
              onClick: onPickFiles,
              "aria-label": "Attach files",
              disabled: isDisabled,
              className: sz.button,
              children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Paperclip, { "aria-hidden": true, className: sz.iconButton })
            }
          ) : null,
          toolbarStart,
          /* @__PURE__ */ jsxRuntime.jsx(
            components.Textarea,
            {
              ...composer.textareaProps,
              rows: 1,
              placeholder,
              "aria-label": placeholder,
              "aria-multiline": "true",
              disabled: isDisabled,
              className: lib.cn(
                "flex-1 resize-none rounded-2xl",
                sz.textarea,
                sz.text,
                textareaClassName
              )
            }
          ),
          toolbarEnd,
          isStreaming ? /* @__PURE__ */ jsxRuntime.jsx(
            components.Button,
            {
              type: "button",
              variant: "secondary",
              size: "icon",
              onClick: onCancel,
              "aria-label": "Stop",
              "aria-keyshortcuts": "Escape",
              className: sz.button,
              children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Square, { "aria-hidden": true, className: sz.iconButton })
            }
          ) : /* @__PURE__ */ jsxRuntime.jsx(
            components.Button,
            {
              type: "button",
              size: "icon",
              onClick: () => void composer.submit(),
              disabled: !composer.canSubmit,
              "aria-label": "Send",
              "aria-keyshortcuts": "Enter",
              className: sz.button,
              children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { "aria-hidden": true, className: sz.iconButton })
            }
          )
        ] })
      ]
    }
  );
}, "Composer"));
function EmptyState({
  greeting,
  description,
  suggestions,
  onPickSuggestion,
  className
}) {
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex flex-col items-center gap-3 px-4 py-12 text-center", className), children: [
    /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid size-10 place-items-center rounded-full bg-muted", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { "aria-hidden": true, className: "size-5 text-muted-foreground" }) }),
    greeting ? /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-base font-semibold", children: greeting }) : null,
    description ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "max-w-md text-sm text-muted-foreground", children: description }) : null,
    suggestions?.length ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 grid w-full max-w-md grid-cols-1 gap-2 sm:grid-cols-2", children: suggestions.map((s) => /* @__PURE__ */ jsxRuntime.jsx(
      "button",
      {
        type: "button",
        onClick: () => onPickSuggestion?.(s.prompt),
        className: "rounded-lg border border-border bg-background/60 px-3 py-2 text-left text-xs hover:bg-accent",
        children: s.label
      },
      s.prompt
    )) }) : null
  ] });
}
chunkOLISEQHS_cjs.__name(EmptyState, "EmptyState");
function ErrorBanner({ error, onDismiss, onRetry, className }) {
  const styles = useChatDestructiveStyles();
  if (!error) return null;
  return /* @__PURE__ */ jsxRuntime.jsxs(
    "div",
    {
      role: "alert",
      className: lib.cn(
        "mx-2.5 my-2 flex items-start gap-2 rounded-md px-3 py-2 text-xs",
        styles.banner,
        className
      ),
      children: [
        /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { "aria-hidden": true, className: "mt-0.5 size-3.5 shrink-0" }),
        /* @__PURE__ */ jsxRuntime.jsx("p", { className: "min-w-0 flex-1 break-words", children: error }),
        onRetry ? /* @__PURE__ */ jsxRuntime.jsxs(
          "button",
          {
            type: "button",
            onClick: onRetry,
            className: lib.cn("inline-flex items-center gap-1 rounded px-1.5 py-0.5", styles.hover),
            children: [
              /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { "aria-hidden": true, className: "size-3" }),
              " Retry"
            ]
          }
        ) : null,
        onDismiss ? /* @__PURE__ */ jsxRuntime.jsx(
          "button",
          {
            type: "button",
            "aria-label": "Dismiss",
            onClick: onDismiss,
            className: lib.cn("rounded p-0.5", styles.hover),
            children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { "aria-hidden": true, className: "size-3" })
          }
        ) : null
      ]
    }
  );
}
chunkOLISEQHS_cjs.__name(ErrorBanner, "ErrorBanner");
function JumpToLatest({ visible, unreadCount = 0, onClick, className }) {
  if (!visible) return null;
  return /* @__PURE__ */ jsxRuntime.jsxs(
    "button",
    {
      type: "button",
      onClick,
      "aria-live": "polite",
      className: lib.cn(
        "pointer-events-auto inline-flex items-center gap-1.5 rounded-full border border-border bg-background px-3 py-1 text-xs shadow-md hover:bg-accent",
        className
      ),
      children: [
        /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { "aria-hidden": true, className: "size-3.5" }),
        unreadCount > 0 ? `${unreadCount} new` : "Jump to latest"
      ]
    }
  );
}
chunkOLISEQHS_cjs.__name(JumpToLatest, "JumpToLatest");

// src/tools/Chat/core/persona.ts
var FALLBACK_USER = { name: "You", initials: "You" };
var FALLBACK_ASSISTANT = { name: "AI", initials: "AI" };
function resolvePersona(message, user, assistant) {
  if (message.sender) return message.sender;
  if (message.role === "user") return user ?? FALLBACK_USER;
  if (message.role === "assistant") return assistant ?? FALLBACK_ASSISTANT;
  return { name: message.role };
}
chunkOLISEQHS_cjs.__name(resolvePersona, "resolvePersona");
function deriveInitials(persona, role) {
  if (persona.initials) return persona.initials;
  if (persona.name) {
    const parts = persona.name.trim().split(/\s+/);
    if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
    return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
  }
  if (role === "user") return "You";
  if (role === "assistant") return "AI";
  return "?";
}
chunkOLISEQHS_cjs.__name(deriveInitials, "deriveInitials");
function StreamingIndicatorRaw({ variant = "dots", label, className }) {
  return /* @__PURE__ */ jsxRuntime.jsxs(
    "span",
    {
      className: lib.cn("inline-flex items-center gap-1.5 text-xs text-muted-foreground", className),
      "aria-live": "off",
      children: [
        variant === "dots" ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex gap-0.5", "aria-hidden": true, children: [
          /* @__PURE__ */ jsxRuntime.jsx("span", { className: "size-1 animate-bounce rounded-full bg-current [animation-delay:-0.2s]" }),
          /* @__PURE__ */ jsxRuntime.jsx("span", { className: "size-1 animate-bounce rounded-full bg-current [animation-delay:-0.1s]" }),
          /* @__PURE__ */ jsxRuntime.jsx("span", { className: "size-1 animate-bounce rounded-full bg-current" })
        ] }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block size-1.5 animate-pulse rounded-full bg-current", "aria-hidden": true }),
        label ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "italic", children: label }) : null
      ]
    }
  );
}
chunkOLISEQHS_cjs.__name(StreamingIndicatorRaw, "StreamingIndicatorRaw");
var StreamingIndicator = react.memo(StreamingIndicatorRaw);
function Sources({ sources, layout = "inline", maxVisible, onClick, className }) {
  if (!sources?.length) return null;
  const visible = maxVisible ? sources.slice(0, maxVisible) : sources;
  const remaining = maxVisible ? Math.max(0, sources.length - maxVisible) : 0;
  return /* @__PURE__ */ jsxRuntime.jsxs(
    "div",
    {
      className: lib.cn(
        "mt-2 flex flex-wrap gap-1.5",
        layout === "grid" && "grid grid-cols-2",
        className
      ),
      children: [
        visible.map((s, i) => {
          const handle = onClick ? () => onClick(s) : void 0;
          const Tag = handle ? "button" : "a";
          const props = handle ? { type: "button", onClick: handle } : { href: s.url, target: "_blank", rel: "noopener noreferrer" };
          return /* @__PURE__ */ jsxRuntime.jsxs(
            Tag,
            {
              ...props,
              className: "inline-flex max-w-full items-center gap-1 rounded-md border border-border bg-background/60 px-2 py-1 text-xs text-foreground/80 hover:bg-accent hover:text-foreground",
              title: s.snippet ?? s.title,
              children: [
                /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: s.title || s.url }),
                /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { "aria-hidden": true, className: "size-3 shrink-0 opacity-60" })
              ]
            },
            `${s.url}-${i}`
          );
        }),
        remaining > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center rounded-md border border-dashed border-border px-2 py-1 text-xs text-muted-foreground", children: [
          "+",
          remaining
        ] }) : null
      ]
    }
  );
}
chunkOLISEQHS_cjs.__name(Sources, "Sources");
function ToolCalls({
  calls,
  defaultExpanded = false,
  expandWhileStreaming = true,
  renderInput,
  renderOutput,
  renderStreaming,
  renderPayload,
  renderAfterCalls,
  renderToolCall,
  hideToolCalls = false,
  className
}) {
  if (!calls?.length) return null;
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("mt-2 space-y-1.5", className), children: [
    !hideToolCalls && calls.map(
      (call) => renderToolCall ? /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderToolCall(call) }, call.id) : /* @__PURE__ */ jsxRuntime.jsx(
        ToolCallItem,
        {
          call,
          defaultExpanded,
          expandWhileStreaming,
          renderInput,
          renderOutput,
          renderStreaming,
          renderPayload
        },
        call.id
      )
    ),
    renderAfterCalls ? renderAfterCalls(calls) : null
  ] });
}
chunkOLISEQHS_cjs.__name(ToolCalls, "ToolCalls");
var ToolCallItem = react.memo(/* @__PURE__ */ chunkOLISEQHS_cjs.__name(function ToolCallItem2({
  call,
  defaultExpanded,
  expandWhileStreaming,
  renderInput,
  renderOutput,
  renderStreaming,
  renderPayload
}) {
  const isRunning = call.status === "running";
  const initialOpen = defaultExpanded || expandWhileStreaming && isRunning;
  const [open, setOpen] = react.useState(initialOpen);
  const userToggledRef = react.useRef(false);
  const wasRunningRef = react.useRef(isRunning);
  react.useEffect(() => {
    if (wasRunningRef.current && !isRunning) {
      if (!userToggledRef.current && !defaultExpanded) {
        setOpen(false);
      }
    }
    wasRunningRef.current = isRunning;
  }, [isRunning, defaultExpanded]);
  const handleToggle = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => {
    userToggledRef.current = true;
    setOpen((v) => !v);
  }, "handleToggle");
  const Icon = open ? lucideReact.ChevronDown : lucideReact.ChevronRight;
  const statusColor = call.status === "success" ? "text-emerald-500" : call.status === "error" ? "text-destructive" : call.status === "cancelled" ? "text-muted-foreground" : "text-amber-500";
  const renderValue = /* @__PURE__ */ chunkOLISEQHS_cjs.__name((value, kind) => {
    if (kind === "input" && renderInput) return renderInput(value, call);
    if (kind === "output" && renderOutput) return renderOutput(value, call);
    if (kind === "streaming" && renderStreaming)
      return renderStreaming(typeof value === "string" ? value : String(value), call);
    if (renderPayload) return renderPayload(value, kind, call);
    return /* @__PURE__ */ jsxRuntime.jsx(DefaultPayload, { value, kind });
  }, "renderValue");
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "overflow-hidden rounded-md border border-border bg-muted/30", children: [
    /* @__PURE__ */ jsxRuntime.jsxs(
      "button",
      {
        type: "button",
        onClick: handleToggle,
        "aria-expanded": open,
        className: "flex w-full items-center gap-2 px-2 py-1.5 text-left text-xs hover:bg-muted/60",
        children: [
          /* @__PURE__ */ jsxRuntime.jsx(Icon, { "aria-hidden": true, className: "size-3 shrink-0 text-muted-foreground" }),
          isRunning ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { "aria-hidden": true, className: "size-3 shrink-0 animate-spin text-amber-500" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: lib.cn("size-2 shrink-0 rounded-full", statusColor.replace("text-", "bg-")) }),
          /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-foreground", children: call.name }),
          /* @__PURE__ */ jsxRuntime.jsx("span", { className: lib.cn("ml-auto", statusColor), children: call.status })
        ]
      }
    ),
    open ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1 border-t border-border px-2 py-1.5 text-[11px]", children: [
      call.input != null ? renderValue(call.input, "input") : null,
      call.streamingText != null ? renderValue(call.streamingText, "streaming") : call.output !== void 0 ? renderValue(call.output, "output") : null
    ] }) : null
  ] });
}, "ToolCallItem"), (prev, next) => {
  const a = prev.call;
  const b = next.call;
  return a.id === b.id && a.status === b.status && a.output === b.output && a.streamingText === b.streamingText && prev.defaultExpanded === next.defaultExpanded && prev.expandWhileStreaming === next.expandWhileStreaming && prev.renderInput === next.renderInput && prev.renderOutput === next.renderOutput && prev.renderStreaming === next.renderStreaming && prev.renderPayload === next.renderPayload;
});
function DefaultPayload({ value, kind }) {
  const isStreamingOrString = kind === "streaming" || typeof value === "string";
  const muted = kind === "input";
  return /* @__PURE__ */ jsxRuntime.jsx(
    "pre",
    {
      className: lib.cn(
        "overflow-auto rounded bg-background/60 p-1.5 font-mono",
        kind === "input" ? "max-h-32" : "max-h-48",
        muted ? "text-muted-foreground" : "text-foreground/90"
      ),
      children: isStreamingOrString ? String(value) : safeStringify(value)
    }
  );
}
chunkOLISEQHS_cjs.__name(DefaultPayload, "DefaultPayload");
function safeStringify(value) {
  try {
    return JSON.stringify(value, null, 2);
  } catch {
    return String(value);
  }
}
chunkOLISEQHS_cjs.__name(safeStringify, "safeStringify");
function MessageActionsRaw({
  role,
  onCopy,
  onRegenerate,
  onEdit,
  onDelete,
  hideOn,
  className
}) {
  if (hideOn?.includes(role)) return null;
  return /* @__PURE__ */ jsxRuntime.jsxs(
    "div",
    {
      className: lib.cn(
        "mt-1 flex items-center gap-0.5 opacity-0 transition-opacity group-hover/msg:opacity-100 focus-within:opacity-100",
        className
      ),
      children: [
        onCopy ? /* @__PURE__ */ jsxRuntime.jsx(ActionButton, { onClick: onCopy, label: "Copy", icon: lucideReact.Copy }) : null,
        onRegenerate && role === "assistant" ? /* @__PURE__ */ jsxRuntime.jsx(ActionButton, { onClick: onRegenerate, label: "Regenerate", icon: lucideReact.RefreshCw }) : null,
        onEdit && role === "user" ? /* @__PURE__ */ jsxRuntime.jsx(ActionButton, { onClick: onEdit, label: "Edit", icon: lucideReact.Pencil }) : null,
        onDelete ? /* @__PURE__ */ jsxRuntime.jsx(ActionButton, { onClick: onDelete, label: "Delete", icon: lucideReact.Trash, destructive: true }) : null
      ]
    }
  );
}
chunkOLISEQHS_cjs.__name(MessageActionsRaw, "MessageActionsRaw");
var ActionButton = react.memo(/* @__PURE__ */ chunkOLISEQHS_cjs.__name(function ActionButton2({
  onClick,
  label,
  icon: Icon,
  destructive
}) {
  const styles = useChatDestructiveStyles();
  return /* @__PURE__ */ jsxRuntime.jsx(
    "button",
    {
      type: "button",
      onClick,
      "aria-label": label,
      className: lib.cn(
        "rounded p-1 text-muted-foreground hover:bg-accent hover:text-foreground",
        destructive && lib.cn(styles.hover, "hover:text-destructive")
      ),
      children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { "aria-hidden": true, className: "size-3" })
    }
  );
}, "ActionButton"));
var MessageActions = react.memo(MessageActionsRaw);
var MessageBubbleInner = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(({
  message,
  isUser: isUserProp,
  showAvatar = true,
  avatarSrc,
  avatarFallback,
  user,
  assistant,
  showTimestamp = false,
  showActions = true,
  isCompact = false,
  className,
  beforeContent,
  afterContent,
  toolCallsRenderer,
  toolCallsProps,
  sourcesRenderer,
  attachmentsRenderer,
  attachmentRenderers,
  onAttachmentOpen,
  onCopy,
  onRegenerate,
  onEdit,
  onDelete,
  messageActionsExtra,
  streamingIndicator
}) => {
  const isUser = isUserProp ?? message.role === "user";
  const isStreaming = !!message.isStreaming;
  const isErr = !!message.isError;
  const { surface: bubbleSurface } = useChatBubbleStyles(
    isUser ? "user" : "assistant",
    isErr
  );
  const ctx = chunkTBSHZO5R_cjs.useChatContextOptional();
  const persona = resolvePersona(
    message,
    user ?? ctx?.config.user,
    assistant ?? ctx?.config.assistant
  );
  const initials = deriveInitials(persona, message.role);
  const personaName = persona.name ?? (isUser ? "You" : "Assistant");
  return /* @__PURE__ */ jsxRuntime.jsxs(
    "div",
    {
      role: "article",
      "aria-label": `${personaName} said: ${message.content.slice(0, 80)}`,
      "aria-busy": isStreaming || void 0,
      "data-role": message.role,
      className: lib.cn(
        "group/msg flex gap-2.5 px-2.5 py-2",
        isUser ? "flex-row-reverse" : "flex-row",
        className
      ),
      children: [
        showAvatar ? /* @__PURE__ */ jsxRuntime.jsxs(
          components.Avatar,
          {
            className: "size-7 shrink-0",
            title: persona.description ?? personaName,
            children: [
              avatarSrc || persona.avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx(components.AvatarImage, { src: avatarSrc ?? persona.avatarUrl, alt: personaName }) : null,
              /* @__PURE__ */ jsxRuntime.jsx(components.AvatarFallback, { className: "text-[10px]", children: avatarFallback ?? initials })
            ]
          }
        ) : null,
        /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("min-w-0 flex-1", isUser && "flex flex-col items-end"), children: [
          beforeContent,
          message.attachments?.length ? attachmentsRenderer ? attachmentsRenderer(message.attachments) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-1.5 w-full", children: attachmentRenderers ? /* @__PURE__ */ jsxRuntime.jsx(
            AttachmentsList,
            {
              attachments: message.attachments,
              renderers: attachmentRenderers,
              onClick: onAttachmentOpen,
              className: isUser ? "items-end" : void 0
            }
          ) : /* @__PURE__ */ jsxRuntime.jsx(
            AttachmentsGrid,
            {
              attachments: message.attachments,
              onClick: onAttachmentOpen,
              className: isUser ? "justify-end" : void 0
            }
          ) }) : null,
          /* @__PURE__ */ jsxRuntime.jsxs(
            "div",
            {
              className: lib.cn(
                "inline-block max-w-full rounded-2xl px-3.5 py-2 text-sm",
                bubbleSurface
              ),
              children: [
                isStreaming && message.toolActivity ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-1.5", children: streamingIndicator ? streamingIndicator(message) : /* @__PURE__ */ jsxRuntime.jsx(StreamingIndicator, { label: message.toolActivity }) }) : null,
                message.content || !isStreaming ? /* @__PURE__ */ jsxRuntime.jsx(
                  chunk7IYXZUJO_cjs.MarkdownMessage,
                  {
                    content: message.content || (isErr ? "*Failed to generate a response.*" : ""),
                    isUser,
                    isCompact,
                    plainText: isStreaming
                  }
                ) : streamingIndicator ? streamingIndicator(message) : /* @__PURE__ */ jsxRuntime.jsx(StreamingIndicator, {})
              ]
            }
          ),
          message.toolCalls?.length ? toolCallsRenderer ? toolCallsRenderer(message.toolCalls) : /* @__PURE__ */ jsxRuntime.jsx(ToolCalls, { calls: message.toolCalls, ...toolCallsProps }) : null,
          message.sources?.length && !isStreaming ? sourcesRenderer ? sourcesRenderer(message.sources) : /* @__PURE__ */ jsxRuntime.jsx(Sources, { sources: message.sources }) : null,
          showActions && !isStreaming ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5", children: [
            /* @__PURE__ */ jsxRuntime.jsx(
              MessageActions,
              {
                role: message.role,
                onCopy,
                onRegenerate,
                onEdit,
                onDelete
              }
            ),
            messageActionsExtra ? messageActionsExtra(message) : null
          ] }) : null,
          showTimestamp ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 text-[10px] text-muted-foreground", children: new Date(message.createdAt).toLocaleTimeString() }) : null,
          afterContent
        ] })
      ]
    }
  );
}, "MessageBubbleInner");
var MessageBubble = react.memo(MessageBubbleInner, (prev, next) => {
  const a = prev.message;
  const b = next.message;
  return a.id === b.id && a.content === b.content && a.isStreaming === b.isStreaming && a.isError === b.isError && (a.version ?? 0) === (b.version ?? 0) && a.toolActivity === b.toolActivity && a.toolCalls === b.toolCalls && a.sources === b.sources && a.attachments === b.attachments;
});
MessageBubble.displayName = "MessageBubble";
var MessageList = react.forwardRef(/* @__PURE__ */ chunkOLISEQHS_cjs.__name(function MessageList2({
  messages: messagesProp,
  renderItem,
  renderEmpty,
  isLoadingMore: isLoadingMoreProp,
  onStartReached,
  className,
  itemClassName,
  noVirtualize = false,
  defaultItemHeight: _deprecatedDefaultItemHeight,
  onAtBottomChange,
  atBottomThreshold = 120,
  scrollAnchorId
}, ref) {
  const ctx = chunkTBSHZO5R_cjs.useChatContextOptional();
  const messages = messagesProp ?? ctx?.messages ?? [];
  const isLoadingMore = isLoadingMoreProp ?? ctx?.isLoadingMore ?? false;
  const { copyToClipboard } = hooks.useCopy();
  const virtuosoRef = react.useRef(null);
  const scrollerRef = react.useRef(null);
  const [isScrollable, setIsScrollable] = react.useState(false);
  const lastReportedAtBottomRef = react.useRef(null);
  const reportAtBottom = react.useCallback(
    (value) => {
      if (lastReportedAtBottomRef.current === value) return;
      lastReportedAtBottomRef.current = value;
      onAtBottomChange?.(value);
    },
    [onAtBottomChange]
  );
  const didInitialScrollRef = react.useRef(false);
  react.useImperativeHandle(
    ref,
    () => ({
      scrollToBottom: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((smooth = false) => {
        virtuosoRef.current?.scrollToIndex({
          index: "LAST",
          behavior: smooth ? "smooth" : "auto",
          align: "end"
        });
      }, "scrollToBottom"),
      scrollToIndex: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((index, smooth = false) => {
        virtuosoRef.current?.scrollToIndex({
          index,
          behavior: smooth ? "smooth" : "auto"
        });
      }, "scrollToIndex")
    }),
    []
  );
  const defaultRenderItem = react.useCallback(
    (m) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: itemClassName, children: /* @__PURE__ */ jsxRuntime.jsx(
      MessageBubble,
      {
        message: m,
        onCopy: () => void copyToClipboard(m.content),
        onRegenerate: ctx ? () => void ctx.regenerate(m.id) : void 0,
        onDelete: ctx ? () => ctx.deleteMessage(m.id) : void 0
      }
    ) }),
    [itemClassName, ctx, copyToClipboard]
  );
  const itemRenderer = renderItem ?? defaultRenderItem;
  react.useEffect(() => {
    if (didInitialScrollRef.current) return;
    if (messages.length === 0) return;
    didInitialScrollRef.current = true;
    const id = requestAnimationFrame(() => {
      virtuosoRef.current?.scrollToIndex({
        index: "LAST",
        align: "end",
        behavior: "auto"
      });
    });
    return () => cancelAnimationFrame(id);
  }, [messages.length]);
  react.useEffect(() => {
    if (scrollAnchorId == null) return;
    if (!didInitialScrollRef.current) return;
    let raf1 = 0;
    let raf2 = 0;
    raf1 = requestAnimationFrame(() => {
      raf2 = requestAnimationFrame(() => {
        virtuosoRef.current?.scrollToIndex({
          index: "LAST",
          align: "end",
          behavior: "smooth"
        });
      });
    });
    return () => {
      cancelAnimationFrame(raf1);
      cancelAnimationFrame(raf2);
    };
  }, [scrollAnchorId]);
  react.useEffect(() => {
    const el = scrollerRef.current;
    if (!el || el === window || !(el instanceof HTMLElement)) return;
    const probe = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => {
      const scrollable = el.scrollHeight > el.clientHeight + 1;
      setIsScrollable(scrollable);
      if (!scrollable) reportAtBottom(true);
    }, "probe");
    probe();
    const ro = new ResizeObserver(probe);
    ro.observe(el);
    return () => ro.disconnect();
  }, [reportAtBottom, messages.length]);
  const computeItemKey = react.useCallback(
    (index, m) => m?.id ?? index,
    []
  );
  const startReachedHandler = react.useMemo(() => {
    if (!onStartReached) return void 0;
    let inFlight = false;
    return () => {
      if (inFlight || isLoadingMore) return;
      inFlight = true;
      try {
        onStartReached();
      } finally {
        queueMicrotask(() => {
          inFlight = false;
        });
      }
    };
  }, [onStartReached, isLoadingMore]);
  if (messages.length === 0) {
    return /* @__PURE__ */ jsxRuntime.jsx(
      "div",
      {
        role: "log",
        "aria-live": "polite",
        "aria-atomic": "false",
        className: lib.cn("flex-1 overflow-y-auto", className),
        children: renderEmpty?.() ?? null
      }
    );
  }
  if (noVirtualize) {
    return /* @__PURE__ */ jsxRuntime.jsxs(
      "div",
      {
        role: "log",
        "aria-live": "polite",
        "aria-atomic": "false",
        className: lib.cn("flex-1 overflow-y-auto", className),
        children: [
          isLoadingMore ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center py-2", children: /* @__PURE__ */ jsxRuntime.jsx(components.Spinner, { className: "size-4 text-muted-foreground" }) }) : null,
          messages.map((m, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: itemRenderer(m, i) }, m.id ?? i))
        ]
      }
    );
  }
  return /* @__PURE__ */ jsxRuntime.jsx(
    reactVirtuoso.Virtuoso,
    {
      ref: virtuosoRef,
      role: "log",
      "aria-live": "polite",
      "aria-atomic": "false",
      className: lib.cn("flex-1", className),
      data: messages,
      computeItemKey,
      itemContent: (index, m) => m ? itemRenderer(m, index) : null,
      initialTopMostItemIndex: messages.length > 0 ? messages.length - 1 : 0,
      atBottomThreshold,
      followOutput: (isAtBottom) => isAtBottom ? "auto" : false,
      scrollerRef: (el) => {
        scrollerRef.current = el;
      },
      atBottomStateChange: (atBottom) => {
        reportAtBottom(!isScrollable ? true : atBottom);
      },
      startReached: startReachedHandler,
      components: isLoadingMore ? {
        Header: /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center py-2", children: /* @__PURE__ */ jsxRuntime.jsx(components.Spinner, { className: "size-4 text-muted-foreground" }) }), "Header")
      } : EMPTY_COMPONENTS
    }
  );
}, "MessageList"));
var EMPTY_COMPONENTS = {};
function ChatRoot(props) {
  const { transport, config, initialSessionId, autoCreateSession, streaming, audio, debug, className, listClassName, ...slots } = props;
  return /* @__PURE__ */ jsxRuntime.jsx(
    chunkTBSHZO5R_cjs.ChatProvider,
    {
      transport,
      config,
      initialSessionId,
      autoCreateSession,
      streaming,
      audio,
      debug,
      children: /* @__PURE__ */ jsxRuntime.jsx(ChatRootShell, { className, listClassName, slots })
    }
  );
}
chunkOLISEQHS_cjs.__name(ChatRoot, "ChatRoot");
function ChatRootShell({ className, listClassName, slots }) {
  const chat = chunkTBSHZO5R_cjs.useChatContext();
  const composer = useChatComposer({
    onSubmit: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((content, attachments) => chat.sendMessage(content, attachments), "onSubmit"),
    disabled: chat.isStreaming
  });
  const onMessagesMouseUp = useFocusOnEmptyClick({
    enabled: slots.focusOnEmptyClick !== false
  });
  useAutoFocusOnStreamEnd();
  const listRef = react.useRef(null);
  const [isAtBottom, setIsAtBottom] = react.useState(true);
  const lastUserMessageId = react.useMemo(() => {
    const msgs = chat.messages;
    for (let i = msgs.length - 1; i >= 0; i -= 1) {
      if (msgs[i].role === "user") return msgs[i].id;
    }
    return null;
  }, [chat.messages]);
  const handleStartReached = chat.hasMore && !chat.isLoadingMore ? () => void chat.loadMore() : void 0;
  const greeting = chat.config.greeting ?? "How can I help?";
  const description = chat.config.description;
  const suggestions = chat.config.suggestions;
  const headerNode = slots.renderHeader ? slots.renderHeader(chat) : slots.header;
  const emptyNode = slots.empty ?? (slots.renderEmpty ? slots.renderEmpty({ setValue: composer.setValue, focus: composer.focus }) : /* @__PURE__ */ jsxRuntime.jsx(
    EmptyState,
    {
      greeting,
      description,
      suggestions,
      onPickSuggestion: (prompt) => {
        composer.setValue(prompt);
        composer.focus();
      }
    }
  ));
  const renderItem = slots.renderMessage ?? ((m) => /* @__PURE__ */ jsxRuntime.jsx(
    MessageBubble,
    {
      message: m,
      toolCallsProps: slots.toolCallsProps,
      attachmentRenderers: slots.attachmentRenderers,
      onAttachmentOpen: slots.onAttachmentOpen,
      onCopy: () => copy(m.content),
      onRegenerate: () => void chat.regenerate(m.id),
      onDelete: () => chat.deleteMessage(m.id)
    },
    m.id
  ));
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("relative flex h-full min-h-0 flex-col overflow-hidden", className), children: [
    slots.banner ?? null,
    headerNode ?? null,
    /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex min-h-0 flex-1 flex-col", onMouseUp: onMessagesMouseUp, children: [
      /* @__PURE__ */ jsxRuntime.jsx(
        ErrorBanner,
        {
          error: chat.error,
          onDismiss: chat.error ? () => chat.clearMessages() : void 0,
          onRetry: chat.error ? () => void chat.regenerate() : void 0
        }
      ),
      /* @__PURE__ */ jsxRuntime.jsx(
        MessageList,
        {
          ref: listRef,
          renderItem,
          renderEmpty: () => /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: emptyNode }),
          className: listClassName,
          onStartReached: handleStartReached,
          onAtBottomChange: setIsAtBottom,
          scrollAnchorId: lastUserMessageId
        }
      ),
      /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-none absolute inset-x-0 bottom-2 flex justify-center", children: slots.jumpToLatest ?? /* @__PURE__ */ jsxRuntime.jsx(
        JumpToLatest,
        {
          visible: !isAtBottom,
          onClick: () => listRef.current?.scrollToBottom(true)
        }
      ) })
    ] }),
    !slots.hideComposer && /* @__PURE__ */ jsxRuntime.jsx(
      Composer,
      {
        composer,
        placeholder: chat.config.placeholder,
        showAttachmentButton: slots.showAttachmentButton,
        onPickFiles: slots.onPickFiles,
        toolbarStart: slots.composerToolbarStart,
        toolbarEnd: slots.composerToolbarEnd,
        attachmentTray: slots.composerAttachmentTray,
        size: slots.composerSize
      }
    ),
    slots.footer ?? null
  ] });
}
chunkOLISEQHS_cjs.__name(ChatRootShell, "ChatRootShell");
function copy(text) {
  if (typeof navigator !== "undefined" && navigator.clipboard) {
    void navigator.clipboard.writeText(text);
  }
}
chunkOLISEQHS_cjs.__name(copy, "copy");

exports.Attachments = Attachments;
exports.AttachmentsGrid = AttachmentsGrid;
exports.AttachmentsList = AttachmentsList;
exports.ChatRoot = ChatRoot;
exports.Composer = Composer;
exports.EmptyState = EmptyState;
exports.ErrorBanner = ErrorBanner;
exports.JumpToLatest = JumpToLatest;
exports.MessageActions = MessageActions;
exports.MessageBubble = MessageBubble;
exports.MessageList = MessageList;
exports.Sources = Sources;
exports.StreamingIndicator = StreamingIndicator;
exports.ToolCalls = ToolCalls;
exports.deriveInitials = deriveInitials;
exports.isSubmittableDraft = isSubmittableDraft;
exports.resolvePersona = resolvePersona;
exports.sanitizeDraft = sanitizeDraft;
exports.useAutoFocusOnStreamEnd = useAutoFocusOnStreamEnd;
exports.useChatBubbleStyles = useChatBubbleStyles;
exports.useChatComposer = useChatComposer;
exports.useChatDestructiveStyles = useChatDestructiveStyles;
exports.useChatRoleStyles = useChatRoleStyles;
exports.useFocusOnEmptyClick = useFocusOnEmptyClick;
exports.useRegisterComposer = useRegisterComposer;
//# sourceMappingURL=chunk-4LFB7I5K.cjs.map
//# sourceMappingURL=chunk-4LFB7I5K.cjs.map