AI writers, SEO, Social media, Settings, Dashboard UI styling changes

This commit is contained in:
ajaysi
2025-06-03 09:14:47 +05:30
parent 5ca2fd5977
commit fad9647b46
13 changed files with 2281 additions and 2091 deletions

View File

@@ -1,439 +0,0 @@
import streamlit as st
from lib.ai_seo_tools.seo_structured_data import ai_structured_data
from lib.ai_seo_tools.content_title_generator import ai_title_generator
from lib.ai_seo_tools.meta_desc_generator import metadesc_generator_main
from lib.ai_seo_tools.image_alt_text_generator import alt_text_gen
from lib.ai_seo_tools.opengraph_generator import og_tag_generator
from lib.ai_seo_tools.optimize_images_for_upload import main_img_optimizer
from lib.ai_seo_tools.google_pagespeed_insights import google_pagespeed_insights
from lib.ai_seo_tools.on_page_seo_analyzer import analyze_onpage_seo
from lib.ai_seo_tools.weburl_seo_checker import url_seo_checker
from lib.ai_marketing_tools.ai_backlinker.backlinking_ui_streamlit import backlinking_ui
from lib.ai_seo_tools.content_gap_analysis.ui import ContentGapAnalysisUI
from lib.ai_seo_tools.content_calendar.ui.dashboard import ContentCalendarDashboard
def render_content_gap_analysis():
"""Render the content gap analysis workflow interface."""
from lib.ai_seo_tools.content_gap_analysis.ui import ContentGapAnalysisUI
# Initialize and run the Content Gap Analysis UI
ui = ContentGapAnalysisUI()
ui.run()
def render_content_calendar():
"""Render the content calendar dashboard."""
import logging
import sys
from datetime import datetime
# Configure logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(sys.stdout),
logging.FileHandler('content_calendar.log', mode='a')
]
)
logger = logging.getLogger('content_calendar')
try:
logger.info("Initializing Content Calendar Dashboard")
dashboard = ContentCalendarDashboard()
logger.info("Rendering Content Calendar Dashboard")
dashboard.render()
logger.info("Content Calendar Dashboard rendered successfully")
except Exception as e:
logger.error(f"Error rendering content calendar: {str(e)}", exc_info=True)
st.error(f"An error occurred while loading the content calendar: {str(e)}")
def render_seo_tools_dashboard():
"""Render a modern dashboard for SEO tools with improved UI and navigation."""
selected_section = st.session_state.get('seo_dashboard_section', 'combinations')
# Define card gradients at the top so it's available in all sections
card_gradients = {
"Content Optimization Suite": "linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)",
"Technical SEO Audit": "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
"Image Optimization Suite": "linear-gradient(135deg, #f7971e 0%, #ffd200 100%)",
"Social Media Optimization": "linear-gradient(135deg, #f953c6 0%, #b91d73 100%)",
"Content Gap Analysis": "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
"Content Calendar": "linear-gradient(135deg, #4CAF50 0%, #2196F3 100%)",
"Structured Data Generator": "linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)",
"Blog Title Generator": "linear-gradient(135deg, #2193b0 0%, #6dd5ed 100%)",
"Meta Description Generator": "linear-gradient(135deg, #f7971e 0%, #ffd200 100%)",
"Image Alt Text Generator": "linear-gradient(135deg, #f953c6 0%, #b91d73 100%)",
"OpenGraph Tags Generator": "linear-gradient(135deg, #f857a6 0%, #ff5858 100%)",
"Image Optimizer": "linear-gradient(135deg, #43cea2 0%, #185a9d 100%)",
"PageSpeed Insights": "linear-gradient(135deg, #ff9966 0%, #ff5e62 100%)",
"On-Page SEO Analyzer": "linear-gradient(135deg, #56ab2f 0%, #a8e063 100%)",
"URL SEO Checker": "linear-gradient(135deg, #3a7bd5 0%, #00d2ff 100%)",
"AI Backlinking Tool": "linear-gradient(135deg, #e96443 0%, #904e95 100%)"
}
# Navigation bar only (no dashboard title/description)
nav_cols = st.columns([1,1,1,1])
nav_labels = ["Tool Combos", "Advanced", "Individual", "About"]
nav_keys = ["combinations", "advanced", "individual", "about"]
for i, label in enumerate(nav_labels):
if nav_cols[i].button(label, key=f"nav_{label}"):
st.session_state['seo_dashboard_section'] = nav_keys[i]
selected_section = nav_keys[i]
st.markdown("<hr style='margin:1.5rem 0;'>", unsafe_allow_html=True)
# Define tool combinations for cross-tool analysis
tool_combinations = {
"Content Optimization Suite": {
"icon": "📊",
"description": "Comprehensive content optimization combining title generation, meta descriptions, and structured data.",
"tools": ["Blog Title Generator", "Meta Description Generator", "Structured Data Generator"],
"path": "content_optimization",
"color": "#4CAF50"
},
"Technical SEO Audit": {
"icon": "🔧",
"description": "Complete technical SEO analysis including page speed, on-page SEO, and URL structure.",
"tools": ["PageSpeed Insights", "On-Page SEO Analyzer", "URL SEO Checker"],
"path": "technical_audit",
"color": "#2196F3"
},
"Image Optimization Suite": {
"icon": "🖼️",
"description": "End-to-end image optimization with alt text generation and performance optimization.",
"tools": ["Image Alt Text Generator", "Image Optimizer"],
"path": "image_optimization",
"color": "#FF9800"
},
"Social Media Optimization": {
"icon": "📱",
"description": "Enhance social media presence with OpenGraph tags and backlink analysis.",
"tools": ["OpenGraph Tags Generator", "AI Backlinking Tool"],
"path": "social_optimization",
"color": "#9C27B0"
}
}
# Define individual SEO tools
seo_tools = {
"Structured Data Generator": {
"icon": "📋",
"description": "Generate structured data (Rich Snippets) to enhance your search results with additional information.",
"color": "#4CAF50",
"path": "structured_data",
"status": "active"
},
"Blog Title Generator": {
"icon": "✏️",
"description": "Create SEO-optimized blog titles that attract clicks and improve search rankings.",
"color": "#2196F3",
"path": "blog_title",
"status": "active"
},
"Meta Description Generator": {
"icon": "📝",
"description": "Generate compelling meta descriptions that improve click-through rates from search results.",
"color": "#FF9800",
"path": "meta_description",
"status": "active"
},
"Image Alt Text Generator": {
"icon": "🖼️",
"description": "Create descriptive alt text for images to improve accessibility and image SEO.",
"color": "#9C27B0",
"path": "alt_text",
"status": "active"
},
"OpenGraph Tags Generator": {
"icon": "📱",
"description": "Generate OpenGraph tags for better social media sharing and visibility.",
"color": "#F44336",
"path": "opengraph",
"status": "active"
},
"Image Optimizer": {
"icon": "📉",
"description": "Optimize and resize images for better website performance and SEO.",
"color": "#607D8B",
"path": "image_optimizer",
"status": "active"
},
"PageSpeed Insights": {
"icon": "",
"description": "Analyze your website's performance using Google PageSpeed Insights.",
"color": "#795548",
"path": "pagespeed",
"status": "active"
},
"On-Page SEO Analyzer": {
"icon": "🔍",
"description": "Analyze and optimize your webpage's SEO elements and content.",
"color": "#009688",
"path": "onpage_seo",
"status": "active"
},
"URL SEO Checker": {
"icon": "🌐",
"description": "Check the SEO health of specific URLs and get improvement suggestions.",
"color": "#3F51B5",
"path": "url_checker",
"status": "active"
},
"AI Backlinking Tool": {
"icon": "🔗",
"description": "Discover and analyze backlink opportunities using AI-powered insights.",
"color": "#E91E63",
"path": "backlinking",
"status": "active"
}
}
# --- Tool Combinations Section ---
if selected_section == 'combinations':
combo_cols = st.columns(2)
for idx, (combo_name, details) in enumerate(tool_combinations.items()):
gradient = card_gradients.get(combo_name, "linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%)")
with combo_cols[idx % 2]:
st.markdown(f"""
<div class="seo-card" style="background: {gradient}; position: relative; overflow: hidden;">
<div class="seo-card-overlay"></div>
<div class="seo-icon">{details['icon']}</div>
<div class="seo-title">{combo_name}</div>
<div class="seo-description">{details['description']}</div>
<div>
{''.join([f'<span class="tool-badge">{tool}</span>' for tool in details['tools']])}
</div>
</div>
""", unsafe_allow_html=True)
if st.button(f"Launch {combo_name}", key=f"combo_{combo_name}", use_container_width=True):
st.query_params["tool"] = details["path"]
st.rerun()
# --- Advanced Features Section ---
elif selected_section == 'advanced':
adv_cols = st.columns(2)
adv_features = [
{
"name": "Content Gap Analysis",
"icon": "🎯",
"description": "Identify content opportunities and optimize your content strategy with AI-powered insights.",
"badges": ["Website Analysis", "Competitor Research", "Keyword Opportunities", "AI Recommendations"],
"gradient": card_gradients["Content Gap Analysis"],
"button": "Start Content Gap Analysis",
"key": "content_gap_analysis",
"path": "content_gap_analysis"
},
{
"name": "Content Calendar",
"icon": "📅",
"description": "Plan, schedule, and manage your content strategy with our AI-powered content calendar.",
"badges": ["Content Planning", "Scheduling", "Performance Tracking", "AI Insights"],
"gradient": card_gradients["Content Calendar"],
"button": "Open Content Calendar",
"key": "content_calendar",
"path": "content_calendar"
}
]
for idx, feature in enumerate(adv_features):
with adv_cols[idx % 2]:
st.markdown(f"""
<div class="seo-card" style="background: {feature['gradient']}; position: relative; overflow: hidden;">
<div class="seo-card-overlay"></div>
<div class="seo-icon">{feature['icon']}</div>
<div class="seo-title">{feature['name']}</div>
<div class="seo-description">{feature['description']}</div>
<div>
{''.join([f'<span class="tool-badge">{badge}</span>' for badge in feature['badges']])}
</div>
</div>
""", unsafe_allow_html=True)
if st.button(feature['button'], key=feature['key'], use_container_width=True):
st.query_params["tool"] = feature["path"]
st.rerun()
# --- Individual Tools Section ---
elif selected_section == 'individual':
cols = st.columns(3)
for idx, (tool_name, details) in enumerate(seo_tools.items()):
gradient = card_gradients.get(tool_name, "linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%)")
with cols[idx % 3]:
st.markdown(f"""
<div class="seo-card" style="background: {gradient}; position: relative; overflow: hidden;">
<div class="seo-card-overlay"></div>
<div class="seo-icon">{details['icon']}</div>
<div class="seo-title">{tool_name}</div>
<div class="seo-description">{details['description']}</div>
</div>
""", unsafe_allow_html=True)
if st.button(f"Use {tool_name}", key=f"btn_{tool_name}", use_container_width=True):
st.query_params["tool"] = details["path"]
st.rerun()
# --- About Section ---
elif selected_section == 'about':
st.markdown("""
<div style='text-align: center; margin: 2rem 0;'>
<h2>About This Dashboard</h2>
<p style='color: #666;'>This dashboard brings together powerful AI-driven SEO tools and workflows to help you optimize your website and content strategy. Use the navigation above to explore combinations, advanced features, or individual tools.</p>
</div>
""", unsafe_allow_html=True)
st.markdown("""
<style>
.seo-card {
border-radius: 14px;
padding: 24px;
margin-bottom: 24px;
box-shadow: 0 4px 16px rgba(44, 62, 80, 0.10), 0 1.5px 4px rgba(44,62,80,0.06);
transition: transform 0.2s cubic-bezier(.4,2,.6,1), box-shadow 0.2s;
height: 100%;
border: 1.5px solid #e3e8ee;
position: relative;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.seo-card-overlay {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(255,255,255,0.72);
z-index: 1;
pointer-events: none;
border-radius: 14px;
box-shadow: 0 2px 8px rgba(44,62,80,0.08);
}
.seo-card:hover {
transform: translateY(-6px) scale(1.025);
box-shadow: 0 8px 32px rgba(44, 62, 80, 0.18), 0 2px 8px rgba(44,62,80,0.10);
border-color: #4CAF50;
}
.seo-icon {
font-size: 2.7rem;
margin-bottom: 18px;
z-index: 2;
position: relative;
text-shadow: 0 2px 8px rgba(44,62,80,0.10);
}
.seo-title {
font-size: 1.25rem;
font-weight: 800;
margin-bottom: 12px;
color: #222b45;
z-index: 2;
position: relative;
text-shadow: 0 2px 8px rgba(44,62,80,0.10);
letter-spacing: 0.01em;
}
.seo-description {
color: #34495e;
font-size: 1.08rem;
margin-bottom: 15px;
z-index: 2;
position: relative;
text-align: center;
line-height: 1.5;
text-shadow: 0 1px 4px rgba(44,62,80,0.08);
}
.tool-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 12px;
font-size: 0.9rem;
margin-right: 8px;
margin-bottom: 8px;
background: rgba(255, 255, 255, 0.95);
color: #2196F3;
font-weight: 600;
border: 1px solid #e3e8ee;
}
</style>
""", unsafe_allow_html=True)
def ai_seo_tools():
"""
A collection of AI-powered SEO tools for content creators, providing various options
such as generating structured data, optimizing images, checking page speed,
and analyzing on-page SEO.
"""
# Check if a specific tool is selected
selected_tool = st.query_params.get("tool")
if selected_tool:
# Map tool paths to their respective functions
tool_functions = {
# Individual tools
"structured_data": ai_structured_data,
"blog_title": ai_title_generator,
"meta_description": metadesc_generator_main,
"alt_text": alt_text_gen,
"opengraph": og_tag_generator,
"image_optimizer": main_img_optimizer,
"pagespeed": google_pagespeed_insights,
"onpage_seo": analyze_onpage_seo,
"url_checker": url_seo_checker,
"backlinking": backlinking_ui,
# Tool combinations
"content_optimization": lambda: run_tool_combination([
ai_title_generator,
metadesc_generator_main,
ai_structured_data
], "Content Optimization Suite"),
"technical_audit": lambda: run_tool_combination([
google_pagespeed_insights,
analyze_onpage_seo,
url_seo_checker
], "Technical SEO Audit"),
"image_optimization": lambda: run_tool_combination([
alt_text_gen,
main_img_optimizer
], "Image Optimization Suite"),
"social_optimization": lambda: run_tool_combination([
og_tag_generator,
backlinking_ui
], "Social Media Optimization"),
# Add Content Gap Analysis and Content Calendar
"content_gap_analysis": render_content_gap_analysis,
"content_calendar": render_content_calendar
}
if selected_tool in tool_functions:
# Clear any existing content
st.empty()
# Execute the selected tool's function
tool_functions[selected_tool]()
else:
st.error(f"Invalid tool selected: {selected_tool}")
render_seo_tools_dashboard()
else:
# Show the dashboard if no tool is selected
render_seo_tools_dashboard()
def run_tool_combination(tools, combination_name):
"""Run a combination of tools and provide cross-tool analysis."""
st.markdown(f"# {combination_name}")
st.markdown("Running comprehensive analysis...")
# Create tabs for each tool in the combination
tabs = st.tabs([f"Step {i+1}" for i in range(len(tools))])
# Run each tool in its own tab
for i, (tab, tool) in enumerate(zip(tabs, tools)):
with tab:
st.markdown(f"### Step {i+1}")
tool()
# Add cross-tool analysis section
st.markdown("## 📊 Cross-Tool Analysis")
st.markdown("Analyzing results across all tools...")
# Add recommendations based on combined results
st.markdown("## 💡 Recommendations")
st.markdown("Based on the combined analysis, here are the key recommendations:")
# Add a button to export the complete analysis
if st.button("📥 Export Complete Analysis", use_container_width=True):
st.info("Analysis export functionality coming soon!")

