@yushaw/sanqian-chat 0.2.40 → 0.2.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1350,6 +1350,8 @@ interface CompactChatHistoryConfig {
1350
1350
  /** Label shown on highlighted rows. */
1351
1351
  highlightedLabel?: string | ((conversation: ConversationInfo) => string | null | undefined);
1352
1352
  }
1353
+ type LegacyNewConversationRef = React.MutableRefObject<(() => void) | null>;
1354
+ type ExtendedNewConversationRef = React.MutableRefObject<((options?: ConversationSwitchOptions) => void) | null>;
1353
1355
  interface CompactChatProps {
1354
1356
  /** Chat adapter for backend communication */
1355
1357
  adapter: ChatAdapter;
@@ -1384,7 +1386,7 @@ interface CompactChatProps {
1384
1386
  /** Ref to expose sendMessage function for external input */
1385
1387
  sendMessageRef?: React.MutableRefObject<((message: string) => void) | null>;
1386
1388
  /** Ref to expose newConversation function */
1387
- newConversationRef?: React.MutableRefObject<((options?: ConversationSwitchOptions) => void) | null>;
1389
+ newConversationRef?: LegacyNewConversationRef | ExtendedNewConversationRef;
1388
1390
  /** Ref to expose loadConversation function */
1389
1391
  loadConversationRef?: React.MutableRefObject<((id: string, options?: ConversationSwitchOptions) => Promise<void>) | null>;
1390
1392
  /** Ref to expose stopStreaming function */
@@ -1350,6 +1350,8 @@ interface CompactChatHistoryConfig {
1350
1350
  /** Label shown on highlighted rows. */
1351
1351
  highlightedLabel?: string | ((conversation: ConversationInfo) => string | null | undefined);
1352
1352
  }
1353
+ type LegacyNewConversationRef = React.MutableRefObject<(() => void) | null>;
1354
+ type ExtendedNewConversationRef = React.MutableRefObject<((options?: ConversationSwitchOptions) => void) | null>;
1353
1355
  interface CompactChatProps {
1354
1356
  /** Chat adapter for backend communication */
1355
1357
  adapter: ChatAdapter;
@@ -1384,7 +1386,7 @@ interface CompactChatProps {
1384
1386
  /** Ref to expose sendMessage function for external input */
1385
1387
  sendMessageRef?: React.MutableRefObject<((message: string) => void) | null>;
1386
1388
  /** Ref to expose newConversation function */
1387
- newConversationRef?: React.MutableRefObject<((options?: ConversationSwitchOptions) => void) | null>;
1389
+ newConversationRef?: LegacyNewConversationRef | ExtendedNewConversationRef;
1388
1390
  /** Ref to expose loadConversation function */
1389
1391
  loadConversationRef?: React.MutableRefObject<((id: string, options?: ConversationSwitchOptions) => Promise<void>) | null>;
1390
1392
  /** Ref to expose stopStreaming function */
@@ -10348,9 +10348,10 @@ var HistoryList = (0, import_react36.memo)(function HistoryList2({
10348
10348
  padding: "0.5rem 0.75rem",
10349
10349
  textAlign: "left",
10350
10350
  cursor: "pointer",
10351
- transition: "background-color 0.15s ease, border-color 0.15s ease",
10352
- border: isHighlighted ? "1px solid var(--chat-accent)" : "1px solid transparent",
10353
- background: isSelected || isHovered ? colors.hover : isHighlighted ? "color-mix(in srgb, var(--chat-accent) 10%, transparent)" : "transparent"
10351
+ transition: "background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease",
10352
+ border: isSelected ? "1px solid var(--chat-border-focus)" : "1px solid transparent",
10353
+ background: isSelected || isHovered ? colors.hover : isHighlighted ? "color-mix(in srgb, var(--chat-accent) 6%, transparent)" : "transparent",
10354
+ boxShadow: isSelected ? "inset 2px 0 0 var(--chat-accent)" : "none"
10354
10355
  },
10355
10356
  onClick: () => onSelect(conv.id),
10356
10357
  onMouseEnter: () => setHoveredId(conv.id),
@@ -10371,11 +10372,13 @@ var HistoryList = (0, import_react36.memo)(function HistoryList2({
10371
10372
  style: {
10372
10373
  fontSize: "0.625rem",
10373
10374
  lineHeight: 1,
10374
- padding: "0.2rem 0.35rem",
10375
+ padding: "0.18rem 0.32rem",
10375
10376
  borderRadius: 999,
10376
- background: "var(--chat-accent)",
10377
- color: "#fff",
10378
- flexShrink: 0
10377
+ border: "1px solid color-mix(in srgb, var(--chat-accent) 30%, transparent)",
10378
+ background: "color-mix(in srgb, var(--chat-accent) 12%, transparent)",
10379
+ color: "color-mix(in srgb, var(--chat-accent) 85%, var(--chat-text) 15%)",
10380
+ flexShrink: 0,
10381
+ fontWeight: 500
10379
10382
  },
10380
10383
  children: resolvedHighlightLabel
10381
10384
  }
@@ -10416,39 +10419,50 @@ var HistoryModal = (0, import_react37.memo)(function HistoryModal2({
10416
10419
  isDarkMode = false
10417
10420
  }) {
10418
10421
  const modalRef = (0, import_react37.useRef)(null);
10422
+ const closeTimerRef = (0, import_react37.useRef)(null);
10419
10423
  const [isClosing, setIsClosing] = (0, import_react37.useState)(false);
10420
10424
  const [shouldRender, setShouldRender] = (0, import_react37.useState)(isOpen);
10425
+ const clearCloseTimer = (0, import_react37.useCallback)(() => {
10426
+ if (closeTimerRef.current === null) return;
10427
+ clearTimeout(closeTimerRef.current);
10428
+ closeTimerRef.current = null;
10429
+ }, []);
10421
10430
  (0, import_react37.useEffect)(() => {
10422
10431
  if (isOpen) {
10432
+ clearCloseTimer();
10423
10433
  setShouldRender(true);
10424
10434
  setIsClosing(false);
10425
- } else if (shouldRender && !isClosing) {
10426
- setIsClosing(true);
10427
- setTimeout(() => {
10428
- setShouldRender(false);
10429
- setIsClosing(false);
10430
- }, ANIMATION_DURATION);
10435
+ return;
10436
+ }
10437
+ if (!shouldRender) {
10438
+ setIsClosing(false);
10439
+ return;
10431
10440
  }
10432
- }, [isOpen, shouldRender, isClosing]);
10433
- const handleClose = (0, import_react37.useCallback)(() => {
10434
10441
  setIsClosing(true);
10435
- setTimeout(() => {
10442
+ clearCloseTimer();
10443
+ closeTimerRef.current = setTimeout(() => {
10436
10444
  setShouldRender(false);
10437
10445
  setIsClosing(false);
10438
- onClose();
10446
+ closeTimerRef.current = null;
10439
10447
  }, ANIMATION_DURATION);
10440
- }, [onClose]);
10448
+ return clearCloseTimer;
10449
+ }, [isOpen, shouldRender, clearCloseTimer]);
10450
+ (0, import_react37.useEffect)(() => clearCloseTimer, [clearCloseTimer]);
10451
+ const handleCloseRequest = (0, import_react37.useCallback)(() => {
10452
+ if (!isOpen || isClosing) return;
10453
+ onClose();
10454
+ }, [isOpen, isClosing, onClose]);
10441
10455
  (0, import_react37.useEffect)(() => {
10442
10456
  if (!shouldRender || isClosing) return;
10443
10457
  const handleKeyDown = (e) => {
10444
10458
  if (e.key === "Escape") {
10445
10459
  e.preventDefault();
10446
- handleClose();
10460
+ handleCloseRequest();
10447
10461
  }
10448
10462
  };
10449
10463
  document.addEventListener("keydown", handleKeyDown);
10450
10464
  return () => document.removeEventListener("keydown", handleKeyDown);
10451
- }, [shouldRender, isClosing, handleClose]);
10465
+ }, [shouldRender, isClosing, handleCloseRequest]);
10452
10466
  (0, import_react37.useEffect)(() => {
10453
10467
  if (!isOpen || !modalRef.current) return;
10454
10468
  modalRef.current.focus();
@@ -10456,11 +10470,12 @@ var HistoryModal = (0, import_react37.memo)(function HistoryModal2({
10456
10470
  const handleBackdropClick = (0, import_react37.useCallback)(
10457
10471
  (e) => {
10458
10472
  if (e.target === e.currentTarget && !isClosing) {
10459
- handleClose();
10473
+ handleCloseRequest();
10460
10474
  }
10461
10475
  },
10462
- [handleClose, isClosing]
10476
+ [handleCloseRequest, isClosing]
10463
10477
  );
10478
+ useWindowDragLock(shouldRender);
10464
10479
  if (!shouldRender) return null;
10465
10480
  const closingClass = isClosing ? " chat-modal-closing" : "";
10466
10481
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
@@ -10529,7 +10544,7 @@ var HistoryModal = (0, import_react37.memo)(function HistoryModal2({
10529
10544
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
10530
10545
  "button",
10531
10546
  {
10532
- onClick: handleClose,
10547
+ onClick: handleCloseRequest,
10533
10548
  "aria-label": closeLabel,
10534
10549
  style: {
10535
10550
  display: "flex",
@@ -10698,7 +10713,6 @@ var CompactChat = (0, import_react38.memo)(function CompactChat2({
10698
10713
  const chatContainerRef = (0, import_react38.useRef)(null);
10699
10714
  const chatInputRef = (0, import_react38.useRef)(null);
10700
10715
  const [showHistory, setShowHistory] = (0, import_react38.useState)(false);
10701
- useWindowDragLock(showHistory);
10702
10716
  const [connectionAlert, setConnectionAlert] = (0, import_react38.useState)(null);
10703
10717
  const portalContainerRef = (0, import_react38.useRef)(inputPortalContainer ?? null);
10704
10718
  portalContainerRef.current = inputPortalContainer ?? null;
@@ -10276,9 +10276,10 @@ var HistoryList = memo16(function HistoryList2({
10276
10276
  padding: "0.5rem 0.75rem",
10277
10277
  textAlign: "left",
10278
10278
  cursor: "pointer",
10279
- transition: "background-color 0.15s ease, border-color 0.15s ease",
10280
- border: isHighlighted ? "1px solid var(--chat-accent)" : "1px solid transparent",
10281
- background: isSelected || isHovered ? colors.hover : isHighlighted ? "color-mix(in srgb, var(--chat-accent) 10%, transparent)" : "transparent"
10279
+ transition: "background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease",
10280
+ border: isSelected ? "1px solid var(--chat-border-focus)" : "1px solid transparent",
10281
+ background: isSelected || isHovered ? colors.hover : isHighlighted ? "color-mix(in srgb, var(--chat-accent) 6%, transparent)" : "transparent",
10282
+ boxShadow: isSelected ? "inset 2px 0 0 var(--chat-accent)" : "none"
10282
10283
  },
10283
10284
  onClick: () => onSelect(conv.id),
10284
10285
  onMouseEnter: () => setHoveredId(conv.id),
@@ -10299,11 +10300,13 @@ var HistoryList = memo16(function HistoryList2({
10299
10300
  style: {
10300
10301
  fontSize: "0.625rem",
10301
10302
  lineHeight: 1,
10302
- padding: "0.2rem 0.35rem",
10303
+ padding: "0.18rem 0.32rem",
10303
10304
  borderRadius: 999,
10304
- background: "var(--chat-accent)",
10305
- color: "#fff",
10306
- flexShrink: 0
10305
+ border: "1px solid color-mix(in srgb, var(--chat-accent) 30%, transparent)",
10306
+ background: "color-mix(in srgb, var(--chat-accent) 12%, transparent)",
10307
+ color: "color-mix(in srgb, var(--chat-accent) 85%, var(--chat-text) 15%)",
10308
+ flexShrink: 0,
10309
+ fontWeight: 500
10307
10310
  },
10308
10311
  children: resolvedHighlightLabel
10309
10312
  }
@@ -10344,39 +10347,50 @@ var HistoryModal = memo17(function HistoryModal2({
10344
10347
  isDarkMode = false
10345
10348
  }) {
10346
10349
  const modalRef = useRef17(null);
10350
+ const closeTimerRef = useRef17(null);
10347
10351
  const [isClosing, setIsClosing] = useState21(false);
10348
10352
  const [shouldRender, setShouldRender] = useState21(isOpen);
10353
+ const clearCloseTimer = useCallback21(() => {
10354
+ if (closeTimerRef.current === null) return;
10355
+ clearTimeout(closeTimerRef.current);
10356
+ closeTimerRef.current = null;
10357
+ }, []);
10349
10358
  useEffect25(() => {
10350
10359
  if (isOpen) {
10360
+ clearCloseTimer();
10351
10361
  setShouldRender(true);
10352
10362
  setIsClosing(false);
10353
- } else if (shouldRender && !isClosing) {
10354
- setIsClosing(true);
10355
- setTimeout(() => {
10356
- setShouldRender(false);
10357
- setIsClosing(false);
10358
- }, ANIMATION_DURATION);
10363
+ return;
10364
+ }
10365
+ if (!shouldRender) {
10366
+ setIsClosing(false);
10367
+ return;
10359
10368
  }
10360
- }, [isOpen, shouldRender, isClosing]);
10361
- const handleClose = useCallback21(() => {
10362
10369
  setIsClosing(true);
10363
- setTimeout(() => {
10370
+ clearCloseTimer();
10371
+ closeTimerRef.current = setTimeout(() => {
10364
10372
  setShouldRender(false);
10365
10373
  setIsClosing(false);
10366
- onClose();
10374
+ closeTimerRef.current = null;
10367
10375
  }, ANIMATION_DURATION);
10368
- }, [onClose]);
10376
+ return clearCloseTimer;
10377
+ }, [isOpen, shouldRender, clearCloseTimer]);
10378
+ useEffect25(() => clearCloseTimer, [clearCloseTimer]);
10379
+ const handleCloseRequest = useCallback21(() => {
10380
+ if (!isOpen || isClosing) return;
10381
+ onClose();
10382
+ }, [isOpen, isClosing, onClose]);
10369
10383
  useEffect25(() => {
10370
10384
  if (!shouldRender || isClosing) return;
10371
10385
  const handleKeyDown = (e) => {
10372
10386
  if (e.key === "Escape") {
10373
10387
  e.preventDefault();
10374
- handleClose();
10388
+ handleCloseRequest();
10375
10389
  }
10376
10390
  };
10377
10391
  document.addEventListener("keydown", handleKeyDown);
10378
10392
  return () => document.removeEventListener("keydown", handleKeyDown);
10379
- }, [shouldRender, isClosing, handleClose]);
10393
+ }, [shouldRender, isClosing, handleCloseRequest]);
10380
10394
  useEffect25(() => {
10381
10395
  if (!isOpen || !modalRef.current) return;
10382
10396
  modalRef.current.focus();
@@ -10384,11 +10398,12 @@ var HistoryModal = memo17(function HistoryModal2({
10384
10398
  const handleBackdropClick = useCallback21(
10385
10399
  (e) => {
10386
10400
  if (e.target === e.currentTarget && !isClosing) {
10387
- handleClose();
10401
+ handleCloseRequest();
10388
10402
  }
10389
10403
  },
10390
- [handleClose, isClosing]
10404
+ [handleCloseRequest, isClosing]
10391
10405
  );
10406
+ useWindowDragLock(shouldRender);
10392
10407
  if (!shouldRender) return null;
10393
10408
  const closingClass = isClosing ? " chat-modal-closing" : "";
10394
10409
  return /* @__PURE__ */ jsx24(
@@ -10457,7 +10472,7 @@ var HistoryModal = memo17(function HistoryModal2({
10457
10472
  /* @__PURE__ */ jsx24(
10458
10473
  "button",
10459
10474
  {
10460
- onClick: handleClose,
10475
+ onClick: handleCloseRequest,
10461
10476
  "aria-label": closeLabel,
10462
10477
  style: {
10463
10478
  display: "flex",
@@ -10626,7 +10641,6 @@ var CompactChat = memo18(function CompactChat2({
10626
10641
  const chatContainerRef = useRef18(null);
10627
10642
  const chatInputRef = useRef18(null);
10628
10643
  const [showHistory, setShowHistory] = useState22(false);
10629
- useWindowDragLock(showHistory);
10630
10644
  const [connectionAlert, setConnectionAlert] = useState22(null);
10631
10645
  const portalContainerRef = useRef18(inputPortalContainer ?? null);
10632
10646
  portalContainerRef.current = inputPortalContainer ?? null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yushaw/sanqian-chat",
3
- "version": "0.2.40",
3
+ "version": "0.2.42",
4
4
  "description": "Floating chat window SDK for Sanqian AI Assistant",
5
5
  "main": "./dist/main/index.js",
6
6
  "types": "./dist/main/index.d.ts",