From 143c775cdff3d586fe28f8a43698ee465758370b Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 18 May 2026 15:04:12 +0000 Subject: [PATCH] fix(footer,nav): left-justify buttons, drop per-page caption bar, hide sidebar Close MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three small follow-ups to the sticky-footer rework: - Left-justify the footer buttons (and reposition the Help popover to anchor at the left edge so it lines up with its trigger). - Remove the per-page ``st.divider() + st.caption("Runs locally…")`` trailing block from all 9 tool pages. The new sticky footer covers that text, so it was rendering as an empty white bar at the bottom of each tool page. - Hide the Close entry from the sidebar nav via CSS. The page stays registered with st.navigation so /close is still routable for the sticky-footer Close button — only the sidebar link + its section header are hidden (via :has() on stNavSection). Co-Authored-By: Claude Opus 4.7 (1M context) --- src/gui/app.py | 8 +++++--- src/gui/components/_legacy.py | 22 ++++++++++++++++++++-- src/gui/pages/1_Deduplicator.py | 11 ----------- src/gui/pages/2_Text_Cleaner.py | 2 -- src/gui/pages/3_Format_Standardizer.py | 2 -- src/gui/pages/4_Missing_Values.py | 2 -- src/gui/pages/5_Column_Mapper.py | 3 --- src/gui/pages/6_Outlier_Detector.py | 11 ----------- src/gui/pages/7_Multi_File_Merger.py | 11 ----------- src/gui/pages/8_Validator_Reporter.py | 11 ----------- src/gui/pages/9_Pipeline_Runner.py | 3 --- 11 files changed, 25 insertions(+), 61 deletions(-) diff --git a/src/gui/app.py b/src/gui/app.py index f7dfe18..157d59f 100644 --- a/src/gui/app.py +++ b/src/gui/app.py @@ -94,6 +94,11 @@ def _build_navigation() -> dict[str, list]: icon="🔑", url_path="activate", ) + # Close is registered so it remains URL-routable (/close) for the + # sticky-footer Close button. It is hidden from the sidebar via CSS + # in ``hide_streamlit_chrome`` rather than being unregistered — + # ``st.navigation`` requires every routable page to be listed; pages + # not in the dict 404 even by direct URL hit. close = st.Page( "pages/99_Close.py", title=_t("nav.close_title") or "Close", @@ -103,9 +108,6 @@ def _build_navigation() -> dict[str, list]: account_header = _t("nav.section_account") or "Account" close_header = _t("nav.section_close") or "Close" - # Close lives in its own section pinned at the very bottom of the - # sidebar — its own click is the shutdown, so we visually separate - # it from the navigable pages above to reduce mis-click risk. return { "": [home], section_label("cleaners"): by_section["cleaners"], diff --git a/src/gui/components/_legacy.py b/src/gui/components/_legacy.py index d345e6b..b2f9668 100644 --- a/src/gui/components/_legacy.py +++ b/src/gui/components/_legacy.py @@ -60,6 +60,24 @@ header[data-testid="stHeader"] { footer { display: none !important; } +/* Hide the "Close" entry from the sidebar nav — the page is kept + registered so the sticky-footer Close link can still route to + /close, but the sidebar entry is redundant and risks mis-clicks. */ +[data-testid="stSidebarNav"] a[href$="/close"], +[data-testid="stSidebarNav"] a[href$="/close/"] { + display: none !important; +} +/* Hide the entire "Close" section so its header label doesn't + orphan above the hidden link. Streamlit tags each nav section + with ``data-testid="stNavSection"``; the :has() selector picks + only the one containing the close link. Modern browsers (Chrome + 105+, Safari 15.4+, Firefox 121+) all support it; older + browsers fall back to showing the empty header, which is + visually harmless. */ +[data-testid="stSidebarNav"] [data-testid="stNavSection"]:has(a[href$="/close"]), +[data-testid="stSidebarNav"] [data-testid="stNavSection"]:has(a[href$="/close/"]) { + display: none !important; +} /* Reclaim top padding lost from hidden header. Slim the bottom too — Streamlit's default leaves several rems below the last widget. */ .stAppViewBlockContainer, @@ -572,7 +590,7 @@ def render_sticky_footer() -> None: z-index: 2147483646 !important; display: flex !important; align-items: center !important; - justify-content: flex-end !important; + justify-content: flex-start !important; gap: 0.4rem !important; font-family: system-ui, -apple-system, sans-serif !important; box-sizing: border-box !important; @@ -605,7 +623,7 @@ def render_sticky_footer() -> None: } #datatools-help-popover { position: fixed !important; - right: 0.75rem !important; + left: 0.75rem !important; bottom: 44px !important; background: white !important; border: 1px solid rgba(49, 51, 63, 0.25) !important; diff --git a/src/gui/pages/1_Deduplicator.py b/src/gui/pages/1_Deduplicator.py index 7c7a978..1a75baa 100644 --- a/src/gui/pages/1_Deduplicator.py +++ b/src/gui/pages/1_Deduplicator.py @@ -402,17 +402,6 @@ else: st.info("Upload a file to get started.") -# --------------------------------------------------------------------------- -# Footer -# --------------------------------------------------------------------------- - - -st.divider() -st.caption( - "Runs locally. Your data never leaves this computer. " - "| DataTools v3.0" -) - # --------------------------------------------------------------------------- # Post-run auto-scroll # --------------------------------------------------------------------------- diff --git a/src/gui/pages/2_Text_Cleaner.py b/src/gui/pages/2_Text_Cleaner.py index de84834..a3248cb 100644 --- a/src/gui/pages/2_Text_Cleaner.py +++ b/src/gui/pages/2_Text_Cleaner.py @@ -384,8 +384,6 @@ with dl_c: ) -st.divider() -st.caption("Runs locally. Your data never leaves this computer. | DataTools v3.0") # --------------------------------------------------------------------------- # Post-run auto-scroll diff --git a/src/gui/pages/3_Format_Standardizer.py b/src/gui/pages/3_Format_Standardizer.py index 71a7c47..eb2960c 100644 --- a/src/gui/pages/3_Format_Standardizer.py +++ b/src/gui/pages/3_Format_Standardizer.py @@ -654,8 +654,6 @@ with dl_c: ) -st.divider() -st.caption("Runs locally. Your data never leaves this computer. | DataTools v3.0") # --------------------------------------------------------------------------- # Post-run auto-scroll diff --git a/src/gui/pages/4_Missing_Values.py b/src/gui/pages/4_Missing_Values.py index 544dea6..ba7dd06 100644 --- a/src/gui/pages/4_Missing_Values.py +++ b/src/gui/pages/4_Missing_Values.py @@ -417,8 +417,6 @@ with dl_c: ) -st.divider() -st.caption("Runs locally. Your data never leaves this computer. | DataTools v3.0") # --------------------------------------------------------------------------- # Post-run auto-scroll diff --git a/src/gui/pages/5_Column_Mapper.py b/src/gui/pages/5_Column_Mapper.py index 80cfed8..20c9412 100644 --- a/src/gui/pages/5_Column_Mapper.py +++ b/src/gui/pages/5_Column_Mapper.py @@ -461,9 +461,6 @@ with dl_c: ) -st.divider() -st.caption("Runs locally. Your data never leaves this computer. | DataTools v3.0") - # --------------------------------------------------------------------------- # Post-run auto-scroll # --------------------------------------------------------------------------- diff --git a/src/gui/pages/6_Outlier_Detector.py b/src/gui/pages/6_Outlier_Detector.py index 2d11209..6949fb4 100644 --- a/src/gui/pages/6_Outlier_Detector.py +++ b/src/gui/pages/6_Outlier_Detector.py @@ -97,14 +97,3 @@ st.selectbox("Action", ["Flag only (add column)", "Remove outlier rows", "Cap / st.divider() st.button("Detect Outliers", type="primary", use_container_width=True, disabled=True) -# --------------------------------------------------------------------------- -# Footer -# --------------------------------------------------------------------------- - - -st.divider() -st.caption( - "Runs locally. Your data never leaves this computer. " - "| DataTools v3.0" -) - diff --git a/src/gui/pages/7_Multi_File_Merger.py b/src/gui/pages/7_Multi_File_Merger.py index 3e95f7b..fc6594f 100644 --- a/src/gui/pages/7_Multi_File_Merger.py +++ b/src/gui/pages/7_Multi_File_Merger.py @@ -95,14 +95,3 @@ st.checkbox("Add source filename column", value=True, disabled=True) st.divider() st.button("Merge Files", type="primary", use_container_width=True, disabled=True) -# --------------------------------------------------------------------------- -# Footer -# --------------------------------------------------------------------------- - - -st.divider() -st.caption( - "Runs locally. Your data never leaves this computer. " - "| DataTools v3.0" -) - diff --git a/src/gui/pages/8_Validator_Reporter.py b/src/gui/pages/8_Validator_Reporter.py index 6bff16e..2b80502 100644 --- a/src/gui/pages/8_Validator_Reporter.py +++ b/src/gui/pages/8_Validator_Reporter.py @@ -102,14 +102,3 @@ st.selectbox("Output format", ["Excel (flagged rows)", "PDF summary", "Both"], d st.divider() st.button("Validate & Generate Report", type="primary", use_container_width=True, disabled=True) -# --------------------------------------------------------------------------- -# Footer -# --------------------------------------------------------------------------- - - -st.divider() -st.caption( - "Runs locally. Your data never leaves this computer. " - "| DataTools v3.0" -) - diff --git a/src/gui/pages/9_Pipeline_Runner.py b/src/gui/pages/9_Pipeline_Runner.py index d5e1c49..09a87d5 100644 --- a/src/gui/pages/9_Pipeline_Runner.py +++ b/src/gui/pages/9_Pipeline_Runner.py @@ -419,9 +419,6 @@ with dl_c: ) -st.divider() -st.caption("Runs locally. Your data never leaves this computer. | DataTools v3.0") - # --------------------------------------------------------------------------- # Post-run auto-scroll # ---------------------------------------------------------------------------