View File

@@ -1,438 +0,0 @@
import streamlit as st
from loguru import logger
import asyncio
from lib.web_crawlers.async_web_crawler import AsyncWebCrawlerService
from lib.personalization.style_analyzer import StyleAnalyzer
import sys
# Configure logger
logger.remove() # Remove default handler
logger.add(
"logs/settings_page.log",
rotation="500 MB",
retention="10 days",
level="DEBUG",
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}",
backtrace=True,
diagnose=True
)
logger.add(
sys.stdout,
level="INFO",
format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{message}</cyan>"
)
def display_style_analysis(analysis_results: dict):
"""Display the style analysis results in a structured format."""
try:
# Writing Style Section
st.markdown("### 🎨 Writing Style Analysis")
writing_style = analysis_results.get("writing_style", {})
writing_style_content = f"""
<ul>
<li><strong>Tone:</strong> {writing_style.get("tone", "N/A")}</li>
<li><strong>Voice:</strong> {writing_style.get("voice", "N/A")}</li>
<li><strong>Complexity:</strong> {writing_style.get("complexity", "N/A")}</li>
<li><strong>Engagement Level:</strong> {writing_style.get("engagement_level", "N/A")}</li>
</ul>
"""
st.markdown(writing_style_content, unsafe_allow_html=True)
# Content Characteristics Section
content_chars = analysis_results.get("content_characteristics", {})
content_chars_content = f"""
<ul>
<li><strong>Sentence Structure:</strong> {content_chars.get("sentence_structure", "N/A")}</li>
<li><strong>Vocabulary Level:</strong> {content_chars.get("vocabulary_level", "N/A")}</li>
<li><strong>Paragraph Organization:</strong> {content_chars.get("paragraph_organization", "N/A")}</li>
<li><strong>Content Flow:</strong> {content_chars.get("content_flow", "N/A")}</li>
</ul>
"""
st.markdown(content_chars_content, unsafe_allow_html=True)
# Target Audience Section
target_audience = analysis_results.get("target_audience", {})
target_audience_content = f"""
<ul>
<li><strong>Demographics:</strong> {', '.join(target_audience.get("demographics", ["N/A"]))}</li>
<li><strong>Expertise Level:</strong> {target_audience.get("expertise_level", "N/A")}</li>
<li><strong>Industry Focus:</strong> {target_audience.get("industry_focus", "N/A")}</li>
<li><strong>Geographic Focus:</strong> {target_audience.get("geographic_focus", "N/A")}</li>
</ul>
"""
st.markdown(target_audience_content, unsafe_allow_html=True)
# Content Type Section
content_type = analysis_results.get("content_type", {})
content_type_content = f"""
<ul>
<li><strong>Primary Type:</strong> {content_type.get("primary_type", "N/A")}</li>
<li><strong>Secondary Types:</strong> {', '.join(content_type.get("secondary_types", ["N/A"]))}</li>
<li><strong>Purpose:</strong> {content_type.get("purpose", "N/A")}</li>
<li><strong>Call to Action:</strong> {content_type.get("call_to_action", "N/A")}</li>
</ul>
"""
st.markdown(content_type_content, unsafe_allow_html=True)
# Recommended Settings Section
recommended = analysis_results.get("recommended_settings", {})
recommended_content = f"""
<ul>
<li><strong>Writing Tone:</strong> {recommended.get("writing_tone", "N/A")}</li>
<li><strong>Target Audience:</strong> {recommended.get("target_audience", "N/A")}</li>
<li><strong>Content Type:</strong> {recommended.get("content_type", "N/A")}</li>
<li><strong>Creativity Level:</strong> {recommended.get("creativity_level", "N/A")}</li>
<li><strong>Geographic Location:</strong> {recommended.get("geographic_location", "N/A")}</li>
</ul>
"""
st.markdown(recommended_content, unsafe_allow_html=True)
except Exception as e:
logger.error(f"Error displaying style analysis: {str(e)}")
st.error(f"Error displaying analysis results: {str(e)}")
def render_settings_page():
"""Renders the settings page with all configuration options in tabs"""
st.title("🛠️ Settings & Configuration")
# Create tabs for different settings categories
tabs = st.tabs([
"👷 Content",
"🩻 Images",
"🤖 LLM",
"🕵️ Search",
"🎨 AI Personalization"
])
# Content Settings Tab
with tabs[0]:
st.header("Content Personalization")
blog_length = st.text_input(
"**Content Length (words)**",
value="2000",
key="settings_blog_length",
help="Approximate word count for blogs. Note: Actual length may vary based on GPT provider and max token count."
)
blog_tone_options = ["Casual", "Professional", "How-to", "Beginner", "Research", "Programming", "Social Media", "Customize"]
blog_tone = st.selectbox(
"**Content Tone**",
options=blog_tone_options,
key="settings_blog_tone",
help="Select the desired tone for the blog content."
)
if blog_tone == "Customize":
custom_tone = st.text_input(
"Enter the tone of your content",
key="settings_custom_tone",
help="Specify the tone of your content."
)
if custom_tone:
blog_tone = custom_tone
else:
st.warning("Please specify the tone of your content.")
blog_demographic_options = ["Professional", "Gen-Z", "Tech-savvy", "Student", "Digital Marketing", "Customize"]
blog_demographic = st.selectbox(
"**Target Audience**",
options=blog_demographic_options,
key="settings_blog_demographic",
help="Select the primary audience for the blog content."
)
blog_type = st.selectbox(
"**Content Type**",
options=["Informational", "Commercial", "Company", "News", "Finance", "Competitor", "Programming", "Scholar"],
key="settings_blog_type",
help="Select the category that best describes the blog content."
)
blog_language = st.selectbox(
"**Content Language**",
options=["English", "Spanish", "German", "Chinese", "Arabic", "Nepali", "Hindi", "Hindustani", "Customize"],
key="settings_blog_language",
help="Select the language in which the blog will be written."
)
blog_output_format = st.selectbox(
"**Content Output Format**",
options=["markdown", "HTML", "plaintext"],
key="settings_blog_output_format",
help="Select the format for the blog output."
)
# Images Settings Tab
with tabs[1]:
st.header("Images Personalization")
image_generation_model = st.selectbox(
"**Image Generation Model**",
options=["stable-diffusion", "dalle2", "dalle3"],
key="settings_image_model",
help="Select the model to generate images for the blog."
)
number_of_blog_images = st.number_input(
"**Number of Blog Images**",
value=1,
min_value=1,
max_value=10,
key="settings_number_of_images",
help="Specify the number of images to include in the blog."
)
# LLM Settings Tab
with tabs[2]:
st.header("LLM Personalization")
gpt_provider = st.selectbox(
"**GPT Provider**",
options=["google", "openai", "minstral"],
key="settings_gpt_provider",
help="Select the provider for the GPT model."
)
model = st.text_input(
"**Model**",
value="gemini-1.5-flash-latest",
key="settings_model",
help="Specify the model version to use from the selected provider."
)
col1, col2 = st.columns(2)
with col1:
temperature = st.slider(
"Temperature",
min_value=0.1,
max_value=1.0,
value=0.7,
step=0.1,
key="settings_temperature",
help="Controls the creativity level of the generated text."
)
max_tokens = st.selectbox(
"Max Tokens",
options=[500, 1000, 2000, 4000, 16000, 32000, 64000],
index=3,
key="settings_max_tokens",
help="Maximum length of the output sequence."
)
with col2:
top_p = st.slider(
"Top-p",
min_value=0.0,
max_value=1.0,
value=0.9,
step=0.1,
key="settings_top_p",
help="Controls diversity in text generation."
)
frequency_penalty = st.slider(
"Frequency Penalty",
min_value=0.0,
max_value=2.0,
value=1.0,
step=0.1,
key="settings_frequency_penalty",
help="Reduces word repetition in output."
)
# Search Settings Tab
with tabs[3]:
st.header("Search Engine Personalization")
geographic_location = st.selectbox(
"**Geographic Location**",
options=["us", "in", "fr", "cn"],
key="settings_geographic_location",
help="Select the geographic location for tailoring search results."
)
search_language = st.selectbox(
"**Search Language**",
options=["en", "zn-cn", "de", "hi"],
key="settings_search_language",
help="Select the language for the search results."
)
number_of_results = st.number_input(
"**Number of Results**",
value=10,
min_value=1,
max_value=20,
key="settings_number_of_results",
help="Specify the number of search results to retrieve."
)
time_range = st.selectbox(
"**Time Range**",
options=["anytime", "past day", "past week", "past month", "past year"],
key="settings_time_range",
help="Select the time range for filtering search results."
)
include_domains = st.text_input(
"**Include Domains**",
value="",
key="settings_include_domains",
help="List specific domains to include in search results (comma-separated)."
)
similar_url = st.text_input(
"**Similar URL**",
value="",
key="settings_similar_url",
help="Provide a URL to find similar results."
)
# AI Personalization Tab
with tabs[4]:
st.header("🎨 AI Style Analysis")
st.markdown("""
<div style='background-color: rgba(255, 255, 255, 0.1); padding: 20px; border-radius: 10px; margin-bottom: 20px;'>
<p>Enter a website URL or provide content samples to analyze your writing style and get personalized recommendations.</p>
</div>
""", unsafe_allow_html=True)
# Create two columns for the layout
col1, col2 = st.columns([2, 1])
with col1:
# Website URL input
st.markdown("### Website URL")
url = st.text_input(
"Enter your website URL",
placeholder="https://example.com",
key="settings_website_url",
help="Provide your website URL to analyze your content style. Leave empty if you want to provide written samples instead."
)
# Alternative: Written samples
if not url:
st.markdown("### Written Samples")
st.markdown("""
<div style='background-color: rgba(255, 255, 255, 0.1); padding: 20px; border-radius: 10px; margin-bottom: 20px;'>
<p>No website URL? No problem! You can provide written samples of your content instead.</p>
<p>Share your best articles, blog posts, or any content that represents your writing style.</p>
</div>
""", unsafe_allow_html=True)
samples = st.text_area(
"Paste your content samples here",
key="settings_content_samples",
help="Paste 2-3 samples of your best content. This helps ALwrity understand your writing style."
)
# ALwrity Style button
st.markdown("<div style='height: 20px'></div>", unsafe_allow_html=True)
if st.button("🎨 Analyze Style", use_container_width=True, key="settings_analyze_style"):
if url:
with st.status("Starting style analysis...", expanded=True) as status:
try:
# Step 1: Initialize crawler
status.update(label="Step 1/4: Initializing web crawler...", state="running")
crawler_service = AsyncWebCrawlerService()
# Step 2: Crawl website
status.update(label="Step 2/4: Crawling website content...", state="running")
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
result = loop.run_until_complete(crawler_service.crawl_website(url))
loop.close()
if result.get('success', False):
content = result.get('content', {})
# Step 3: Initialize style analyzer
status.update(label="Step 3/4: Analyzing content style...", state="running")
style_analyzer = StyleAnalyzer()
# Step 4: Perform style analysis
status.update(label="Step 4/4: Generating style recommendations...", state="running")
style_analysis = style_analyzer.analyze_content_style(content)
if style_analysis.get('error'):
status.update(label="Analysis failed", state="error")
st.error(f"Style analysis failed: {style_analysis['error']}")
else:
status.update(label="Analysis complete!", state="complete")
# Display style analysis results
display_style_analysis(style_analysis)
# Display original content in tabs
tab1, tab2, tab3 = st.tabs(["Content", "Metadata", "Links"])
with tab1:
st.markdown("### Main Content")
st.markdown(content.get('main_content', 'No content found'))
with tab2:
st.markdown("### Metadata")
st.markdown(f"""
**Title:** {content.get('title', 'No title found')}
**Description:** {content.get('description', 'No description found')}
**Meta Tags:**
{content.get('meta_tags', {})}
""")
with tab3:
st.markdown("### Links")
for link in content.get('links', []):
st.markdown(f"- [{link.get('text', '')}]({link.get('href', '')})")
else:
status.update(label="Crawling failed", state="error")
st.error("Failed to crawl the website. Please check the URL and try again.")
except Exception as e:
status.update(label="Analysis failed", state="error")
st.error(f"An error occurred during analysis: {str(e)}")
elif samples:
with st.status("Starting style analysis...", expanded=True) as status:
try:
# Initialize style analyzer
status.update(label="Analyzing content style...", state="running")
style_analyzer = StyleAnalyzer()
# Perform style analysis
style_analysis = style_analyzer.analyze_content_style({"main_content": samples})
if style_analysis.get('error'):
status.update(label="Analysis failed", state="error")
st.error(f"Style analysis failed: {style_analysis['error']}")
else:
status.update(label="Analysis complete!", state="complete")
# Display style analysis results
display_style_analysis(style_analysis)
except Exception as e:
status.update(label="Analysis failed", state="error")
st.error(f"An error occurred during analysis: {str(e)}")
else:
st.warning("Please provide either a website URL or content samples to analyze.")
# Save Settings Button
if st.button("💾 Save Settings", type="primary", use_container_width=True, key="settings_save_button"):
# Save all settings to session state
st.session_state.update({
'blog_length': blog_length,
'blog_tone': blog_tone,
'blog_demographic': blog_demographic,
'blog_type': blog_type,
'blog_language': blog_language,
'blog_output_format': blog_output_format,
'image_generation_model': image_generation_model,
'number_of_blog_images': number_of_blog_images,
'gpt_provider': gpt_provider,
'model': model,
'temperature': temperature,
'top_p': top_p,
'max_tokens': max_tokens,
'frequency_penalty': frequency_penalty,
'geographic_location': geographic_location,
'search_language': search_language,
'number_of_results': number_of_results,
'time_range': time_range,
'include_domains': include_domains,
'similar_url': similar_url
})
st.success("✅ Settings saved successfully!")

