Onboarding Improvements

This commit is contained in:
ajay.calsoft
2025-04-16 10:32:59 +00:00
parent 036fde9e81
commit 372bf71a0a
3 changed files with 252 additions and 281 deletions

View File

@@ -5,12 +5,12 @@ from loguru import logger
import streamlit as st import streamlit as st
import os import os
import sys import sys
# Assuming api_key_tests are in the parent directory relative to this component # Corrected import: Assuming validation functions are in validation.py in the parent directory
from ..api_key_tests import ( from ..validation import (
test_openai_api_key, test_openai_api_key,
test_gemini_api_key, test_gemini_api_key,
test_anthropic_api_key, # Keep if needed elsewhere, not used in this step currently # test_anthropic_api_key, # Keep commented if not used or add if needed
test_deepseek_api_key, # Keep if needed elsewhere # test_deepseek_api_key, # Keep commented if not used or add if needed
test_mistral_api_key test_mistral_api_key
) )
@@ -23,11 +23,24 @@ def _validate_provider_key(provider_name: str, key_value: str) -> bool:
try: try:
logger.debug(f"Validating key for {provider_name}...") logger.debug(f"Validating key for {provider_name}...")
if provider_name == "openai": if provider_name == "openai":
is_valid = test_openai_api_key(key_value) # Ensure the function exists in validation.py
if callable(getattr(sys.modules[__name__], 'test_openai_api_key', None)):
is_valid = test_openai_api_key(key_value)
else:
logger.error("test_openai_api_key not found in validation module")
is_valid = False # Assume invalid if test func missing
elif provider_name == "gemini": elif provider_name == "gemini":
is_valid = test_gemini_api_key(key_value) if callable(getattr(sys.modules[__name__], 'test_gemini_api_key', None)):
is_valid = test_gemini_api_key(key_value)
else:
logger.error("test_gemini_api_key not found in validation module")
is_valid = False
elif provider_name == "mistral": elif provider_name == "mistral":
is_valid = test_mistral_api_key(key_value) if callable(getattr(sys.modules[__name__], 'test_mistral_api_key', None)):
is_valid = test_mistral_api_key(key_value)
else:
logger.error("test_mistral_api_key not found in validation module")
is_valid = False
else: else:
logger.warning(f"Validation not implemented for provider: {provider_name}") logger.warning(f"Validation not implemented for provider: {provider_name}")
return False # Or True if unknown providers are allowed without validation return False # Or True if unknown providers are allowed without validation
@@ -134,14 +147,15 @@ def render_ai_providers_setup(api_key_manager) -> Dict[str, Any]:
) )
# Feedback Area for OpenAI # Feedback Area for OpenAI
openai_status = st.session_state.get("openai_status", "unsaved") openai_status = st.session_state.get("openai_status", "unsaved")
feedback_placeholder_openai = st.empty()
if openai_status == "saving": if openai_status == "saving":
st.spinner("Validating OpenAI key...") feedback_placeholder_openai.info("Validating OpenAI key...", icon="")
elif openai_status == "valid": elif openai_status == "valid":
st.success("OpenAI key saved and valid!", icon="") feedback_placeholder_openai.success("OpenAI key saved and valid!", icon="")
elif openai_status == "invalid": elif openai_status == "invalid":
st.error("Invalid OpenAI key. Please check and try again.", icon="") feedback_placeholder_openai.error("Invalid OpenAI key. Please check and try again.", icon="")
elif openai_status == "error": elif openai_status == "error":
st.error("Error saving/validating OpenAI key.", icon="⚠️") feedback_placeholder_openai.error("Error saving/validating OpenAI key.", icon="⚠️")
# --- Google Gemini --- # --- Google Gemini ---
st.subheader("Google Gemini (Required)") st.subheader("Google Gemini (Required)")
@@ -157,14 +171,15 @@ def render_ai_providers_setup(api_key_manager) -> Dict[str, Any]:
) )
# Feedback Area for Gemini # Feedback Area for Gemini
gemini_status = st.session_state.get("gemini_status", "unsaved") gemini_status = st.session_state.get("gemini_status", "unsaved")
feedback_placeholder_gemini = st.empty()
if gemini_status == "saving": if gemini_status == "saving":
st.spinner("Validating Gemini key...") feedback_placeholder_gemini.info("Validating Gemini key...", icon="")
elif gemini_status == "valid": elif gemini_status == "valid":
st.success("Gemini key saved and valid!", icon="") feedback_placeholder_gemini.success("Gemini key saved and valid!", icon="")
elif gemini_status == "invalid": elif gemini_status == "invalid":
st.error("Invalid Gemini key. Please check and try again.", icon="") feedback_placeholder_gemini.error("Invalid Gemini key. Please check and try again.", icon="")
elif gemini_status == "error": elif gemini_status == "error":
st.error("Error saving/validating Gemini key.", icon="⚠️") feedback_placeholder_gemini.error("Error saving/validating Gemini key.", icon="⚠️")
# --- Mistral AI (Optional) --- # --- Mistral AI (Optional) ---
st.subheader("Mistral AI (Optional)") st.subheader("Mistral AI (Optional)")
@@ -180,20 +195,19 @@ def render_ai_providers_setup(api_key_manager) -> Dict[str, Any]:
) )
# Feedback Area for Mistral # Feedback Area for Mistral
mistral_status = st.session_state.get("mistral_status", "unsaved") mistral_status = st.session_state.get("mistral_status", "unsaved")
feedback_placeholder_mistral = st.empty()
if mistral_status == "saving": if mistral_status == "saving":
st.spinner("Validating Mistral key...") feedback_placeholder_mistral.info("Validating Mistral key...", icon="")
elif mistral_status == "valid": elif mistral_status == "valid":
st.success("Mistral key saved and valid!", icon="") feedback_placeholder_mistral.success("Mistral key saved and valid!", icon="")
elif mistral_status == "invalid": elif mistral_status == "invalid":
st.error("Invalid Mistral key. Please check and try again.", icon="") feedback_placeholder_mistral.error("Invalid Mistral key. Please check and try again.", icon="")
elif mistral_status == "error": elif mistral_status == "error":
st.error("Error saving/validating Mistral key.", icon="⚠️") feedback_placeholder_mistral.error("Error saving/validating Mistral key.", icon="⚠️")
# --- Final Notes --- # --- Final Notes ---
st.info("Note: At least one AI provider (OpenAI or Google Gemini) must have a valid API key to proceed.") st.info("Note: At least one AI provider (OpenAI or Google Gemini) must have a valid API key to proceed.")
# REMOVED the old saving logic block here
# Return value is not strictly needed if navigation relies on session state status # Return value is not strictly needed if navigation relies on session state status
return {} return {}

