bradcharl14's picture
UI fixes
9b017d9 verified
Raw History Blame Contribute Delete
14 kB
import os
import warnings
import logging
# Suppress technical initialization warnings from Torch/Transformers/HF
os.environ["TORCH_LOGS"] = "-all"
os.environ["TORCH_CPP_LOG_LEVEL"] = "3"
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
os.environ["HUGGINGFACE_HUB_VERBOSITY"] = "error"
warnings.filterwarnings("ignore", message=".*torch.classes.*")
logging.getLogger("torch").setLevel(logging.ERROR)
import streamlit as st
# Page Config must be first Streamlit command
st.set_page_config(
page_title="AutoContentForge AI",
page_icon="⚒️",
layout="wide",
initial_sidebar_state="expanded",
)
from streamlit_calendar import calendar
from forge_utils import ContentExtractor, ForgeAI
# Initialize ForgeAI with caching
@st.cache_resource
def get_forge():
return ForgeAI()
forge = get_forge()
# Session State for Content
if 'forge_results' not in st.session_state:
st.session_state.forge_results = None
if 'extracted_content' not in st.session_state:
st.session_state.extracted_content = None
# Custom CSS for Premium Look
st.markdown("""
<style>
/* Global Text Color */
.stApp {
color: #ffffff;
}
.main {
background-color: #0e1117;
color: #ffffff;
}
p, span, label, h1, h2, h3, h4, h5, h6 {
color: #ffffff !important;
}
/* Sidebar Carbon Black */
[data-testid="stSidebar"] {
background-color: #1a1c1e;
border-right: 1px solid rgba(255, 255, 255, 0.05);
}
/* Global Button Styling - Force Red */
button, [data-testid="stBaseButton-secondary"], [data-testid="stBaseButton-primary"] {
background-color: #4A4A4A !important;
color: white !important;
border: none !important;
border-radius: 6px !important;
transition: all 0.3s ease !important;
}
button:hover, [data-testid="stBaseButton-secondary"]:hover, [data-testid="stBaseButton-primary"]:hover {
background-color: #4A4A4A !important;
transform: translateY(-1px) !important;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1) !important;
color: white !important;
}
button:active {
background-color: #991b1b !important;
transform: translateY(0px) !important;
}
/* Fix Help Tooltips (Black text on white background) */
div[data-testid="stTooltipContent"] {
background-color: #ffffff !important;
color: #000000 !important;
border: 1px solid #ddd !important;
}
div[data-testid="stTooltipContent"] p {
color: #000000 !important;
}
/* Ensure help icons themselves are visible */
[data-testid="stWidgetLabel"] .st-at {
color: #4A4A4A !important;
}
/* Tab and other refinements */
.stTabs [data-baseweb="tab-list"] {
gap: 24px;
background-color: transparent;
}
.stTabs [data-baseweb="tab"] {
height: 50px;
white-space: pre-wrap;
background-color: #1a1c24;
border-radius: 4px 4px 0px 0px;
color: #ffffff !important;
font-size: 16px;
font-weight: 500;
padding: 10px 20px;
}
.stTabs [aria-selected="true"] {
background-color: #6d28d9 !important;
border-bottom: 2px solid #8b5cf6 !important;
}
.pro-glass-card {
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 20px;
margin-bottom: 20px;
color: #ffffff;
}
.platform-card {
background: #1a1c24;
border-left: 5px solid #6d28d9;
padding: 15px;
border-radius: 8px;
margin-bottom: 10px;
color: #ffffff;
}
.sidebar-pro-cta {
background: linear-gradient(135deg, #6d28d9, #305CDE);
color: white !important;
padding: 15px;
border-radius: 8px;
text-align: center;
margin-bottom: 20px;
}
.sidebar-pro-cta small, .sidebar-pro-cta strong {
color: white !important;
}
</style>
""", unsafe_allow_html=True)
# Sidebar Branding & Pro CTA
with st.sidebar:
st.title("⚒️ AutoContentForge AI")
st.caption("Automate Your Content. Forge Every Platform.")
st.markdown("---")
st.markdown("""
<div class="sidebar-pro-cta">
<strong>🚀 Upgrade to Pro</strong><br>
<small>Self-Hosted Docker Version</small><br>
<br>
• Direct Social Posting<br>
• Full Bulk Scheduling<br>
• Video Automation<br>
• Google Docs sync
</div>
""", unsafe_allow_html=True)
if st.button("Contact for Pro Version", use_container_width=True):
st.info("Email: rajue@smackcoders.com with subject 'AutoContentForge AI Pro Inquiry'")
st.markdown("---")
st.markdown("### Resources")
st.markdown("[Documentation](#)")
st.markdown("[Pricing](#)")
st.markdown("[YouTube Channel](#)")
# Main Navigation Tabs
tab1, tab2, tab3 = st.tabs([
"📝 Single Post",
"📦 Bulk Posting",
"⚙️ Admin Settings"
])
# Initialize session states for the new workflow
if 'master_variations' not in st.session_state:
st.session_state.master_variations = None
if 'selected_variation' not in st.session_state:
st.session_state.selected_variation = None
if 'current_hashtags' not in st.session_state:
st.session_state.current_hashtags = ""
# Module 1: Single Post
with tab1:
st.header("📝 Create Single Social Post")
st.write("Fetch from a URL or use the manual editor to forge your master variations.")
col_in, col_out = st.columns([1, 1.2])
with col_in:
st.subheader("1. Source Content")
input_mode = st.radio(
"Input Mode",
["URL Fetch", "Manual Editor"],
horizontal=True,
help="Choose 'URL Fetch' to extract content from a website, or 'Manual Editor' to paste your own text directly."
)
if input_mode == "URL Fetch":
source_url = st.text_input(
"Enter Content URL",
placeholder="https://example.com/blog-post",
help="Paste the full link (including https://) of the article or blog post you want to repurpose."
)
if st.button("🚀 Fetch & Analyze"):
if not source_url or "://" not in source_url:
st.error("Please enter a valid URL (including https://)")
else:
with st.spinner("Forging 3 master variations..."):
extracted = ContentExtractor.from_url(source_url)
if "error" in extracted:
st.error(extracted["error"])
else:
st.session_state.extracted_content = extracted
st.session_state.master_variations = forge.generate_master_variations(extracted['text'])
st.session_state.current_hashtags = forge.extract_hashtags(extracted['text'])
st.session_state.selected_variation = None # Reset selection
st.success("Successfully generated 3 variations!")
else:
pasted_title = st.text_input("Post Title (Optional)", help="Give your post a title for easier tracking.")
pasted_text = st.text_area(
"Master Content / Article Text",
height=200,
help="Paste the core content you want to turn into social posts. Aim for at least 50 words for the best AI results."
)
if st.button("✨ Generate Variations"):
if len(pasted_text) < 50:
st.warning("Please provide more content for better AI results.")
else:
with st.spinner("Processing..."):
st.session_state.extracted_content = {"title": pasted_title or "Manual Input", "text": pasted_text}
st.session_state.master_variations = forge.generate_master_variations(pasted_text)
st.session_state.current_hashtags = forge.extract_hashtags(pasted_text)
st.session_state.selected_variation = None
st.success("Variations generated!")
if st.session_state.master_variations:
st.markdown("---")
st.subheader("2. Choose Master Variation")
st.info("💡 **Tip:** Each variation has a different tone. Expand them to see which one fits your brand voice best.")
for var in st.session_state.master_variations:
with st.expander(f"🔹 {var['type']}", expanded=(st.session_state.selected_variation == var['id'])):
st.write(var['text'])
if st.button(f"Select {var['type']}", key=f"sel_{var['id']}", help=f"Choose the {var['type']} to be adapted for your social networks."):
st.session_state.selected_variation = var['id']
st.rerun()
with col_out:
st.subheader("3. Preview & Adapt")
if not st.session_state.selected_variation:
st.info("Select a master variation on the left to see platform-specific previews.")
else:
selected_var_obj = next(v for v in st.session_state.master_variations if v['id'] == st.session_state.selected_variation)
platforms = st.multiselect(
"Select Social Networks",
["LinkedIn", "Twitter/X", "Instagram", "Facebook", "YouTube", "Bluesky"],
default=["LinkedIn", "Twitter/X"],
help="Choose which platforms you want to generate previews for. The AI will format your selected variation for each."
)
if st.session_state.extracted_content.get("images"):
st.image(st.session_state.extracted_content['images'][0], caption="Extracted Media", width=300)
else:
st.write("📸 *No media found. You can add media in the Pro version or manual editor.*")
st.markdown("---")
for platform in platforms:
adapted_text = forge.adapt_for_platform(selected_var_obj['text'], platform, st.session_state.current_hashtags)
with st.container():
st.markdown(f"""
<div class="platform-card">
<strong>{platform} Preview</strong><br>
<p style='font-size: 0.9em; color: #ccc;'>
{adapted_text if isinstance(adapted_text, str) else adapted_text.get('title')}
</p>
</div>
""", unsafe_allow_html=True)
if isinstance(adapted_text, dict) and platform == "YouTube":
st.caption(f"Description: {adapted_text.get('description')}")
if st.button("📅 Post / Schedule Now", type="primary"):
st.success("✅ Successfully queued! (Flow simulation)")
st.balloons()
st.info("🔒 In Pro: This would instantly publish or sync to your drag-and-drop calendar.")
# Module 2: Bulk Posting
with tab2:
st.header("📦 Bulk URL Posting")
st.write("Forge content from multiple URLs at once. Perfect for content migration or high-volume publishing.")
bulk_urls = st.text_area(
"List of URLs (one per line)",
height=250,
placeholder="https://site.com/post-1\nhttps://site.com/post-2",
help="Enter a list of blog post links. In the Pro version, the AI will process all of them in one batch."
)
st.markdown("""
<div class="pro-glass-card">
<strong>Pro Feature Highlights:</strong><br>
• Automatic batch processing of sitemaps<br>
• Intelligent image matching for bulk lists<br>
• Scheduled intervals (e.g., 1 post every 2 hours)<br>
• CSV / JSON Export for external schedulers
</div>
""", unsafe_allow_html=True)
st.button("🚀 Process Bulk List", disabled=True, help="This feature is exclusive to the Pro version.")
st.markdown("---")
if st.button("Unlock Bulk & Automation", key="bulk_pro", help="Contact us to upgrade to the full self-hosted version."):
st.info("Email: rajue@smackcoders.com with subject 'AutoContentForge AI Pro Inquiry'")
# Module 3: Admin Settings
with tab3:
st.header("⚙️ Admin Settings")
st.write("Authorize and manage your social network connections.")
networks = [
{"name": "LinkedIn", "icon": "👔"},
{"name": "Twitter/X", "icon": "🐦"},
{"name": "Instagram", "icon": "📸"},
{"name": "Facebook", "icon": "👥"},
{"name": "YouTube", "icon": "🎥"},
{"name": "Bluesky", "icon": "🦋"}
]
cols = st.columns(3)
for i, net in enumerate(networks):
with cols[i % 3]:
st.markdown(f"### {net['icon']} {net['name']}")
st.button(f"Authorize {net['name']}", key=f"auth_btn_{net['name']}", help=f"Connect your {net['name']} account via OAuth for direct posting.")
st.caption(f"Status: Not Connected")
st.markdown("---")
st.subheader("Global Settings")
st.checkbox("Auto-add smart hashtags to all posts", value=True, help="Automatically suggest and append hashtags based on content analysis.")
st.checkbox("Notify me on successful publishing", value=True, help="Send a notification once a post is live on the selected platform.")
st.selectbox("Default AI Tone", ["Professional", "Conversational", "Exciting", "Detailed"], help="Select the default voice for your AI-generated master variations.")
if st.button("Save Settings", help="Save your preferences for future sessions."):
st.success("Settings saved locally! (Simulation)")