From 502a72cd46193662cf35c2a783435311810ca7e6 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 16 May 2026 20:38:01 +0000 Subject: [PATCH] =?UTF-8?q?feat(nav):=20=E2=86=90=20Back=20to=20Home=20lin?= =?UTF-8?q?k=20on=20every=20tool=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Multi-file workflow: a user uploads several files on Home, clicks "Open " on one file's findings, lands on a tool page. The sidebar lets them get back to Home, but a top-of-page back affordance is more discoverable and keeps the hand in the same screen region as the upload list they're working through. - New ``back_to_home_link()`` helper in components/_legacy.py renders a secondary button that calls ``st.switch_page("app.py")`` — under ``st.navigation`` that routes to the default (Home) page. - Wired into every tool page (1-9) directly after ``hide_streamlit_chrome()`` and BEFORE the license gate so a Lite user who lands on a locked tool can navigate away without paying. - New i18n key ``nav.back_to_home`` ("← Back to Home" / "← Volver al inicio") in en/es packs. 2008 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/gui/components/__init__.py | 1 + src/gui/components/_legacy.py | 19 +++++++++++++++++++ src/gui/pages/1_Deduplicator.py | 2 ++ 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 | 2 ++ src/gui/pages/6_Outlier_Detector.py | 2 ++ src/gui/pages/7_Multi_File_Merger.py | 2 ++ src/gui/pages/8_Validator_Reporter.py | 2 ++ src/gui/pages/9_Pipeline_Runner.py | 2 ++ src/i18n/packs/en.json | 3 ++- src/i18n/packs/es.json | 3 ++- 13 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/gui/components/__init__.py b/src/gui/components/__init__.py index cc64258..2c36695 100644 --- a/src/gui/components/__init__.py +++ b/src/gui/components/__init__.py @@ -46,6 +46,7 @@ from .activation import ( # noqa: F401 re-exported __all__ = [ # Shared chrome / pickup + "back_to_home_link", "hide_streamlit_chrome", "shutdown_app", "pickup_or_upload", diff --git a/src/gui/components/_legacy.py b/src/gui/components/_legacy.py index 7f75775..9a9bcb5 100644 --- a/src/gui/components/_legacy.py +++ b/src/gui/components/_legacy.py @@ -187,6 +187,25 @@ def _farewell_script() -> str: ) +def back_to_home_link(*, key: str = "_back_to_home_link") -> None: + """Render a small "← Back to Home" affordance near the top of a tool page. + + Tool pages reached from the home findings panel benefit from an + explicit return-to-home control so a user working through findings + on multiple uploaded files can hop between files without hunting + through the sidebar. + + Implementation note: ``st.switch_page("app.py")`` routes back to the + entry script which, under ``st.navigation``, lands on the default + page (Home). Streamlit's button is used (rather than ``st.page_link``) + because the entry script is a navigation manager, not a registered + Page object, and ``page_link`` to ``app.py`` renders inconsistently + across Streamlit minor versions. + """ + if st.button(_t("nav.back_to_home"), key=key, type="secondary"): + st.switch_page("app.py") + + def shutdown_app() -> None: """Terminate the Streamlit server immediately, no confirm. diff --git a/src/gui/pages/1_Deduplicator.py b/src/gui/pages/1_Deduplicator.py index 2198e75..bbadd60 100644 --- a/src/gui/pages/1_Deduplicator.py +++ b/src/gui/pages/1_Deduplicator.py @@ -18,6 +18,7 @@ from src.core.dedup import deduplicate, DeduplicationResult from src.core.io import read_file, list_sheets, detect_encoding, detect_delimiter from src.gui.components import ( apply_review_decisions, + back_to_home_link, config_panel, hide_streamlit_chrome, match_group_card, @@ -28,6 +29,7 @@ from src.gui.components import ( from src.license import FeatureFlag hide_streamlit_chrome() +back_to_home_link() require_feature_or_render_upgrade(FeatureFlag.DEDUPLICATOR) # --------------------------------------------------------------------------- diff --git a/src/gui/pages/2_Text_Cleaner.py b/src/gui/pages/2_Text_Cleaner.py index bcf7704..c4315a0 100644 --- a/src/gui/pages/2_Text_Cleaner.py +++ b/src/gui/pages/2_Text_Cleaner.py @@ -15,6 +15,7 @@ if str(_project_root) not in sys.path: sys.path.insert(0, str(_project_root)) from src.gui.components import ( + back_to_home_link, hide_streamlit_chrome, pickup_or_upload, render_hidden_aware_preview, @@ -30,6 +31,7 @@ from src.core.text_clean import ( ) hide_streamlit_chrome() +back_to_home_link() require_feature_or_render_upgrade(FeatureFlag.TEXT_CLEANER) diff --git a/src/gui/pages/3_Format_Standardizer.py b/src/gui/pages/3_Format_Standardizer.py index 1406cd6..bad6fb9 100644 --- a/src/gui/pages/3_Format_Standardizer.py +++ b/src/gui/pages/3_Format_Standardizer.py @@ -15,6 +15,7 @@ if str(_project_root) not in sys.path: sys.path.insert(0, str(_project_root)) from src.gui.components import ( + back_to_home_link, hide_streamlit_chrome, pickup_or_upload, require_feature_or_render_upgrade, @@ -28,6 +29,7 @@ from src.core.format_standardize import ( from src.license import FeatureFlag hide_streamlit_chrome() +back_to_home_link() require_feature_or_render_upgrade(FeatureFlag.FORMAT_STANDARDIZER) diff --git a/src/gui/pages/4_Missing_Values.py b/src/gui/pages/4_Missing_Values.py index 62d07f6..c49ed7a 100644 --- a/src/gui/pages/4_Missing_Values.py +++ b/src/gui/pages/4_Missing_Values.py @@ -15,6 +15,7 @@ if str(_project_root) not in sys.path: sys.path.insert(0, str(_project_root)) from src.gui.components import ( + back_to_home_link, hide_streamlit_chrome, pickup_or_upload, require_feature_or_render_upgrade, @@ -29,6 +30,7 @@ from src.core.missing import ( from src.license import FeatureFlag hide_streamlit_chrome() +back_to_home_link() require_feature_or_render_upgrade(FeatureFlag.MISSING_HANDLER) diff --git a/src/gui/pages/5_Column_Mapper.py b/src/gui/pages/5_Column_Mapper.py index 6146623..84ca1e4 100644 --- a/src/gui/pages/5_Column_Mapper.py +++ b/src/gui/pages/5_Column_Mapper.py @@ -15,6 +15,7 @@ if str(_project_root) not in sys.path: sys.path.insert(0, str(_project_root)) from src.gui.components import ( + back_to_home_link, hide_streamlit_chrome, pickup_or_upload, require_feature_or_render_upgrade, @@ -30,6 +31,7 @@ from src.core.column_mapper import ( from src.license import FeatureFlag hide_streamlit_chrome() +back_to_home_link() require_feature_or_render_upgrade(FeatureFlag.COLUMN_MAPPER) diff --git a/src/gui/pages/6_Outlier_Detector.py b/src/gui/pages/6_Outlier_Detector.py index 19455d3..06897e4 100644 --- a/src/gui/pages/6_Outlier_Detector.py +++ b/src/gui/pages/6_Outlier_Detector.py @@ -12,12 +12,14 @@ if str(_project_root) not in sys.path: sys.path.insert(0, str(_project_root)) from src.gui.components import ( + back_to_home_link, hide_streamlit_chrome, require_feature_or_render_upgrade, ) from src.license import FeatureFlag hide_streamlit_chrome() +back_to_home_link() require_feature_or_render_upgrade(FeatureFlag.OUTLIER_DETECTOR) # --------------------------------------------------------------------------- diff --git a/src/gui/pages/7_Multi_File_Merger.py b/src/gui/pages/7_Multi_File_Merger.py index 0c7ce2f..f957fac 100644 --- a/src/gui/pages/7_Multi_File_Merger.py +++ b/src/gui/pages/7_Multi_File_Merger.py @@ -12,12 +12,14 @@ if str(_project_root) not in sys.path: sys.path.insert(0, str(_project_root)) from src.gui.components import ( + back_to_home_link, hide_streamlit_chrome, require_feature_or_render_upgrade, ) from src.license import FeatureFlag hide_streamlit_chrome() +back_to_home_link() require_feature_or_render_upgrade(FeatureFlag.MULTI_FILE_MERGER) # --------------------------------------------------------------------------- diff --git a/src/gui/pages/8_Validator_Reporter.py b/src/gui/pages/8_Validator_Reporter.py index 7372b7a..929866a 100644 --- a/src/gui/pages/8_Validator_Reporter.py +++ b/src/gui/pages/8_Validator_Reporter.py @@ -12,12 +12,14 @@ if str(_project_root) not in sys.path: sys.path.insert(0, str(_project_root)) from src.gui.components import ( + back_to_home_link, hide_streamlit_chrome, require_feature_or_render_upgrade, ) from src.license import FeatureFlag hide_streamlit_chrome() +back_to_home_link() require_feature_or_render_upgrade(FeatureFlag.VALIDATOR_REPORTER) # --------------------------------------------------------------------------- diff --git a/src/gui/pages/9_Pipeline_Runner.py b/src/gui/pages/9_Pipeline_Runner.py index 1ca6b20..c260d0c 100644 --- a/src/gui/pages/9_Pipeline_Runner.py +++ b/src/gui/pages/9_Pipeline_Runner.py @@ -15,6 +15,7 @@ if str(_project_root) not in sys.path: sys.path.insert(0, str(_project_root)) from src.gui.components import ( + back_to_home_link, hide_streamlit_chrome, pickup_or_upload, require_feature_or_render_upgrade, @@ -31,6 +32,7 @@ from src.core.pipeline import ( from src.license import FeatureFlag hide_streamlit_chrome() +back_to_home_link() require_feature_or_render_upgrade(FeatureFlag.PIPELINE_RUNNER) diff --git a/src/i18n/packs/en.json b/src/i18n/packs/en.json index 81f5d25..4ac853d 100644 --- a/src/i18n/packs/en.json +++ b/src/i18n/packs/en.json @@ -149,6 +149,7 @@ "section_account": "Account", "activate_title": "Activate", "close_title": "Close", - "section_close": "Close" + "section_close": "Close", + "back_to_home": "← Back to Home" } } diff --git a/src/i18n/packs/es.json b/src/i18n/packs/es.json index a62f4c6..75a20c6 100644 --- a/src/i18n/packs/es.json +++ b/src/i18n/packs/es.json @@ -149,6 +149,7 @@ "section_account": "Cuenta", "activate_title": "Activar", "close_title": "Cerrar", - "section_close": "Cerrar" + "section_close": "Cerrar", + "back_to_home": "← Volver al inicio" } }