View File

@@ -8,8 +8,9 @@ import streamlit as st
from lib.utils.file_processor import load_image
from lib.utils.content_generators import content_planning_tools
from lib.utils.alwrity_utils import ai_social_writer
from lib.utils.seo_tools import ai_seo_tools
from lib.utils.settings_page import render_settings_page
from lib.alwrity_ui.seo_tools_dashboard import ai_seo_tools
from lib.alwrity_ui.settings_page import render_settings_page
from lib.alwrity_ui.navigation_styles import apply_navigation_styles, apply_compact_layout
from loguru import logger
# Import social media writer functions
@@ -20,378 +21,13 @@ from lib.ai_writers.insta_ai_writer import insta_writer
from lib.ai_writers.youtube_writers.youtube_ai_writer import youtube_main_menu
from lib.ai_writers.ai_writer_dashboard import get_ai_writers, list_ai_writers
from lib.chatbot_custom.enhanced_alwrity_chatbot import run_enhanced_chatbot
from lib.alwrity_ui.social_media_dashboard import render_social_tools_dashboard
def render_social_tools_dashboard():
"""Render a modern dashboard for social media tools."""
st.markdown("""
<style>
.social-card {
background: white;
border-radius: 10px;
padding: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease;
height: 100%;
}
.social-card:hover {
transform: translateY(-5px);
}
.social-icon {
font-size: 2.5rem;
margin-bottom: 15px;
}
.social-title {
font-size: 1.2rem;
font-weight: 600;
margin-bottom: 10px;
}
.social-description {
color: #666;
font-size: 0.9rem;
margin-bottom: 15px;
}
.social-button {
width: 100%;
padding: 8px 16px;
border-radius: 5px;
border: none;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
}
</style>
""", unsafe_allow_html=True)
# Define social tools with their details and paths
social_tools = {
"Facebook": {
"icon": "📘",
"description": "Create engaging Facebook posts and manage your content strategy",
"color": "#4267B2",
"path": "facebook"
},
"LinkedIn": {
"icon": "💼",
"description": "Generate professional LinkedIn content and optimize your profile",
"color": "#0077B5",
"path": "linkedin"
},
"Twitter": {
"icon": "🐦",
"description": "Craft viral tweets and manage your Twitter presence",
"color": "#1DA1F2",
"path": "twitter"
},
"Instagram": {
"icon": "📸",
"description": "Create Instagram captions and plan your visual content",
"color": "#E1306C",
"path": "instagram"
},
"YouTube": {
"icon": "🎥",
"description": "Generate video scripts and optimize your YouTube content",
"color": "#FF0000",
"path": "youtube"
}
}
# Create a grid of cards
cols = st.columns(3)
for idx, (platform, details) in enumerate(social_tools.items()):
with cols[idx % 3]:
st.markdown(f"""
<div class="social-card">
<div class="social-icon">{details['icon']}</div>
<div class="social-title">{platform}</div>
<div class="social-description">{details['description']}</div>
</div>
""", unsafe_allow_html=True)
if st.button(f"Open {platform}", key=f"btn_{platform}",
help=f"Launch {platform} tools",
use_container_width=True):
# Set query parameters to redirect to the specific tool
st.query_params["tool"] = details["path"]
st.rerun()
def setup_ui():
"""Set up the UI with custom styling."""
# Add custom CSS
st.markdown("""
<style>
/* Main app styling */
.stApp {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
}
/* Compact layout styling with zero top padding when sub-tab selected */
.main .block-container {
padding-top: 0 !important; /* Remove all top padding */
padding-bottom: 0;
max-width: 100%;
}
/* Remove extra padding and margins */
.stMarkdown {
margin: 0;
padding: 0;
}
/* Header styling with zero margins when in sub-tab */
.sub-tab-active h1, .sub-tab-active h2, .sub-tab-active h3 {
display: none; /* Hide headers in sub-tab mode */
}
/* Remove extra padding in containers */
.stMarkdown {
margin-bottom: 0;
}
/* Header styling */
h1, h2, h3 {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-weight: 600;
margin-top: 0;
margin-bottom: 0.5rem; /* Reduced from 1rem */
padding-top: 0;
}
/* Reduce spacing between elements */
.element-container {
margin-bottom: 0.5rem; /* Reduced from 1rem */
}
/* Button styling */
.stButton > button {
border-radius: 8px;
font-weight: 500;
transition: all 0.3s ease;
margin-bottom: 0.25rem; /* Reduced from 0.5rem */
}
.stButton > button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
/* Input field styling */
.stTextInput > div > div > input {
border-radius: 8px;
border: 1px solid rgba(0,0,0,0.1);
padding: 0.5rem 1rem;
}
/* Checkbox styling */
.stCheckbox > label {
font-weight: 500;
}
/* Expander styling */
.streamlit-expanderHeader {
font-weight: 500;
color: #2c3e50;
margin-bottom: 0.5rem;
}
/* Success message styling */
.stSuccess {
background: linear-gradient(135deg, #43c6ac 0%, #191654 100%);
padding: 0.75rem;
border-radius: 8px;
color: white;
margin-bottom: 1rem;
}
/* Error message styling */
.stError {
background: linear-gradient(135deg, #ff6b6b 0%, #ff8e8e 100%);
padding: 0.75rem;
border-radius: 8px;
color: white;
margin-bottom: 1rem;
}
/* Info message styling */
.stInfo {
padding: 0.75rem;
margin-bottom: 1rem;
}
/* Sidebar navigation styling */
.sidebar-nav {
padding: 0.5rem 0;
}
.nav-button {
width: 100%;
text-align: left;
padding: 0.5rem 1rem;
background: transparent;
border: none;
color: #2c3e50;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
margin: 0.2rem 0;
border-radius: 4px;
}
.nav-button:hover {
background: rgba(0,0,0,0.05);
padding-left: 0.5rem;
}
.nav-button.active {
background: #1565C0;
color: white;
}
/* Enhanced Sub-menu styling with minimal spacing */
.sub-menu {
padding-left: 1rem;
margin: 0;
border-left: 2px solid rgba(21, 101, 192, 0.3);
background: rgba(255, 255, 255, 0.05);
border-radius: 0 8px 8px 0;
padding-top: 0;
padding-bottom: 0;
}
/* Sub-menu button styling with minimal gaps */
.sub-menu .stButton > button {
font-size: 0.9rem;
text-align: left;
padding: 0.4rem 0.8rem;
background: transparent;
border: none;
color: #2c3e50;
font-weight: 500;
transition: all 0.2s ease;
margin: 0;
border-radius: 4px;
min-height: 0;
height: auto;
line-height: 1.2;
width: 100%;
}
/* Platform-specific button styles */
.facebook-button .stButton > button {
color: #4267B2;
background: rgba(66, 103, 178, 0.1);
}
.linkedin-button .stButton > button {
color: #0077B5;
background: rgba(0, 119, 181, 0.1);
}
.twitter-button .stButton > button {
color: #1DA1F2;
background: rgba(29, 161, 242, 0.1);
}
.instagram-button .stButton > button {
color: #E1306C;
background: rgba(225, 48, 108, 0.1);
}
.youtube-button .stButton > button {
color: #FF0000;
background: rgba(255, 0, 0, 0.1);
}
/* Platform-specific hover states */
.facebook-button .stButton > button:hover {
background: rgba(66, 103, 178, 0.2) !important;
color: #4267B2 !important;
}
.linkedin-button .stButton > button:hover {
background: rgba(0, 119, 181, 0.2) !important;
color: #0077B5 !important;
}
.twitter-button .stButton > button:hover {
background: rgba(29, 161, 242, 0.2) !important;
color: #1DA1F2 !important;
}
.instagram-button .stButton > button:hover {
background: rgba(225, 48, 108, 0.2) !important;
color: #E1306C !important;
}
.youtube-button .stButton > button:hover {
background: rgba(255, 0, 0, 0.2) !important;
color: #FF0000 !important;
}
/* Platform-specific active states */
.facebook-button.active .stButton > button {
background: #4267B2 !important;
color: white !important;
}
.linkedin-button.active .stButton > button {
background: #0077B5 !important;
color: white !important;
}
.twitter-button.active .stButton > button {
background: #1DA1F2 !important;
color: white !important;
}
.instagram-button.active .stButton > button {
background: #E1306C !important;
color: white !important;
}
.youtube-button.active .stButton > button {
background: #FF0000 !important;
color: white !important;
}
/* Remove any extra spacing from button containers */
.sub-menu .stButton {
margin: 0;
padding: 0;
}
.sub-menu > div {
margin: 0;
padding: 0;
}
.sub-menu .element-container {
margin: 0;
padding: 0;
}
/* Ensure minimal gaps between elements */
.sub-menu > div:not(:last-child) {
margin-bottom: 1px;
}
/* Sidebar icon styling */
.sidebar-icon {
padding: 1rem;
text-align: center;
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
}
.sidebar-icon img {
width: 80px !important;
height: auto !important;
margin: 0 auto;
}
</style>
""", unsafe_allow_html=True)
# Apply navigation-specific styling
apply_navigation_styles()
def setup_alwrity_ui():
@@ -475,14 +111,8 @@ def setup_alwrity_ui():
render_social_tools_dashboard()
else:
# Show the dashboard if no tool is selected
st.markdown("""
<style>
.main .block-container {
padding-top: 0.25rem !important;
}
</style>
""", unsafe_allow_html=True)
st.title(f"{nav_items[st.session_state.active_tab][0]} {st.session_state.active_tab}")
apply_compact_layout()
logger.info(f"{nav_items[st.session_state.active_tab][0]} {st.session_state.active_tab}")
render_social_tools_dashboard()
else:
# Handle other tabs as before
@@ -511,15 +141,8 @@ def setup_alwrity_ui():
logger.info("No writer selected, showing dashboard")
get_ai_writers()
else:
st.markdown("""
<style>
.main .block-container {
padding-top: 0.25rem !important;
padding-bottom: 0;
}
</style>
""", unsafe_allow_html=True)
st.title(f"{nav_items[st.session_state.active_tab][0]} {st.session_state.active_tab}")
apply_compact_layout()
logger.info(f"{nav_items[st.session_state.active_tab][0]} {st.session_state.active_tab}")
nav_items[st.session_state.active_tab][1]()
logger.info("Finished setting up ALwrity UI")