diff --git a/src/gui/components/_legacy.py b/src/gui/components/_legacy.py
index 69f79ee..d345e6b 100644
--- a/src/gui/components/_legacy.py
+++ b/src/gui/components/_legacy.py
@@ -518,29 +518,215 @@ html_download_button = local_download_button
def render_sticky_footer() -> None:
- """No-op shim.
+ """Slim fixed-position footer with Close and Help controls.
- The previous implementation injected a CSS-positioned ````
- footer into the page body. It looked nice (always-visible bar at
- the viewport bottom) but the click was a FULL-page navigation,
- which on the user's Streamlit build doesn't preserve
- ``st.session_state`` — uploads disappeared after every Back to
- Home click. Soft navigation via ``st.switch_page`` requires a
- real Streamlit button widget, which we can't reliably
- CSS-position to the viewport bottom because Streamlit owns the
- widget's DOM container and remounts it on every rerun.
+ Mounted as a direct child of ```` via a component-iframe so
+ it lives outside every Streamlit container — required because
+ ``.stApp`` carries ``zoom: 0.85`` and Streamlit's content
+ columns add padding/positioning context that would otherwise
+ distort or clip the bar.
- So the sticky footer is retired. Each tool page renders the
- real Streamlit-button version of ``back_to_home_link`` near the
- top AND near the bottom (the two-instance pattern from before
- the sticky-footer attempt), so the button is visible at both
- ends of the page without ever risking state loss.
+ Close is a full-page ```` link to the Close
+ page, which runs ``shutdown_app`` on render. State loss is fine
+ here — the process is terminating. (This was the reason the
+ Back-to-Home variant of this footer was retired; that case
+ needed a soft nav widget. Close does not.)
- Kept as a callable so existing tool-page imports + call sites
- don't have to be touched in this commit — they all resolve to
- this no-op.
+ Help is pure UI: clicking toggles a small overlay panel
+ containing the version and support email — no navigation, so
+ no state loss.
"""
- return
+ import html as _html
+ import json as _json
+
+ from src import __version__
+
+ close_label = _html.escape(_t("footer.close"))
+ help_label = _html.escape(_t("footer.help"))
+ help_title = _html.escape(_t("footer.help_title"))
+ help_version = _html.escape(
+ _t("footer.help_version").format(version=__version__)
+ )
+ support_email = "support@datatools.app"
+ help_support = _html.escape(
+ _t("footer.help_support").format(email=support_email)
+ )
+ help_dismiss = _html.escape(_t("footer.help_dismiss"))
+
+ st.markdown(
+ """
+
+""",
+ unsafe_allow_html=True,
+ )
+
+ from streamlit.components.v1 import html as _components_html
+ _components_html(
+ f"""
+
+""",
+ height=0,
+ )
def _render_sticky_footer_DISABLED() -> None:
diff --git a/src/gui/pages/1_Deduplicator.py b/src/gui/pages/1_Deduplicator.py
index 4875403..7c7a978 100644
--- a/src/gui/pages/1_Deduplicator.py
+++ b/src/gui/pages/1_Deduplicator.py
@@ -406,7 +406,6 @@ else:
# Footer
# ---------------------------------------------------------------------------
-back_to_home_link(key="_back_to_home_link_bottom")
st.divider()
st.caption(
diff --git a/src/gui/pages/2_Text_Cleaner.py b/src/gui/pages/2_Text_Cleaner.py
index 27fbd9e..de84834 100644
--- a/src/gui/pages/2_Text_Cleaner.py
+++ b/src/gui/pages/2_Text_Cleaner.py
@@ -383,7 +383,6 @@ with dl_c:
mime="application/json",
)
-back_to_home_link(key="_back_to_home_link_bottom")
st.divider()
st.caption("Runs locally. Your data never leaves this computer. | DataTools v3.0")
diff --git a/src/gui/pages/3_Format_Standardizer.py b/src/gui/pages/3_Format_Standardizer.py
index 00bd70c..71a7c47 100644
--- a/src/gui/pages/3_Format_Standardizer.py
+++ b/src/gui/pages/3_Format_Standardizer.py
@@ -653,7 +653,6 @@ with dl_c:
mime="application/json",
)
-back_to_home_link(key="_back_to_home_link_bottom")
st.divider()
st.caption("Runs locally. Your data never leaves this computer. | DataTools v3.0")
diff --git a/src/gui/pages/4_Missing_Values.py b/src/gui/pages/4_Missing_Values.py
index 9f912e5..544dea6 100644
--- a/src/gui/pages/4_Missing_Values.py
+++ b/src/gui/pages/4_Missing_Values.py
@@ -416,7 +416,6 @@ with dl_c:
mime="application/json",
)
-back_to_home_link(key="_back_to_home_link_bottom")
st.divider()
st.caption("Runs locally. Your data never leaves this computer. | DataTools v3.0")
diff --git a/src/gui/pages/5_Column_Mapper.py b/src/gui/pages/5_Column_Mapper.py
index dc082a5..80cfed8 100644
--- a/src/gui/pages/5_Column_Mapper.py
+++ b/src/gui/pages/5_Column_Mapper.py
@@ -460,7 +460,6 @@ with dl_c:
mime="application/json",
)
-back_to_home_link(key="_back_to_home_link_bottom")
st.divider()
st.caption("Runs locally. Your data never leaves this computer. | DataTools v3.0")
diff --git a/src/gui/pages/6_Outlier_Detector.py b/src/gui/pages/6_Outlier_Detector.py
index bc5ee09..2d11209 100644
--- a/src/gui/pages/6_Outlier_Detector.py
+++ b/src/gui/pages/6_Outlier_Detector.py
@@ -101,7 +101,6 @@ st.button("Detect Outliers", type="primary", use_container_width=True, disabled=
# Footer
# ---------------------------------------------------------------------------
-back_to_home_link(key="_back_to_home_link_bottom")
st.divider()
st.caption(
diff --git a/src/gui/pages/7_Multi_File_Merger.py b/src/gui/pages/7_Multi_File_Merger.py
index 2aa838e..3e95f7b 100644
--- a/src/gui/pages/7_Multi_File_Merger.py
+++ b/src/gui/pages/7_Multi_File_Merger.py
@@ -99,7 +99,6 @@ st.button("Merge Files", type="primary", use_container_width=True, disabled=True
# Footer
# ---------------------------------------------------------------------------
-back_to_home_link(key="_back_to_home_link_bottom")
st.divider()
st.caption(
diff --git a/src/gui/pages/8_Validator_Reporter.py b/src/gui/pages/8_Validator_Reporter.py
index c1a8f20..6bff16e 100644
--- a/src/gui/pages/8_Validator_Reporter.py
+++ b/src/gui/pages/8_Validator_Reporter.py
@@ -106,7 +106,6 @@ st.button("Validate & Generate Report", type="primary", use_container_width=True
# Footer
# ---------------------------------------------------------------------------
-back_to_home_link(key="_back_to_home_link_bottom")
st.divider()
st.caption(
diff --git a/src/gui/pages/9_Pipeline_Runner.py b/src/gui/pages/9_Pipeline_Runner.py
index b4a26c4..d5e1c49 100644
--- a/src/gui/pages/9_Pipeline_Runner.py
+++ b/src/gui/pages/9_Pipeline_Runner.py
@@ -418,7 +418,6 @@ with dl_c:
mime="application/json",
)
-back_to_home_link(key="_back_to_home_link_bottom")
st.divider()
st.caption("Runs locally. Your data never leaves this computer. | DataTools v3.0")
diff --git a/src/i18n/packs/en.json b/src/i18n/packs/en.json
index c0a15e8..12701b0 100644
--- a/src/i18n/packs/en.json
+++ b/src/i18n/packs/en.json
@@ -171,5 +171,13 @@
"close_title": "Close",
"section_close": "Close",
"back_to_home": "← Back to Home"
+ },
+ "footer": {
+ "close": "Close",
+ "help": "Help",
+ "help_title": "DataTools",
+ "help_version": "Version {version}",
+ "help_support": "Support: {email}",
+ "help_dismiss": "Close"
}
}
diff --git a/src/i18n/packs/es.json b/src/i18n/packs/es.json
index 1b41a7b..90266a1 100644
--- a/src/i18n/packs/es.json
+++ b/src/i18n/packs/es.json
@@ -171,5 +171,13 @@
"close_title": "Cerrar",
"section_close": "Cerrar",
"back_to_home": "← Volver al inicio"
+ },
+ "footer": {
+ "close": "Cerrar",
+ "help": "Ayuda",
+ "help_title": "DataTools",
+ "help_version": "Versión {version}",
+ "help_support": "Soporte: {email}",
+ "help_dismiss": "Cerrar"
}
}