ST_AI / app.py
UCS2014's picture
Update app.py
a266ee9 verified
raw
history blame
8.11 kB
import base64, mimetypes
from html import escape
from pathlib import Path
import streamlit as st
# ========= PATHS (subfolder-safe) =========
BASE_DIR = Path(__file__).parent # -> Landing_Page/
ASSETS = BASE_DIR / "assets" # -> Landing_Page/assets/
# ========= CONTROLS (tweak these) =========
HERO_LOGO_SIZE = 180 # px (top company logo size)
TOP_SPACER = 160 # px
CARD_WIDTH = 300 # px
CARD_THUMB_HEIGHT = 180 # px
GRID_GAP = 100 # px
BUTTON_TEXT = "Click to Run APP"
OPEN_IN_NEW_TAB = False
# NEW: Background image controls
BACKGROUND_IMAGE = ASSETS / "bg.png" # <-- put your background image here
BACKGROUND_OPACITY = 0.3 # 0.0 (invisible) ... 1.0 (fully opaque)
# ========= BRAND / APPS =========
COMPANY_NAME = "Smart Thinking - Logging"
TAGLINE = "We Deliver Smart AI-Based Solutions For O&G Industry"
APP1 = {
"title": "ST_Log_GR",
"url": "https://smart-thinking-gr.hf.space/",
"thumb": ASSETS / "app1.png",
"blurb": "Real-Time GR Logging Prediction",
"bg": "#F7FBFF",
"border":"#713F12",
}
APP2 = {
"title": "ST_Log_Sonic (Ts)",
"url": "https://smart-thinking-sonic-ts.hf.space",
"thumb": ASSETS / "app2.png",
"blurb": "Real-Time Shear Slowness Logging Prediction",
"bg": "#F6FFF7",
"border":"#713F12",
}
APP3 = {
"title": "ST_Log_Sonic (Tc)",
"url": "https://smart-thinking-sonic-tc.hf.space",
"thumb": ASSETS / "app3.png",
"blurb": "Real-Time Compression Slowness Logging Prediction",
"bg": "#F9F6FF",
"border":"#713F12",
}
LOGO_PATH = ASSETS / "logo.png"
# ========= PAGE META =========
page_icon = str(LOGO_PATH) if LOGO_PATH.exists() else "🧭"
st.set_page_config(page_title=f"{COMPANY_NAME} — Apps", page_icon=page_icon, layout="wide")
# ========= IMAGE HELPERS =========
def data_uri(path: Path) -> str | None:
if not path.exists(): return None
mime, _ = mimetypes.guess_type(path.name)
if not mime: mime = "image/png"
b64 = base64.b64encode(path.read_bytes()).decode("utf-8")
return f"data:{mime};base64,{b64}"
def img_tag(path: Path, alt: str, cls: str) -> str:
uri = data_uri(path)
if uri:
return f'<img class="{cls}" src="{uri}" alt="{escape(alt)}" />'
return f'<div class="{cls}" style="display:flex;align-items:center;justify-content:center;color:#50545c;background:linear-gradient(180deg,#f6f7fb,#eceff4);">{escape(alt)}</div>'
# ========= NEW: BACKGROUND INJECTION =========
def inject_background(image_path: Path, opacity: float = 0.12) -> None:
"""
Paint a page-wide background image with adjustable opacity.
Uses a ::before layer attached to the Streamlit app container.
"""
uri = data_uri(image_path)
if not uri:
return
# Tip: tweak filter() to taste (e.g., blur or desaturate for readability)
st.markdown(f"""
<style>
:root {{
--bg-opacity: {max(0.0, min(opacity, 1.0))};
}}
/* Full-page fixed background behind all content */
[data-testid="stAppViewContainer"]::before {{
content: "";
position: fixed;
inset: 0;
z-index: -1;
background-image: url('{uri}');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
opacity: var(--bg-opacity);
filter: saturate(0.9) brightness(1.05); /* add blur(2px) if you want */
}}
/* Keep main container transparent to let the bg show through the gaps */
[data-testid="stAppViewContainer"] > .main {{
background: transparent !important;
}}
</style>
""", unsafe_allow_html=True)
# Apply background (safe no-op if image is missing)
inject_background(BACKGROUND_IMAGE, BACKGROUND_OPACITY)
# ========= CSS (layout & components) =========
st.markdown(
f"""
<style>
:root {{
--top-spacer: {TOP_SPACER}px;
--card-width: {CARD_WIDTH}px;
--grid-gap: {GRID_GAP}px;
--tagline-gap: 0px;
--grid-top-space: 20px;
}}
html, body, [data-testid="stAppViewContainer"] {{ height: 100%; }}
[data-testid="stAppViewContainer"] > .main {{ padding-top: 0 !important; padding-bottom: 0 !important; }}
.block-container {{
max-width: 1120px;
min-height: 100vh;
display: flex; flex-direction: column;
justify-content: center;
gap: 10px;
padding: var(--top-spacer) 0 8px !important;
}}
/* HERO */
.hero {{ text-align:center; margin: 0 0 8px; }}
.hero img {{
width: {HERO_LOGO_SIZE}px; max-width: 95vw; height: auto;
display:block; margin: 0 auto 8px;
filter: drop-shadow(0 2px 8px rgba(0,0,0,.12));
}}
.hero h1 {{ font-size: 2.3rem; margin: .2rem 0 0; }}
.hero p {{
color: #2b2f36;
margin: var(--tagline-gap) auto 0;
max-width: 720px;
font-style: italic;
text-shadow: 0 1px 2px rgba(255,255,255,0.35); /* slight lift over bg */
}}
/* GRID */
.grid {{
display: flex;
gap: var(--grid-gap);
justify-content: center;
align-items: stretch;
flex-wrap: nowrap;
margin-top: var(--grid-top-space);
}}
@media (max-width: calc(3 * var(--card-width) + 2 * var(--grid-gap) + 64px)) {{
.grid {{ flex-wrap: wrap; }}
}}
.card {{
width: var(--card-width);
background: var(--card-bg, #fff);
border: 1px solid var(--card-border, rgba(0,0,0,.06));
border-radius:16px; padding:14px;
box-shadow:0 4px 18px rgba(0,0,0,.08);
transition:transform .12s ease, box-shadow .12s ease;
text-align:center; display:flex; flex-direction:column; gap:10px;
align-items: center;
backdrop-filter: none; /* set to blur(2px) if you want frosted cards */
}}
.card:hover {{ transform: translateY(-2px); box-shadow: 0 10px 28px rgba(0,0,0,.12); }}
.thumb {{
width: 100%;
height: {CARD_THUMB_HEIGHT}px;
border-radius:14px;
border:1px solid rgba(0,0,0,.06);
object-fit: contain;
background:#fff;
}}
.card h3, .card p {{ text-align: center; width: 100%; }}
.card p {{ color:#5a5f6a; min-height:30px; margin:0 8px 2px; }}
.btn {{
display:inline-block; padding:10px 14px; border-radius:12px;
border:1px solid #e5e7eb; text-decoration:none;
background:#f3f4f6; color:#111827; font-weight:500;
margin: 2px auto 0;
align-self: center;
}}
.btn:hover {{ background:#e5e7eb; }}
.footer {{ text-align:center; color:#374151; font-size:1.0em; margin-top: 6px; }}
.footer hr {{ margin: 6px 0; }}
</style>
""",
unsafe_allow_html=True,
)
# ========= HERO =========
st.markdown(
'<div class="hero">'
+ (img_tag(LOGO_PATH, "logo", "") if LOGO_PATH.exists() else "")
+ f"<h1>{escape(COMPANY_NAME)}</h1>"
+ f"<p>{escape(TAGLINE)}</p>"
+ "</div>",
unsafe_allow_html=True,
)
# ========= CARDS =========
def app_card(app: dict) -> str:
target = "_blank" if OPEN_IN_NEW_TAB else "_self"
style = f"--card-bg:{app.get('bg','#fff')}; --card-border:{app.get('border','rgba(0,0,0,.06)')};"
return (
f"<div class='card' style='{style}'>"
+ img_tag(app["thumb"], app["title"], "thumb")
+ f"<h3>{escape(app['title'])}</h3>"
+ f"<p>{escape(app['blurb'])}</p>"
+ f"<a class='btn' href='{escape(app['url'])}' target='{target}'>{escape(BUTTON_TEXT)}</a>"
+ "</div>"
)
st.markdown("<div class='grid'>" + app_card(APP1) + app_card(APP2) + app_card(APP3) + "</div>", unsafe_allow_html=True)
# ========= FOOTER =========
st.markdown(
"""
<hr>
<div class='footer'>
© 2025 Smart Thinking AI-Solutions Team. All rights reserved.<br>
Website: <a href="https://smartthinking.com.sa" target="_blank" rel="noopener noreferrer">smartthinking.com.sa</a>
</div>
""",
unsafe_allow_html=True,
)