Skip to content

Commit 360c113

Browse files
committed
Improved Color Contrast and Ngrok banner
1 parent 6fb8bc0 commit 360c113

File tree

5 files changed

+56
-28
lines changed

5 files changed

+56
-28
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
All notable changes to LocalLab will be documented in this file.
44

5+
## [0.5.4] - 2024-04-30
6+
7+
### Improved
8+
9+
- Enhanced log coloring with lighter shades for better readability
10+
- Redesigned ngrok tunnel banner with dynamic width to accommodate long URLs
11+
- Improved visual aesthetics of the ngrok tunnel banner with modern styling
12+
- Added automatic width adjustment for banners based on content length
13+
- Fine-tuned color scheme to ensure all logs remain visible while not competing with important banners
14+
515
## [0.5.3] - 2024-04-30
616

717
### Improved

locallab/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
LocalLab - A lightweight AI inference server for running LLMs locally
33
"""
44

5-
__version__ = "0.5.3" # Updated to match setup.py
5+
__version__ = "0.5.4" # Updated to match setup.py
66

77
# Only import what's necessary initially, lazy-load the rest
88
from .logger import get_logger

locallab/logger/__init__.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,15 @@ class Colors:
5757
BRIGHT_YELLOW = Fore.YELLOW
5858
BRIGHT_RED = Fore.RED
5959

60-
# Subdued colors for regular logs
61-
SUBDUED_DEBUG = Fore.BLUE + Style.DIM # Dimmed blue for debug
62-
SUBDUED_INFO = Fore.WHITE + Style.DIM # Dimmed white for info
60+
# Subdued colors for regular logs - using lighter shades
61+
SUBDUED_DEBUG = Fore.CYAN + Style.DIM # Dimmed cyan for debug
62+
SUBDUED_INFO = Fore.WHITE # White for info (not dimmed)
6363
SUBDUED_WARNING = Fore.YELLOW + Style.DIM # Dimmed yellow for warnings
6464
SUBDUED_ERROR = Fore.RED + Style.DIM # Dimmed red for errors
6565

66+
# Special colors for very unimportant logs
67+
VERY_SUBDUED = Fore.LIGHTBLACK_EX # Light gray for very unimportant logs
68+
6669
# Reset
6770
RESET = Style.RESET_ALL
6871

@@ -122,8 +125,8 @@ def format(self, record):
122125

123126
# Determine the appropriate color based on log level and importance
124127
if is_ngrok_log or is_uvicorn_log:
125-
# Make ngrok and uvicorn logs very subdued (dark gray)
126-
color = Fore.BLACK + Style.BRIGHT # This creates a dark gray color on most terminals
128+
# Make ngrok and uvicorn logs subdued but still readable (light gray)
129+
color = Colors.VERY_SUBDUED
127130
elif record.levelno == logging.DEBUG:
128131
color = Colors.BRIGHT_CYAN if is_important else Colors.SUBDUED_DEBUG
129132
elif record.levelno == logging.INFO:

locallab/utils/networking.py

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,43 +22,58 @@ def setup_ngrok(port: int) -> Optional[str]:
2222
try:
2323
from pyngrok import ngrok, conf
2424
from colorama import Fore, Style
25-
25+
2626
# Get ngrok token using the standardized function
2727
auth_token = get_ngrok_token()
28-
28+
2929
if not auth_token:
3030
logger.error(f"{Fore.RED}Ngrok auth token not found. Please configure it using 'locallab config'{Style.RESET_ALL}")
3131
return None
32-
32+
3333
# Ensure token is properly set in environment
3434
set_env_var(NGROK_TOKEN_ENV, auth_token)
35-
35+
3636
# Configure ngrok
3737
ngrok.set_auth_token(auth_token)
3838
conf.get_default().auth_token = auth_token
39-
39+
4040
# Start tunnel with simplified configuration
4141
tunnel = ngrok.connect(
4242
addr=port,
4343
proto="http",
4444
bind_tls=True # Enable HTTPS
4545
)
46-
46+
4747
public_url = tunnel.public_url
48-
48+
4949
# Store the URL in environment for clients
5050
os.environ["LOCALLAB_NGROK_URL"] = public_url
51-
51+
52+
# Calculate banner width based on URL length (minimum 80 characters)
53+
url_length = len(public_url)
54+
banner_width = max(80, url_length + 20) # Add padding for aesthetics
55+
56+
# Create dynamic width horizontal lines
57+
h_line = "═" * (banner_width - 2)
58+
59+
# Create centered title with proper padding
60+
title = "✨ NGROK TUNNEL ACTIVE ✨"
61+
title_padding = (banner_width - len(title) - 2) // 2
62+
padded_title = " " * title_padding + title + " " * title_padding
63+
# Adjust if odd number
64+
if len(padded_title) < banner_width - 2:
65+
padded_title += " "
66+
5267
# Display banner
5368
logger.info(f"""
54-
{Fore.GREEN}╔═══════════════════════════════════════════════════════════════════{Style.RESET_ALL}
55-
{Fore.GREEN} ✨ NGROK TUNNEL ACTIVE ✨ {Style.RESET_ALL}
56-
{Fore.GREEN}╠═══════════════════════════════════════════════════════════════════{Style.RESET_ALL}
57-
{Fore.CYAN} Public URL: {Fore.YELLOW}{public_url}{Style.RESET_ALL}
58-
{Fore.GREEN}╚═══════════════════════════════════════════════════════════════════{Style.RESET_ALL}
69+
{Fore.CYAN}{h_line}{Style.RESET_ALL}
70+
{Fore.CYAN}{padded_title}{Style.RESET_ALL}
71+
{Fore.CYAN}{h_line}{Style.RESET_ALL}
72+
{Fore.CYAN}{Style.RESET_ALL} {Fore.GREEN}Public URL:{Style.RESET_ALL} {Fore.YELLOW}{public_url}{Style.RESET_ALL}{" " * (banner_width - len(public_url) - 14)}{Fore.CYAN}{Style.RESET_ALL}
73+
{Fore.CYAN}{h_line}{Style.RESET_ALL}
5974
""")
6075
return public_url
61-
76+
6277
except Exception as e:
6378
logger.error(f"{Fore.RED}Failed to setup ngrok: {str(e)}{Style.RESET_ALL}")
6479
logger.info(f"{Fore.YELLOW}Please check your ngrok token using 'locallab config'{Style.RESET_ALL}")
@@ -91,7 +106,7 @@ def get_network_interfaces() -> List[Dict[str, str]]:
91106
})
92107
except Exception:
93108
pass
94-
109+
95110
return interfaces
96111

97112
def get_public_ip() -> Optional[str]:
@@ -105,15 +120,15 @@ def get_public_ip() -> Optional[str]:
105120
def get_network_interfaces() -> dict:
106121
"""
107122
Get information about available network interfaces
108-
123+
109124
Returns:
110125
A dictionary with interface names as keys and their addresses as values
111126
"""
112127
interfaces = {}
113128
try:
114129
import socket
115130
import netifaces
116-
131+
117132
for interface in netifaces.interfaces():
118133
addresses = netifaces.ifaddresses(interface)
119134
# Get IPv4 addresses if available
@@ -143,13 +158,13 @@ def get_network_interfaces() -> dict:
143158
except Exception as e:
144159
logger.warning(f"Failed to get network interface information: {str(e)}")
145160
interfaces["error"] = str(e)
146-
161+
147162
return interfaces
148163

149164
async def get_public_ip() -> str:
150165
"""
151166
Get the public IP address of this machine
152-
167+
153168
Returns:
154169
The public IP address as a string, or an empty string if it cannot be determined
155170
"""
@@ -159,7 +174,7 @@ async def get_public_ip() -> str:
159174
"https://checkip.amazonaws.com",
160175
"https://ipinfo.io/ip"
161176
]
162-
177+
163178
try:
164179
# Try to use httpx for async requests
165180
import httpx
@@ -184,6 +199,6 @@ async def get_public_ip() -> str:
184199
continue
185200
except ImportError:
186201
logger.warning(f"{Fore.YELLOW}Neither httpx nor requests packages found. Cannot determine public IP.{Style.RESET_ALL}")
187-
202+
188203
# If we couldn't get the IP from any service, return empty string
189204
return ""

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747

4848
setup(
4949
name="locallab",
50-
version="0.5.3",
50+
version="0.5.4",
5151
packages=find_packages(include=["locallab", "locallab.*"]),
5252
install_requires=install_requires,
5353
extras_require={

0 commit comments

Comments
 (0)