@hef2024/llmasaservice-ui 0.21.0 → 0.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AICHATPANEL-PORT-INVENTORY.md +2 -0
- package/CONTROLLED-COLLAPSE-IMPLEMENTATION.md +274 -0
- package/DEBUG-ERROR-HANDLING.md +2 -0
- package/FIX-APPLIED.md +2 -0
- package/IMPLEMENTATION-COMPLETE.md +2 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +53 -11
- package/dist/index.mjs +53 -11
- package/docs/CHANGELOG-ERROR-HANDLING.md +2 -0
- package/docs/CONTROLLED-COLLAPSE-QUICK-START.md +147 -0
- package/docs/CONTROLLED-COLLAPSE-STATE.md +651 -0
- package/docs/CONVERSATION-HISTORY.md +2 -0
- package/docs/ERROR-HANDLING-413.md +2 -0
- package/docs/ERROR-HANDLING-SUMMARY.md +2 -0
- package/package.json +1 -1
- package/src/AIAgentPanel.tsx +59 -6
- package/src/AIChatPanel.tsx +25 -5
- package/src/components/ui/Button.tsx +2 -0
- package/src/components/ui/Dialog.tsx +2 -0
- package/src/components/ui/Input.tsx +2 -0
- package/src/components/ui/Select.tsx +2 -0
- package/src/components/ui/ToolInfoModal.tsx +2 -0
- package/src/components/ui/Tooltip.tsx +2 -0
- package/src/components/ui/index.ts +2 -0
package/src/AIAgentPanel.tsx
CHANGED
|
@@ -96,6 +96,8 @@ export interface AIAgentPanelProps {
|
|
|
96
96
|
theme?: 'light' | 'dark';
|
|
97
97
|
collapsible?: boolean;
|
|
98
98
|
defaultCollapsed?: boolean;
|
|
99
|
+
isCollapsed?: boolean;
|
|
100
|
+
onCollapsedChange?: (isCollapsed: boolean) => void;
|
|
99
101
|
defaultWidth?: number;
|
|
100
102
|
minWidth?: number;
|
|
101
103
|
maxWidth?: number;
|
|
@@ -618,6 +620,8 @@ const AIAgentPanel = React.forwardRef<AIAgentPanelHandle, AIAgentPanelProps>(({
|
|
|
618
620
|
theme = 'light',
|
|
619
621
|
collapsible = true,
|
|
620
622
|
defaultCollapsed = false,
|
|
623
|
+
isCollapsed: controlledIsCollapsed,
|
|
624
|
+
onCollapsedChange,
|
|
621
625
|
defaultWidth = 720,
|
|
622
626
|
minWidth = 520,
|
|
623
627
|
maxWidth = 1200,
|
|
@@ -660,8 +664,36 @@ const AIAgentPanel = React.forwardRef<AIAgentPanelHandle, AIAgentPanelProps>(({
|
|
|
660
664
|
customerEmailCaptureMode,
|
|
661
665
|
customerEmailCapturePlaceholder,
|
|
662
666
|
}, ref) => {
|
|
667
|
+
// Dev mode warnings for prop conflicts
|
|
668
|
+
useEffect(() => {
|
|
669
|
+
if (process.env.NODE_ENV === 'development') {
|
|
670
|
+
// Warn if isCollapsed is provided without onCollapsedChange (user can't interact)
|
|
671
|
+
if (controlledIsCollapsed !== undefined && !onCollapsedChange) {
|
|
672
|
+
console.warn(
|
|
673
|
+
'AIAgentPanel: You provided `isCollapsed` prop without `onCollapsedChange`. ' +
|
|
674
|
+
'This will render a read-only collapsed state. To allow user interaction, provide both props.'
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// Warn if both isCollapsed and defaultCollapsed are provided (conflicting props)
|
|
679
|
+
if (controlledIsCollapsed !== undefined && defaultCollapsed !== false) {
|
|
680
|
+
console.warn(
|
|
681
|
+
'AIAgentPanel: You provided both `isCollapsed` and `defaultCollapsed` props. ' +
|
|
682
|
+
'When using controlled mode (isCollapsed), the defaultCollapsed prop is ignored. ' +
|
|
683
|
+
'Remove defaultCollapsed to avoid confusion.'
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}, [controlledIsCollapsed, onCollapsedChange, defaultCollapsed]);
|
|
688
|
+
|
|
663
689
|
// Panel state - only start collapsed if collapsible is true
|
|
664
|
-
const [
|
|
690
|
+
const [uncontrolledIsCollapsed, setUncontrolledIsCollapsed] = useState(collapsible && defaultCollapsed);
|
|
691
|
+
|
|
692
|
+
// Determine if controlled
|
|
693
|
+
const isControlled = controlledIsCollapsed !== undefined;
|
|
694
|
+
|
|
695
|
+
// Use controlled value if provided, otherwise use internal state
|
|
696
|
+
const isCollapsed = isControlled ? controlledIsCollapsed : uncontrolledIsCollapsed;
|
|
665
697
|
const [isHistoryCollapsed, setIsHistoryCollapsed] = useState(() => {
|
|
666
698
|
if (typeof window !== 'undefined') {
|
|
667
699
|
const saved = localStorage.getItem('ai-agent-panel-history-collapsed');
|
|
@@ -1876,8 +1908,17 @@ const AIAgentPanel = React.forwardRef<AIAgentPanelHandle, AIAgentPanelProps>(({
|
|
|
1876
1908
|
// Toggle collapse - only if collapsible is enabled
|
|
1877
1909
|
const toggleCollapse = useCallback(() => {
|
|
1878
1910
|
if (!collapsible) return;
|
|
1879
|
-
|
|
1880
|
-
|
|
1911
|
+
|
|
1912
|
+
const newValue = !isCollapsed;
|
|
1913
|
+
|
|
1914
|
+
// Update internal state if uncontrolled
|
|
1915
|
+
if (!isControlled) {
|
|
1916
|
+
setUncontrolledIsCollapsed(newValue);
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
// Call callback if provided
|
|
1920
|
+
onCollapsedChange?.(newValue);
|
|
1921
|
+
}, [collapsible, isCollapsed, isControlled, onCollapsedChange]);
|
|
1881
1922
|
|
|
1882
1923
|
// Toggle history collapse
|
|
1883
1924
|
const toggleHistoryCollapse = useCallback(() => {
|
|
@@ -1927,7 +1968,11 @@ const AIAgentPanel = React.forwardRef<AIAgentPanelHandle, AIAgentPanelProps>(({
|
|
|
1927
1968
|
variant="ghost"
|
|
1928
1969
|
size="icon"
|
|
1929
1970
|
onClick={() => {
|
|
1930
|
-
|
|
1971
|
+
// Expand panel in controlled or uncontrolled mode
|
|
1972
|
+
if (!isControlled) {
|
|
1973
|
+
setUncontrolledIsCollapsed(false);
|
|
1974
|
+
}
|
|
1975
|
+
onCollapsedChange?.(false);
|
|
1931
1976
|
setShowSearch(true);
|
|
1932
1977
|
}}
|
|
1933
1978
|
>
|
|
@@ -1941,7 +1986,11 @@ const AIAgentPanel = React.forwardRef<AIAgentPanelHandle, AIAgentPanelProps>(({
|
|
|
1941
1986
|
variant="ghost"
|
|
1942
1987
|
size="icon"
|
|
1943
1988
|
onClick={() => {
|
|
1944
|
-
|
|
1989
|
+
// Expand panel in controlled or uncontrolled mode
|
|
1990
|
+
if (!isControlled) {
|
|
1991
|
+
setUncontrolledIsCollapsed(false);
|
|
1992
|
+
}
|
|
1993
|
+
onCollapsedChange?.(false);
|
|
1945
1994
|
handleNewConversation();
|
|
1946
1995
|
}}
|
|
1947
1996
|
>
|
|
@@ -1964,7 +2013,11 @@ const AIAgentPanel = React.forwardRef<AIAgentPanelHandle, AIAgentPanelProps>(({
|
|
|
1964
2013
|
variant={agent.id === currentAgentId ? 'secondary' : 'ghost'}
|
|
1965
2014
|
size="icon"
|
|
1966
2015
|
onClick={() => {
|
|
1967
|
-
|
|
2016
|
+
// Expand panel in controlled or uncontrolled mode
|
|
2017
|
+
if (!isControlled) {
|
|
2018
|
+
setUncontrolledIsCollapsed(false);
|
|
2019
|
+
}
|
|
2020
|
+
onCollapsedChange?.(false);
|
|
1968
2021
|
if (hasActiveConversation && activeConvForAgent) {
|
|
1969
2022
|
// Switch to the existing conversation
|
|
1970
2023
|
setCurrentConversationId(activeConvForAgent.conversationId);
|
package/src/AIChatPanel.tsx
CHANGED
|
@@ -1470,12 +1470,28 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
|
|
|
1470
1470
|
}, [thumbsDownClick, interactionClicked]);
|
|
1471
1471
|
|
|
1472
1472
|
// Scroll to bottom - throttled using RAF to prevent layout thrashing
|
|
1473
|
-
const scrollToBottom = useCallback(() => {
|
|
1473
|
+
const scrollToBottom = useCallback((force: boolean = false) => {
|
|
1474
1474
|
// Cancel any pending scroll
|
|
1475
1475
|
if (scrollRAFRef.current) {
|
|
1476
1476
|
cancelAnimationFrame(scrollRAFRef.current);
|
|
1477
1477
|
}
|
|
1478
1478
|
|
|
1479
|
+
// Check if we should scroll - only if user is near bottom or force is true
|
|
1480
|
+
if (!force && responseAreaRef.current) {
|
|
1481
|
+
const scrollViewport = responseAreaRef.current.querySelector('[data-radix-scroll-area-viewport]') as HTMLElement;
|
|
1482
|
+
const scrollElement = scrollViewport || responseAreaRef.current;
|
|
1483
|
+
|
|
1484
|
+
const scrollTop = scrollElement.scrollTop;
|
|
1485
|
+
const scrollHeight = scrollElement.scrollHeight;
|
|
1486
|
+
const clientHeight = scrollElement.clientHeight;
|
|
1487
|
+
const isNearBottom = scrollHeight - scrollTop - clientHeight < 100;
|
|
1488
|
+
|
|
1489
|
+
// If user is not near bottom, don't scroll (prevents scroll on layout changes like toast)
|
|
1490
|
+
if (!isNearBottom) {
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1479
1495
|
// Throttle to max once per 100ms to prevent performance issues during streaming
|
|
1480
1496
|
const now = Date.now();
|
|
1481
1497
|
if (now - lastScrollTimeRef.current < 100) {
|
|
@@ -1551,8 +1567,9 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
|
|
|
1551
1567
|
|
|
1552
1568
|
// Scroll to bottom immediately to show the new prompt
|
|
1553
1569
|
// Use setTimeout to ensure the DOM has updated
|
|
1570
|
+
// Force scroll since this is a user-initiated action
|
|
1554
1571
|
setTimeout(() => {
|
|
1555
|
-
scrollToBottom();
|
|
1572
|
+
scrollToBottom(true);
|
|
1556
1573
|
}, 0);
|
|
1557
1574
|
|
|
1558
1575
|
// Now proceed with API calls in the background (conversation creation + LLM call)
|
|
@@ -1808,7 +1825,8 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
|
|
|
1808
1825
|
// 3. We have content to show (response exists)
|
|
1809
1826
|
const shouldAutoScroll = scrollToEnd || !userHasScrolled;
|
|
1810
1827
|
if (!idle && shouldAutoScroll && response) {
|
|
1811
|
-
|
|
1828
|
+
// Use non-forced scroll - will only scroll if near bottom
|
|
1829
|
+
scrollToBottom(false);
|
|
1812
1830
|
}
|
|
1813
1831
|
}, [response, scrollToBottom, idle, userHasScrolled, scrollToEnd]); // Removed history dependency
|
|
1814
1832
|
|
|
@@ -2037,8 +2055,9 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
|
|
|
2037
2055
|
// Auto-scroll when the agent suggestion card appears
|
|
2038
2056
|
useEffect(() => {
|
|
2039
2057
|
// Small delay to ensure the card is fully rendered in the DOM
|
|
2058
|
+
// Force scroll since this is new content being added
|
|
2040
2059
|
const timer = setTimeout(() => {
|
|
2041
|
-
scrollToBottom();
|
|
2060
|
+
scrollToBottom(true);
|
|
2042
2061
|
}, 100);
|
|
2043
2062
|
return () => clearTimeout(timer);
|
|
2044
2063
|
}, []); // Empty deps - only run on mount
|
|
@@ -2158,8 +2177,9 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
|
|
|
2158
2177
|
onClick={() => {
|
|
2159
2178
|
onAgentChange(agentId);
|
|
2160
2179
|
// Scroll to bottom after a brief delay to let React re-render
|
|
2180
|
+
// Force scroll since this is a user-initiated action
|
|
2161
2181
|
setTimeout(() => {
|
|
2162
|
-
scrollToBottom();
|
|
2182
|
+
scrollToBottom(true);
|
|
2163
2183
|
}, 100);
|
|
2164
2184
|
}}
|
|
2165
2185
|
>
|