Skip to content

Commit 5230ba9

Browse files
committed
feat(ui): Enhance chat interface aesthetics with modern indicators and improved ASCII art
1 parent 288a21e commit 5230ba9

File tree

2 files changed

+106
-47
lines changed

2 files changed

+106
-47
lines changed

locallab/cli/chat.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -413,11 +413,8 @@ async def _process_message(self, message: str):
413413

414414
# Mode override handled silently for cleaner interface
415415

416-
# Enhanced chat-style loading indicator with horizontal padding
417-
loading_text = Text()
418-
loading_text.append(" Thinking", style="dim bright_white") # Added horizontal padding
419-
loading_text.append("...", style="dim bright_cyan")
420-
self.ui.console.print(loading_text)
416+
# Aesthetic minimal generating indicator
417+
self._show_generating_indicator()
421418

422419
# Choose generation method based on active mode
423420
if active_mode == GenerationMode.STREAM:
@@ -437,6 +434,7 @@ async def _process_message(self, message: str):
437434
if response_text:
438435
model_name = self.model_info.get('model_id', 'AI') if self.model_info else 'AI'
439436
logger.debug(f"Chat mode: extracted {len(response_text)} characters")
437+
self._clear_generating_indicator()
440438
self.ui.display_ai_response(response_text, model_name)
441439
else:
442440
logger.error("Chat mode: failed to extract text from response")
@@ -466,6 +464,7 @@ async def _process_message(self, message: str):
466464
if response_text:
467465
model_name = self.model_info.get('model_id', 'AI') if self.model_info else 'AI'
468466
logger.debug(f"Simple generation: extracted {len(response_text)} characters")
467+
self._clear_generating_indicator()
469468
self.ui.display_ai_response(response_text, model_name)
470469
else:
471470
logger.error("Simple generation: failed to extract text from response")
@@ -1314,7 +1313,21 @@ def _display_batch_stats(self, prompts: list, responses: list):
13141313
if self.model_info:
13151314
model_name = self.model_info.get('model_id', 'Unknown')
13161315
self.ui.display_info(f" Model used: {model_name}")
1317-
1316+
1317+
def _show_generating_indicator(self):
1318+
"""Show aesthetic minimal generating indicator with text"""
1319+
from rich.text import Text
1320+
loading_text = Text()
1321+
loading_text.append(" ", style="dim white") # 6 spaces for alignment
1322+
loading_text.append("◦ ◦ ◦", style="dim magenta") # Aesthetic triple dots
1323+
loading_text.append(" generating...", style="dim white") # Clear feedback text
1324+
self.ui.console.print(loading_text)
1325+
1326+
def _clear_generating_indicator(self):
1327+
"""Clear the generating indicator by moving cursor up and clearing line"""
1328+
# Move cursor up one line and clear it
1329+
self.ui.console.print("\033[1A\033[K", end="")
1330+
13181331

13191332
def validate_url(ctx, param, value):
13201333
"""Validate URL parameter"""

locallab/cli/ui.py

