feat(nav): ← Back to Home link on every tool page
Multi-file workflow: a user uploads several files on Home, clicks
"Open <Tool>" 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) <noreply@anthropic.com>
This commit is contained in:
@@ -46,6 +46,7 @@ from .activation import ( # noqa: F401 re-exported
|
|||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
# Shared chrome / pickup
|
# Shared chrome / pickup
|
||||||
|
"back_to_home_link",
|
||||||
"hide_streamlit_chrome",
|
"hide_streamlit_chrome",
|
||||||
"shutdown_app",
|
"shutdown_app",
|
||||||
"pickup_or_upload",
|
"pickup_or_upload",
|
||||||
|
|||||||
@@ -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:
|
def shutdown_app() -> None:
|
||||||
"""Terminate the Streamlit server immediately, no confirm.
|
"""Terminate the Streamlit server immediately, no confirm.
|
||||||
|
|
||||||
|
|||||||
@@ -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.core.io import read_file, list_sheets, detect_encoding, detect_delimiter
|
||||||
from src.gui.components import (
|
from src.gui.components import (
|
||||||
apply_review_decisions,
|
apply_review_decisions,
|
||||||
|
back_to_home_link,
|
||||||
config_panel,
|
config_panel,
|
||||||
hide_streamlit_chrome,
|
hide_streamlit_chrome,
|
||||||
match_group_card,
|
match_group_card,
|
||||||
@@ -28,6 +29,7 @@ from src.gui.components import (
|
|||||||
from src.license import FeatureFlag
|
from src.license import FeatureFlag
|
||||||
|
|
||||||
hide_streamlit_chrome()
|
hide_streamlit_chrome()
|
||||||
|
back_to_home_link()
|
||||||
require_feature_or_render_upgrade(FeatureFlag.DEDUPLICATOR)
|
require_feature_or_render_upgrade(FeatureFlag.DEDUPLICATOR)
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ if str(_project_root) not in sys.path:
|
|||||||
sys.path.insert(0, str(_project_root))
|
sys.path.insert(0, str(_project_root))
|
||||||
|
|
||||||
from src.gui.components import (
|
from src.gui.components import (
|
||||||
|
back_to_home_link,
|
||||||
hide_streamlit_chrome,
|
hide_streamlit_chrome,
|
||||||
pickup_or_upload,
|
pickup_or_upload,
|
||||||
render_hidden_aware_preview,
|
render_hidden_aware_preview,
|
||||||
@@ -30,6 +31,7 @@ from src.core.text_clean import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
hide_streamlit_chrome()
|
hide_streamlit_chrome()
|
||||||
|
back_to_home_link()
|
||||||
require_feature_or_render_upgrade(FeatureFlag.TEXT_CLEANER)
|
require_feature_or_render_upgrade(FeatureFlag.TEXT_CLEANER)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ if str(_project_root) not in sys.path:
|
|||||||
sys.path.insert(0, str(_project_root))
|
sys.path.insert(0, str(_project_root))
|
||||||
|
|
||||||
from src.gui.components import (
|
from src.gui.components import (
|
||||||
|
back_to_home_link,
|
||||||
hide_streamlit_chrome,
|
hide_streamlit_chrome,
|
||||||
pickup_or_upload,
|
pickup_or_upload,
|
||||||
require_feature_or_render_upgrade,
|
require_feature_or_render_upgrade,
|
||||||
@@ -28,6 +29,7 @@ from src.core.format_standardize import (
|
|||||||
from src.license import FeatureFlag
|
from src.license import FeatureFlag
|
||||||
|
|
||||||
hide_streamlit_chrome()
|
hide_streamlit_chrome()
|
||||||
|
back_to_home_link()
|
||||||
require_feature_or_render_upgrade(FeatureFlag.FORMAT_STANDARDIZER)
|
require_feature_or_render_upgrade(FeatureFlag.FORMAT_STANDARDIZER)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ if str(_project_root) not in sys.path:
|
|||||||
sys.path.insert(0, str(_project_root))
|
sys.path.insert(0, str(_project_root))
|
||||||
|
|
||||||
from src.gui.components import (
|
from src.gui.components import (
|
||||||
|
back_to_home_link,
|
||||||
hide_streamlit_chrome,
|
hide_streamlit_chrome,
|
||||||
pickup_or_upload,
|
pickup_or_upload,
|
||||||
require_feature_or_render_upgrade,
|
require_feature_or_render_upgrade,
|
||||||
@@ -29,6 +30,7 @@ from src.core.missing import (
|
|||||||
from src.license import FeatureFlag
|
from src.license import FeatureFlag
|
||||||
|
|
||||||
hide_streamlit_chrome()
|
hide_streamlit_chrome()
|
||||||
|
back_to_home_link()
|
||||||
require_feature_or_render_upgrade(FeatureFlag.MISSING_HANDLER)
|
require_feature_or_render_upgrade(FeatureFlag.MISSING_HANDLER)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ if str(_project_root) not in sys.path:
|
|||||||
sys.path.insert(0, str(_project_root))
|
sys.path.insert(0, str(_project_root))
|
||||||
|
|
||||||
from src.gui.components import (
|
from src.gui.components import (
|
||||||
|
back_to_home_link,
|
||||||
hide_streamlit_chrome,
|
hide_streamlit_chrome,
|
||||||
pickup_or_upload,
|
pickup_or_upload,
|
||||||
require_feature_or_render_upgrade,
|
require_feature_or_render_upgrade,
|
||||||
@@ -30,6 +31,7 @@ from src.core.column_mapper import (
|
|||||||
from src.license import FeatureFlag
|
from src.license import FeatureFlag
|
||||||
|
|
||||||
hide_streamlit_chrome()
|
hide_streamlit_chrome()
|
||||||
|
back_to_home_link()
|
||||||
require_feature_or_render_upgrade(FeatureFlag.COLUMN_MAPPER)
|
require_feature_or_render_upgrade(FeatureFlag.COLUMN_MAPPER)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,12 +12,14 @@ if str(_project_root) not in sys.path:
|
|||||||
sys.path.insert(0, str(_project_root))
|
sys.path.insert(0, str(_project_root))
|
||||||
|
|
||||||
from src.gui.components import (
|
from src.gui.components import (
|
||||||
|
back_to_home_link,
|
||||||
hide_streamlit_chrome,
|
hide_streamlit_chrome,
|
||||||
require_feature_or_render_upgrade,
|
require_feature_or_render_upgrade,
|
||||||
)
|
)
|
||||||
from src.license import FeatureFlag
|
from src.license import FeatureFlag
|
||||||
|
|
||||||
hide_streamlit_chrome()
|
hide_streamlit_chrome()
|
||||||
|
back_to_home_link()
|
||||||
require_feature_or_render_upgrade(FeatureFlag.OUTLIER_DETECTOR)
|
require_feature_or_render_upgrade(FeatureFlag.OUTLIER_DETECTOR)
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -12,12 +12,14 @@ if str(_project_root) not in sys.path:
|
|||||||
sys.path.insert(0, str(_project_root))
|
sys.path.insert(0, str(_project_root))
|
||||||
|
|
||||||
from src.gui.components import (
|
from src.gui.components import (
|
||||||
|
back_to_home_link,
|
||||||
hide_streamlit_chrome,
|
hide_streamlit_chrome,
|
||||||
require_feature_or_render_upgrade,
|
require_feature_or_render_upgrade,
|
||||||
)
|
)
|
||||||
from src.license import FeatureFlag
|
from src.license import FeatureFlag
|
||||||
|
|
||||||
hide_streamlit_chrome()
|
hide_streamlit_chrome()
|
||||||
|
back_to_home_link()
|
||||||
require_feature_or_render_upgrade(FeatureFlag.MULTI_FILE_MERGER)
|
require_feature_or_render_upgrade(FeatureFlag.MULTI_FILE_MERGER)
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -12,12 +12,14 @@ if str(_project_root) not in sys.path:
|
|||||||
sys.path.insert(0, str(_project_root))
|
sys.path.insert(0, str(_project_root))
|
||||||
|
|
||||||
from src.gui.components import (
|
from src.gui.components import (
|
||||||
|
back_to_home_link,
|
||||||
hide_streamlit_chrome,
|
hide_streamlit_chrome,
|
||||||
require_feature_or_render_upgrade,
|
require_feature_or_render_upgrade,
|
||||||
)
|
)
|
||||||
from src.license import FeatureFlag
|
from src.license import FeatureFlag
|
||||||
|
|
||||||
hide_streamlit_chrome()
|
hide_streamlit_chrome()
|
||||||
|
back_to_home_link()
|
||||||
require_feature_or_render_upgrade(FeatureFlag.VALIDATOR_REPORTER)
|
require_feature_or_render_upgrade(FeatureFlag.VALIDATOR_REPORTER)
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ if str(_project_root) not in sys.path:
|
|||||||
sys.path.insert(0, str(_project_root))
|
sys.path.insert(0, str(_project_root))
|
||||||
|
|
||||||
from src.gui.components import (
|
from src.gui.components import (
|
||||||
|
back_to_home_link,
|
||||||
hide_streamlit_chrome,
|
hide_streamlit_chrome,
|
||||||
pickup_or_upload,
|
pickup_or_upload,
|
||||||
require_feature_or_render_upgrade,
|
require_feature_or_render_upgrade,
|
||||||
@@ -31,6 +32,7 @@ from src.core.pipeline import (
|
|||||||
from src.license import FeatureFlag
|
from src.license import FeatureFlag
|
||||||
|
|
||||||
hide_streamlit_chrome()
|
hide_streamlit_chrome()
|
||||||
|
back_to_home_link()
|
||||||
require_feature_or_render_upgrade(FeatureFlag.PIPELINE_RUNNER)
|
require_feature_or_render_upgrade(FeatureFlag.PIPELINE_RUNNER)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -149,6 +149,7 @@
|
|||||||
"section_account": "Account",
|
"section_account": "Account",
|
||||||
"activate_title": "Activate",
|
"activate_title": "Activate",
|
||||||
"close_title": "Close",
|
"close_title": "Close",
|
||||||
"section_close": "Close"
|
"section_close": "Close",
|
||||||
|
"back_to_home": "← Back to Home"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,6 +149,7 @@
|
|||||||
"section_account": "Cuenta",
|
"section_account": "Cuenta",
|
||||||
"activate_title": "Activar",
|
"activate_title": "Activar",
|
||||||
"close_title": "Cerrar",
|
"close_title": "Cerrar",
|
||||||
"section_close": "Cerrar"
|
"section_close": "Cerrar",
|
||||||
|
"back_to_home": "← Volver al inicio"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user