diff options
| author | Tyler <tyler@tylerhoang.xyz> | 2026-05-17 02:35:16 -0700 |
|---|---|---|
| committer | Tyler <tyler@tylerhoang.xyz> | 2026-05-17 02:35:16 -0700 |
| commit | 119eaf8afe9924ce0e5eef62f4a1546caf0c4bab (patch) | |
| tree | eb20074326e517f07d065ac7832726213990ebd4 | |
| parent | 811fb970ac87c6f9e78e24889011623f097041a5 (diff) | |
Move ticker search to top bar
| -rw-r--r-- | app.py | 206 |
1 files changed, 163 insertions, 43 deletions
@@ -220,11 +220,96 @@ button[kind="secondary"]:hover { padding-bottom: 3rem !important; } -/* ── Sticky market bar ──────────────────────────────────────────────────── */ -.st-key-market_bar_sticky { +/* ── Sticky top bar / market bar ────────────────────────────────────────── */ +.st-key-prism_topbar_sticky { position: sticky !important; top: 0 !important; - z-index: 200 !important; + z-index: 260 !important; + background: var(--ink-0) !important; + border-bottom: 1px solid var(--line-1) !important; + margin-top: -0.5rem !important; + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; +} + +.st-key-prism_topbar_sticky [data-testid="stHorizontalBlock"] { + align-items: end; + gap: var(--sp-3); +} + +.st-key-prism_topbar_sticky .stTextInput, +.st-key-prism_topbar_sticky [data-testid="stSelectbox"], +.st-key-prism_topbar_sticky [data-testid="stButton"] { + margin-bottom: 0 !important; +} + +.st-key-prism_topbar_sticky .stTextInput label, +.st-key-prism_topbar_sticky [data-testid="stSelectbox"] label { + display: none !important; +} + +.st-key-prism_topbar_sticky .stTextInput input { + min-height: 36px !important; + background: var(--ink-2) !important; + border: 1px solid var(--line-2) !important; + border-radius: var(--r-2) !important; + color: var(--fg-1) !important; + font-family: var(--font-mono) !important; + font-size: var(--fs-13) !important; + padding-left: 34px !important; + padding-right: 60px !important; +} + +.st-key-prism_topbar_sticky .stTextInput input::placeholder { + color: var(--fg-3) !important; +} + +.st-key-prism_topbar_sticky .stTextInput input:focus { + border-color: var(--brass) !important; + box-shadow: none !important; +} + +.st-key-prism_topbar_sticky .stTextInput:has(input[aria-label="Global ticker search"]) { + position: relative; +} + +.st-key-prism_topbar_sticky .stTextInput:has(input[aria-label="Global ticker search"])::before { + content: "⌕"; + position: absolute; + left: 12px; + bottom: 8px; + z-index: 5; + pointer-events: none; + font-family: var(--font-mono); + font-size: 18px; + color: var(--fg-3); + line-height: 1; +} + +.st-key-prism_topbar_sticky .stTextInput:has(input[aria-label="Global ticker search"])::after { + content: "⌘ K"; + position: absolute; + right: 10px; + bottom: 8px; + z-index: 5; + pointer-events: none; + font-family: var(--font-mono); + font-size: var(--fs-12); + color: var(--fg-3); + border: 1px solid var(--line-2); + padding: 1px 6px; + border-radius: var(--r-1); + background: var(--ink-1); +} + +.psm-top-spacer { + min-height: 36px; +} + +.st-key-market_bar_sticky { + position: sticky !important; + top: 52px !important; + z-index: 240 !important; background: var(--ink-0) !important; padding-bottom: 0.5rem !important; margin-bottom: -0.5rem !important; @@ -630,6 +715,21 @@ if "active_tab" not in st.session_state: render_persistence_bridge() + +def open_topbar_search(): + query = st.session_state.get("topbar_search_query", "").strip() + if not query: + return + + results = search_tickers(query) + if results: + st.session_state["ticker"] = results[0]["symbol"] + else: + st.session_state["ticker"] = query.upper() + + +ticker = st.session_state["ticker"] + # ── Sidebar ─────────────────────────────────────────────────────────────────── with st.sidebar: @@ -706,34 +806,6 @@ with st.sidebar: ) components.html(_clock_html, height=32, scrolling=False) - with st.form("ticker_search_form", clear_on_submit=False): - query = st.text_input( - "Ticker or company", - placeholder="AAPL, Apple, MSFT…", - key="search_query", - ).strip() - - results = search_tickers(query) if query else [] - selected_symbol = None - - if results: - options = {f"{r['symbol']} — {r['name']} ({r['exchange']})": r["symbol"] for r in results} - choice = st.selectbox( - "Matches", - options=list(options.keys()), - label_visibility="collapsed", - ) - selected_symbol = options[choice] - elif query: - selected_symbol = query.upper() - - submitted = st.form_submit_button("Open", width="stretch", type="primary") - - if submitted and selected_symbol: - st.session_state["ticker"] = selected_symbol - - 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): @@ -784,29 +856,77 @@ with st.sidebar: render_top_movers(True) -# ── Market Bar ──────────────────────────────────────────────────────────────── +# ── Sticky Top Bar + Market Bar ─────────────────────────────────────────────── + +with st.container(key="prism_topbar_sticky"): + _search_col, _match_col, _open_col = st.columns([3.2, 2.3, 0.7], vertical_alignment="bottom") + with _search_col: + _topbar_query = st.text_input( + "Global ticker search", + placeholder="Search ticker, company, or filing…", + key="topbar_search_query", + on_change=open_topbar_search, + ).strip() + + _topbar_results = search_tickers(_topbar_query) if _topbar_query else [] + _topbar_selected_symbol = _topbar_query.upper() if _topbar_query else None + + with _match_col: + if _topbar_results: + _topbar_options = { + r["symbol"] + " — " + r["name"] + " (" + r["exchange"] + ")": r["symbol"] + for r in _topbar_results + } + _topbar_choice = st.selectbox( + "Matches", + options=list(_topbar_options.keys()), + key="topbar_search_match", + label_visibility="collapsed", + ) + _topbar_selected_symbol = _topbar_options[_topbar_choice] + else: + st.markdown("<div class='psm-top-spacer'></div>", unsafe_allow_html=True) + + with _open_col: + if st.button("Open", key="topbar_search_open", use_container_width=True, type="primary"): + if _topbar_selected_symbol: + st.session_state["ticker"] = _topbar_selected_symbol + st.rerun() + +ticker = st.session_state["ticker"] with st.container(key="market_bar_sticky"): render_market_bar() st.divider() -# ── ⌘K / Ctrl+K shortcut — focuses sidebar ticker search ───────────────────── +# ── ⌘K / Ctrl+K shortcut — focuses top-bar 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');" + "(function() {" + " var placeholder = 'Search ticker, company, or filing…';" + " var doc = window.parent && window.parent.document ? window.parent.document : document;" + " if (doc.__prismTopbarFocusVersion === 1) { return; }" + " doc.__prismTopbarFocusVersion = 1;" + " function findSearchInput() {" + " var inputs = doc.querySelectorAll('input');" " for (var i = 0; i < inputs.length; i++) {" - " if (inputs[i].placeholder && inputs[i].placeholder.indexOf('AAPL') > -1) {" + " if (inputs[i].placeholder === placeholder) { return inputs[i]; }" + " }" + " return null;" + " }" + " doc.addEventListener('keydown', function(e) {" + " var key = e.key ? e.key.toLowerCase() : '';" + " if (key === 'k' && (e.metaKey || e.ctrlKey)) {" + " var input = findSearchInput();" + " if (input) {" " e.preventDefault();" - " inputs[i].focus(); inputs[i].select(); break;" + " input.focus();" + " input.select();" " }" " }" - " }" - "});" + " }, true);" + "}());" "</script>", height=0, scrolling=False, @@ -839,7 +959,7 @@ if not ticker: 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> + ">Enter a company name or symbol in the top search bar.</div> </div> """, unsafe_allow_html=True) st.stop() |
