diff options
Diffstat (limited to 'app.py')
| -rw-r--r-- | app.py | 101 |
1 files changed, 81 insertions, 20 deletions
@@ -220,6 +220,16 @@ button[kind="secondary"]:hover { padding-bottom: 3rem !important; } +/* ── Sticky market bar ──────────────────────────────────────────────────── */ +.st-key-market_bar_sticky { + position: sticky !important; + top: 0 !important; + z-index: 200 !important; + background: var(--ink-0) !important; + padding-bottom: 0.5rem !important; + margin-bottom: -0.5rem !important; +} + /* ── Tabs ───────────────────────────────────────────────────────────────── */ .stTabs [data-baseweb="tab-list"] { background: var(--ink-2) !important; @@ -404,9 +414,17 @@ hr { ::-webkit-scrollbar-thumb { background: var(--ink-3); border-radius: 3px; } ::-webkit-scrollbar-thumb:hover { background: var(--ink-4); } -/* Hide the watchlist click receiver input */ -[data-testid="stSidebar"] [data-testid="stTextInput"]:has(input[aria-label="wl_click_receiver"]) { - display: none !important; +/* Hide click receiver inputs without removing them from React's event system */ +[data-testid="stSidebar"] [data-testid="stTextInput"]:has(input[aria-label="wl_click_receiver"]), +[data-testid="stTextInput"]:has(input[aria-label="qt_click_receiver"]), +[data-testid="stTextInput"]:has(input[aria-label="persist_wl"]), +[data-testid="stTextInput"]:has(input[aria-label="persist_tk"]) { + visibility: hidden !important; + height: 0 !important; + min-height: 0 !important; + overflow: hidden !important; + margin: 0 !important; + padding: 0 !important; } /* ── Ticker Header Band ──────────────────────────────────────────────────── */ @@ -584,9 +602,11 @@ _prism_template = go.layout.Template(layout=_prism_layout) pio.templates["prism"] = _prism_template pio.templates.default = "prism" +from components.persistence import render_persistence_bridge from components.market_bar import render_market_bar from components.top_movers import render_top_movers from components.watchlist import render_watchlist +from components.quotetable import render_quotetable from components.overview import render_overview from components.financials import render_financials from components.valuation import render_valuation @@ -594,6 +614,7 @@ from components.insiders import render_insiders from components.filings import render_filings from components.news import render_news from components.options import render_options +from components.macro import render_macro from services.data_service import get_company_info, search_tickers, get_latest_price import streamlit.components.v1 as components @@ -607,6 +628,7 @@ if "watchlist" not in st.session_state: if "active_tab" not in st.session_state: st.session_state["active_tab"] = "overview" +render_persistence_bridge() # ── Sidebar ─────────────────────────────────────────────────────────────────── @@ -712,6 +734,12 @@ with st.sidebar: ticker = st.session_state["ticker"] + # ── Exit to landing page ────────────────────────────────────────────── + if st.session_state.get("ticker"): + if st.button("← Watchlist", key="exit_ticker", use_container_width=True): + st.session_state["ticker"] = None + st.rerun() + # ── Workspace nav ───────────────────────────────────────────────────── st.markdown("<div class='psm-nav-section'>Workspace</div>", unsafe_allow_html=True) _nav = [ @@ -722,6 +750,7 @@ with st.sidebar: ("insiders", "○ Insiders"), ("filings", "▤ Filings"), ("news", "◉ News"), + ("macro", "⬡ Macro"), ] for _tab_id, _tab_label in _nav: _is_active = st.session_state["active_tab"] == _tab_id @@ -757,30 +786,62 @@ with st.sidebar: # ── Market Bar ──────────────────────────────────────────────────────────────── -with st.container(): +with st.container(key="market_bar_sticky"): render_market_bar() st.divider() +# ── ⌘K / Ctrl+K shortcut — focuses sidebar ticker search ───────────────────── +components.html( + "<script>" + "document.addEventListener('keydown', function(e) {" + " if (e.key === '/' && !e.metaKey && !e.ctrlKey && !e.altKey" + " && document.activeElement.tagName !== 'INPUT'" + " && document.activeElement.tagName !== 'TEXTAREA') {" + " var inputs = window.parent.document.querySelectorAll('input');" + " for (var i = 0; i < inputs.length; i++) {" + " if (inputs[i].placeholder && inputs[i].placeholder.indexOf('AAPL') > -1) {" + " e.preventDefault();" + " inputs[i].focus(); inputs[i].select(); break;" + " }" + " }" + " }" + "});" + "</script>", + height=0, + scrolling=False, +) + # ── Main Content ────────────────────────────────────────────────────────────── +if st.session_state["active_tab"] == "macro": + try: + render_macro() + except Exception as e: + st.error(f"Macro data failed to load: {e}") + st.stop() + if not ticker: - st.markdown(""" - <div style="padding:48px 0 32px;text-align:center;"> - <div style=" - font-family:'EB Garamond',Georgia,serif; - font-style:italic;font-size:2.375rem; - color:#F2ECDC;font-weight:400; - letter-spacing:-0.01em;line-height:1.1; - margin-bottom:12px; - ">Search for a ticker to begin.</div> - <div style=" - font-family:'IBM Plex Sans',sans-serif; - font-size:0.875rem;color:#5E5849; - letter-spacing:0.01em; - ">Enter a company name or symbol in the sidebar.</div> - </div> - """, unsafe_allow_html=True) + _watchlist = st.session_state.get("watchlist", []) + if _watchlist: + render_quotetable(_watchlist) + else: + st.markdown(""" + <div style="padding:48px 0 32px;text-align:center;"> + <div style=" + font-family:'EB Garamond',Georgia,serif; + font-style:italic;font-size:2.375rem; + color:#F2ECDC;font-weight:400; + letter-spacing:-0.01em;line-height:1.1; + margin-bottom:12px; + ">Search for a ticker to begin.</div> + <div style=" + font-family:'IBM Plex Sans',sans-serif; + font-size:0.875rem;color:#5E5849; + letter-spacing:0.01em; + ">Enter a company name or symbol in the sidebar.</div> + </div> + """, unsafe_allow_html=True) st.stop() # ── Ticker Header + KPI Strip ───────────────────────────────────────────────── |
