@hef2024/llmasaservice-ui 0.20.1 → 0.20.3

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.
@@ -1575,6 +1575,88 @@
1575
1575
  font-size: 10px;
1576
1576
  }
1577
1577
 
1578
+ /* Detail Section Title Row (with toggle) */
1579
+ .ai-chat-context-popover__detail-section-title-row {
1580
+ display: flex;
1581
+ align-items: center;
1582
+ gap: 12px;
1583
+ flex: 1;
1584
+ min-width: 0;
1585
+ }
1586
+
1587
+ /* Disabled Section Styling */
1588
+ .ai-chat-context-popover__detail-section--disabled {
1589
+ opacity: 0.6;
1590
+ }
1591
+
1592
+ .ai-chat-context-popover__detail-section--disabled .ai-chat-context-popover__detail-section-title {
1593
+ text-decoration: line-through;
1594
+ opacity: 0.7;
1595
+ }
1596
+
1597
+ /* Context Toggle Switch */
1598
+ .ai-chat-context-toggle {
1599
+ position: relative;
1600
+ display: inline-flex;
1601
+ width: 36px;
1602
+ height: 20px;
1603
+ cursor: pointer;
1604
+ user-select: none;
1605
+ flex-shrink: 0;
1606
+ }
1607
+
1608
+ .ai-chat-context-toggle__input {
1609
+ opacity: 0;
1610
+ width: 0;
1611
+ height: 0;
1612
+ position: absolute;
1613
+ }
1614
+
1615
+ .ai-chat-context-toggle__slider {
1616
+ position: absolute;
1617
+ top: 0;
1618
+ left: 0;
1619
+ right: 0;
1620
+ bottom: 0;
1621
+ background-color: #cbd5e1;
1622
+ transition: background-color 0.2s ease;
1623
+ border-radius: 20px;
1624
+ }
1625
+
1626
+ .ai-chat-context-toggle__slider:before {
1627
+ position: absolute;
1628
+ content: "";
1629
+ height: 16px;
1630
+ width: 16px;
1631
+ left: 2px;
1632
+ bottom: 2px;
1633
+ background-color: white;
1634
+ transition: transform 0.2s ease;
1635
+ border-radius: 50%;
1636
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
1637
+ }
1638
+
1639
+ .ai-chat-context-toggle__input:checked + .ai-chat-context-toggle__slider {
1640
+ background-color: #3b82f6;
1641
+ }
1642
+
1643
+ .ai-chat-context-toggle__input:checked + .ai-chat-context-toggle__slider:before {
1644
+ transform: translateX(16px);
1645
+ }
1646
+
1647
+ .ai-chat-context-toggle:hover .ai-chat-context-toggle__slider {
1648
+ opacity: 0.9;
1649
+ }
1650
+
1651
+ /* Dark theme toggle */
1652
+ .dark-theme .ai-chat-context-toggle__slider {
1653
+ background-color: #4b5563;
1654
+ }
1655
+
1656
+ .dark-theme .ai-chat-context-toggle__input:checked + .ai-chat-context-toggle__slider {
1657
+ background-color: #60a5fa;
1658
+ }
1659
+
1578
1660
  /* Detail Content */
