From df32cff36ad421b87d83dbc4c725f34e86d98f0e Mon Sep 17 00:00:00 2001 From: Fabrizio Guespe Date: Sat, 25 Oct 2025 01:55:23 -0300 Subject: [PATCH 1/6] "Remove command suggestions feature" --- src/cli.tsx | 108 +------------------------- src/components/ChatView.tsx | 8 +- src/components/CommandSuggestions.tsx | 71 ----------------- src/components/Input.tsx | 19 +---- src/components/Layout.tsx | 44 ++++------- src/components/StatusBar.tsx | 27 +++++++ src/hooks/useXMTP.ts | 31 +++++++- src/utils/formatters.ts | 20 ++++- 8 files changed, 97 insertions(+), 231 deletions(-) delete mode 100644 src/components/CommandSuggestions.tsx diff --git a/src/cli.tsx b/src/cli.tsx index f8e0aa2..a115a9a 100644 --- a/src/cli.tsx +++ b/src/cli.tsx @@ -5,7 +5,6 @@ import type { XmtpEnv } from "@xmtp/agent-sdk"; import { privateKeyToAddress } from "viem/accounts"; import { Layout } from "./components/Layout.js"; import type { Command } from "./components/CommandPalette.js"; -import { CommandSuggestion } from "./components/CommandSuggestions.js"; import { useXMTP } from "./hooks/useXMTP.js"; import { useKeyboard } from "./hooks/useKeyboard.js"; import { useStore } from "./store/state.js"; @@ -90,92 +89,13 @@ const App: React.FC = ({ env, agentIdentifiers }) => { const [errorTimeout, setErrorTimeout] = useState(null); const [isInputActive, setIsInputActive] = useState(false); - // Command suggestions state - const [showSuggestions, setShowSuggestions] = useState(false); - const [selectedSuggestionIndex, setSelectedSuggestionIndex] = useState(0); - // Handle suggestion navigation - const handleSuggestionNavigation = (direction: 'up' | 'down') => { - if (!showSuggestions || suggestions.length === 0) return; - - if (direction === 'up') { - setSelectedSuggestionIndex(prev => - prev > 0 ? prev - 1 : suggestions.length - 1 - ); - } else { - setSelectedSuggestionIndex(prev => - prev < suggestions.length - 1 ? prev + 1 : 0 - ); - } - }; - - // Handle suggestion selection - const handleSuggestionSelect = (suggestion: CommandSuggestion) => { - // Find the corresponding command - const command = commands.find(cmd => cmd.id === suggestion.id); - if (command) { - setInputValue(`/${command.name.toLowerCase().replace(/\s+/g, '-')}`); - setShowSuggestions(false); - setSelectedSuggestionIndex(0); - } - }; - - // Handle input change with suggestions + // Handle input change const handleInputChange = (value: string) => { setInputValue(value); - - // Show suggestions when user types "/" - if (value.startsWith("/")) { - setShowSuggestions(true); - setSelectedSuggestionIndex(0); - } else { - setShowSuggestions(false); - setSelectedSuggestionIndex(0); - } }; - // Custom keyboard handler for suggestions - useInput((input, key) => { - if (!isInputActive || !showSuggestions) return; - - // Handle arrow keys for suggestion navigation - if (key.upArrow) { - handleSuggestionNavigation('up'); - return; - } - - if (key.downArrow) { - handleSuggestionNavigation('down'); - return; - } - - // Handle tab for suggestion completion - if (key.tab && suggestions.length > 0) { - const selectedSuggestion = suggestions[selectedSuggestionIndex]; - if (selectedSuggestion) { - handleSuggestionSelect(selectedSuggestion); - } - return; - } - - // Handle enter to execute selected suggestion - if (key.return && suggestions.length > 0) { - const selectedSuggestion = suggestions[selectedSuggestionIndex]; - if (selectedSuggestion) { - // Find the corresponding command and execute it - const command = commands.find(cmd => cmd.id === selectedSuggestion.id); - if (command) { - setInputValue(`/${command.name.toLowerCase().replace(/\s+/g, '-')}`); - setShowSuggestions(false); - setSelectedSuggestionIndex(0); - // Execute the command immediately - command.action(); - } - } - return; - } - }, { isActive: isInputActive && showSuggestions }); // XMTP hook const { @@ -265,23 +185,6 @@ const App: React.FC = ({ env, agentIdentifiers }) => { }, ]; - // Command suggestions logic - const getCommandSuggestions = (query: string): CommandSuggestion[] => { - if (!query.startsWith("/")) return []; - - const commandQuery = query.slice(1).toLowerCase(); - return commands.filter(cmd => - cmd.name.toLowerCase().includes(commandQuery) || - cmd.description.toLowerCase().includes(commandQuery) - ).slice(0, 6).map(cmd => ({ - id: cmd.id, - name: cmd.name, - description: cmd.description, - shortcut: cmd.shortcut, - })); - }; - - const suggestions = getCommandSuggestions(inputValue); // Keyboard navigation useKeyboard({ @@ -309,11 +212,6 @@ const App: React.FC = ({ env, agentIdentifiers }) => { toggleCommandPalette(); return; } - if (showSuggestions) { - setShowSuggestions(false); - setSelectedSuggestionIndex(0); - return; - } if (isInputActive) { setIsInputActive(false); setInputValue(""); @@ -481,10 +379,6 @@ const App: React.FC = ({ env, agentIdentifiers }) => { commands={commands} onCommandExecute={(cmd) => cmd.action()} onCommandPaletteClose={() => toggleCommandPalette()} - suggestions={suggestions} - selectedSuggestionIndex={selectedSuggestionIndex} - showSuggestions={showSuggestions} - onSuggestionSelect={handleSuggestionSelect} /> ); }; diff --git a/src/components/ChatView.tsx b/src/components/ChatView.tsx index 496beb3..0db6f00 100644 --- a/src/components/ChatView.tsx +++ b/src/components/ChatView.tsx @@ -47,7 +47,7 @@ export const ChatView: React.FC = ({ if (!conversation) { // Show conversation selector instead of empty state return ( - + = ({ } return ( - + {/* Chat header */} - + {conversation.type === "group" ? "👥 GROUP: " : "💬 DM: "} {conversation.name} @@ -68,7 +68,7 @@ export const ChatView: React.FC = ({ {/* Messages */} - + {visibleMessages.length === 0 ? ( No messages yet... ) : ( diff --git a/src/components/CommandSuggestions.tsx b/src/components/CommandSuggestions.tsx deleted file mode 100644 index 1332a4c..0000000 --- a/src/components/CommandSuggestions.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import React from "react"; -import { Box, Text } from "ink"; - -const RED = "#fc4c34"; - -export interface CommandSuggestion { - id: string; - name: string; - description: string; - shortcut?: string; -} - -interface CommandSuggestionsProps { - suggestions: CommandSuggestion[]; - selectedIndex: number; - query: string; - visible: boolean; -} - -export const CommandSuggestions: React.FC = ({ - suggestions, - selectedIndex, - query, - visible, -}) => { - if (!visible || suggestions.length === 0) { - return null; - } - - return ( - - - - 💡 Command suggestions for "{query}" - - - - - {suggestions.slice(0, 6).map((suggestion, index) => { - const isSelected = index === selectedIndex; - return ( - - {isSelected && ( - - )} - - /{suggestion.name} - - - {suggestion.description} - {suggestion.shortcut && ( - ({suggestion.shortcut}) - )} - - ); - })} - - - - ↑↓: Navigate • Tab: Complete • Enter: Execute • Esc: Close - - - ); -}; \ No newline at end of file diff --git a/src/components/Input.tsx b/src/components/Input.tsx index a64bd19..40196f8 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -1,7 +1,6 @@ import React from "react"; import { Box, Text } from "ink"; import TextInput from "ink-text-input"; -import { CommandSuggestions, CommandSuggestion } from "./CommandSuggestions"; const RED = "#fc4c34"; @@ -11,10 +10,6 @@ interface InputProps { onSubmit: (value: string) => void; placeholder?: string; commandMode?: boolean; - suggestions?: CommandSuggestion[]; - selectedSuggestionIndex?: number; - showSuggestions?: boolean; - onSuggestionSelect?: (suggestion: CommandSuggestion) => void; } export const Input: React.FC = ({ @@ -23,10 +18,6 @@ export const Input: React.FC = ({ onSubmit, placeholder = "Type a message...", commandMode = false, - suggestions = [], - selectedSuggestionIndex = 0, - showSuggestions = false, - onSuggestionSelect, }) => { return ( @@ -49,15 +40,7 @@ export const Input: React.FC = ({ - {/* Command suggestions */} - - - {commandMode && !showSuggestions && ( + {commandMode && ( 💡 Press Enter to execute command diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx index d9d5a7d..ac1c460 100644 --- a/src/components/Layout.tsx +++ b/src/components/Layout.tsx @@ -5,7 +5,6 @@ import { ChatView } from "./ChatView.js"; import { StatusBar } from "./StatusBar.js"; import { Input } from "./Input.js"; import { CommandPalette, type Command } from "./CommandPalette.js"; -import { CommandSuggestion } from "./CommandSuggestions.js"; import type { ConversationInfo, FormattedMessage } from "../types/index.js"; interface LayoutProps { @@ -37,11 +36,6 @@ interface LayoutProps { onCommandExecute: (command: Command) => void; onCommandPaletteClose: () => void; - // Command suggestions props - suggestions?: CommandSuggestion[]; - selectedSuggestionIndex?: number; - showSuggestions?: boolean; - onSuggestionSelect?: (suggestion: CommandSuggestion) => void; } export const Layout: React.FC = ({ @@ -63,15 +57,11 @@ export const Layout: React.FC = ({ commands, onCommandExecute, onCommandPaletteClose, - suggestions = [], - selectedSuggestionIndex = 0, - showSuggestions = false, - onSuggestionSelect, }) => { return ( - + {/* Main content area */} - + {/* Sidebar */} {showSidebar && ( = ({ borderColor="gray" paddingX={1} paddingY={0} + minHeight={0} > {/* Chat view */} - { - // This will be handled by the main app - }} - /> + + { + // This will be handled by the main app + }} + /> + {/* Input inside chat window border */} - + {isInputActive || currentConversation ? ( = ({ : "Enter Ethereum address or inbox ID..." } commandMode={commandMode} - suggestions={suggestions} - selectedSuggestionIndex={selectedSuggestionIndex} - showSuggestions={showSuggestions} - onSuggestionSelect={onSuggestionSelect} /> ) : ( = ({ conversationCount={conversations.length} error={error} statusMessage={statusMessage} + commands={commands} /> diff --git a/src/components/StatusBar.tsx b/src/components/StatusBar.tsx index 1b49d2b..116d44f 100644 --- a/src/components/StatusBar.tsx +++ b/src/components/StatusBar.tsx @@ -1,5 +1,6 @@ import React from "react"; import { Box, Text } from "ink"; +import type { Command } from "./CommandPalette"; const RED = "#fc4c34"; @@ -9,6 +10,7 @@ interface StatusBarProps { conversationCount: number; error?: string; statusMessage?: string; + commands?: Command[]; } export const StatusBar: React.FC = ({ @@ -17,6 +19,7 @@ export const StatusBar: React.FC = ({ conversationCount, error, statusMessage, + commands = [], }) => { const statusColor = connectionStatus === "connected" @@ -72,6 +75,30 @@ export const StatusBar: React.FC = ({ + {/* Command suggestions */} + {commands.length > 0 && ( + + 💡 + Commands: + {commands.slice(0, 4).map((cmd, index) => ( + + {cmd.name} + {cmd.shortcut && ( + ({cmd.shortcut}) + )} + {index < Math.min(commands.length, 4) - 1 && ( + + )} + + ))} + + )} + {/* Status message - at the very bottom */} {statusMessage && ( { const streamRef = useRef | null>(null); const isStreamingRef = useRef(false); const refreshIntervalRef = useRef(null); + const isInitializingRef = useRef(false); // Initialize agent useEffect(() => { + // Prevent multiple initializations + if (isInitializingRef.current || agent || globalAgent) { + if (globalAgent && !agent) { + setAgent(globalAgent); + setAddress(globalAgent.address || ""); + setInboxId(globalAgent.client.inboxId); + setUrl(getTestUrl(globalAgent.client) || ""); + setIsLoading(false); + } + return; + } + const initAgent = async () => { + isInitializingRef.current = true; try { onStatusChange?.("Initializing XMTP client..."); @@ -137,6 +154,9 @@ export const useXMTP = (options: UseXMTPOptions): UseXMTPReturn => { dbPath: (inboxId) => "." + `/cli-${env}-${inboxId.slice(0, 8)}.db3`, }); + // Store globally to prevent re-initialization + globalAgent = newAgent; + setAgent(newAgent); setAddress(newAgent.address || ""); setInboxId(newAgent.client.inboxId); @@ -196,18 +216,25 @@ export const useXMTP = (options: UseXMTPOptions): UseXMTPReturn => { setError(errMsg); onError?.(errMsg); setIsLoading(false); + isInitializingRef.current = false; } }; initAgent(); - // Cleanup interval on unmount + // Cleanup function return () => { if (refreshIntervalRef.current) { clearInterval(refreshIntervalRef.current); + refreshIntervalRef.current = null; + } + if (streamRef.current) { + // Stop streaming if active + isStreamingRef.current = false; + streamRef.current = null; } }; - }, [env, agentIdentifiers, onError, onStatusChange]); + }, [env, agentIdentifiers]); // Find or create conversation const findOrCreateConversationInternal = async ( diff --git a/src/utils/formatters.ts b/src/utils/formatters.ts index dd152d2..dd5ecde 100644 --- a/src/utils/formatters.ts +++ b/src/utils/formatters.ts @@ -22,10 +22,26 @@ export const formatMessage = ( if (typeof message.content === "string") { content = message.content; } else { + // For non-string content (like JSON objects), show a summary instead of full JSON try { - content = JSON.stringify(message.content, null, 2); + const obj = message.content as Record; + if (obj && typeof obj === "object") { + // Show a summary of the object structure + const keys = Object.keys(obj); + if (keys.length === 0) { + content = "[Empty object]"; + } else if (keys.length <= 3) { + // For small objects, show key-value pairs + content = keys.map(key => `${key}: ${typeof obj[key] === 'object' ? '[Object]' : obj[key]}`).join(", "); + } else { + // For large objects, show just the keys + content = `[Object with ${keys.length} properties: ${keys.slice(0, 3).join(", ")}${keys.length > 3 ? "..." : ""}]`; + } + } else { + content = `[${typeof message.content}]`; + } } catch { - content = JSON.stringify(message.content); + content = "[Complex data]"; } } From 34d102da7c3ec7705293a9240e18a816d8afae17 Mon Sep 17 00:00:00 2001 From: Fabrizio Guespe Date: Sat, 25 Oct 2025 01:57:13 -0300 Subject: [PATCH 2/6] "Update color scheme in UI" --- src/components/ChatView.tsx | 13 +++++----- src/components/Input.tsx | 7 +++--- src/components/Layout.tsx | 6 ++--- src/components/StatusBar.tsx | 48 +++++++++++++++++------------------- 4 files changed, 36 insertions(+), 38 deletions(-) diff --git a/src/components/ChatView.tsx b/src/components/ChatView.tsx index 0db6f00..1e401f8 100644 --- a/src/components/ChatView.tsx +++ b/src/components/ChatView.tsx @@ -3,7 +3,8 @@ import { Box, Text } from "ink"; import type { FormattedMessage, ConversationInfo } from "../types/index.js"; import { ConversationSelector } from "./ConversationSelector.js"; -const RED = "#fc4c34"; +const DIM_GREY = "#666666"; +const LIGHT_GREY = "#888888"; interface ChatViewProps { conversation: ConversationInfo | null; @@ -60,8 +61,8 @@ export const ChatView: React.FC = ({ return ( {/* Chat header */} - - + + {conversation.type === "group" ? "👥 GROUP: " : "💬 DM: "} {conversation.name} @@ -70,7 +71,7 @@ export const ChatView: React.FC = ({ {/* Messages */} {visibleMessages.length === 0 ? ( - No messages yet... + No messages yet... ) : ( visibleMessages.map((msg) => { const prefix = `[${msg.timestamp}] ${msg.sender}: `; @@ -79,8 +80,8 @@ export const ChatView: React.FC = ({ return ( - [{msg.timestamp}] - + [{msg.timestamp}] + {msg.sender}: {formattedContent} diff --git a/src/components/Input.tsx b/src/components/Input.tsx index 40196f8..74d344e 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -2,7 +2,8 @@ import React from "react"; import { Box, Text } from "ink"; import TextInput from "ink-text-input"; -const RED = "#fc4c34"; +const DIM_GREY = "#666666"; +const LIGHT_GREY = "#888888"; interface InputProps { value: string; @@ -23,7 +24,7 @@ export const Input: React.FC = ({ @@ -42,7 +43,7 @@ export const Input: React.FC = ({ {commandMode && ( - + 💡 Press Enter to execute command diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx index ac1c460..0442d8f 100644 --- a/src/components/Layout.tsx +++ b/src/components/Layout.tsx @@ -78,7 +78,7 @@ export const Layout: React.FC = ({ flexGrow={1} marginLeft={showSidebar ? 1 : 0} borderStyle="round" - borderColor="gray" + borderColor="#666666" paddingX={1} paddingY={0} minHeight={0} @@ -114,11 +114,11 @@ export const Layout: React.FC = ({ ) : ( - + {conversations.length === 0 ? "No conversations available. Press Enter to start a new chat..." : selectedConversationIndex === -1 diff --git a/src/components/StatusBar.tsx b/src/components/StatusBar.tsx index 116d44f..4e4b6b4 100644 --- a/src/components/StatusBar.tsx +++ b/src/components/StatusBar.tsx @@ -2,7 +2,8 @@ import React from "react"; import { Box, Text } from "ink"; import type { Command } from "./CommandPalette"; -const RED = "#fc4c34"; +const DIM_GREY = "#666666"; +const LIGHT_GREY = "#888888"; interface StatusBarProps { connectionStatus: "connected" | "disconnected" | "connecting"; @@ -21,12 +22,7 @@ export const StatusBar: React.FC = ({ statusMessage, commands = [], }) => { - const statusColor = - connectionStatus === "connected" - ? "green" - : connectionStatus === "connecting" - ? "yellow" - : "red"; + const statusColor = LIGHT_GREY; const statusText = connectionStatus === "connected" ? "●" @@ -40,18 +36,18 @@ export const StatusBar: React.FC = ({ {error && ( - ⚠ {error} + ⚠ {error} )} {/* Status bar */} = ({ {/* Left side: connection status */} {statusText} - + {" "} {address} - • {conversationCount} chats + • {conversationCount} chats - ^B: - Sidebar - ^K: - Switch - ^C: - Quit + ^B: + Sidebar + ^K: + Switch + ^C: + Quit @@ -79,20 +75,20 @@ export const StatusBar: React.FC = ({ {commands.length > 0 && ( - 💡 - Commands: + 💡 + Commands: {commands.slice(0, 4).map((cmd, index) => ( - {cmd.name} + {cmd.name} {cmd.shortcut && ( - ({cmd.shortcut}) + ({cmd.shortcut}) )} {index < Math.min(commands.length, 4) - 1 && ( - + )} ))} @@ -103,10 +99,10 @@ export const StatusBar: React.FC = ({ {statusMessage && ( - ✓ {statusMessage} + ✓ {statusMessage} )} From 3a200a7945c4e25b12fdf293698f8643231e87b9 Mon Sep 17 00:00:00 2001 From: Fabrizio Guespe Date: Sat, 25 Oct 2025 01:59:24 -0300 Subject: [PATCH 3/6] "Update color scheme to grey" --- src/cli.tsx | 9 +++++---- src/components/CommandPalette.tsx | 27 +++++++++++++------------ src/components/ConversationSelector.tsx | 21 ++++++++++--------- src/components/Sidebar.tsx | 20 +++++++++--------- 4 files changed, 40 insertions(+), 37 deletions(-) diff --git a/src/cli.tsx b/src/cli.tsx index a115a9a..9eb337e 100644 --- a/src/cli.tsx +++ b/src/cli.tsx @@ -9,7 +9,8 @@ import { useXMTP } from "./hooks/useXMTP.js"; import { useKeyboard } from "./hooks/useKeyboard.js"; import { useStore } from "./store/state.js"; -const RED = "#fc4c34"; +const DIM_GREY = "#666666"; +const LIGHT_GREY = "#888888"; // ============================================================================ // Help Display @@ -343,14 +344,14 @@ const App: React.FC = ({ env, agentIdentifiers }) => { return ( - + 🔄 Initializing XMTP Client... {agent && ( - ✓ Agent initialized - + ✓ Agent initialized + Address: {address.slice(0, 10)}...{address.slice(-8)} diff --git a/src/components/CommandPalette.tsx b/src/components/CommandPalette.tsx index 59b9ad3..88c3783 100644 --- a/src/components/CommandPalette.tsx +++ b/src/components/CommandPalette.tsx @@ -2,7 +2,8 @@ import React, { useState, useEffect } from "react"; import { Box, Text, useInput } from "ink"; import TextInput from "ink-text-input"; -const RED = "#fc4c34"; +const DIM_GREY = "#666666"; +const LIGHT_GREY = "#888888"; export interface Command { id: string; @@ -79,21 +80,21 @@ export const CommandPalette: React.FC = ({ {/* Header */} - + Command Palette - (Press Esc to close) + (Press Esc to close) {/* Search input */} - - + + = ({ {/* Command list */} {filteredCommands.length === 0 ? ( - No commands found + No commands found ) : ( filteredCommands.slice(0, 10).map((cmd, index) => { const isSelected = index === selectedIndex; return ( - + {isSelected ? "▶ " : " "} - + {cmd.name} - - {cmd.description} + - {cmd.description} {cmd.shortcut && ( - ({cmd.shortcut}) + ({cmd.shortcut}) )} ); @@ -129,8 +130,8 @@ export const CommandPalette: React.FC = ({ {/* Footer */} - - ↑↓: Navigate • Enter: Execute • Esc: Close + + ↑↓: Navigate • Enter: Execute • Esc: Close ); diff --git a/src/components/ConversationSelector.tsx b/src/components/ConversationSelector.tsx index 67f262f..d1c72a8 100644 --- a/src/components/ConversationSelector.tsx +++ b/src/components/ConversationSelector.tsx @@ -3,7 +3,8 @@ import { Box, Text } from "ink"; import type { ConversationInfo } from "../types/index.js"; import { formatTime } from "../utils/formatters.js"; -const RED = "#fc4c34"; +const DIM_GREY = "#666666"; +const LIGHT_GREY = "#888888"; interface ConversationSelectorProps { conversations: ConversationInfo[]; @@ -23,7 +24,7 @@ export const ConversationSelector: React.FC = ({ 📱 XMTP Chat - Main Menu - + 💡 TIP: Use ↑↓ arrows to navigate, then press Enter @@ -38,7 +39,7 @@ export const ConversationSelector: React.FC = ({ {selectedIndex === -1 && ( - + Press Enter, then type an address @@ -47,7 +48,7 @@ export const ConversationSelector: React.FC = ({ {/* Divider */} {conversations.length > 0 && ( - ────────────────────────────────────── + ────────────────────────────────────── )} @@ -55,9 +56,9 @@ export const ConversationSelector: React.FC = ({ {conversations.length === 0 ? ( - 📭 No conversations yet! + 📭 No conversations yet! - + Select "New Chat" above to start chatting @@ -85,7 +86,7 @@ export const ConversationSelector: React.FC = ({ {/* Type badge */} {conv.type === "group" ? "👥" : "👤"} @@ -103,7 +104,7 @@ export const ConversationSelector: React.FC = ({ {/* Last message preview */} {conv.lastMessage && ( - + {" "} - {conv.lastMessage.slice(0, 30)} {conv.lastMessage.length > 30 ? "..." : ""} @@ -112,7 +113,7 @@ export const ConversationSelector: React.FC = ({ {/* Time */} {conv.lastMessageAt && ( - ({formatTime(conv.lastMessageAt)}) + ({formatTime(conv.lastMessageAt)}) )} {/* Unread count */} @@ -132,7 +133,7 @@ export const ConversationSelector: React.FC = ({ {/* Navigation hints */} ⌨️ Controls: - ↑↓: Navigate • Enter: Select • Esc: Back + ↑↓: Navigate • Enter: Select • Esc: Back ); diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index e4d1efb..0a21ab1 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -2,8 +2,8 @@ import React from "react"; import { Box, Text } from "ink"; import type { ConversationInfo } from "../types/index.js"; -const RED = "#fc4c34"; -const DIM_RED = "#cc3f2a"; +const DIM_GREY = "#666666"; +const LIGHT_GREY = "#888888"; interface SidebarProps { conversations: ConversationInfo[]; @@ -23,12 +23,12 @@ export const Sidebar: React.FC = ({ flexDirection="column" width={width} borderStyle="round" - borderColor="gray" + borderColor={DIM_GREY} paddingX={1} > {/* Header */} - + Conversations ({conversations.length}) @@ -36,7 +36,7 @@ export const Sidebar: React.FC = ({ {/* Conversation List */} {conversations.length === 0 ? ( - No conversations + No conversations ) : ( conversations.map((conv, index) => { const isSelected = index === selectedIndex; @@ -46,18 +46,18 @@ export const Sidebar: React.FC = ({ return ( {/* Selection indicator */} - + {isSelected ? "▶" : " "} {/* Current conversation indicator */} - + {isCurrent ? "●" : " "} {/* Type badge */} {conv.type === "group" ? "[G]" : "[D]"} @@ -66,7 +66,7 @@ export const Sidebar: React.FC = ({ {/* Name */} {" "} @@ -90,7 +90,7 @@ export const Sidebar: React.FC = ({ {/* Footer hints */} - ↑↓:Navigate Enter:Open + ↑↓:Navigate Enter:Open ); From 1ef678b449ed95be912c218b00057a7e56bf3504 Mon Sep 17 00:00:00 2001 From: Fabrizio Guespe Date: Sat, 25 Oct 2025 02:04:31 -0300 Subject: [PATCH 4/6] "Refactor UI and simplify commands" --- src/cli.tsx | 10 +++---- src/components/ChatView.tsx | 13 ++++----- src/components/Input.tsx | 7 ++--- src/components/StatusBar.tsx | 55 +++++++++++------------------------- 4 files changed, 30 insertions(+), 55 deletions(-) diff --git a/src/cli.tsx b/src/cli.tsx index 9eb337e..5635c73 100644 --- a/src/cli.tsx +++ b/src/cli.tsx @@ -9,6 +9,7 @@ import { useXMTP } from "./hooks/useXMTP.js"; import { useKeyboard } from "./hooks/useKeyboard.js"; import { useStore } from "./store/state.js"; +const RED = "#fc4c34"; const DIM_GREY = "#666666"; const LIGHT_GREY = "#888888"; @@ -41,9 +42,8 @@ KEYBINDINGS: Ctrl+C Quit COMMANDS: - /back Return to main conversation list - /list Toggle sidebar visibility - /chat Switch to conversation number + /b Return to main conversation list + /c Switch to conversation number /new
Create new conversation with address /refresh Refresh inbox and update conversations /exit, /quit Exit application @@ -262,7 +262,7 @@ const App: React.FC = ({ env, agentIdentifiers }) => { return; } - if (cmd.startsWith("chat ")) { + if (cmd.startsWith("c ")) { const parts = cmd.split(" "); const index = parseInt(parts[1]) - 1; if ( @@ -316,7 +316,7 @@ const App: React.FC = ({ env, agentIdentifiers }) => { } } - setErrorWithTimeout("Unknown command. Try /back, /list, /chat , /new
, /refresh, or /exit"); + setErrorWithTimeout("Unknown command. Try /b, /c <n>, /new <address>, /refresh, or /exit"); return; } diff --git a/src/components/ChatView.tsx b/src/components/ChatView.tsx index 1e401f8..0db6f00 100644 --- a/src/components/ChatView.tsx +++ b/src/components/ChatView.tsx @@ -3,8 +3,7 @@ import { Box, Text } from "ink"; import type { FormattedMessage, ConversationInfo } from "../types/index.js"; import { ConversationSelector } from "./ConversationSelector.js"; -const DIM_GREY = "#666666"; -const LIGHT_GREY = "#888888"; +const RED = "#fc4c34"; interface ChatViewProps { conversation: ConversationInfo | null; @@ -61,8 +60,8 @@ export const ChatView: React.FC = ({ return ( {/* Chat header */} - - + + {conversation.type === "group" ? "👥 GROUP: " : "💬 DM: "} {conversation.name} @@ -71,7 +70,7 @@ export const ChatView: React.FC = ({ {/* Messages */} {visibleMessages.length === 0 ? ( - No messages yet... + No messages yet... ) : ( visibleMessages.map((msg) => { const prefix = `[${msg.timestamp}] ${msg.sender}: `; @@ -80,8 +79,8 @@ export const ChatView: React.FC = ({ return ( - [{msg.timestamp}] - + [{msg.timestamp}] + {msg.sender}: {formattedContent} diff --git a/src/components/Input.tsx b/src/components/Input.tsx index 74d344e..40196f8 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -2,8 +2,7 @@ import React from "react"; import { Box, Text } from "ink"; import TextInput from "ink-text-input"; -const DIM_GREY = "#666666"; -const LIGHT_GREY = "#888888"; +const RED = "#fc4c34"; interface InputProps { value: string; @@ -24,7 +23,7 @@ export const Input: React.FC = ({ @@ -43,7 +42,7 @@ export const Input: React.FC = ({ {commandMode && ( - + 💡 Press Enter to execute command diff --git a/src/components/StatusBar.tsx b/src/components/StatusBar.tsx index 4e4b6b4..6ebf2f3 100644 --- a/src/components/StatusBar.tsx +++ b/src/components/StatusBar.tsx @@ -2,8 +2,7 @@ import React from "react"; import { Box, Text } from "ink"; import type { Command } from "./CommandPalette"; -const DIM_GREY = "#666666"; -const LIGHT_GREY = "#888888"; +const RED = "#fc4c34"; interface StatusBarProps { connectionStatus: "connected" | "disconnected" | "connecting"; @@ -22,7 +21,12 @@ export const StatusBar: React.FC = ({ statusMessage, commands = [], }) => { - const statusColor = LIGHT_GREY; + const statusColor = + connectionStatus === "connected" + ? "green" + : connectionStatus === "connecting" + ? "yellow" + : "red"; const statusText = connectionStatus === "connected" ? "●" @@ -36,18 +40,18 @@ export const StatusBar: React.FC = ({ {error && ( - ⚠ {error} + ⚠ {error} )} {/* Status bar */} = ({ {/* Left side: connection status */} {statusText} - + {" "} {address} - • {conversationCount} chats + • {conversationCount} chats - ^B: - Sidebar - ^K: - Switch - ^C: - Quit + ^B: + Sidebar - {/* Command suggestions */} - {commands.length > 0 && ( - - 💡 - Commands: - {commands.slice(0, 4).map((cmd, index) => ( - - {cmd.name} - {cmd.shortcut && ( - ({cmd.shortcut}) - )} - {index < Math.min(commands.length, 4) - 1 && ( - - )} - - ))} - - )} {/* Status message - at the very bottom */} {statusMessage && ( - ✓ {statusMessage} + ✓ {statusMessage} )} From b723bad5ac7cfec4f3361907fea852cf1c56b8c8 Mon Sep 17 00:00:00 2001 From: Fabrizio Guespe Date: Sat, 25 Oct 2025 02:05:09 -0300 Subject: [PATCH 5/6] Update text color to red --- src/cli.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cli.tsx b/src/cli.tsx index 5635c73..8924da1 100644 --- a/src/cli.tsx +++ b/src/cli.tsx @@ -344,14 +344,14 @@ const App: React.FC = ({ env, agentIdentifiers }) => { return ( - + 🔄 Initializing XMTP Client... {agent && ( - ✓ Agent initialized - + ✓ Agent initialized + Address: {address.slice(0, 10)}...{address.slice(-8)} From c4c543c366d25f29bf83d1c8796bac1b71bbb3de Mon Sep 17 00:00:00 2001 From: Fabrizio Guespe Date: Sat, 25 Oct 2025 02:06:21 -0300 Subject: [PATCH 6/6] "Update UI color scheme" --- src/cli.tsx | 4 ++-- src/components/Sidebar.tsx | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cli.tsx b/src/cli.tsx index 8924da1..b76d217 100644 --- a/src/cli.tsx +++ b/src/cli.tsx @@ -350,8 +350,8 @@ const App: React.FC = ({ env, agentIdentifiers }) => { {agent && ( - ✓ Agent initialized - + ✓ Agent initialized + Address: {address.slice(0, 10)}...{address.slice(-8)} diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 0a21ab1..c863555 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -2,8 +2,8 @@ import React from "react"; import { Box, Text } from "ink"; import type { ConversationInfo } from "../types/index.js"; +const RED = "#fc4c34"; const DIM_GREY = "#666666"; -const LIGHT_GREY = "#888888"; interface SidebarProps { conversations: ConversationInfo[]; @@ -28,7 +28,7 @@ export const Sidebar: React.FC = ({ > {/* Header */} - + Conversations ({conversations.length}) @@ -46,18 +46,18 @@ export const Sidebar: React.FC = ({ return ( {/* Selection indicator */} - + {isSelected ? "▶" : " "} {/* Current conversation indicator */} - + {isCurrent ? "●" : " "} {/* Type badge */} {conv.type === "group" ? "[G]" : "[D]"} @@ -66,7 +66,7 @@ export const Sidebar: React.FC = ({ {/* Name */} {" "} @@ -90,7 +90,7 @@ export const Sidebar: React.FC = ({ {/* Footer hints */} - ↑↓:Navigate Enter:Open + ↑↓:Navigate Enter:Open );