From 73e61ae9638305c341fc181e2f2b2962e3e51f6b Mon Sep 17 00:00:00 2001 From: aumpatel Date: Wed, 16 Jul 2025 22:39:41 -0400 Subject: [PATCH 1/3] Implemented smart auto-scroll in chat --- plexe/ui/index.html | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/plexe/ui/index.html b/plexe/ui/index.html index 9c5b415..8103bdd 100644 --- a/plexe/ui/index.html +++ b/plexe/ui/index.html @@ -270,20 +270,43 @@ const [messages, setMessages] = useState([]); const [input, setInput] = useState(''); const [isLoading, setIsLoading] = useState(false); + const [userScrolledUp, setUserScrolledUp] = useState(false); + const [showScrollButton, setShowScrollButton] = useState(false); const messagesEndRef = useRef(null); + const messagesContainerRef = useRef(null); const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + setShowScrollButton(false); + }; + + const handleScroll = (e) => { + const { scrollTop, scrollHeight, clientHeight } = e.target; + const isAtBottom = scrollHeight - scrollTop <= clientHeight + 10; + setUserScrolledUp(!isAtBottom); }; useEffect(() => { - scrollToBottom(); - }, [messages]); + if (messages.length > 0) { + const lastMessage = messages[messages.length - 1]; + if (lastMessage.role === 'user') { + // User sent message - always scroll to bottom + scrollToBottom(); + setShowScrollButton(false); + } else { + // AI sent message - check if user scrolled up + if (userScrolledUp) { + setShowScrollButton(true); + } else { + scrollToBottom(); + } + } + } + }, [messages, userScrolledUp]); useEffect(() => { const unsubscribe = wsRuntime.subscribe((data) => { if (data.type === 'status') return; - setMessages(prev => [...prev, { id: data.id || Date.now().toString(), role: data.role || 'assistant', @@ -291,30 +314,30 @@ }]); setIsLoading(false); }); - return unsubscribe; }, [wsRuntime]); const handleSubmit = (e) => { e.preventDefault(); if (!input.trim() || isLoading) return; - const userMessage = { id: Date.now().toString(), role: 'user', content: input.trim() }; - setMessages(prev => [...prev, userMessage]); setInput(''); setIsLoading(true); - wsRuntime.send(userMessage.content); }; return React.createElement('div', { className: 'flex flex-col h-full' }, // Messages area - React.createElement('div', { className: 'flex-1 overflow-y-auto p-4' }, + React.createElement('div', { + className: 'flex-1 overflow-y-auto p-4 relative', + onScroll: handleScroll, + ref: messagesContainerRef + }, messages.length === 0 && React.createElement('div', { className: 'text-center text-gray-500 mt-8' }, React.createElement('p', { className: 'text-lg mb-2' }, '👋 Hello! I\'m your Plexe Assistant.'), React.createElement('p', { className: 'text-sm' }, 'I help you build machine learning models through natural conversation.'), @@ -332,6 +355,11 @@ ), React.createElement('div', { ref: messagesEndRef }) ), + // Scroll to bottom button + showScrollButton && React.createElement('button', { + onClick: scrollToBottom, + className: 'fixed bottom-20 right-4 bg-blue-500 text-white rounded-full p-3 shadow-lg hover:bg-blue-600 transition-colors z-10' + }, '↓'), // Input area React.createElement('form', { onSubmit: handleSubmit, className: 'border-t p-4' }, React.createElement('div', { className: 'flex space-x-2' }, From 89e8a7cb63d40d09c6ab6354137e750793063065 Mon Sep 17 00:00:00 2001 From: aumpatel Date: Fri, 1 Aug 2025 18:01:53 -0400 Subject: [PATCH 2/3] Improve ObjectRegistry delete method with better error handling and optional confirmation --- plexe/core/object_registry.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/plexe/core/object_registry.py b/plexe/core/object_registry.py index 821babf..1f510e9 100644 --- a/plexe/core/object_registry.py +++ b/plexe/core/object_registry.py @@ -111,18 +111,40 @@ def get_all(self, t: Type[T]) -> Dict[str, T]: """ return {name: item.item for name, item in self._items.items() if name.startswith(str(t))} - def delete(self, t: Type[T], name: str) -> None: + def delete(self, t: Type[T], name: str, confirm: bool = False) -> None: """ Delete an item by name. :param t: type prefix for the item :param name: the name of the item to delete + :param confirm: whether to show confirmation prompt (default: False for backward compatibility) """ uri = self._get_uri(t, name) + if uri in self._items: + item = self._items[uri] + + if confirm: + print(f"About to delete: {uri}") + print(f"Item type: {type(item.item).__name__}") + print(f"Immutable: {item.immutable}") + response = input("Continue? (y/n): ") + if response.lower() != 'y': + logger.info(f"Delete cancelled for: {uri}") + return + del self._items[uri] + logger.info(f"Registry: Deleted {uri}") else: - raise KeyError(f"Item '{uri}' not found in registry") + # Provide helpful error message with available items + similar_items = self.list_by_type(t) + if similar_items: + error_msg = f"Item '{uri}' not found. Available {t.__name__} items: {similar_items}" + else: + error_msg = f"No {t.__name__} items found in registry" + + logger.warning(f"⚠️ {error_msg}") + raise KeyError(error_msg) def clear(self) -> None: """ From 671fa267317f44f9a823ce7c8595929b082b43f4 Mon Sep 17 00:00:00 2001 From: aumpatel Date: Fri, 1 Aug 2025 18:21:57 -0400 Subject: [PATCH 3/3] Improve ObjectRegistry delete method with better error handling and optional confirmation --- plexe/core/object_registry.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plexe/core/object_registry.py b/plexe/core/object_registry.py index 1f510e9..1eb83a3 100644 --- a/plexe/core/object_registry.py +++ b/plexe/core/object_registry.py @@ -120,19 +120,19 @@ def delete(self, t: Type[T], name: str, confirm: bool = False) -> None: :param confirm: whether to show confirmation prompt (default: False for backward compatibility) """ uri = self._get_uri(t, name) - + if uri in self._items: item = self._items[uri] - + if confirm: print(f"About to delete: {uri}") print(f"Item type: {type(item.item).__name__}") print(f"Immutable: {item.immutable}") response = input("Continue? (y/n): ") - if response.lower() != 'y': + if response.lower() != "y": logger.info(f"Delete cancelled for: {uri}") return - + del self._items[uri] logger.info(f"Registry: Deleted {uri}") else: @@ -142,7 +142,7 @@ def delete(self, t: Type[T], name: str, confirm: bool = False) -> None: error_msg = f"Item '{uri}' not found. Available {t.__name__} items: {similar_items}" else: error_msg = f"No {t.__name__} items found in registry" - + logger.warning(f"⚠️ {error_msg}") raise KeyError(error_msg)