@hef2024/llmasaservice-ui 0.20.0 → 0.20.2

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.
@@ -245,3 +245,4 @@ Closes: Issue about silent 413 failures
245
245
  For questions about this implementation, see:
246
246
  - `docs/ERROR-HANDLING-413.md` - Detailed technical documentation
247
247
  - GitHub Issues - Report bugs or request enhancements
248
+
@@ -861,3 +861,4 @@ Minimal interface embedded in another application.
861
861
  ## Feedback
862
862
 
863
863
  If you have questions or suggestions about conversation history configuration, please open an issue on GitHub or contact support.
864
+
@@ -251,3 +251,4 @@ Potential improvements for future iterations:
251
251
  - Errors are detected by monitoring the `error` property from `useLLM` hook
252
252
  - The implementation is consistent across both modern (AIChatPanel) and legacy (ChatPanel) components
253
253
  - Error state is properly synchronized with loading state to prevent UI inconsistencies
254
+
@@ -129,3 +129,4 @@ The implementation is straightforward:
129
129
  - That's it!
130
130
 
131
131
  No configuration needed, works automatically! 🎉
132
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hef2024/llmasaservice-ui",
3
- "version": "0.20.0",
3
+ "version": "0.20.2",
4
4
  "description": "Prebuilt UI components for LLMAsAService.io",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -57,7 +57,7 @@
57
57
  "dotenv": "^16.4.7",
58
58
  "llmasaservice-client": "^0.10.4",
59
59
  "process": "^0.11.10",
60
- "react-markdown": "^9.0.1",
60
+ "react-markdown": "^10.1.0",
61
61
  "react-syntax-highlighter": "^15.5.0",
62
62
  "rehype-raw": "^7.0.0",
63
63
  "remark-gfm": "^4.0.0",
