diff --git a/backend/bot/app/web/webapp/assets.py b/backend/bot/app/web/webapp/assets.py index 29d7098..80f3c8c 100644 --- a/backend/bot/app/web/webapp/assets.py +++ b/backend/bot/app/web/webapp/assets.py @@ -1474,7 +1474,7 @@ def _resolve_hashed_js_asset_name(*, kind: str, base_name: str) -> str: if minified_assets: minified_assets.sort(reverse=True) return _set_cached_asset_name(kind, minified_assets[0][1]) - return _set_cached_asset_name(kind, f"{base_name}.js") + return _set_cached_asset_name(kind, _stable_asset_name_with_version(f"{base_name}.js")) def _resolve_webapp_css_asset_name() -> str: @@ -1506,7 +1506,19 @@ def _resolve_hashed_css_asset_name(*, kind: str, base_name: str) -> str: if hashed_assets: hashed_assets.sort(reverse=True) return _set_cached_asset_name(kind, hashed_assets[0][1]) - return _set_cached_asset_name(kind, f"{base_name}.css") + return _set_cached_asset_name(kind, _stable_asset_name_with_version(f"{base_name}.css")) + + +def _stable_asset_name_with_version(filename: str) -> str: + path = ASSET_DIR / filename + try: + stat = path.stat() + except OSError: + return filename + + raw_version = f"{filename}:{int(stat.st_mtime_ns)}:{int(stat.st_size)}" + version = hashlib.sha256(raw_version.encode("utf-8")).hexdigest()[:8] + return f"{filename}?v={version}" def _get_cached_asset_name(kind: str) -> Optional[str]: diff --git a/frontend/src/styles/webapp.css b/frontend/src/styles/webapp.css index fd0d5fa..6805cdf 100644 --- a/frontend/src/styles/webapp.css +++ b/frontend/src/styles/webapp.css @@ -2154,8 +2154,11 @@ a { display: grid; grid-template-columns: repeat(var(--bottom-nav-visible-items, 3), minmax(0, 1fr)); grid-auto-flow: column; + grid-auto-columns: minmax(0, 1fr); + grid-auto-rows: 1fr; + height: var(--bottom-nav-height); gap: 2px; - min-height: 64px; + min-height: var(--bottom-nav-height); overflow: hidden; border: 1px solid var(--border); border-radius: var(--radius); @@ -2174,7 +2177,11 @@ a { .bottom-nav button { display: grid; + grid-template-rows: 22px minmax(0, 12px); + width: 100%; + height: 100%; min-width: 0; + min-height: 0; place-items: center; align-content: center; gap: 4px; @@ -2185,6 +2192,13 @@ a { font-weight: 800; overflow: hidden; padding: 6px 2px; + line-height: 1; +} + +.bottom-nav button > svg { + width: 21px; + height: 21px; + min-width: 0; } .bottom-nav button.active { @@ -2193,10 +2207,12 @@ a { .bottom-nav .bottom-nav-label { display: block; + width: 100%; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + font-size: 10px; line-height: 1.1; } @@ -2204,12 +2220,40 @@ a { display: none !important; } +@media (max-width: 460px) { + .bottom-nav.bottom-nav-many { + height: 58px; + min-height: 58px; + } + + .bottom-nav.bottom-nav-many button { + grid-template-rows: 1fr; + gap: 0; + padding: 0 2px; + } + + .bottom-nav.bottom-nav-many .bottom-nav-label { + display: none; + } + + .bottom-nav.bottom-nav-many .nav-attention-dot { + top: 8px; + right: calc(50% - 18px); + } + + .bottom-nav.bottom-nav-many .nav-badge-floating { + top: 6px; + right: calc(50% - 24px); + } +} + @media (max-width: 380px) { .bottom-nav.bottom-nav-many { min-height: 58px; } .bottom-nav.bottom-nav-many button { + grid-template-rows: 1fr; gap: 0; padding: 0 2px; } @@ -2231,10 +2275,12 @@ a { @media (max-width: 360px) { .bottom-nav { + height: 56px; min-height: 56px; } .bottom-nav button { + grid-template-rows: 1fr; gap: 0; padding: 0 2px; } @@ -3149,6 +3195,7 @@ a { display: grid !important; grid-template-columns: 22px 1fr !important; grid-template-rows: auto !important; + height: auto !important; align-items: center !important; justify-items: start !important; gap: 12px !important; diff --git a/tests/test_webapp_assets.py b/tests/test_webapp_assets.py index c4a3b62..d147192 100644 --- a/tests/test_webapp_assets.py +++ b/tests/test_webapp_assets.py @@ -335,6 +335,15 @@ class WebAppAssetTests(unittest.IsolatedAsyncioTestCase): self.assertIn("