1579
1661
  .ai-chat-context-popover__detail-content {
1580
1662
  margin: 0;
@@ -88,6 +88,8 @@ export interface AIChatPanelProps {
88
88
  totalContextTokens?: number;
89
89
  maxContextTokens?: number;
90
90
  enableContextDetailView?: boolean;
91
+ disabledSectionIds?: Set<string>;
92
+ onToggleSection?: (sectionId: string, enabled: boolean) => void;
91
93
 
92
94
  // Callback when a new conversation is created via API
93
95
  onConversationCreated?: (conversationId: string) => void;
@@ -295,6 +297,8 @@ interface ChatInputProps {
295
297
  totalContextTokens?: number;
296
298
  maxContextTokens?: number;
297
299
  enableContextDetailView?: boolean;
300
+ disabledSectionIds?: Set<string>;
301
+ onToggleSection?: (sectionId: string, enabled: boolean) => void;
298
302
  onContextViewerToggle?: () => void;
299
303
  }
300
304
 
@@ -313,11 +317,14 @@ const ChatInput = React.memo<ChatInputProps>(({
313
317
  totalContextTokens = 0,
314
318
  maxContextTokens = 8000,
315
319
  enableContextDetailView = false,
320
+ disabledSectionIds = new Set(),
321
+ onToggleSection,
316
322
  }) => {
317
323
  const [inputValue, setInputValue] = useState('');
318
324
  const [dropdownOpen, setDropdownOpen] = useState(false);
319
325
  const [contextViewerOpen, setContextViewerOpen] = useState(false);
320
326
  const [contextViewMode, setContextViewMode] = useState<'summary' | 'detail'>('summary');
327
+ const [expandedSectionId, setExpandedSectionId] = useState<string | null>(null);
321
328
  const textareaRef = useRef<HTMLTextAreaElement | null>(null);
322
329
  const containerRef = useRef<HTMLDivElement | null>(null);
323
330
  const contextPopoverRef = useRef<HTMLDivElement | null>(null);
@@ -363,6 +370,7 @@ const ChatInput = React.memo<ChatInputProps>(({
363
370
  if (contextPopoverRef.current && !contextPopoverRef.current.contains(event.target as Node)) {
364
371
  setContextViewerOpen(false);
365
372
  setContextViewMode('summary');
373
+ setExpandedSectionId(null);
366
374
  }
367
375
  };
368
376
  if (contextViewerOpen) {
@@ -466,6 +474,9 @@ const ChatInput = React.memo<ChatInputProps>(({
466
474
  setContextViewerOpen(!contextViewerOpen);
467
475
  if (!contextViewerOpen) {
468
476
  setContextViewMode('summary');
477
+ setExpandedSectionId(null);
478
+ } else {
479
+ setExpandedSectionId(null);
469
480
  }
470
481
  }}
471
482
  type="button"
@@ -488,7 +499,10 @@ const ChatInput = React.memo<ChatInputProps>(({
488
499
  <span className="ai-chat-context-popover__title">Context</span>
489
500
  <button
490
501
  className="ai-chat-context-popover__close"
491
- onClick={() => setContextViewerOpen(false)}
502
+ onClick={() => {
503
+ setContextViewerOpen(false);
504
+ setExpandedSectionId(null);
505
+ }}
492
506
  type="button"
493
507
  >
494
508
  ×
@@ -517,6 +531,7 @@ const ChatInput = React.memo<ChatInputProps>(({
517
531
  className={`ai-chat-context-popover__section-item ${enableContextDetailView ? 'ai-chat-context-popover__section-item--clickable' : ''}`}
518
532
  onClick={() => {
519
533
  if (enableContextDetailView) {
534
+ setExpandedSectionId(section.id);
520
535
  setContextViewMode('detail');
521
536
  }
522
537
  }}
@@ -532,7 +547,10 @@ const ChatInput = React.memo<ChatInputProps>(({
532
547
  {enableContextDetailView && (
533
548
  <button
534
549
  className="ai-chat-context-popover__expand-btn"
535
- onClick={() => setContextViewMode('detail')}
550
+ onClick={() => {
551
+ setExpandedSectionId(null);
552
+ setContextViewMode('detail');
553
+ }}
536
554
  type="button"
537
555
  >
538
556
  View details →
@@ -547,7 +565,10 @@ const ChatInput = React.memo<ChatInputProps>(({
547
565
  <div className="ai-chat-context-popover__header">
548
566
  <button
549
567
  className="ai-chat-context-popover__back"
550
- onClick={() => setContextViewMode('summary')}
568
+ onClick={() => {
569
+ setContextViewMode('summary');
570
+ setExpandedSectionId(null);
571
+ }}
551
572
  type="button"
552
573
  >
553
574
  ← Back
@@ -555,7 +576,10 @@ const ChatInput = React.memo<ChatInputProps>(({
555
576
  <span className="ai-chat-context-popover__title">Context Details</span>
556
577
  <button
557
578
  className="ai-chat-context-popover__close"
558
- onClick={() => setContextViewerOpen(false)}
579
+ onClick={() => {
580
+ setContextViewerOpen(false);
581
+ setExpandedSectionId(null);
582
+ }}
559
583
  type="button"
560
584
  >
561
585
  ×
@@ -580,10 +604,35 @@ const ChatInput = React.memo<ChatInputProps>(({
580
604
  <div className="ai-chat-context-popover__detail-sections">
581
605
  {contextSections.map((section) => {
582
606
  const format = detectFormat(section.data);
607
+ const isEnabled = !disabledSectionIds.has(section.id);
583
608
  return (
584
- <details key={section.id} className="ai-chat-context-popover__detail-section" open>
609
+ <details
610
+ key={section.id}
611
+ className={`ai-chat-context-popover__detail-section ${!isEnabled ? 'ai-chat-context-popover__detail-section--disabled' : ''}`}
612
+ open={expandedSectionId === section.id}
613
+ >
585
614
  <summary className="ai-chat-context-popover__detail-section-header">
586
- <span className="ai-chat-context-popover__detail-section-title">{section.title}</span>
615
+ <div className="ai-chat-context-popover__detail-section-title-row">
616
+ <span className="ai-chat-context-popover__detail-section-title">{section.title}</span>
617
+ <label
618
+ className="ai-chat-context-toggle"
619
+ onClick={(e) => e.stopPropagation()}
620
+ title={isEnabled ? "Disable this context section" : "Enable this context section"}
621
+ >
622
+ <input
623
+ type="checkbox"
624
+ checked={isEnabled}
625
+ onChange={(e) => {
626
+ e.stopPropagation();
627
+ if (onToggleSection) {
628
+ onToggleSection(section.id, !isEnabled);
629
+ }
630
+ }}
631
+ className="ai-chat-context-toggle__input"
632
+ />
633
+ <span className="ai-chat-context-toggle__slider"></span>
634
+ </label>
635
+ </div>
587
636
  <span className="ai-chat-context-popover__detail-section-meta">
588
637
  <code>{`{{${section.id}}}`}</code>
589
638
  <span>~{section.tokens || Math.ceil(JSON.stringify(section.data).length / 4)}</span>
@@ -701,6 +750,8 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
701
750
  totalContextTokens = 0,
702
751
  maxContextTokens = 8000,
703
752
  enableContextDetailView = false,
753
+ disabledSectionIds: propDisabledSectionIds,
754
+ onToggleSection: propOnToggleSection,
704
755
  onConversationCreated,
705
756
  // UI Customization Props
706
757
  cssUrl,
@@ -770,6 +821,11 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
770
821
  const [sessionApprovedTools, setSessionApprovedTools] = useState<string[]>([]);
771
822
  const [alwaysApprovedTools, setAlwaysApprovedTools] = useState<string[]>([]);
772
823
 
824
+ // Context section toggle state (disabled sections)
825
+ // Use internal state only if prop is not provided
826
+ const [internalDisabledSectionIds, setInternalDisabledSectionIds] = useState<Set<string>>(new Set());
827
+ const disabledSectionIds = propDisabledSectionIds ?? internalDisabledSectionIds;
828
+
773
829
  // Email capture mode effect - like ChatPanel
774
830
  useEffect(() => {
775
831
  setShowEmailPanel(customerEmailCaptureMode !== 'HIDE');
@@ -891,6 +947,24 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
891
947
  };
892
948
  }, []);
893
949
 
950
+ // Handle toggling context sections on/off
951
+ const handleToggleSection = useCallback((sectionId: string, enabled: boolean) => {
952
+ // Use prop callback if provided, otherwise use internal state
953
+ if (propOnToggleSection) {
954
+ propOnToggleSection(sectionId, enabled);
955
+ } else {
956
+ setInternalDisabledSectionIds(prev => {
957
+ const next = new Set(prev);
958
+ if (enabled) {
959
+ next.delete(sectionId);
960
+ } else {
961
+ next.add(sectionId);
962
+ }
963
+ return next;
964
+ });
965
+ }
966
+ }, [propOnToggleSection]);
967
+
894
968
  // Ensure a conversation exists before sending the first message
895
969
  // This creates a conversation on the server and returns the conversation ID
896
970
  const ensureConversation = useCallback(() => {
@@ -2633,6 +2707,8 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
2633
2707
  totalContextTokens={totalContextTokens}
2634
2708
  maxContextTokens={maxContextTokens}
2635
2709
  enableContextDetailView={enableContextDetailView}
2710
+ disabledSectionIds={disabledSectionIds}
2711
+ onToggleSection={handleToggleSection}
2636
2712
  />
2637
2713
 
2638
2714
  {/* Footer */}
@@ -64,3 +64,6 @@ const ToolInfoModal: React.FC<ToolInfoModalProps> = ({
64
64
 
65
65
  export default ToolInfoModal;
66
66
 
67
+
68
+
69
+
@@ -106,7 +106,6 @@ export function useAgentRegistry(
106
106
 
107
107
  const data = await response.json();
108
108
  const agentData = Array.isArray(data) ? data[0] : data;
109
-
110
109
 
111
110
  if (!agentData) {
112
111
  throw new Error(`No data returned for agent ${agentId}`);