Lines changed: 87 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -33,52 +33,65 @@ def display_welcome(self, server_url: str, mode: str, model_info: Optional[Dict[
3333
# Clear any previous content and start fresh
3434
self.console.clear()
3535

36-
# ASCII Art Banner with complete "LOCALLAB" text - no border, just bottom line
37-
banner_lines = [
36+
# Perfect ASCII Art with proper visual hierarchy - smaller LOCALLAB, larger CHAT
37+
locallab_lines = [
3838
"",
3939
" ██╗ ██████╗ ██████╗ █████╗ ██╗ ██╗ █████╗ ██████╗",
4040
" ██║ ██╔═══██╗██╔════╝██╔══██╗██║ ██║ ██╔══██╗██╔══██╗",
4141
" ██║ ██║ ██║██║ ███████║██║ ██║ ███████║██████╔╝",
4242
" ██║ ██║ ██║██║ ██╔══██║██║ ██║ ██╔══██║██╔══██╗",
4343
" ███████╗╚██████╔╝╚██████╗██║ ██║███████╗███████╗██║ ██║██████╔╝",
4444
" ╚══════╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚═╝╚═════╝",
45-
"",
46-
" Chat Interface",
47-
"",
48-
"─────────────────────────────────────────────────────────────────"
45+
""
46+
]
47+
48+
chat_lines = [
49+
" ╔██████╗██╗ ██╗ █████╗ ████████╗",
50+
" ██╔════╝██║ ██║██╔══██╗╚══██╔══╝",
51+
" ██║ ███████║███████║ ██║ ",
52+
" ██║ ██╔══██║██╔══██║ ██║ ",
53+
" ╚██████╗██║ ██║██║ ██║ ██║ ",
54+
" ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ",
55+
""
4956
]
5057

51-
# Display banner with balanced color - lighter but not too dark
52-
for line in banner_lines:
58+
# Display LOCALLAB banner with bluish-purplish color
59+
for line in locallab_lines:
60+
banner_text = Text()
61+
banner_text.append(line, style="blue") # Little bluish-purplish - elegant and sophisticated
62+
self.console.print(banner_text)
63+
64+
# Display CHAT banner with purplish color
65+
for line in chat_lines:
5366
banner_text = Text()
54-
banner_text.append(line, style="bright_blue")
67+
banner_text.append(line, style="magenta") # Purplish type - beautiful and distinctive
5568
self.console.print(banner_text)
5669

5770
# Connection status with modern styling and horizontal padding
5871
status_text = Text()
5972
status_text.append(" ●", style="bright_green") # Added horizontal padding
6073
status_text.append(" Connected", style="dim bright_white")
6174

62-
# Model info if available
75+
# Model info with warm, connection-building color
6376
if model_info and (model_info.get('model_id') or model_info.get('id')):
6477
model_name = model_info.get('model_id') or model_info.get('id') or model_info.get('name')
6578
display_name = model_name.split('/')[-1] if '/' in model_name else model_name
6679
display_name = display_name.replace('-Instruct', '').replace('-Chat', '')
67-
status_text.append(f" {display_name}", style="dim bright_cyan")
80+
status_text.append(f" {display_name}", style="green")
6881

6982
self.console.print(status_text)
7083

71-
# Concise usage guide with horizontal padding
84+
# Modern usage guide with sophisticated styling
7285
usage_text = Text()
73-
usage_text.append(" Usage: ", style="dim bright_white") # Added horizontal padding
86+
usage_text.append(" ", style="dim magenta") # Modern arrow indicator
7487
usage_text.append("Type your message or use ", style="dim white")
75-
usage_text.append("--stream", style="bright_cyan")
76-
usage_text.append(", ", style="dim white")
77-
usage_text.append("--chat", style="bright_cyan")
78-
usage_text.append(", ", style="dim white")
79-
usage_text.append("--batch", style="bright_cyan")
80-
usage_text.append(", ", style="dim white")
81-
usage_text.append("--simple", style="bright_cyan")
88+
usage_text.append("--stream", style="cyan")
89+
usage_text.append("", style="dim magenta") # Elegant separator
90+
usage_text.append("--chat", style="cyan")
91+
usage_text.append("", style="dim magenta")
92+
usage_text.append("--batch", style="cyan")
93+
usage_text.append("", style="dim magenta")
94+
usage_text.append("--simple", style="cyan")
8295
usage_text.append(" to override modes", style="dim white")
8396

8497
self.console.print(usage_text)
@@ -120,10 +133,10 @@ def display_help(self):
120133
self.console.print(panel)
121134

122135
def get_user_input(self) -> Optional[str]:
123-
"""Get user input with enhanced chat-style prompt"""
136+
"""Get user input with innovative aesthetic prompt"""
124137
try:
125-
# Enhanced chat-style prompt with better visual distinction
126-
prompt_text = "[bold bright_white]You[/bold bright_white][dim white]:[/dim white]"
138+
# Modern prompt with sophisticated styling
139+
prompt_text = "[dim magenta]▸[/dim magenta] [bold white]You[/bold white][dim white]:[/dim white]"
127140
user_input = Prompt.ask(prompt_text, console=self.console)
128141

129142
if user_input.strip():
@@ -135,12 +148,13 @@ def get_user_input(self) -> Optional[str]:
135148
return None
136149

137150
def display_user_message(self, message: str):
138-
"""Display user message with enhanced chat-style formatting and horizontal padding"""
139-
# Create a more chat-like user message display with padding
151+
"""Display user message with innovative aesthetic formatting"""
152+
# Modern user message display with sophisticated elements
140153
user_text = Text()
141-
user_text.append(" You", style="bold bright_white") # Added horizontal padding
154+
user_text.append(" ▸ ", style="dim magenta") # Modern arrow indicator
155+
user_text.append("You", style="bold white")
142156
user_text.append(": ", style="dim white")
143-
user_text.append(message, style="bright_white")
157+
user_text.append(message, style="white")
144158

145159
self.console.print(user_text)
146160
# No extra spacing after user message for tighter conversation flow
@@ -152,29 +166,58 @@ def display_ai_response(self, response: str, model_name: Optional[str] = None):
152166
ai_label = ai_label.replace('-Instruct', '').replace('-Chat', '') # Clean up model name
153167

154168
header = Text()
155-
header.append(" " + ai_label, style="bold bright_cyan") # Added horizontal padding
169+
header.append(" ◂ ", style="dim blue") # Modern AI indicator (left arrow)
170+
header.append(ai_label, style="green") # Warm, connection-building color
156171
header.append(": ", style="dim white")
157172

158173
self.console.print(header, end="")
159174

160-
# Enhanced markdown rendering with subdued styling for better hierarchy
175+
# Enhanced rendering with guaranteed proper indentation
161176
try:
162-
rendered_content = self._render_enhanced_markdown(response)
163-
self.console.print(rendered_content)
177+
# Check if response contains markdown that needs special handling
178+
if self._contains_markdown(response) or self._contains_code_blocks(response):
179+
# For markdown content, render first then add padding to preserve formatting
180+
rendered_content = self._render_enhanced_markdown(response)
181+
# Use Rich's Padding to ensure indentation is preserved
182+
from rich.padding import Padding
183+
padded_content = Padding(rendered_content, (0, 0, 0, 6)) # Left padding of 6
184+
self.console.print(padded_content)
185+
else:
186+
# For plain text, add padding directly
187+
indented_response = self._add_text_padding(response)
188+
self.console.print(indented_response, style="white")
164189
except Exception as e:
165-
# Fallback to plain text with subdued styling (lighter than user text)
166-
self.console.print(response, style="white")
190+
# Fallback to plain text with guaranteed indentation
191+
indented_response = self._add_text_padding(response)
192+
self.console.print(indented_response, style="white")
167193

168194
# Add spacing after AI response for better conversation flow
169195
self.console.print()
170-
196+
197+
def _add_text_padding(self, text: str) -> str:
198+
"""Add consistent horizontal padding to plain text"""
199+
# Add 6 spaces to each line for consistent indentation
200+
lines = text.split('\n')
201+
padded_lines = [' ' + line for line in lines] # 6 spaces for alignment
202+
return '\n'.join(padded_lines)
203+
204+
def _contains_markdown(self, text: str) -> bool:
205+
"""Check if text contains markdown formatting"""
206+
markdown_indicators = ['**', '*', '`', '#', '- ', '* ', '+ ', '1. ', '[', '](']
207+
return any(indicator in text for indicator in markdown_indicators)
208+
209+
def _contains_code_blocks(self, text: str) -> bool:
210+
"""Check if text contains code blocks"""
211+
return '```' in text or '`' in text
212+
171213
def display_streaming_response(self, model_name: Optional[str] = None):
172214
"""Start displaying a streaming response with enhanced chat-style formatting and horizontal padding"""
173215
ai_label = model_name.split('/')[-1] if model_name and '/' in model_name else (model_name or "AI")
174216
ai_label = ai_label.replace('-Instruct', '').replace('-Chat', '') # Clean up model name
175217

176218
header = Text()
177-
header.append(" " + ai_label, style="bold bright_cyan") # Added horizontal padding
219+
header.append(" ◂ ", style="dim blue") # Modern AI indicator (left arrow)
220+
header.append(ai_label, style="green") # Warm, connection-building color
178221
header.append(": ", style="dim white")
179222
self.console.print(header, end="")
180223

@@ -189,9 +232,10 @@ def display_error(self, error_message: str, silent: bool = False):
189232
return
190233

191234
error_text = Text()
192-
error_text.append(" System", style="bold red") # Added horizontal padding
235+
error_text.append(" ⚠ ", style="dim red") # Modern warning indicator
236+
error_text.append("System", style="bold red")
193237
error_text.append(": ", style="dim white")
194-
error_text.append(error_message, style="bright_white")
238+
error_text.append(error_message, style="white")
195239

196240
self.console.print(error_text)
197241
self.console.print() # Add spacing after error
@@ -212,9 +256,10 @@ def display_subtle_status(self, message: str, style: str = "dim white"):
212256
self.console.print(status_text)
213257

214258
def display_info(self, info_message: str):
215-
"""Display info message with modern minimal styling and horizontal padding"""
259+
"""Display info message with innovative aesthetic styling"""
216260
info_text = Text()
217-
info_text.append(" " + info_message, style="dim white") # Added horizontal padding
261+
info_text.append(" ℹ ", style="dim blue") # Modern info indicator
262+
info_text.append(info_message, style="dim white")
218263

219264
self.console.print(info_text)
220265

@@ -228,9 +273,10 @@ def clear_screen(self):
228273
os.system('cls' if os.name == 'nt' else 'clear')
229274

230275
def display_goodbye(self):
231-
"""Display minimal goodbye message with horizontal padding"""
276+
"""Display innovative aesthetic goodbye message"""
232277
goodbye_text = Text()
233-
goodbye_text.append(" Goodbye", style="dim white") # Added horizontal padding
278+
goodbye_text.append(" ✓ ", style="dim magenta") # Modern checkmark indicator
279+
goodbye_text.append("Goodbye", style="dim white")
234280

235281
self.console.print(goodbye_text)
236282

0 commit comments

Comments
 (0)