@@ -73,6 +73,9 @@ export interface AIAgentPanelProps {
73
73
  // Agent Configuration - can be string IDs or full config objects
74
74
  agents: (string | AgentConfig)[];
75
75
  defaultAgent?: string;
76
+
77
+ // Controlled selection - when provided, external state drives which agent is active
78
+ selectedAgent?: string;
76
79
 
77
80
  // Customer ID - REQUIRED for conversation history
78
81
  customerId: string;
@@ -141,6 +144,23 @@ export interface AIAgentPanelProps {
141
144
 
142
145
  // Enable/disable conversation history panel
143
146
  showConversationHistory?: boolean;
147
+
148
+ // New props from ChatPanel port
149
+ cssUrl?: string;
150
+ markdownClass?: string;
151
+ width?: string;
152
+ height?: string;
153
+ scrollToEnd?: boolean;
154
+ prismStyle?: any;
155
+ showSaveButton?: boolean;
156
+ showEmailButton?: boolean;
157
+ messages?: { role: "user" | "assistant"; content: string }[];
158
+ showCallToAction?: boolean;
159
+ callToActionButtonText?: string;
160
+ callToActionEmailAddress?: string;
161
+ callToActionEmailSubject?: string;
162
+ customerEmailCaptureMode?: "HIDE" | "OPTIONAL" | "REQUIRED";
163
+ customerEmailCapturePlaceholder?: string;
144
164
  }
145
165
 
146
166
  // Icons
@@ -377,10 +397,28 @@ interface ChatPanelWrapperProps {
377
397
  totalContextTokens: number;
378
398
  maxContextTokens: number;
379
399
  enableContextDetailView: boolean;
400
+ disabledSectionIds: Set<string>;
401
+ onToggleSection: (sectionId: string, enabled: boolean) => void;
380
402
  // Conversation creation callback
381
403
  onConversationCreated: (tempId: string, realId: string) => void;
382
404
  // Per-conversation initial prompt
383
405
  conversationInitialPrompt?: string;
406
+ // New props from ChatPanel port
407
+ cssUrl?: string;
408
+ markdownClass?: string;
409
+ width?: string;
410
+ height?: string;
411
+ scrollToEnd?: boolean;
412
+ prismStyle?: any;
413
+ showSaveButton?: boolean;
414
+ showEmailButton?: boolean;
415
+ messages?: { role: "user" | "assistant"; content: string }[];
416
+ showCallToAction?: boolean;
417
+ callToActionButtonText?: string;
418
+ callToActionEmailAddress?: string;
419
+ callToActionEmailSubject?: string;
420
+ customerEmailCaptureMode?: "HIDE" | "OPTIONAL" | "REQUIRED";
421
+ customerEmailCapturePlaceholder?: string;
384
422
  }
385
423
 
386
424
  // Remove React.memo temporarily to debug - ChatPanelWrapper needs to re-render when agentId changes
@@ -412,13 +450,32 @@ const ChatPanelWrapper = (({
412
450
  totalContextTokens,
413
451
  maxContextTokens,
414
452
  enableContextDetailView,
453
+ disabledSectionIds,
454
+ onToggleSection,
415
455
  onConversationCreated,
416
456
  conversationInitialPrompt,
457
+ // New props from ChatPanel port
458
+ cssUrl,
459
+ markdownClass,
460
+ width,
461
+ height,
462
+ scrollToEnd,
463
+ prismStyle,
464
+ showSaveButton,
465
+ showEmailButton,
466
+ messages,
467
+ showCallToAction,
468
+ callToActionButtonText,
469
+ callToActionEmailAddress,
470
+ callToActionEmailSubject,
471
+ customerEmailCaptureMode,
472
+ customerEmailCapturePlaceholder,
417
473
  }) => {
418
474
  const convAgentProfile = getAgent(activeConv.agentId);
419
475
  const convAgentMetadata = convAgentProfile?.metadata;
420
476
  const isVisible = currentConversationId === activeConv.conversationId;
421
477
 
478
+
422
479
  // Memoize callbacks to prevent recreating on every render
423
480
  const historyCallback = useCallback(
424
481
  (history: Record<string, { content: string; callId: string }>) => {
@@ -505,7 +562,7 @@ const ChatPanelWrapper = (({
505
562
  customer={effectiveCustomer}
506
563
  showPoweredBy={showPoweredBy}
507
564
  showNewConversationButton={false}
508
- createConversationOnFirstChat={convAgentMetadata.createConversationOnFirstChat ?? true}
565
+ createConversationOnFirstChat={convAgentProfile?.createConversationOnFirstChat ?? true}
509
566
  mcpServers={mcpServers}
510
567
  actions={actions}
511
568
  followOnQuestions={effectiveFollowOnQuestions}
@@ -520,7 +577,24 @@ const ChatPanelWrapper = (({
520
577
  totalContextTokens={totalContextTokens}
521
578
  maxContextTokens={maxContextTokens}
522
579
  enableContextDetailView={enableContextDetailView}
580
+ disabledSectionIds={disabledSectionIds}
581
+ onToggleSection={onToggleSection}
523
582
  onConversationCreated={conversationCreatedCallback}
583
+ cssUrl={cssUrl}
584
+ markdownClass={markdownClass}
585
+ width={width}
586
+ height={height}
587
+ scrollToEnd={scrollToEnd ?? convAgentMetadata.displayAutoScroll}
588
+ prismStyle={prismStyle}
589
+ showSaveButton={showSaveButton ?? convAgentMetadata.displayShowSaveButton ?? true}
590
+ showEmailButton={showEmailButton ?? convAgentMetadata.displayShowEmailButton ?? true}
591
+ messages={messages}
592
+ showCallToAction={showCallToAction ?? convAgentMetadata.displayShowCallToAction ?? false}
593
+ callToActionButtonText={callToActionButtonText ?? convAgentMetadata.displayCallToActionButtonText ?? 'Submit'}
594
+ callToActionEmailAddress={callToActionEmailAddress ?? convAgentMetadata.displayCallToActionEmailAddress ?? ''}
595
+ callToActionEmailSubject={callToActionEmailSubject ?? convAgentMetadata.displayCallToActionEmailSubject ?? 'Agent CTA submitted'}
596
+ customerEmailCaptureMode={customerEmailCaptureMode ?? convAgentMetadata?.customerEmailCaptureMode ?? 'HIDE'}
597
+ customerEmailCapturePlaceholder={customerEmailCapturePlaceholder ?? convAgentMetadata?.customerEmailCapturePlaceholder ?? 'Please enter your email...'}
524
598
  />
525
599
  </div>
526
600
  );
@@ -531,6 +605,7 @@ ChatPanelWrapper.displayName = 'ChatPanelWrapper';
531
605
  const AIAgentPanel = React.forwardRef<AIAgentPanelHandle, AIAgentPanelProps>(({
532
606
  agents,
533
607
  defaultAgent,
608
+ selectedAgent,
534
609
  customerId,
535
610
  apiKey,
536
611
  context,
@@ -568,6 +643,22 @@ const AIAgentPanel = React.forwardRef<AIAgentPanelHandle, AIAgentPanelProps>(({
568
643
  followOnPrompt = '',
569
644
  historyListLimit = 50,
570
645
  showConversationHistory = true,
646
+ // New props from ChatPanel port
647
+ cssUrl,
648
+ markdownClass,
649
+ width,
650
+ height,
651
+ scrollToEnd,
652
+ prismStyle,
653
+ showSaveButton,
654
+ showEmailButton,
655
+ messages,
656
+ showCallToAction,
657
+ callToActionButtonText,
658
+ callToActionEmailAddress,
659
+ callToActionEmailSubject,
660
+ customerEmailCaptureMode,
661
+ customerEmailCapturePlaceholder,
571
662
  }, ref) => {
572
663
  // Panel state - only start collapsed if collapsible is true
573
664
  const [isCollapsed, setIsCollapsed] = useState(collapsible && defaultCollapsed);
@@ -625,6 +716,36 @@ const AIAgentPanel = React.forwardRef<AIAgentPanelHandle, AIAgentPanelProps>(({
625
716
  defaultAgent || agentIds[0] || ''
626
717
  );
627
718
 
719
+ // Sync with controlled selectedAgent prop when it changes
720
+ useEffect(() => {
721
+ // Only sync if selectedAgent is provided (controlled mode) and differs from current
722
+ if (selectedAgent && selectedAgent !== currentAgentId) {
723
+ const oldAgentId = currentAgentId;
724
+ setCurrentAgentId(selectedAgent);
725
+
726
+ // Fire onAgentSwitch callback for programmatic changes too
727
+ if (onAgentSwitch) {
728
+ onAgentSwitch(oldAgentId, selectedAgent);
729
+ }
730
+
731
+ // Update the current conversation's agent ID to match
732
+ if (currentConversationIdRef.current) {
733
+ setActiveConversations(prev => {
734
+ const existing = prev.get(currentConversationIdRef.current!);
735
+ if (existing) {
736
+ const next = new Map(prev);
737
+ next.set(currentConversationIdRef.current!, {
738
+ ...existing,
739
+ agentId: selectedAgent,
740
+ });
741
+ return next;
742
+ }
743
+ return prev;
744
+ });
745
+ }
746
+ }
747
+ }, [selectedAgent]); // Note: intentionally excluding currentAgentId and onAgentSwitch to only trigger on prop change
748
+
628
749
  // API-based conversation state
629
750
  const [apiConversations, setApiConversations] = useState<APIConversationSummary[]>([]);
630
751
  const [conversationsLoading, setConversationsLoading] = useState(false);
@@ -651,6 +772,9 @@ const AIAgentPanel = React.forwardRef<AIAgentPanelHandle, AIAgentPanelProps>(({
651
772
  'Older': false,
652
773
  });
653
774
 
775
+ // Context section toggle state (per-conversation disabled sections)
776
+ const [disabledContextSections, setDisabledContextSections] = useState<Map<string, Set<string>>>(new Map());
777
+
654
778
  // Agent registry hook
655
779
  const {
656
780
  agents: agentProfiles,
@@ -864,14 +988,9 @@ const AIAgentPanel = React.forwardRef<AIAgentPanelHandle, AIAgentPanelProps>(({
864
988
 
865
989
  setConversationsLoading(true);
866
990
  setConversationsError(null);
867
- console.log("projectId", projectId);
868
- console.log("customerId", customerId);
869
- console.log("apiKey", apiKey);
870
991
 
871
992
  try {
872
- console.log('fetchConversations - customerId:', customerId);
873
993
  const url = `https://api.llmasaservice.io/conversations?customer_id=${customerId}`;
874
- console.log('fetchConversations - URL:', url);
875
994
 
876
995
  const response = await fetch(url, {
877
996
  signal,
@@ -1428,9 +1547,30 @@ console.log("apiKey", apiKey);
1428
1547
  }
1429
1548
  }, [pageContextSections, currentAgentId, agentIds, getAgent, localOverrides]);
1430
1549
 
1550
+ // Get disabled sections for current conversation
1551
+ const currentDisabledSections = useMemo((): Set<string> => {
1552
+ return currentConversationId
1553
+ ? disabledContextSections.get(currentConversationId) || new Set<string>()
1554
+ : new Set<string>();
1555
+ }, [currentConversationId, disabledContextSections]);
1556
+
1557
+ // Filtered context (only enabled sections)
1558
+ const filteredContext = useMemo(() => {
1559
+ const enabledSections = mergedContext.sections.filter(
1560
+ section => !currentDisabledSections.has(section.id)
1561
+ );
1562
+ const totalTokens = enabledSections.reduce((sum, s) => sum + (s.tokens || 0), 0);
1563
+
1564
+ return {
1565
+ sections: enabledSections,
1566
+ totalTokens,
1567
+ };
1568
+ }, [mergedContext.sections, currentDisabledSections]);
1569
+
1431
1570
  // Build data array for ChatPanel
1432
1571
  const chatPanelData = useMemo(() => {
1433
- const contextData = mergedContext.sections.map((section) => ({
1572
+ // Use filtered sections (disabled sections are excluded)
1573
+ const contextData = filteredContext.sections.map((section) => ({
1434
1574
  key: section.id,
1435
1575
  data: JSON.stringify(section.data),
1436
1576
  }));
@@ -1447,7 +1587,7 @@ console.log("apiKey", apiKey);
1447
1587
  }
1448
1588
 
1449
1589
  return [...data, ...contextData];
1450
- }, [data, mergedContext.sections, buildAgentAwarenessInstructions, currentAgentId, enableAgentSuggestions]);
1590
+ }, [data, filteredContext.sections, buildAgentAwarenessInstructions, currentAgentId, enableAgentSuggestions]);
1451
1591
 
1452
1592
  // Handle agent switch - updates the agent for the current conversation without starting a new one
1453
1593
  const handleAgentSwitch = useCallback(
@@ -1616,6 +1756,26 @@ console.log("apiKey", apiKey);
1616
1756
  setHandoffSource('agent');
1617
1757
  }, []);
1618
1758
 
1759
+ // Handle context section toggle for current conversation
1760
+ const handleContextSectionToggle = useCallback((sectionId: string, enabled: boolean) => {
1761
+ if (!currentConversationId) return;
1762
+
1763
+ setDisabledContextSections(prev => {
1764
+ const next = new Map(prev);
1765
+ const conversationDisabled = next.get(currentConversationId) || new Set();
1766
+ const nextDisabled = new Set(conversationDisabled);
1767
+
1768
+ if (enabled) {
1769
+ nextDisabled.delete(sectionId);
1770
+ } else {
1771
+ nextDisabled.add(sectionId);
1772
+ }
1773
+
1774
+ next.set(currentConversationId, nextDisabled);
1775
+ return next;
1776
+ });
1777
+ }, [currentConversationId]);
1778
+
1619
1779
  // Handle conversation created - update temp ID to real ID from API
1620
1780
  const handleConversationCreated = useCallback((tempId: string, realId: string) => {
1621
1781
  console.log('Conversation created:', tempId, '->', realId);
@@ -2113,11 +2273,28 @@ console.log("apiKey", apiKey);
2113
2273
  handleAgentSwitch={handleAgentSwitch}
2114
2274
  agentsLoading={agentsLoading}
2115
2275
  contextSections={mergedContext.sections}
2116
- totalContextTokens={mergedContext.totalTokens || 0}
2276
+ totalContextTokens={filteredContext.totalTokens}
2117
2277
  maxContextTokens={maxContextTokens}
2118
2278
  enableContextDetailView={enableContextDetailView}
2279
+ disabledSectionIds={currentDisabledSections}
2280
+ onToggleSection={handleContextSectionToggle}
2119
2281
  onConversationCreated={handleConversationCreated}
2120
2282
  conversationInitialPrompt={activeConv.conversationInitialPrompt}
2283
+ cssUrl={cssUrl}
2284
+ markdownClass={markdownClass}
2285
+ width={width}
2286
+ height={height}
2287
+ scrollToEnd={scrollToEnd}
2288
+ prismStyle={prismStyle}
2289
+ showSaveButton={showSaveButton}
2290
+ showEmailButton={showEmailButton}
2291
+ messages={messages}
2292
+ showCallToAction={showCallToAction}
2293
+ callToActionButtonText={callToActionButtonText}
2294
+ callToActionEmailAddress={callToActionEmailAddress}
2295
+ callToActionEmailSubject={callToActionEmailSubject}
2296
+ customerEmailCaptureMode={customerEmailCaptureMode}
2297
+ customerEmailCapturePlaceholder={customerEmailCapturePlaceholder}
2121
2298
  />
2122
2299
  ))}
2123
2300