View File

@@ -4,12 +4,11 @@ import streamlit as st
from loguru import logger from loguru import logger
from typing import Dict, Any from typing import Dict, Any
from ..manager import APIKeyManager from ..manager import APIKeyManager
# Removed import of render_navigation_buttons as it's handled in base
import os import os
from dotenv import load_dotenv # Keep if api_key_manager uses it from dotenv import load_dotenv # Keep if api_key_manager uses it
import sys import sys
# Import test functions (adjust path if needed) # Corrected import: Assuming validation functions are in validation.py in the parent directory
from ..api_key_tests import ( from ..validation import (
test_serpapi_key, test_serpapi_key,
test_tavily_key, test_tavily_key,
test_metaphor_key, test_metaphor_key,
@@ -28,14 +27,31 @@ def _validate_research_key(provider_name: str, key_value: str) -> bool:
return False return False
try: try:
logger.debug(f"Validating key for {provider_name}...") logger.debug(f"Validating key for {provider_name}...")
# Ensure the function exists in validation.py before calling
if provider_name == "serpapi": if provider_name == "serpapi":
is_valid = test_serpapi_key(key_value) if callable(getattr(sys.modules[__name__], 'test_serpapi_key', None)):
is_valid = test_serpapi_key(key_value)
else:
logger.error("test_serpapi_key not found in validation module")
is_valid = False
elif provider_name == "tavily": elif provider_name == "tavily":
is_valid = test_tavily_key(key_value) if callable(getattr(sys.modules[__name__], 'test_tavily_key', None)):
is_valid = test_tavily_key(key_value)
else:
logger.error("test_tavily_key not found in validation module")
is_valid = False
elif provider_name == "metaphor": elif provider_name == "metaphor":
is_valid = test_metaphor_key(key_value) if callable(getattr(sys.modules[__name__], 'test_metaphor_key', None)):
is_valid = test_metaphor_key(key_value)
else:
logger.error("test_metaphor_key not found in validation module")
is_valid = False
elif provider_name == "firecrawl": elif provider_name == "firecrawl":
is_valid = test_firecrawl_key(key_value) if callable(getattr(sys.modules[__name__], 'test_firecrawl_key', None)):
is_valid = test_firecrawl_key(key_value)
else:
logger.error("test_firecrawl_key not found in validation module")
is_valid = False
else: else:
logger.warning(f"Validation not implemented for research provider: {provider_name}") logger.warning(f"Validation not implemented for research provider: {provider_name}")
return False # Default to False for unknown providers return False # Default to False for unknown providers
@@ -219,7 +235,5 @@ def render_ai_research_setup(api_key_manager: APIKeyManager) -> Dict[str, Any]:
st.info("Integrations for Bing Search and Google Search APIs are planned.") st.info("Integrations for Bing Search and Google Search APIs are planned.")
st.info("You can skip this step if you don't need these research tools. Click Continue to proceed.") st.info("You can skip this step if you don't need these research tools. Click Continue to proceed.")
# Removed the old saving logic block here
return {} return {}

View File

@@ -2,265 +2,208 @@
import streamlit as st import streamlit as st
from loguru import logger from loguru import logger
from ...website_analyzer import analyze_website # Removed website_analyzer imports as analysis is separate now
from ...website_analyzer.seo_analyzer import analyze_seo # from ...website_analyzer import analyze_website
# from ...website_analyzer.seo_analyzer import analyze_seo
import asyncio import asyncio
import sys import sys
from typing import Dict, Any from typing import Dict, Any
from ..manager import APIKeyManager import requests
from .base import render_navigation_buttons import ssl
import socket
from urllib.parse import urlparse
from ..manager import APIKeyManager
# Navigation is handled in base.py now
# from .base import render_navigation_buttons
# Configure logger (minimal example)
logger.add(sys.stderr, level="INFO")
# --- Validation Helpers ---
def _is_valid_url_format(url: str) -> bool:
"""Checks if the URL has a valid basic format (scheme and netloc)."""
try:
result = urlparse(url)
return all([result.scheme in ['http', 'https'], result.netloc])
except ValueError:
return False
def _check_url_reachability(url: str) -> tuple[bool, str]:
"""Checks if the URL is reachable and returns status code or error."""
try:
response = requests.head(url, allow_redirects=True, timeout=5) # HEAD request is faster
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
logger.info(f"URL {url} reachable, status code: {response.status_code}")
return True, f"Reachable (Status: {response.status_code})"
except requests.exceptions.Timeout:
logger.warning(f"URL {url} timed out.")
return False, "Timeout: Server did not respond in time."
except requests.exceptions.RequestException as e:
logger.warning(f"URL {url} not reachable: {e}")
# Provide a more user-friendly message for common errors
if isinstance(e, requests.exceptions.ConnectionError):
return False, "Connection Error: Could not connect to the server."
elif isinstance(e, requests.exceptions.HTTPError):
return False, f"HTTP Error: {e.response.status_code}"
return False, f"Error: {type(e).__name__}"
def _check_ssl_certificate(url: str) -> tuple[bool, str]:
"""Checks if the URL has a valid SSL certificate (for https)."""
parsed_url = urlparse(url)
if parsed_url.scheme != 'https':
return True, "(HTTP URL)" # Not applicable for http
hostname = parsed_url.netloc
port = 443
context = ssl.create_default_context()
try:
with socket.create_connection((hostname, port), timeout=3) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
cert = ssock.getpeercert()
# Basic check: does it exist? More thorough checks (expiry, chain) are possible
if cert:
logger.info(f"SSL certificate found for {hostname}")
return True, "Valid SSL Certificate"
else:
logger.warning(f"No SSL certificate found for {hostname}")
return False, "No SSL Certificate found"
except ssl.SSLCertVerificationError as e:
logger.warning(f"SSL Verification Error for {hostname}: {e}")
return False, f"SSL Verification Error: {e.verify_message}"
except socket.timeout:
logger.warning(f"SSL check timed out for {hostname}")
return False, "SSL Check Timeout"
except Exception as e:
logger.error(f"Error checking SSL for {hostname}: {e}", exc_info=True)
return False, f"SSL Check Error: {type(e).__name__}"
# --- Main Component Logic ---
def _validate_website_url(url: str) -> tuple[str, str]:
"""Performs quick validation (format, reachability, basic SSL)."""
if not url:
return "unsaved", ""
# 1. Format Check
if not _is_valid_url_format(url):
logger.warning(f"Invalid URL format: {url}")
return "invalid_format", "Invalid URL format. Please include http:// or https://"
# 2. Reachability Check
reachable, reach_status = _check_url_reachability(url)
if not reachable:
logger.warning(f"URL not reachable: {url} ({reach_status})")
return "unreachable", reach_status # Return specific error message
# 3. Basic SSL Check (only if reachable and HTTPS)
if urlparse(url).scheme == 'https':
ssl_valid, ssl_status = _check_ssl_certificate(url)
if not ssl_valid:
logger.warning(f"SSL check failed for {url} ({ssl_status})")
return "ssl_error", ssl_status # Return specific error message
logger.info(f"URL validation successful for: {url}")
return "valid", "URL is valid and reachable."
def _handle_website_url_change(api_key_manager: APIKeyManager):
"""Save and validate website URL when input changes."""
url_input_widget_key = "website_url_input"
status_widget_key = "website_url_status"
if url_input_widget_key not in st.session_state:
logger.warning(f"Input widget key '{url_input_widget_key}' not found.")
return
url_value = st.session_state[url_input_widget_key]
logger.debug(f"Handling website URL change. URL: {url_value}")
# Save the URL regardless of validity for now (maybe refine later)
# api_key_manager might not be the right place, consider storing directly in session state
# or a dedicated config manager if this isn't an API key.
# Let's store in session_state for now.
st.session_state['configured_website_url'] = url_value
logger.info(f"Saved website URL to session state: {url_value}")
if not url_value:
st.session_state[status_widget_key] = ("unsaved", "")
logger.info("Cleared website URL.")
return
st.session_state[status_widget_key] = ("saving", "") # Indicate validation is running
st.rerun()
try:
validation_status, message = _validate_website_url(url_value)
st.session_state[status_widget_key] = (validation_status, message)
logger.info(f"Website URL validation complete. Status: {validation_status}, Msg: {message}")
except Exception as e:
logger.error(f"Error during website URL validation: {e}", exc_info=True)
st.session_state[status_widget_key] = ("error", "An unexpected error occurred during validation.")
# Configure logger to output to both file and stdout
logger.remove() # Remove default handler
logger.add(
"logs/website_setup.log",
rotation="500 MB",
retention="10 days",
level="DEBUG",
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}"
)
logger.add(
sys.stdout,
level="INFO",
format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{message}</cyan>"
)
def render_website_setup(api_key_manager: APIKeyManager) -> Dict[str, Any]: def render_website_setup(api_key_manager: APIKeyManager) -> Dict[str, Any]:
"""Render the website setup step. """Render the website setup step with immediate feedback."""
Args:
api_key_manager (APIKeyManager): The API key manager instance
Returns:
Dict[str, Any]: Current state
"""
logger.info("[render_website_setup] Rendering website setup component") logger.info("[render_website_setup] Rendering website setup component")
st.markdown("### Step 2: Website Setup") status_key = "website_url_status"
# Initialize status
if status_key not in st.session_state:
st.session_state[status_key] = ("unsaved", "")
# Optionally pre-validate if a URL exists from previous session/config
# pre_existing_url = api_key_manager.get_config("website_url") # Example
# if pre_existing_url:
# st.session_state[status_key] = _validate_website_url(pre_existing_url)
st.markdown("""
<div class='setup-header'>
<h2>Step 2: Website Setup (Optional)</h2>
<p>Enter your primary website URL. This helps Alwrity personalize suggestions and analyze your content.</p>
</div>
""", unsafe_allow_html=True)
# Create two columns for input and results # Get current value from session state if available, otherwise empty
col1, col2 = st.columns([1, 1]) current_url = st.session_state.get('configured_website_url', "")
with col1: st.text_input(
st.markdown("#### Enter Website URL") "Website URL",
url = st.text_input("Website URL", placeholder="https://example.com") value=current_url,
logger.debug(f"[render_website_setup] URL input value: {url}") placeholder="https://example.com",
key="website_url_input",
analyze_type = st.radio( on_change=_handle_website_url_change,
"Analysis Type", args=(api_key_manager,) # Pass manager if needed by save logic
["Basic Analysis", "Full Analysis with SEO"], )
help="Choose between basic website analysis or comprehensive SEO analysis"
)
if st.button("Analyze Website"):
if url:
with st.spinner("Analyzing website..."):
try:
logger.info(f"[render_website_setup] Starting website analysis for URL: {url}")
# Call the analyze_website function
results = analyze_website(url)
# If full analysis is selected, add SEO analysis
if analyze_type == "Full Analysis with SEO":
seo_results = analyze_seo(url)
if seo_results.success:
results['data']['seo_analysis'] = {
'overall_score': seo_results.overall_score,
'meta_tags': {
'title': seo_results.meta_tags.title,
'description': seo_results.meta_tags.description,
'keywords': seo_results.meta_tags.keywords,
'has_robots': seo_results.meta_tags.has_robots,
'has_sitemap': seo_results.meta_tags.has_sitemap
},
'content': {
'word_count': seo_results.content.word_count,
'readability_score': seo_results.content.readability_score,
'content_quality_score': seo_results.content.content_quality_score,
'headings_structure': seo_results.content.headings_structure,
'keyword_density': seo_results.content.keyword_density
},
'recommendations': [
{
'priority': rec.priority,
'category': rec.category,
'issue': rec.issue,
'recommendation': rec.recommendation,
'impact': rec.impact
}
for rec in seo_results.recommendations
]
}
logger.debug(f"[render_website_setup] Analysis results received: {results.get('success', False)}")
# Store results in session state
st.session_state.website_analysis = results
logger.info("[render_website_setup] Results stored in session state")
if not results.get('success', False):
error_msg = results.get('error', 'Analysis failed')
logger.error(f"[render_website_setup] Analysis failed: {error_msg}")
st.error(error_msg)
else:
logger.info("[render_website_setup] Analysis completed successfully")
st.success("✅ Website analysis completed successfully!")
except Exception as e:
error_msg = f"Analysis failed: {str(e)}"
logger.error(f"[render_website_setup] {error_msg}")
st.error(error_msg)
else:
logger.warning("[render_website_setup] No URL provided")
st.warning("Please enter a valid URL")
with col2: # --- Feedback Area ---
st.markdown("#### Analysis Results") status, message = st.session_state.get(status_key, ("unsaved", ""))
feedback_placeholder = st.empty()
# Check if we have analysis results
if 'website_analysis' in st.session_state:
results = st.session_state.website_analysis
if results.get('success', False):
data = results.get('data', {})
analysis = data.get('analysis', {})
# Create tabs for different sections
if analyze_type == "Full Analysis with SEO":
tab1, tab2, tab3, tab4, tab5 = st.tabs([
"Basic Metrics",
"Content Analysis",
"SEO Analysis",
"Technical SEO",
"Strategy"
])
else:
tab1, tab2, tab3, tab4 = st.tabs([
"Basic Metrics",
"Content Analysis",
"Technical Info",
"Strategy"
])
with tab1:
st.markdown("##### Basic Metrics")
basic_info = analysis.get('basic_info', {})
st.write(f"Status Code: {basic_info.get('status_code')}")
st.write(f"Content Type: {basic_info.get('content_type')}")
st.write(f"Title: {basic_info.get('title')}")
st.write(f"Meta Description: {basic_info.get('meta_description')}")
# SSL Info
ssl_info = analysis.get('ssl_info', {})
if ssl_info.get('has_ssl'):
st.success("SSL Certificate is valid")
st.write(f"Expiry: {ssl_info.get('expiry')}")
else:
st.error("No valid SSL certificate found")
with tab2:
st.markdown("##### Content Analysis")
content_info = analysis.get('content_info', {})
# Content Overview
st.markdown("###### 📊 Content Overview")
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("Word Count", content_info.get('word_count', 0))
with col2:
st.metric("Headings", content_info.get('heading_count', 0))
with col3:
st.metric("Images", content_info.get('image_count', 0))
with col4:
st.metric("Links", content_info.get('link_count', 0))
if analyze_type == "Full Analysis with SEO":
with tab3:
st.markdown("##### SEO Analysis")
seo_data = data.get('seo_analysis', {})
# Display SEO Score
seo_score = seo_data.get('overall_score', 0)
st.markdown(f"### SEO Score: {seo_score}/100")
st.progress(seo_score / 100)
# Meta Tags Analysis
st.markdown("#### Meta Tags Analysis")
meta_analysis = seo_data.get('meta_tags', {})
for key, value in meta_analysis.items():
if isinstance(value, bool):
st.write(f"{'' if value else ''} {key.replace('_', ' ').title()}")
elif isinstance(value, dict):
st.write(f"**{key.replace('_', ' ').title()}:**")
st.write(f"Status: {value.get('status', 'N/A')}")
st.write(f"Value: {value.get('value', 'N/A')}")
if value.get('recommendation'):
st.write(f"Recommendation: {value['recommendation']}")
else:
st.write(f"**{key.replace('_', ' ').title()}:** {value}")
# Content Analysis
st.markdown("#### AI Content Analysis")
content_analysis = seo_data.get('content', {})
st.write(f"**Word Count:** {content_analysis.get('word_count', 0)}")
st.write(f"**Readability Score:** {content_analysis.get('readability_score', 0)}/100")
st.write(f"**Content Quality Score:** {content_analysis.get('content_quality_score', 0)}/100")
# Recommendations
st.markdown("#### SEO Recommendations")
recommendations = seo_data.get('recommendations', [])
for rec in recommendations:
st.write(f"**{rec.get('priority', '').upper()} Priority - {rec.get('category', '')}**")
st.write(f"Issue: {rec.get('issue', '')}")
st.write(f"Recommendation: {rec.get('recommendation', '')}")
st.write(f"Impact: {rec.get('impact', '')}")
st.write("---")
with tab4:
st.markdown("##### Technical SEO")
technical_seo = seo_data.get('technical_analysis', {})
# Mobile Friendliness
st.markdown("#### Mobile Friendliness")
mobile_friendly = technical_seo.get('mobile_friendly', False)
st.write(f"{'' if mobile_friendly else ''} Mobile Friendly")
# Page Speed
st.markdown("#### Page Speed")
speed_metrics = technical_seo.get('speed_metrics', {})
for metric, value in speed_metrics.items():
st.write(f"**{metric.replace('_', ' ').title()}:** {value}")
# Technical Issues
st.markdown("#### Technical Issues")
issues = technical_seo.get('issues', [])
for issue in issues:
st.write(f"{issue}")
with tab4 if analyze_type == "Basic Analysis" else tab5:
st.markdown("##### Strategy Recommendations")
strategy_info = analysis.get('strategy', {})
if strategy_info:
for category, recommendations in strategy_info.items():
st.markdown(f"###### {category.replace('_', ' ').title()}")
for rec in recommendations:
st.write(f"{rec}")
else:
st.info("No strategy recommendations available")
else:
error_msg = results.get('error', 'Analysis failed')
logger.error(f"[render_website_setup] Displaying error: {error_msg}")
st.error(error_msg)
else:
logger.debug("[render_website_setup] No analysis results in session state")
st.info("Enter a URL and click 'Analyze Website' to see results")
# Navigation buttons if status == "saving":
if render_navigation_buttons(2, 5, True): feedback_placeholder.info("Validating URL...", icon="")
# Move to next step (AI Research Setup) elif status == "valid":
st.session_state.current_step = 3 feedback_placeholder.success(message, icon="")
st.session_state.next_step = "ai_research_setup" elif status == "invalid_format":
st.rerun() feedback_placeholder.error(f"Format Error: {message}", icon="")
elif status == "unreachable":
return {"current_step": 2, "changes_made": True} feedback_placeholder.error(f"Reachability Error: {message}", icon="")
elif status == "ssl_error":
feedback_placeholder.warning(f"SSL Warning: {message}", icon="⚠️") # Warning for SSL
elif status == "error":
feedback_placeholder.error(f"Validation Error: {message}", icon="⚠️")
elif status == "unsaved" and current_url: # Show warning if field has text but isn't validated yet
feedback_placeholder.warning("URL not yet validated.", icon="⚠️")
# --- Removed Analysis Section ---
# The detailed website analysis should be a separate feature, not part of the initial setup validation.
st.markdown("--- ---“)
st.markdown(" *The detailed Website Analyzer tool is available separately in the main application.*")
st.info("Entering your website URL is optional. Click Continue to proceed.")
# Return value is not strictly needed if navigation relies on session state status
return {}
# Removed old analysis logic and button handling as it's handled in base.py