Spaces:
Paused
Paused
File size: 12,573 Bytes
5b186ea 9c6863e 216b468 8ec6473 3a3b3ae 8ec6473 5b186ea f9d59bf 216b468 8b91916 2dc2d54 5b186ea 9c6863e 8caa672 7ebdbd1 9c6863e 7ebdbd1 9c6863e 7ebdbd1 9c6863e 8caa672 9c6863e 8caa672 9c6863e 8caa672 9c6863e 8caa672 9c6863e 8caa672 9c6863e 7ebdbd1 9c6863e a2a6274 380507d a2a6274 9c6863e 8caa672 a2a6274 380507d 5b186ea 9c6863e 8caa672 a2a6274 380507d a2a6274 9c6863e 8caa672 a2a6274 eb15cd9 8caa672 eb15cd9 a2a6274 c7e583c 9c6863e aabdd76 fdd7e80 8caa672 fdd7e80 5b186ea fdd7e80 8caa672 5b186ea 8a8dcb3 7ebdbd1 9c6863e f639641 a2a6274 9c6863e 8caa672 9c6863e 8caa672 9c6863e 8caa672 9c6863e 8caa672 9c6863e a2a6274 aabdd76 a2a6274 9c6863e a2a6274 d5b988e dd16bfb 9c6863e f639641 fdd7e80 9c6863e 16bde8f aabdd76 16bde8f a2a6274 8caa672 dd16bfb 34ceced 280e045 8caa672 dd16bfb d5b988e 9c6863e dd16bfb d5b988e dd16bfb d5b988e a2a6274 41d63dd 8caa672 a2a6274 8caa672 d5b988e a2a6274 8caa672 41d63dd acb4956 a2a6274 5b186ea 8caa672 280e045 9c6863e a2a6274 dd16bfb 9c6863e 8caa672 dd16bfb a2a6274 41d63dd 8caa672 5b186ea 8caa672 9c6863e 8caa672 280e045 5b186ea 8caa672 a2a6274 41d63dd 8caa672 ea51ae8 a2a6274 acb4956 8b91916 34ceced 8caa672 a2a6274 d5b988e acb4956 d5b988e 8caa672 c7e583c 280e045 a2a6274 f9d59bf 9c6863e 280e045 d5b988e 9c6863e 280e045 8a8dcb3 9c6863e acb4956 d5b988e 8caa672 9c6863e 8caa672 acb4956 8caa672 9c6863e 34ceced 674b03f 9c6863e 5b186ea 9c6863e 674b03f 9c6863e 674b03f 9c6863e ea51ae8 e2645b8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 |
# -*- coding: utf-8 -*-
from typing import Optional, Dict, Any
import base64, mimetypes
from html import escape
from pathlib import Path
import streamlit as st
# ========= PATHS =========
BASE_DIR = Path(__file__).parent
ASSETS = BASE_DIR / "assets"
# ========= META =========
st.set_page_config(page_title="ST_LOG SUITE", layout="wide")
# ========= GLOBAL THEME (one place) =========
THEME: Dict[str, Any] = {
"strip": {
"gap": 10, "below_gap": 30, "pill_pad_v": 8, "pill_pad_h": 14,
"pill_font": 16, "tagline_font": 15,
"bg1": "#D4AF37", "bg2": "#B88917", "text": "#000052",
"tagline_color": "#000000",
},
"page": {
"top_padding": 50,
"container_width": 1120,
"bg_radial": True,
},
"hero": {
"width": 400,
"margin_bottom": 30,
"logo": ASSETS / "AI_Suite_Log_logo.png",
},
# ---------- GRID CONTROLS (you asked for this) ----------
"grid": {
"card_width": 340, # fixed card width to keep sizes consistent
"gap_x": 40, # horizontal gap between cards (px)
"gap_y": 40, # vertical gap between rows (px)
"cols_desktop": 4, # cards per row on desktop
"cols_tablet": 2, # cards per row for medium widths
"cols_mobile": 1, # cards per row on phones
# Optional: force left/center alignment of the whole grid row
"justify": "center", # 'start' | 'center' | 'end'
},
"card": {
"radius": 22, "border_width": 2, "pad_v": 24, "pad_h": 20,
"border": "#0B1220", "border_hover": "#243447",
"bg_top": "#FFFFFF", "bg_bot": "#FBFCFE",
"title_color": "#0B1220", "blurb_color": "#566275",
},
"icon": {
"diam": 118, "img": 106,
"circle_bg": "#F1F5F9",
"circle_border": "rgba(12,18,32,0.10)",
},
"button": {
"pad_v": 12, "pad_h": 20, "radius": 14,
"bg1": "#0B1220", "bg2": "#162338",
"text": "#FFFFFF", "border": "rgba(11,18,32,.55)",
},
}
# ========= CARDS =========
CARDS = [
{
"title": " ST_Log_GR",
"blurb": "Real-time gamma-ray log prediction.",
"url": "https://smart-thinking-gr.hf.space/",
"icon": ASSETS / "GR_logo.png",
"style": {"bg_top": "#EAF7F1", "bg_bot": "#F6FBF8", "border": "#0F3D3E"},
},
{
"title": " ST_Log_Sonic (Ts)",
"blurb": "Predict shear slowness (DtS) in real time.",
"url": "https://smart-thinking-sonic-ts.hf.space",
"icon": ASSETS / "Ts_logo.png",
"style": {"bg_top": "#EAF7FD", "bg_bot": "#F5FBFF", "border": "#0E4A6E"},
},
{
"title": " ST_Log_Sonic (Tc)",
"blurb": "Predict compressional slowness (DtC) in real time.",
"url": "https://smart-thinking-sonic-tc.hf.space",
"icon": ASSETS / "Tc_logo.png",
"style": {"bg_top": "#EEF0FF", "bg_bot": "#F7F8FF", "border": "#3E4EB8"},
},
{
"title": " ST_Log_RHOB",
"blurb": "Predict formation bulk density in real time.",
"url": "https://smart-thinking-rhob.hf.space",
"icon": ASSETS / "RHOB_logo.png",
"style": {"bg_top": "#EAF7FD", "bg_bot": "#F5FBFF", "border": "#0E4A6E"},
},
]
# ========= Helpers =========
def data_uri(path: Path) -> Optional[str]:
if not path or 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 = "", style: str = "") -> str:
uri = data_uri(path)
if not uri: return ""
cls_attr = f' class="{cls}"' if cls else ""
style_attr = f' style="{style}"' if style else ""
return f'<img{cls_attr}{style_attr} src="{uri}" alt="{escape(alt)}" />'
# ========= CSS =========
strip = THEME["strip"]; page = THEME["page"]; hero = THEME["hero"]
grid = THEME["grid"]; card = THEME["card"]; icon = THEME["icon"]; button = THEME["button"]
bg_radial_css = """
background:
radial-gradient(980px 460px at 50% -140px,
rgba(2,12,30,0.06) 0%,
rgba(2,12,30,0.04) 28%,
rgba(255,255,255,0.98) 60%,
rgba(255,255,255,1) 100%);
""" if page["bg_radial"] else "background: #fff;"
st.markdown(f"""
<style>
:root {{
--top-pad: {page["top_padding"]}px;
--strip-gap: {strip["gap"]}px;
--strip-row-mb: {strip["below_gap"]}px;
--strip-pill-pv: {strip["pill_pad_v"]}px;
--strip-pill-ph: {strip["pill_pad_h"]}px;
--strip-pill-fs: {strip["pill_font"]}px;
--tagline-fs: {strip["tagline_font"]}px;
--hero-w: {hero["width"]}px;
--hero-mb: {hero["margin_bottom"]}px;
/* GRID controls you can tweak */
--card-w: {grid["card_width"]}px;
--grid-gap-x: {grid["gap_x"]}px;
--grid-gap-y: {grid["gap_y"]}px;
--grid-cols: {grid["cols_desktop"]};
--grid-justify: {grid["justify"]};
--card-r: {card["radius"]}px;
--card-bw: {card["border_width"]}px;
--card-pv: {card["pad_v"]}px;
--card-ph: {card["pad_h"]}px;
--cardBgTop: {card["bg_top"]};
--cardBgBot: {card["bg_bot"]};
--cardStroke: {card["border"]};
--cardStrokeHover: {card["border_hover"]};
--icon-d: {icon["diam"]}px;
--icon-img: {icon["img"]}px;
--titleColor: {card["title_color"]};
--blurbColor: {card["blurb_color"]};
--btn1: {button["bg1"]};
--btn2: {button["bg2"]};
--btnText: {button["text"]};
--btnBorder: {button["border"]};
}}
html, body, [data-testid="stAppViewContainer"] {{ height: 100%; }}
[data-testid="stAppViewContainer"] > .main {{ padding-top: 0 !important; padding-bottom: 0 !important; }}
.block-container {{
max-width: {page["container_width"]}px;
min-height: 100vh;
display: flex; flex-direction: column;
gap: 14px;
padding: var(--top-pad) 0 28px !important;
{bg_radial_css}
}}
/* ===== TOP-LEFT STRIP ===== */
.suite-row {{
display:flex; align-items:center; gap: var(--strip-gap);
justify-content:flex-start; flex-wrap: wrap;
margin: 0 0 var(--strip-row-mb) 0;
}}
.suite-pill {{
background: linear-gradient(90deg, {strip["bg1"]} 0%, {strip["bg2"]} 100%);
color: {strip["text"]};
padding: var(--strip-pill-pv) var(--strip-pill-ph);
border-radius: 999px;
font-weight: 800; letter-spacing: .25px;
font-size: var(--strip-pill-fs);
box-shadow: 0 6px 14px rgba(2,12,30,.18);
white-space: nowrap;
}}
.suite-tagline {{
color: {strip["tagline_color"]}; font-weight: 600; opacity: .95;
font-size: var(--tagline-fs);
white-space: nowrap;
}}
/* ===== HERO ===== */
.hero {{ text-align:center; margin: 0 0 var(--hero-mb); }}
.hero img {{
width: var(--hero-w); max-width: 92vw; height: auto;
display:block; margin: 0 auto var(--hero-mb);
filter: drop-shadow(0 6px 16px rgba(0,0,0,.10));
}}
/* ===== GRID (cards per row are controlled via --grid-cols) ===== */
.grid {{
display: grid;
grid-template-columns: repeat(var(--grid-cols), var(--card-w));
column-gap: var(--grid-gap-x);
row-gap: var(--grid-gap-y);
justify-content: var(--grid-justify);
align-items: stretch;
margin-top: 10px;
}}
/* Override # of columns on smaller screens */
@media (max-width: 1120px) {{ .grid {{ --grid-cols: {grid["cols_tablet"]}; }} }}
@media (max-width: 720px) {{ .grid {{ --grid-cols: {grid["cols_mobile"]}; }} }}
/* ===== CARD ===== */
.card {{
position: relative;
width: var(--card-w);
border-radius: {card["radius"]}px;
padding: var(--card-pv) var(--card-ph);
background: linear-gradient(180deg, var(--cardBgTop) 0%, var(--cardBgBot) 100%);
border: var(--card-bw) solid var(--cardStroke);
box-shadow: 0 14px 32px rgba(2,20,35,.12),
0 1px 0 rgba(255,255,255,0.70) inset,
0 -1px 6px rgba(255,255,255,0.18) inset;
transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease, filter .18s ease;
text-align:center; display:flex; flex-direction:column; gap:16px; align-items: center;
}}
.card:hover {{
transform: translateY(-6px) scale(1.01);
border-color: var(--cardStrokeHover);
box-shadow: 0 22px 56px rgba(2,20,35,.18),
0 1px 0 rgba(255,255,255,0.82) inset,
0 -1px 10px rgba(255,255,255,0.28) inset;
filter: saturate(1.03);
}}
/* Icon (kept same styling) */
.icon-wrap {{
width: var(--icon-d); height: var(--icon-d);
border-radius: 9999px; display:grid; place-items:center;
background: {icon["circle_bg"]};
border: 1px solid {icon["circle_border"]};
box-shadow: inset 0 1px 0 rgba(255,255,255,.95), 0 10px 22px rgba(2,20,35,.07);
}}
.icon-wrap img {{
width: calc(var(--icon-img) - 12px);
height: calc(var(--icon-img) - 12px);
padding: 6px; box-sizing: border-box; object-fit: contain;
background: #FFFFFF; border: 2px solid var(--cardStroke); border-radius: 14px; display:block;
}}
.card h3 {{ margin: 0; font-size: 25px; font-weight: 900; color: var(--titleColor); letter-spacing: .15px; }}
.card p {{ color: var(--blurbColor); min-height: 40px; margin: 0 10px; font-size: 16px; }}
.btn {{
display:inline-block; padding: {button["pad_v"]}px {button["pad_h"]}px;
border-radius: {button["radius"]}px;
border: 1px solid {button["border"]};
background: linear-gradient(180deg, {button["bg1"]} 0%, {button["bg2"]} 100%);
font-weight: 800; letter-spacing:.3px; font-size: 16px;
margin: 6px auto 0;
box-shadow: 0 12px 28px rgba(11,18,32,.28), inset 0 1px 0 rgba(255,255,255,.10);
color: {button["text"]}; text-decoration: none;
}}
a.btn, a.btn:link, a.btn:visited, a.btn:hover, a.btn:active, a.btn:focus {{
color: {button["text"]} !important; text-decoration: none !important;
}}
.btn:hover {{ filter: brightness(1.03) saturate(1.04); transform: translateY(-1px); }}
.footer {{ text-align:center; color:#3f4a5a; font-size:0.96em; margin-top: 26px; }}
.footer hr {{ margin: 12px 0; border-color: rgba(0,0,0,.08); }}
</style>
""", unsafe_allow_html=True)
# ========= Strip (top-left) =========
st.markdown(
f"""
<div class="suite-row">
<span class="suite-pill">{"ST_LOG SUITE"}</span>
<span class="suite-tagline">{"Generating AI-Based Well Logging Profiles While Drilling"}</span>
</div>
""",
unsafe_allow_html=True,
)
# ========= Hero =========
hero_html = img_tag(THEME["hero"]["logo"], "ST_LOG SUITE")
st.markdown(f"<div class='hero'>{hero_html}</div>", unsafe_allow_html=True)
# ========= Cards =========
def build_card_vars(style: Dict[str, Any]) -> str:
s = []
if "width" in style: s.append(f"--card-w:{int(style['width'])}px")
if "radius" in style: s.append(f"--c-radius:{int(style['radius'])}px")
if "pad_v" in style: s.append(f"--card-pv:{int(style['pad_v'])}px")
if "pad_h" in style: s.append(f"--card-ph:{int(style['pad_h'])}px")
if "bg_top" in style: s.append(f"--cardBgTop:{style['bg_top']}")
if "bg_bot" in style: s.append(f"--cardBgBot:{style['bg_bot']}")
if "border" in style: s.append(f"--cardStroke:{style['border']}")
if "border_width" in style: s.append(f"--card-bw:{int(style['border_width'])}px")
if "title_color" in style: s.append(f"--titleColor:{style['title_color']}")
if "blurb_color" in style: s.append(f"--blurbColor:{style['blurb_color']}")
return "; ".join(s)
def app_card(card_cfg: Dict[str, Any]) -> str:
style = card_cfg.get("style", {})
vars_inline = build_card_vars(style)
icon_html = img_tag(card_cfg.get("icon"), "icon") if card_cfg.get("icon") and card_cfg["icon"].exists() else ""
target = "_self"
return (
f"<div class='card' style='{vars_inline}'>"
+ f"<div class='icon-wrap'>{icon_html}</div>"
+ f"<h3>{escape(card_cfg['title'])}</h3>"
+ f"<p>{escape(card_cfg['blurb'])}</p>"
+ f"<a class='btn' href='{escape(card_cfg['url'])}' target='{target}' rel='noopener'>Run App</a>"
+ "</div>"
)
st.markdown("<div class='grid'>" + "".join(app_card(c) for c in CARDS) + "</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,
)
|