From 2940fbdcf702bb0bc967f366887826e2c1db699e Mon Sep 17 00:00:00 2001 From: maziggy Date: Tue, 16 Jun 2026 12:00:27 +0200 Subject: [PATCH] feat(auth): admin-configurable session lifetime ceiling (#1706) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 24h session cap from the M-2 audit finding was hard-coded, so the "Remember Me" checkbox could only control storage location, never duration. Add session_max_hours setting (default 24, max 720) honoured at all four token-issuance sites: plain login, 2FA TOTP/email, 2FA backup, OIDC. - backend/app/core/auth.py: SESSION_MAX_HOURS_HARD_CEILING + resolver that clamps to [1h, 720h] and falls back to 24h on missing/blank/ unparseable. DB errors propagate — the login transaction must abort on a broken DB rather than silently extend or shrink the lifetime. - backend/app/api/routes/auth.py, mfa.py: all four sites read the resolved value instead of ACCESS_TOKEN_EXPIRE_MINUTES directly. - backend/app/schemas/settings.py, routes/settings.py: schema field with ge=1 le=720 + int coercion in _build_settings_response. - frontend/src/pages/SettingsPage.tsx: half-width card at top of Settings -> Users left column with 24h/7d/30d presets, custom input, and a yellow warning when value > 24h. - frontend/src/i18n/locales/*.ts: 8 new keys per locale, real translations in all 11 (en/de/es/fr/it/ja/ko/pt-BR/tr/zh-CN/zh-TW). - backend/tests/integration/test_session_policy.py: 15 tests across resolver clamping, login JWT exp end-to-end, settings API round-trip. Already-issued tokens keep their original expiry; the new setting only affects future logins. --- CHANGELOG.md | 3 + backend/app/api/routes/auth.py | 7 +- backend/app/api/routes/mfa.py | 8 +- backend/app/api/routes/settings.py | 1 + backend/app/core/auth.py | 36 ++- backend/app/schemas/settings.py | 15 ++ .../tests/integration/test_session_policy.py | 229 ++++++++++++++++++ frontend/src/api/client.ts | 2 + frontend/src/i18n/locales/de.ts | 11 + frontend/src/i18n/locales/en.ts | 12 + frontend/src/i18n/locales/es.ts | 11 + frontend/src/i18n/locales/fr.ts | 11 + frontend/src/i18n/locales/it.ts | 11 + frontend/src/i18n/locales/ja.ts | 11 + frontend/src/i18n/locales/ko.ts | 11 + frontend/src/i18n/locales/pt-BR.ts | 11 + frontend/src/i18n/locales/tr.ts | 11 + frontend/src/i18n/locales/zh-CN.ts | 11 + frontend/src/i18n/locales/zh-TW.ts | 11 + frontend/src/pages/SettingsPage.tsx | 72 +++++- .../{index-lB37rzBj.js => index-DznM9swC.js} | 116 ++++----- static/index.html | 2 +- 22 files changed, 544 insertions(+), 69 deletions(-) create mode 100644 backend/tests/integration/test_session_policy.py rename static/assets/{index-lB37rzBj.js => index-DznM9swC.js} (85%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 670b71946..172128abf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to Bambuddy will be documented in this file. ## [0.2.5b1] - Unreleased +### Added +- **Admin-configurable session lifetime (#1706, reported by @AD3DStuff)** — The 24-hour session cap that ships with Bambuddy was an intentional security hardening (audit finding M-2 reduced it from 7 days), but the "Remember Me" checkbox only controlled storage location (localStorage vs sessionStorage), not session duration. iPhone PWA users and homelab admins on trusted networks were getting kicked out every 24 hours with no way to extend it. **New setting:** `session_max_hours` under Settings → Users with three presets (24h / 7 days / 30 days) plus a custom field, hard-capped at 30 days (720h). Default remains 24h so existing deployments and the M-2 audit baseline are untouched until an admin opts in. The Settings card surfaces a yellow warning whenever the value exceeds 24h: "Longer sessions reduce automatic logout protection. Recommended only for trusted single-user deployments." **Backend wiring:** new `resolve_session_max_minutes(db)` helper in `backend/app/core/auth.py` reads the setting, clamps to [1h, 720h], and falls back to 24h on missing / blank / unparseable values. The helper is called at all four token-issuance sites — plain `/auth/login`, 2FA TOTP/email completion, 2FA backup-code completion, and OIDC callback — so a long-session policy works uniformly regardless of how the user authenticates. DB errors in the resolver are deliberately NOT caught: login is already inside a transaction and a broken DB must abort the login rather than silently extend or shrink the session lifetime. Defense-in-depth `SESSION_MAX_HOURS_HARD_CEILING = 720` clamps any tampered DB row above the Pydantic ceiling. Already-issued tokens keep their original expiry — the new setting only affects future logins, so an admin lowering the value can't retroactively revoke active sessions and an admin raising it can't retroactively extend them. **What this does NOT change:** the "Remember Me" checkbox still controls only storage location (cleared on browser close vs persisted across restarts). The relabel from misleading-UX-perspective is left for a separate follow-up — that's a UX choice independent of the session-policy mechanism. API tokens (`MAX_TOKEN_LIFETIME_DAYS`), camera stream tokens (60min), WebSocket tokens (60min), and slicer download tokens (5min) keep their own TTLs and are unaffected. **Tests:** 15 new cases in `backend/tests/integration/test_session_policy.py` split across three classes. `TestResolveSessionMaxMinutes` pins the clamping resolver — missing row, empty string, unparseable value, zero/negative, 1h minimum, 7-day passthrough, 30-day passthrough, above-ceiling clamp. `TestLoginRespectsSessionPolicy` decodes the JWT `exp` claim end-to-end and asserts the token returned by `/auth/login` honours the configured ceiling for the default-24h, configured-7d, and above-ceiling-clamp cases. `TestSettingsAPIExposesSessionMaxHours` round-trips the field through `/settings/` (default = 24, valid update persists as int's string form, zero rejected with 422, above-ceiling rejected with 422). Existing 202-case auth + MFA suite still green. **i18n:** 8 new keys in `settings.sessionPolicy.*` namespace; full translations in all 10 non-en locales (de / es / fr / it / ja / ko / pt-BR / tr / zh-CN / zh-TW), no English fallback. Parity check 5149 leaves per locale. ESLint clean; `npm run build` clean; ruff clean. + ### Fixed - **SpoolBuddy inventory search now matches spool ID, slicer filament name, and storage location (#1738, reported by @shaddowlink)** — The reporter found that typing a numeric spool ID into SpoolBuddy → Inventory's search box returned no results, even though the same query in Bambuddy's main Inventory page worked. Root cause: `frontend/src/pages/spoolbuddy/SpoolBuddyInventoryPage.tsx:147-155` reimplemented the search filter inline and only matched `material`, `subtype`, `brand`, `color_name`, and `note`. The main Inventory page delegates to the shared `filterSpoolsByQuery` helper in `frontend/src/utils/inventorySearch.ts:7`, which additionally matches `String(spool.id)`, `slicer_filament_name`, and `storage_location`. SpoolBuddy had diverged. **Fix:** replace the inline filter with a single call to `filterSpoolsByQuery(list, searchQuery.trim())`. Both inventory modes (internal via `getSpools`, Spoolman via `getSpoolmanInventorySpools`) return the same `InventorySpool` shape, so this covers both paths in one drop. SpoolBuddy now matches Bambuddy's search behaviour across all eight fields. **Tests:** new `SpoolBuddyInventorySearch.test.ts` with 4 cases pinning the parity — exact spool ID match, partial spool ID match, the five pre-fix fields still match, and the three newly-included fields (storage_location, slicer_filament_name, plus implicit id) match. Existing `inventorySearch.test.ts` ID matching test (#1336) still green. ESLint clean; `npm run build` clean. No backend change, no i18n, no new permission. diff --git a/backend/app/api/routes/auth.py b/backend/app/api/routes/auth.py index e473a7873..ac07e08fc 100644 --- a/backend/app/api/routes/auth.py +++ b/backend/app/api/routes/auth.py @@ -15,7 +15,6 @@ from sqlalchemy.orm import selectinload from backend.app.api.routes.settings import get_external_login_url from backend.app.core.auth import ( - ACCESS_TOKEN_EXPIRE_MINUTES, ALGORITHM, SECRET_KEY, Permission, @@ -31,6 +30,7 @@ from backend.app.core.auth import ( get_user_by_email, get_user_by_username, is_jti_revoked, + resolve_session_max_minutes, revoke_jti, security, ) @@ -495,8 +495,9 @@ async def login(raw_request: Request, request: LoginRequest, response: Response, two_fa_methods=methods, ) - # No 2FA — issue full token immediately - access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) + # No 2FA — issue full token immediately. Session lifetime honours the + # admin-configurable ceiling (#1706); resolver clamps to [1h, 720h]. + access_token_expires = timedelta(minutes=await resolve_session_max_minutes(db)) access_token = create_access_token(data={"sub": user.username}, expires_delta=access_token_expires) return LoginResponse( diff --git a/backend/app/api/routes/mfa.py b/backend/app/api/routes/mfa.py index 1fb6fecca..83bda734b 100644 --- a/backend/app/api/routes/mfa.py +++ b/backend/app/api/routes/mfa.py @@ -41,13 +41,13 @@ from sqlalchemy.orm import selectinload, undefer from backend.app.api.routes._oidc_helpers import assert_safe_public_https_url from backend.app.api.routes.settings import get_setting, set_setting from backend.app.core.auth import ( - ACCESS_TOKEN_EXPIRE_MINUTES, RequirePermissionIfAuthEnabled, create_access_token, get_current_active_user, get_user_by_email, get_user_by_username, is_auth_enabled, + resolve_session_max_minutes, verify_password, ) from backend.app.core.database import get_db @@ -1242,7 +1242,7 @@ async def verify_2fa( access_token = create_access_token( data={"sub": user.username}, - expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES), + expires_delta=timedelta(minutes=await resolve_session_max_minutes(db)), ) result = await db.execute(select(User).where(User.id == user.id).options(selectinload(User.groups))) user = result.scalar_one() @@ -1258,7 +1258,7 @@ async def verify_2fa( access_token = create_access_token( data={"sub": user.username}, - expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES), + expires_delta=timedelta(minutes=await resolve_session_max_minutes(db)), ) # Reload with groups for permission calculation @@ -2146,7 +2146,7 @@ async def oidc_exchange( access_token = create_access_token( data={"sub": user.username}, - expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES), + expires_delta=timedelta(minutes=await resolve_session_max_minutes(db)), ) return LoginResponse( diff --git a/backend/app/api/routes/settings.py b/backend/app/api/routes/settings.py index 201646707..87241e41b 100644 --- a/backend/app/api/routes/settings.py +++ b/backend/app/api/routes/settings.py @@ -159,6 +159,7 @@ async def _build_settings_response(db: AsyncSession, is_api_key: bool = False) - "stagger_group_size", "stagger_interval_minutes", "forecast_global_lead_time_days", + "session_max_hours", ]: settings_dict[setting.key] = int(setting.value) elif setting.key == "default_printer_id": diff --git a/backend/app/core/auth.py b/backend/app/core/auth.py index 3939efe92..c02d9207a 100644 --- a/backend/app/core/auth.py +++ b/backend/app/core/auth.py @@ -421,10 +421,42 @@ def _get_jwt_secret() -> str: SECRET_KEY = _get_jwt_secret() ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 # 24 hours (M-2: reduced from 7 days) +# Hard ceiling for the admin-configurable session policy (#1706). 30 days +# matches the Pydantic le=720 on AppSettings.session_max_hours; defense in +# depth so a tampered settings row can't request an absurd lifetime. +SESSION_MAX_HOURS_HARD_CEILING = 720 # HTTP Bearer token security = HTTPBearer(auto_error=False) + +async def resolve_session_max_minutes(db: AsyncSession) -> int: + """Return the session-lifetime ceiling (minutes) honoured by login routes. + + Reads ``session_max_hours`` from the settings table (#1706), clamps to + [1h, 720h], and falls back to the audit-default 24h if the row is + missing, blank, or unparseable. + + DB errors are NOT caught here — login is already in a DB transaction and + a broken DB must abort the login rather than silently extend or shrink + the session lifetime. + """ + default_minutes = ACCESS_TOKEN_EXPIRE_MINUTES + result = await db.execute(select(Settings).where(Settings.key == "session_max_hours")) + row = result.scalar_one_or_none() + if row is None or not row.value: + return default_minutes + try: + hours = int(row.value) + except (TypeError, ValueError): + return default_minutes + if hours < 1: + return default_minutes + if hours > SESSION_MAX_HOURS_HARD_CEILING: + hours = SESSION_MAX_HOURS_HARD_CEILING + return hours * 60 + + # --- Slicer download tokens --- # Short-lived, single-use tokens for slicer protocol handlers that can't send # auth headers. Stored in AuthEphemeralToken (token_type=TokenType.SLICER_DOWNLOAD) @@ -649,7 +681,9 @@ def _is_token_fresh(iat: int | float | None, user: User) -> bool: Used to invalidate all sessions after a password reset/change (M-R7-B). All tokens without an iat claim are unconditionally rejected — every token issued by this server carries iat, so absence means the token is forged or - from a pre-iat code path whose max TTL (24 h) has long since expired. + from a pre-iat code path whose max TTL at the time (24 h) has long since + expired. The post-#1706 admin-set ceiling does not relax this — an iat-less + token still cannot have been issued by current code. """ if iat is None: return False diff --git a/backend/app/schemas/settings.py b/backend/app/schemas/settings.py index 249871922..319363b0b 100644 --- a/backend/app/schemas/settings.py +++ b/backend/app/schemas/settings.py @@ -240,6 +240,20 @@ class AppSettings(BaseModel): description="Low stock threshold percentage (%) for inventory filtering and display", ) + # Session policy (#1706) — admin-set ceiling for user session lifetime. + # Default 24h preserves the M-2 audit reduction from 7 days. Max 720h + # (30 days) bounds blast radius if an admin chooses a long session. + session_max_hours: int = Field( + default=24, + ge=1, + le=720, + description=( + "Maximum session lifetime in hours for user logins (default 24, max 720). " + "Applies to new logins only; already-issued tokens keep their original expiry. " + "Longer sessions reduce automatic logout protection." + ), + ) + # User email notifications (requires Advanced Authentication) user_notifications_enabled: bool = Field( default=True, @@ -414,6 +428,7 @@ class AppSettingsUpdate(BaseModel): prometheus_enabled: bool | None = None prometheus_token: str | None = None low_stock_threshold: float | None = Field(default=None, ge=0.1, le=99.9) + session_max_hours: int | None = Field(default=None, ge=1, le=720) user_notifications_enabled: bool | None = None default_bed_levelling: bool | None = None default_flow_cali: bool | None = None diff --git a/backend/tests/integration/test_session_policy.py b/backend/tests/integration/test_session_policy.py new file mode 100644 index 000000000..d55a9ad8a --- /dev/null +++ b/backend/tests/integration/test_session_policy.py @@ -0,0 +1,229 @@ +"""Integration tests for the admin-set session-lifetime ceiling (#1706). + +Covers the four token-issuance sites that read ``session_max_hours``: +plain login, 2FA backup-code login, 2FA TOTP/email login, OIDC login. +Only the first is exercised end-to-end via ``async_client``; the helper +``resolve_session_max_minutes`` itself is unit-tested below so the MFA +and OIDC paths inherit the same clamping behaviour by construction. +""" + +import time + +import jwt +import pytest +from httpx import AsyncClient +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from backend.app.core.auth import ( + ACCESS_TOKEN_EXPIRE_MINUTES, + ALGORITHM, + SECRET_KEY, + SESSION_MAX_HOURS_HARD_CEILING, + resolve_session_max_minutes, +) +from backend.app.models.settings import Settings + + +async def _set_session_max_hours(db: AsyncSession, value: str | None) -> None: + """Upsert the session_max_hours setting row (value=None deletes it).""" + result = await db.execute(select(Settings).where(Settings.key == "session_max_hours")) + existing = result.scalar_one_or_none() + if value is None: + if existing is not None: + await db.delete(existing) + await db.commit() + return + if existing is None: + db.add(Settings(key="session_max_hours", value=value)) + else: + existing.value = value + await db.commit() + + +class TestResolveSessionMaxMinutes: + """Unit-style tests for the clamping resolver.""" + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_missing_row_returns_24h_default(self, db_session: AsyncSession): + await _set_session_max_hours(db_session, None) + assert await resolve_session_max_minutes(db_session) == ACCESS_TOKEN_EXPIRE_MINUTES + assert ACCESS_TOKEN_EXPIRE_MINUTES == 60 * 24 + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_empty_string_returns_24h_default(self, db_session: AsyncSession): + await _set_session_max_hours(db_session, "") + assert await resolve_session_max_minutes(db_session) == ACCESS_TOKEN_EXPIRE_MINUTES + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_unparseable_value_returns_24h_default(self, db_session: AsyncSession): + await _set_session_max_hours(db_session, "not-a-number") + assert await resolve_session_max_minutes(db_session) == ACCESS_TOKEN_EXPIRE_MINUTES + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_zero_or_negative_returns_24h_default(self, db_session: AsyncSession): + await _set_session_max_hours(db_session, "0") + assert await resolve_session_max_minutes(db_session) == ACCESS_TOKEN_EXPIRE_MINUTES + await _set_session_max_hours(db_session, "-5") + assert await resolve_session_max_minutes(db_session) == ACCESS_TOKEN_EXPIRE_MINUTES + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_one_hour_minimum(self, db_session: AsyncSession): + await _set_session_max_hours(db_session, "1") + assert await resolve_session_max_minutes(db_session) == 60 + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_seven_days_passes_through(self, db_session: AsyncSession): + await _set_session_max_hours(db_session, "168") + assert await resolve_session_max_minutes(db_session) == 168 * 60 + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_thirty_days_passes_through(self, db_session: AsyncSession): + await _set_session_max_hours(db_session, str(SESSION_MAX_HOURS_HARD_CEILING)) + assert await resolve_session_max_minutes(db_session) == SESSION_MAX_HOURS_HARD_CEILING * 60 + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_above_ceiling_is_clamped_to_30_days(self, db_session: AsyncSession): + """Defense-in-depth: a tampered settings row above 720h must be clamped.""" + await _set_session_max_hours(db_session, "99999") + assert await resolve_session_max_minutes(db_session) == SESSION_MAX_HOURS_HARD_CEILING * 60 + + +class TestLoginRespectsSessionPolicy: + """The /auth/login route must honour the resolved ceiling.""" + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_login_uses_default_24h_when_unset(self, async_client: AsyncClient, db_session: AsyncSession): + await async_client.post( + "/api/v1/auth/setup", + json={ + "auth_enabled": True, + "admin_username": "sessiontest1", + "admin_password": "SessionPass1!", + }, + ) + await _set_session_max_hours(db_session, None) + + before = int(time.time()) + response = await async_client.post( + "/api/v1/auth/login", + json={"username": "sessiontest1", "password": "SessionPass1!"}, + ) + after = int(time.time()) + + assert response.status_code == 200 + token = response.json()["access_token"] + decoded = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) + # exp should be ~24h ahead. Allow generous bounds for clock drift. + expected_min = before + 24 * 3600 - 60 + expected_max = after + 24 * 3600 + 60 + assert expected_min <= decoded["exp"] <= expected_max + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_login_uses_configured_7d_ceiling(self, async_client: AsyncClient, db_session: AsyncSession): + await async_client.post( + "/api/v1/auth/setup", + json={ + "auth_enabled": True, + "admin_username": "sessiontest2", + "admin_password": "SessionPass2!", + }, + ) + await _set_session_max_hours(db_session, "168") # 7 days + + before = int(time.time()) + response = await async_client.post( + "/api/v1/auth/login", + json={"username": "sessiontest2", "password": "SessionPass2!"}, + ) + after = int(time.time()) + + assert response.status_code == 200 + token = response.json()["access_token"] + decoded = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) + expected_min = before + 168 * 3600 - 60 + expected_max = after + 168 * 3600 + 60 + assert expected_min <= decoded["exp"] <= expected_max + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_login_clamps_above_ceiling(self, async_client: AsyncClient, db_session: AsyncSession): + """A settings row above the 720h ceiling must be clamped at login time.""" + await async_client.post( + "/api/v1/auth/setup", + json={ + "auth_enabled": True, + "admin_username": "sessiontest3", + "admin_password": "SessionPass3!", + }, + ) + await _set_session_max_hours(db_session, "5000") # would be ~208 days + + before = int(time.time()) + response = await async_client.post( + "/api/v1/auth/login", + json={"username": "sessiontest3", "password": "SessionPass3!"}, + ) + after = int(time.time()) + + assert response.status_code == 200 + token = response.json()["access_token"] + decoded = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) + # Clamped to 30 days, not 5000 hours. + expected_min = before + SESSION_MAX_HOURS_HARD_CEILING * 3600 - 60 + expected_max = after + SESSION_MAX_HOURS_HARD_CEILING * 3600 + 60 + assert expected_min <= decoded["exp"] <= expected_max + + +class TestSettingsAPIExposesSessionMaxHours: + """The /settings API must round-trip session_max_hours as an int.""" + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_default_is_24(self, async_client: AsyncClient, db_session: AsyncSession): + await _set_session_max_hours(db_session, None) + response = await async_client.get("/api/v1/settings/") + assert response.status_code == 200 + assert response.json()["session_max_hours"] == 24 + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_update_accepts_valid_value(self, async_client: AsyncClient, db_session: AsyncSession): + response = await async_client.patch( + "/api/v1/settings/", + json={"session_max_hours": 168}, + ) + assert response.status_code == 200 + assert response.json()["session_max_hours"] == 168 + # Persisted as the int's string form so the resolver round-trips. + result = await db_session.execute(select(Settings).where(Settings.key == "session_max_hours")) + row = result.scalar_one() + assert row.value == "168" + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_update_rejects_zero(self, async_client: AsyncClient): + response = await async_client.patch( + "/api/v1/settings/", + json={"session_max_hours": 0}, + ) + assert response.status_code == 422 + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_update_rejects_above_ceiling(self, async_client: AsyncClient): + response = await async_client.patch( + "/api/v1/settings/", + json={"session_max_hours": SESSION_MAX_HOURS_HARD_CEILING + 1}, + ) + assert response.status_code == 422 diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 1fcea2a8b..1c09481d3 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -1155,6 +1155,8 @@ export interface AppSettings { bed_cooled_threshold: number; // Inventory low stock threshold low_stock_threshold: number; + // Session policy (#1706) — admin-set ceiling, hours, [1, 720] + session_max_hours: number; // User email notifications toggle user_notifications_enabled: boolean; // Default print options diff --git a/frontend/src/i18n/locales/de.ts b/frontend/src/i18n/locales/de.ts index fbff82e9d..93774582a 100644 --- a/frontend/src/i18n/locales/de.ts +++ b/frontend/src/i18n/locales/de.ts @@ -2381,6 +2381,17 @@ export default { linkedAccountsDesc: 'Diese externen Identitätsanbieter sind mit deinem Konto verknüpft.', oidcUnlinked: 'Konto getrennt.', }, + // Session Policy (#1706) + sessionPolicy: { + title: 'Sitzungsrichtlinie', + description: 'Maximale Sitzungsdauer für neue Benutzeranmeldungen. Bereits ausgegebene Token behalten ihren ursprünglichen Ablauf.', + preset24h: '24 Stunden', + preset7d: '7 Tage', + preset30d: '30 Tage', + customHoursLabel: 'Individuelle Sitzungsdauer in Stunden', + hoursSuffix: 'Stunden', + warning: 'Längere Sitzungen reduzieren den automatischen Abmeldeschutz. Nur für vertrauenswürdige Einzelnutzer-Installationen empfohlen.', + }, // OIDC provider settings oidc: { diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index ee56f186d..88f6cb400 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -2392,6 +2392,18 @@ export default { oidcUnlinked: 'Account unlinked.', }, + // Session Policy (#1706) — admin-configurable session lifetime ceiling. + sessionPolicy: { + title: 'Session Policy', + description: 'Maximum session lifetime for new user logins. Already-issued tokens keep their original expiry.', + preset24h: '24 hours', + preset7d: '7 days', + preset30d: '30 days', + customHoursLabel: 'Custom session lifetime in hours', + hoursSuffix: 'hours', + warning: 'Longer sessions reduce automatic logout protection. Recommended only for trusted single-user deployments.', + }, + // OIDC provider settings oidc: { title: 'SSO / OIDC Providers', diff --git a/frontend/src/i18n/locales/es.ts b/frontend/src/i18n/locales/es.ts index 65a3aa4d4..0ef2a1402 100644 --- a/frontend/src/i18n/locales/es.ts +++ b/frontend/src/i18n/locales/es.ts @@ -2384,6 +2384,17 @@ export default { linkedAccountsDesc: 'Estos proveedores de identidad externos están vinculados a su cuenta.', oidcUnlinked: 'Cuenta desvinculada.', }, + // Session Policy (#1706) + sessionPolicy: { + title: 'Política de sesión', + description: 'Duración máxima de sesión para nuevos inicios de sesión. Los tokens ya emitidos conservan su caducidad original.', + preset24h: '24 horas', + preset7d: '7 días', + preset30d: '30 días', + customHoursLabel: 'Duración de sesión personalizada en horas', + hoursSuffix: 'horas', + warning: 'Las sesiones más largas reducen la protección de cierre automático. Recomendado solo para implementaciones de usuario único en entornos de confianza.', + }, // OIDC provider settings oidc: { diff --git a/frontend/src/i18n/locales/fr.ts b/frontend/src/i18n/locales/fr.ts index 2092bedfb..01d07b715 100644 --- a/frontend/src/i18n/locales/fr.ts +++ b/frontend/src/i18n/locales/fr.ts @@ -2324,6 +2324,17 @@ export default { linkedAccountsDesc: 'Ces fournisseurs d\'identité externes sont liés à votre compte.', oidcUnlinked: 'Compte dissocié.', }, + // Session Policy (#1706) + sessionPolicy: { + title: 'Politique de session', + description: 'Durée maximale des sessions pour les nouvelles connexions utilisateur. Les jetons déjà émis conservent leur expiration d\'origine.', + preset24h: '24 heures', + preset7d: '7 jours', + preset30d: '30 jours', + customHoursLabel: 'Durée de session personnalisée en heures', + hoursSuffix: 'heures', + warning: 'Les sessions plus longues réduisent la protection de déconnexion automatique. Recommandé uniquement pour les déploiements mono-utilisateur de confiance.', + }, // OIDC provider settings oidc: { diff --git a/frontend/src/i18n/locales/it.ts b/frontend/src/i18n/locales/it.ts index d4170ff47..075ba5173 100644 --- a/frontend/src/i18n/locales/it.ts +++ b/frontend/src/i18n/locales/it.ts @@ -2323,6 +2323,17 @@ export default { linkedAccountsDesc: 'Questi provider di identità esterni sono collegati al tuo account.', oidcUnlinked: 'Account scollegato.', }, + // Session Policy (#1706) + sessionPolicy: { + title: 'Criterio di sessione', + description: 'Durata massima della sessione per i nuovi accessi utente. I token già emessi mantengono la scadenza originale.', + preset24h: '24 ore', + preset7d: '7 giorni', + preset30d: '30 giorni', + customHoursLabel: 'Durata personalizzata della sessione in ore', + hoursSuffix: 'ore', + warning: 'Le sessioni più lunghe riducono la protezione di disconnessione automatica. Consigliato solo per installazioni mono-utente attendibili.', + }, // OIDC provider settings oidc: { diff --git a/frontend/src/i18n/locales/ja.ts b/frontend/src/i18n/locales/ja.ts index 320a6ed7a..852b3a75d 100644 --- a/frontend/src/i18n/locales/ja.ts +++ b/frontend/src/i18n/locales/ja.ts @@ -2380,6 +2380,17 @@ export default { linkedAccountsDesc: 'これらの外部IDプロバイダーがあなたのアカウントにリンクされています。', oidcUnlinked: 'アカウントのリンクを解除しました。', }, + // Session Policy (#1706) + sessionPolicy: { + title: 'セッションポリシー', + description: '新しいユーザーログインの最大セッション有効期間。すでに発行されたトークンは元の有効期限を保持します。', + preset24h: '24時間', + preset7d: '7日', + preset30d: '30日', + customHoursLabel: 'カスタムセッション有効期間(時間)', + hoursSuffix: '時間', + warning: '長いセッションは自動ログアウト保護を弱めます。信頼できる単一ユーザー環境でのみ推奨されます。', + }, // OIDC provider settings oidc: { diff --git a/frontend/src/i18n/locales/ko.ts b/frontend/src/i18n/locales/ko.ts index 146458628..e727331ac 100644 --- a/frontend/src/i18n/locales/ko.ts +++ b/frontend/src/i18n/locales/ko.ts @@ -2238,6 +2238,17 @@ export default { linkedAccountsDesc: '이 외부 ID 제공자가 계정에 연결되어 있습니다.', oidcUnlinked: '계정 연결이 해제되었습니다.' }, + // 세션 정책 (#1706) + sessionPolicy: { + title: '세션 정책', + description: '신규 사용자 로그인의 최대 세션 수명입니다. 이미 발급된 토큰은 원래 만료 시간을 유지합니다.', + preset24h: '24시간', + preset7d: '7일', + preset30d: '30일', + customHoursLabel: '사용자 지정 세션 수명(시간)', + hoursSuffix: '시간', + warning: '세션이 길어질수록 자동 로그아웃 보호가 약해집니다. 신뢰할 수 있는 단일 사용자 배포에서만 권장됩니다.' + }, oidc: { title: 'SSO / OIDC 제공자', desc: '외부 ID 제공자를 통해 싱글 사인온을 허용하도록 OpenID Connect 제공자를 설정하세요.', diff --git a/frontend/src/i18n/locales/pt-BR.ts b/frontend/src/i18n/locales/pt-BR.ts index 9370b7178..2d500a29f 100644 --- a/frontend/src/i18n/locales/pt-BR.ts +++ b/frontend/src/i18n/locales/pt-BR.ts @@ -2323,6 +2323,17 @@ export default { linkedAccountsDesc: 'Estes provedores de identidade externos estão vinculados à sua conta.', oidcUnlinked: 'Conta desvinculada.', }, + // Session Policy (#1706) + sessionPolicy: { + title: 'Política de sessão', + description: 'Duração máxima da sessão para novos logins de usuário. Tokens já emitidos mantêm sua expiração original.', + preset24h: '24 horas', + preset7d: '7 dias', + preset30d: '30 dias', + customHoursLabel: 'Duração personalizada da sessão em horas', + hoursSuffix: 'horas', + warning: 'Sessões mais longas reduzem a proteção de logout automático. Recomendado apenas para implantações de usuário único confiáveis.', + }, // OIDC provider settings oidc: { diff --git a/frontend/src/i18n/locales/tr.ts b/frontend/src/i18n/locales/tr.ts index 0b847402b..e9804740b 100644 --- a/frontend/src/i18n/locales/tr.ts +++ b/frontend/src/i18n/locales/tr.ts @@ -2384,6 +2384,17 @@ export default { linkedAccountsDesc: 'Bu harici kimlik sağlayıcıları hesabınıza bağlıdır.', oidcUnlinked: 'Hesap bağlantısı kaldırıldı.', }, + // Session Policy (#1706) + sessionPolicy: { + title: 'Oturum Politikası', + description: 'Yeni kullanıcı girişleri için maksimum oturum süresi. Daha önce verilmiş belirteçler özgün son kullanma tarihlerini korur.', + preset24h: '24 saat', + preset7d: '7 gün', + preset30d: '30 gün', + customHoursLabel: 'Özel oturum süresi (saat)', + hoursSuffix: 'saat', + warning: 'Daha uzun oturumlar otomatik oturum kapatma korumasını azaltır. Yalnızca güvenilir tek kullanıcılı dağıtımlar için önerilir.', + }, // OIDC sağlayıcı ayarları oidc: { diff --git a/frontend/src/i18n/locales/zh-CN.ts b/frontend/src/i18n/locales/zh-CN.ts index 75fc1bdf0..aa9758d13 100644 --- a/frontend/src/i18n/locales/zh-CN.ts +++ b/frontend/src/i18n/locales/zh-CN.ts @@ -2368,6 +2368,17 @@ export default { linkedAccountsDesc: '以下外部身份提供商已与您的账户关联。', oidcUnlinked: '账户已解除关联。', }, + // Session Policy (#1706) + sessionPolicy: { + title: '会话策略', + description: '新用户登录的最长会话有效期。已颁发的令牌保留其原有的过期时间。', + preset24h: '24 小时', + preset7d: '7 天', + preset30d: '30 天', + customHoursLabel: '自定义会话有效期(小时)', + hoursSuffix: '小时', + warning: '更长的会话会减弱自动注销保护。仅建议在受信任的单用户部署中使用。', + }, // OIDC provider settings oidc: { diff --git a/frontend/src/i18n/locales/zh-TW.ts b/frontend/src/i18n/locales/zh-TW.ts index 47e37fb5e..0f357e91a 100644 --- a/frontend/src/i18n/locales/zh-TW.ts +++ b/frontend/src/i18n/locales/zh-TW.ts @@ -2368,6 +2368,17 @@ export default { linkedAccountsDesc: '以下外部身份提供者已與您的帳戶連結。', oidcUnlinked: '帳戶已解除連結。', }, + // Session Policy (#1706) + sessionPolicy: { + title: '工作階段政策', + description: '新使用者登入的最長工作階段有效期。已發行的權杖會保留其原有的到期時間。', + preset24h: '24 小時', + preset7d: '7 天', + preset30d: '30 天', + customHoursLabel: '自訂工作階段有效期(小時)', + hoursSuffix: '小時', + warning: '較長的工作階段會削弱自動登出保護。僅建議在受信任的單一使用者部署中使用。', + }, // OIDC provider settings oidc: { diff --git a/frontend/src/pages/SettingsPage.tsx b/frontend/src/pages/SettingsPage.tsx index 58ac9905b..3aca18e93 100644 --- a/frontend/src/pages/SettingsPage.tsx +++ b/frontend/src/pages/SettingsPage.tsx @@ -87,6 +87,7 @@ registerSettingsSearch({ labelKey: 'settings.tabs.spoolbuddy', tab: 'spoolbuddy' registerSettingsSearch({ labelKey: 'settings.currentUser', tab: 'users', subTab: 'users', keywords: 'current user profile password change', anchor: 'card-currentuser' }); registerSettingsSearch({ labelKey: 'settings.users', tab: 'users', subTab: 'users', keywords: 'users accounts list', anchor: 'card-users' }); registerSettingsSearch({ labelKey: 'settings.groups', tab: 'users', subTab: 'users', keywords: 'groups roles permissions administrators operators viewers', anchor: 'card-groups' }); +registerSettingsSearch({ labelKey: 'settings.sessionPolicy.title', labelFallback: 'Session Policy', tab: 'users', subTab: 'users', keywords: 'session timeout expiry logout remember me jwt token lifetime', anchor: 'card-session-policy' }); registerSettingsSearch({ labelKey: 'settings.email.smtpSettings', labelFallback: 'SMTP Configuration', tab: 'users', subTab: 'email', keywords: 'smtp email send server port password auth starttls ssl', anchor: 'card-smtp' }); registerSettingsSearch({ labelKey: 'settings.ldap.title', labelFallback: 'LDAP Authentication', tab: 'users', subTab: 'ldap', keywords: 'ldap active directory ad authentication bind dn search base group mapping', anchor: 'card-ldap' }); registerSettingsSearch({ labelKey: 'settings.tabs.backup', tab: 'backup', keywords: 'backup github restore download cloud sync profiles archives', anchor: 'card-backup' }); @@ -1012,7 +1013,8 @@ export function SettingsPage() { (settings.default_nozzle_offset_cali ?? true) !== (localSettings.default_nozzle_offset_cali ?? true) || (settings.stagger_group_size ?? 2) !== (localSettings.stagger_group_size ?? 2) || (settings.stagger_interval_minutes ?? 5) !== (localSettings.stagger_interval_minutes ?? 5) || - (settings.require_plate_clear ?? false) !== (localSettings.require_plate_clear ?? false); + (settings.require_plate_clear ?? false) !== (localSettings.require_plate_clear ?? false) || + (settings.session_max_hours ?? 24) !== (localSettings.session_max_hours ?? 24); if (!hasChanges) { return; @@ -1099,6 +1101,7 @@ export function SettingsPage() { stagger_group_size: localSettings.stagger_group_size, stagger_interval_minutes: localSettings.stagger_interval_minutes, require_plate_clear: localSettings.require_plate_clear, + session_max_hours: localSettings.session_max_hours, }; updateMutation.mutate(settingsToSave); }, 500); @@ -5117,8 +5120,73 @@ export function SettingsPage() { {authEnabled && (
- {/* Left Column: Current User + User List */} + {/* Left Column: Session Policy + Current User + User List */}
+ {/* Session Policy (#1706) — admin-set ceiling for user session lifetime */} + + +

+ + {t('settings.sessionPolicy.title')} +

+
+ +

+ {t('settings.sessionPolicy.description')} +

+
+ {[ + { hours: 24, labelKey: 'settings.sessionPolicy.preset24h' }, + { hours: 168, labelKey: 'settings.sessionPolicy.preset7d' }, + { hours: 720, labelKey: 'settings.sessionPolicy.preset30d' }, + ].map((preset) => { + const current = localSettings?.session_max_hours ?? 24; + const isActive = current === preset.hours; + return ( + + ); + })} +
+ { + const raw = parseInt(e.target.value, 10); + if (Number.isNaN(raw)) return; + updateSetting('session_max_hours', Math.max(1, Math.min(720, raw))); + }} + disabled={authEnabled && !hasPermission('settings:update')} + aria-label={t('settings.sessionPolicy.customHoursLabel')} + className="w-20 px-2 py-2 bg-bambu-dark-tertiary text-white text-sm rounded-lg border border-bambu-dark-tertiary focus:border-bambu-green focus:outline-none disabled:opacity-50" + /> + {t('settings.sessionPolicy.hoursSuffix')} +
+
+ {(localSettings?.session_max_hours ?? 24) > 24 && ( +
+ +

+ {t('settings.sessionPolicy.warning')} +

+
+ )} +
+
+ {/* Current User Card */} {user && ( diff --git a/static/assets/index-lB37rzBj.js b/static/assets/index-DznM9swC.js similarity index 85% rename from static/assets/index-lB37rzBj.js rename to static/assets/index-DznM9swC.js index 7c965785c..75e89d816 100644 --- a/static/assets/index-lB37rzBj.js +++ b/static/assets/index-DznM9swC.js @@ -5,47 +5,47 @@ function Eme(t,e){for(var a=0;a$||ft[M]!==Et[$]){var qt=` `+ft[M].replace(" at new "," at ");return h.displayName&&qt.includes("")&&(qt=qt.replace("",h.displayName)),qt}while(1<=M&&0<=$);break}}}finally{V=!1,Error.prepareStackTrace=_}return(_=h?h.displayName||h.name:"")?I(_):""}function te(h,w){switch(h.tag){case 26:case 27:case 5:return I(h.type);case 16:return I("Lazy");case 13:return h.child!==w&&w!==null?I("Suspense Fallback"):I("Suspense");case 19:return I("SuspenseList");case 0:case 15:return re(h.type,!1);case 11:return re(h.type.render,!1);case 1:return re(h.type,!0);case 31:return I("Activity");default:return""}}function Z(h){try{var w="",_=null;do w+=te(h,_),_=h,h=h.return;while(h);return w}catch(M){return` Error generating stack: `+M.message+` -`+M.stack}}var se=Object.prototype.hasOwnProperty,he=t.unstable_scheduleCallback,ue=t.unstable_cancelCallback,fe=t.unstable_shouldYield,G=t.unstable_requestPaint,L=t.unstable_now,ee=t.unstable_getCurrentPriorityLevel,be=t.unstable_ImmediatePriority,xe=t.unstable_UserBlockingPriority,ve=t.unstable_NormalPriority,Ee=t.unstable_LowPriority,Ne=t.unstable_IdlePriority,qe=t.log,Qe=t.unstable_setDisableYieldValue,je=null,Ie=null;function Re(h){if(typeof qe=="function"&&Qe(h),Ie&&typeof Ie.setStrictMode=="function")try{Ie.setStrictMode(je,h)}catch{}}var Ge=Math.clz32?Math.clz32:ze,Xe=Math.log,Ue=Math.LN2;function ze(h){return h>>>=0,h===0?32:31-(Xe(h)/Ue|0)|0}var Ye=256,De=262144,Ze=4194304;function Ve(h){var w=h&42;if(w!==0)return w;switch(h&-h){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return h&261888;case 262144:case 524288:case 1048576:case 2097152:return h&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return h&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return h}}function Me(h,w,_){var M=h.pendingLanes;if(M===0)return 0;var $=0,ae=h.suspendedLanes,Ce=h.pingedLanes;h=h.warmLanes;var He=M&134217727;return He!==0?(M=He&~ae,M!==0?$=Ve(M):(Ce&=He,Ce!==0?$=Ve(Ce):_||(_=He&~h,_!==0&&($=Ve(_))))):(He=M&~ae,He!==0?$=Ve(He):Ce!==0?$=Ve(Ce):_||(_=M&~h,_!==0&&($=Ve(_)))),$===0?0:w!==0&&w!==$&&(w&ae)===0&&(ae=$&-$,_=w&-w,ae>=_||ae===32&&(_&4194048)!==0)?w:$}function Oe(h,w){return(h.pendingLanes&~(h.suspendedLanes&~h.pingedLanes)&w)===0}function et(h,w){switch(h){case 1:case 2:case 4:case 8:case 64:return w+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return w+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function $e(){var h=Ze;return Ze<<=1,(Ze&62914560)===0&&(Ze=4194304),h}function we(h){for(var w=[],_=0;31>_;_++)w.push(h);return w}function Le(h,w){h.pendingLanes|=w,w!==268435456&&(h.suspendedLanes=0,h.pingedLanes=0,h.warmLanes=0)}function Be(h,w,_,M,$,ae){var Ce=h.pendingLanes;h.pendingLanes=_,h.suspendedLanes=0,h.pingedLanes=0,h.warmLanes=0,h.expiredLanes&=_,h.entangledLanes&=_,h.errorRecoveryDisabledLanes&=_,h.shellSuspendCounter=0;var He=h.entanglements,ft=h.expirationTimes,Et=h.hiddenUpdates;for(_=Ce&~_;0<_;){var qt=31-Ge(_),Wt=1<"u")return null;try{return h.activeElement||h.body}catch{return h.body}}var wr=/[\n"\\]/g;function ja(h){return h.replace(wr,function(w){return"\\"+w.charCodeAt(0).toString(16)+" "})}function $a(h,w,_,M,$,ae,Ce,He){h.name="",Ce!=null&&typeof Ce!="function"&&typeof Ce!="symbol"&&typeof Ce!="boolean"?h.type=Ce:h.removeAttribute("type"),w!=null?Ce==="number"?(w===0&&h.value===""||h.value!=w)&&(h.value=""+Qt(w)):h.value!==""+Qt(w)&&(h.value=""+Qt(w)):Ce!=="submit"&&Ce!=="reset"||h.removeAttribute("value"),w!=null?Wr(h,Ce,Qt(w)):_!=null?Wr(h,Ce,Qt(_)):M!=null&&h.removeAttribute("value"),$==null&&ae!=null&&(h.defaultChecked=!!ae),$!=null&&(h.checked=$&&typeof $!="function"&&typeof $!="symbol"),He!=null&&typeof He!="function"&&typeof He!="symbol"&&typeof He!="boolean"?h.name=""+Qt(He):h.removeAttribute("name")}function Ba(h,w,_,M,$,ae,Ce,He){if(ae!=null&&typeof ae!="function"&&typeof ae!="symbol"&&typeof ae!="boolean"&&(h.type=ae),w!=null||_!=null){if(!(ae!=="submit"&&ae!=="reset"||w!=null)){Jt(h);return}_=_!=null?""+Qt(_):"",w=w!=null?""+Qt(w):_,He||w===h.value||(h.value=w),h.defaultValue=w}M=M??$,M=typeof M!="function"&&typeof M!="symbol"&&!!M,h.checked=He?h.checked:!!M,h.defaultChecked=!!M,Ce!=null&&typeof Ce!="function"&&typeof Ce!="symbol"&&typeof Ce!="boolean"&&(h.name=Ce),Jt(h)}function Wr(h,w,_){w==="number"&&Ha(h.ownerDocument)===h||h.defaultValue===""+_||(h.defaultValue=""+_)}function tt(h,w,_,M){if(h=h.options,w){w={};for(var $=0;$<_.length;$++)w["$"+_[$]]=!0;for(_=0;_"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),yp=!1;if(nr)try{var Td={};Object.defineProperty(Td,"passive",{get:function(){yp=!0}}),window.addEventListener("test",Td,Td),window.removeEventListener("test",Td,Td)}catch{yp=!1}var mi=null,Fu=null,ki=null;function _f(){if(ki)return ki;var h,w=Fu,_=w.length,M,$="value"in mi?mi.value:mi.textContent,ae=$.length;for(h=0;h<_&&w[h]===$[h];h++);var Ce=_-h;for(M=1;M<=Ce&&w[_-M]===$[ae-M];M++);return ki=$.slice(h,1=hi),_p=" ",Mb=!1;function ye(h,w){switch(h){case"keyup":return Av.indexOf(w.keyCode)!==-1;case"keydown":return w.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function zt(h){return h=h.detail,typeof h=="object"&&"data"in h?h.data:null}var ha=!1;function Ma(h,w){switch(h){case"compositionend":return zt(w);case"keypress":return w.which!==32?null:(Mb=!0,_p);case"textInput":return h=w.data,h===_p&&Mb?null:h;default:return null}}function Wa(h,w){if(ha)return h==="compositionend"||!ml&&ye(h,w)?(h=_f(),ki=Fu=mi=null,ha=!1,h):null;switch(h){case"paste":return null;case"keypress":if(!(w.ctrlKey||w.altKey||w.metaKey)||w.ctrlKey&&w.altKey){if(w.char&&1=w)return{node:_,offset:w-h};h=M}e:{for(;_;){if(_.nextSibling){_=_.nextSibling;break e}_=_.parentNode}_=void 0}_=Lo(_)}}function Ro(h,w){return h&&w?h===w?!0:h&&h.nodeType===3?!1:w&&w.nodeType===3?Ro(h,w.parentNode):"contains"in h?h.contains(w):h.compareDocumentPosition?!!(h.compareDocumentPosition(w)&16):!1:!1}function mo(h){h=h!=null&&h.ownerDocument!=null&&h.ownerDocument.defaultView!=null?h.ownerDocument.defaultView:window;for(var w=Ha(h.document);w instanceof h.HTMLIFrameElement;){try{var _=typeof w.contentWindow.location.href=="string"}catch{_=!1}if(_)h=w.contentWindow;else break;w=Ha(h.document)}return w}function Ci(h){var w=h&&h.nodeName&&h.nodeName.toLowerCase();return w&&(w==="input"&&(h.type==="text"||h.type==="search"||h.type==="tel"||h.type==="url"||h.type==="password")||w==="textarea"||h.contentEditable==="true")}var Dd=nr&&"documentMode"in document&&11>=document.documentMode,fi=null,Sr=null,Or=null,Zn=!1;function Cp(h,w,_){var M=_.window===_?_.document:_.nodeType===9?_:_.ownerDocument;Zn||fi==null||fi!==Ha(M)||(M=fi,"selectionStart"in M&&Ci(M)?M={start:M.selectionStart,end:M.selectionEnd}:(M=(M.ownerDocument&&M.ownerDocument.defaultView||window).getSelection(),M={anchorNode:M.anchorNode,anchorOffset:M.anchorOffset,focusNode:M.focusNode,focusOffset:M.focusOffset}),Or&&Ss(Or,M)||(Or=M,M=v_(Sr,"onSelect"),0>=Ce,$-=Ce,Ld=1<<32-Ge(w)+$|_<<$|M,Rd=ae+h}else Ld=1<or?(jr=Aa,Aa=null):jr=Aa.sibling;var Ur=Dt(Ct,Aa,Mt[or],Gt);if(Ur===null){Aa===null&&(Aa=jr);break}h&&Aa&&Ur.alternate===null&&w(Ct,Aa),bt=ae(Ur,bt,or),Br===null?za=Ur:Br.sibling=Ur,Br=Ur,Aa=jr}if(or===Mt.length)return _(Ct,Aa),Dr&&Hu(Ct,or),za;if(Aa===null){for(;oror?(jr=Aa,Aa=null):jr=Aa.sibling;var Wp=Dt(Ct,Aa,Ur.value,Gt);if(Wp===null){Aa===null&&(Aa=jr);break}h&&Aa&&Wp.alternate===null&&w(Ct,Aa),bt=ae(Wp,bt,or),Br===null?za=Wp:Br.sibling=Wp,Br=Wp,Aa=jr}if(Ur.done)return _(Ct,Aa),Dr&&Hu(Ct,or),za;if(Aa===null){for(;!Ur.done;or++,Ur=Mt.next())Ur=Wt(Ct,Ur.value,Gt),Ur!==null&&(bt=ae(Ur,bt,or),Br===null?za=Ur:Br.sibling=Ur,Br=Ur);return Dr&&Hu(Ct,or),za}for(Aa=M(Aa);!Ur.done;or++,Ur=Mt.next())Ur=Ot(Aa,Ct,or,Ur.value,Gt),Ur!==null&&(h&&Ur.alternate!==null&&Aa.delete(Ur.key===null?or:Ur.key),bt=ae(Ur,bt,or),Br===null?za=Ur:Br.sibling=Ur,Br=Ur);return h&&Aa.forEach(function(Mme){return w(Ct,Mme)}),Dr&&Hu(Ct,or),za}function ln(Ct,bt,Mt,Gt){if(typeof Mt=="object"&&Mt!==null&&Mt.type===v&&Mt.key===null&&(Mt=Mt.props.children),typeof Mt=="object"&&Mt!==null){switch(Mt.$$typeof){case f:e:{for(var za=Mt.key;bt!==null;){if(bt.key===za){if(za=Mt.type,za===v){if(bt.tag===7){_(Ct,bt.sibling),Gt=$(bt,Mt.props.children),Gt.return=Ct,Ct=Gt;break e}}else if(bt.elementType===za||typeof za=="object"&&za!==null&&za.$$typeof===F&&Ff(za)===bt.type){_(Ct,bt.sibling),Gt=$(bt,Mt.props),Lv(Gt,Mt),Gt.return=Ct,Ct=Gt;break e}_(Ct,bt);break}else w(Ct,bt);bt=bt.sibling}Mt.type===v?(Gt=Af(Mt.props.children,Ct.mode,Gt,Mt.key),Gt.return=Ct,Ct=Gt):(Gt=zk(Mt.type,Mt.key,Mt.props,null,Ct.mode,Gt),Lv(Gt,Mt),Gt.return=Ct,Ct=Gt)}return Ce(Ct);case g:e:{for(za=Mt.key;bt!==null;){if(bt.key===za)if(bt.tag===4&&bt.stateNode.containerInfo===Mt.containerInfo&&bt.stateNode.implementation===Mt.implementation){_(Ct,bt.sibling),Gt=$(bt,Mt.children||[]),Gt.return=Ct,Ct=Gt;break e}else{_(Ct,bt);break}else w(Ct,bt);bt=bt.sibling}Gt=Dj(Mt,Ct.mode,Gt),Gt.return=Ct,Ct=Gt}return Ce(Ct);case F:return Mt=Ff(Mt),ln(Ct,bt,Mt,Gt)}if(X(Mt))return Ca(Ct,bt,Mt,Gt);if(O(Mt)){if(za=O(Mt),typeof za!="function")throw Error(n(150));return Mt=za.call(Mt),qa(Ct,bt,Mt,Gt)}if(typeof Mt.then=="function")return ln(Ct,bt,Vk(Mt),Gt);if(Mt.$$typeof===k)return ln(Ct,bt,Bk(Ct,Mt),Gt);Gk(Ct,Mt)}return typeof Mt=="string"&&Mt!==""||typeof Mt=="number"||typeof Mt=="bigint"?(Mt=""+Mt,bt!==null&&bt.tag===6?(_(Ct,bt.sibling),Gt=$(bt,Mt),Gt.return=Ct,Ct=Gt):(_(Ct,bt),Gt=Ej(Mt,Ct.mode,Gt),Gt.return=Ct,Ct=Gt),Ce(Ct)):_(Ct,bt)}return function(Ct,bt,Mt,Gt){try{Fv=0;var za=ln(Ct,bt,Mt,Gt);return Ub=null,za}catch(Aa){if(Aa===Bb||Aa===Hk)throw Aa;var Br=pl(29,Aa,null,Ct.mode);return Br.lanes=Gt,Br.return=Ct,Br}}}var Rf=iU(!0),sU=iU(!1),jp=!1;function Gj(h){h.updateQueue={baseState:h.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function $j(h,w){h=h.updateQueue,w.updateQueue===h&&(w.updateQueue={baseState:h.baseState,firstBaseUpdate:h.firstBaseUpdate,lastBaseUpdate:h.lastBaseUpdate,shared:h.shared,callbacks:null})}function Mp(h){return{lane:h,tag:0,payload:null,callback:null,next:null}}function Ep(h,w,_){var M=h.updateQueue;if(M===null)return null;if(M=M.shared,($r&2)!==0){var $=M.pending;return $===null?w.next=w:(w.next=$.next,$.next=w),M.pending=w,w=Rk(h),q5(h,null,_),w}return Lk(h,M,w,_),Rk(h)}function Rv(h,w,_){if(w=w.updateQueue,w!==null&&(w=w.shared,(_&4194048)!==0)){var M=w.lanes;M&=h.pendingLanes,_|=M,w.lanes=_,Lt(h,_)}}function Wj(h,w){var _=h.updateQueue,M=h.alternate;if(M!==null&&(M=M.updateQueue,_===M)){var $=null,ae=null;if(_=_.firstBaseUpdate,_!==null){do{var Ce={lane:_.lane,tag:_.tag,payload:_.payload,callback:null,next:null};ae===null?$=ae=Ce:ae=ae.next=Ce,_=_.next}while(_!==null);ae===null?$=ae=w:ae=ae.next=w}else $=ae=w;_={baseState:M.baseState,firstBaseUpdate:$,lastBaseUpdate:ae,shared:M.shared,callbacks:M.callbacks},h.updateQueue=_;return}h=_.lastBaseUpdate,h===null?_.firstBaseUpdate=w:h.next=w,_.lastBaseUpdate=w}var Kj=!1;function zv(){if(Kj){var h=Ob;if(h!==null)throw h}}function Iv(h,w,_,M){Kj=!1;var $=h.updateQueue;jp=!1;var ae=$.firstBaseUpdate,Ce=$.lastBaseUpdate,He=$.shared.pending;if(He!==null){$.shared.pending=null;var ft=He,Et=ft.next;ft.next=null,Ce===null?ae=Et:Ce.next=Et,Ce=ft;var qt=h.alternate;qt!==null&&(qt=qt.updateQueue,He=qt.lastBaseUpdate,He!==Ce&&(He===null?qt.firstBaseUpdate=Et:He.next=Et,qt.lastBaseUpdate=ft))}if(ae!==null){var Wt=$.baseState;Ce=0,qt=Et=ft=null,He=ae;do{var Dt=He.lane&-536870913,Ot=Dt!==He.lane;if(Ot?(Ar&Dt)===Dt:(M&Dt)===Dt){Dt!==0&&Dt===Ib&&(Kj=!0),qt!==null&&(qt=qt.next={lane:0,tag:He.tag,payload:He.payload,callback:null,next:null});e:{var Ca=h,qa=He;Dt=w;var ln=_;switch(qa.tag){case 1:if(Ca=qa.payload,typeof Ca=="function"){Wt=Ca.call(ln,Wt,Dt);break e}Wt=Ca;break e;case 3:Ca.flags=Ca.flags&-65537|128;case 0:if(Ca=qa.payload,Dt=typeof Ca=="function"?Ca.call(ln,Wt,Dt):Ca,Dt==null)break e;Wt=m({},Wt,Dt);break e;case 2:jp=!0}}Dt=He.callback,Dt!==null&&(h.flags|=64,Ot&&(h.flags|=8192),Ot=$.callbacks,Ot===null?$.callbacks=[Dt]:Ot.push(Dt))}else Ot={lane:Dt,tag:He.tag,payload:He.payload,callback:He.callback,next:null},qt===null?(Et=qt=Ot,ft=Wt):qt=qt.next=Ot,Ce|=Dt;if(He=He.next,He===null){if(He=$.shared.pending,He===null)break;Ot=He,He=Ot.next,Ot.next=null,$.lastBaseUpdate=Ot,$.shared.pending=null}}while(!0);qt===null&&(ft=Wt),$.baseState=ft,$.firstBaseUpdate=Et,$.lastBaseUpdate=qt,ae===null&&($.shared.lanes=0),zp|=Ce,h.lanes=Ce,h.memoizedState=Wt}}function oU(h,w){if(typeof h!="function")throw Error(n(191,h));h.call(w)}function lU(h,w){var _=h.callbacks;if(_!==null)for(h.callbacks=null,h=0;h<_.length;h++)oU(_[h],w)}var Hb=K(null),$k=K(0);function cU(h,w){h=Ju,me($k,h),me(Hb,w),Ju=h|w.baseLanes}function Yj(){me($k,Ju),me(Hb,Hb.current)}function Qj(){Ju=$k.current,oe(Hb),oe($k)}var hl=K(null),Jl=null;function Dp(h){var w=h.alternate;me(gi,gi.current&1),me(hl,h),Jl===null&&(w===null||Hb.current!==null||w.memoizedState!==null)&&(Jl=h)}function Xj(h){me(gi,gi.current),me(hl,h),Jl===null&&(Jl=h)}function dU(h){h.tag===22?(me(gi,gi.current),me(hl,h),Jl===null&&(Jl=h)):Fp()}function Fp(){me(gi,gi.current),me(hl,hl.current)}function fl(h){oe(hl),Jl===h&&(Jl=null),oe(gi)}var gi=K(0);function Wk(h){for(var w=h;w!==null;){if(w.tag===13){var _=w.memoizedState;if(_!==null&&(_=_.dehydrated,_===null||rE(_)||nE(_)))return w}else if(w.tag===19&&(w.memoizedProps.revealOrder==="forwards"||w.memoizedProps.revealOrder==="backwards"||w.memoizedProps.revealOrder==="unstable_legacy-backwards"||w.memoizedProps.revealOrder==="together")){if((w.flags&128)!==0)return w}else if(w.child!==null){w.child.return=w,w=w.child;continue}if(w===h)break;for(;w.sibling===null;){if(w.return===null||w.return===h)return null;w=w.return}w.sibling.return=w.return,w=w.sibling}return null}var Gu=0,ir=null,sn=null,Ni=null,Kk=!1,qb=!1,zf=!1,Yk=0,Ov=0,Vb=null,wue=0;function Jn(){throw Error(n(321))}function Zj(h,w){if(w===null)return!1;for(var _=0;_ae?ae:8;var Ce=ne.T,He={};ne.T=He,pM(h,!1,w,_);try{var ft=$(),Et=ne.S;if(Et!==null&&Et(He,ft),ft!==null&&typeof ft=="object"&&typeof ft.then=="function"){var qt=vue(ft,M);Uv(h,w,qt,xl(h))}else Uv(h,w,M,xl(h))}catch(Wt){Uv(h,w,{then:function(){},status:"rejected",reason:Wt},xl())}finally{Y.p=ae,Ce!==null&&He.types!==null&&(Ce.types=He.types),ne.T=Ce}}function Pue(){}function uM(h,w,_,M){if(h.tag!==5)throw Error(n(476));var $=BU(h).queue;OU(h,$,w,ce,_===null?Pue:function(){return UU(h),_(M)})}function BU(h){var w=h.memoizedState;if(w!==null)return w;w={memoizedState:ce,baseState:ce,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:$u,lastRenderedState:ce},next:null};var _={};return w.next={memoizedState:_,baseState:_,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:$u,lastRenderedState:_},next:null},h.memoizedState=w,h=h.alternate,h!==null&&(h.memoizedState=w),w}function UU(h){var w=BU(h);w.next===null&&(w=h.alternate.memoizedState),Uv(h,w.next.queue,{},xl())}function mM(){return _s(n0)}function HU(){return bi().memoizedState}function qU(){return bi().memoizedState}function Nue(h){for(var w=h.return;w!==null;){switch(w.tag){case 24:case 3:var _=xl();h=Mp(_);var M=Ep(w,h,_);M!==null&&(Uo(M,w,_),Rv(M,w,_)),w={cache:Uj()},h.payload=w;return}w=w.return}}function Tue(h,w,_){var M=xl();_={lane:M,revertLane:0,gesture:null,action:_,hasEagerState:!1,eagerState:null,next:null},t_(h)?GU(w,_):(_=jj(h,w,_,M),_!==null&&(Uo(_,h,M),$U(_,w,M)))}function VU(h,w,_){var M=xl();Uv(h,w,_,M)}function Uv(h,w,_,M){var $={lane:M,revertLane:0,gesture:null,action:_,hasEagerState:!1,eagerState:null,next:null};if(t_(h))GU(w,$);else{var ae=h.alternate;if(h.lanes===0&&(ae===null||ae.lanes===0)&&(ae=w.lastRenderedReducer,ae!==null))try{var Ce=w.lastRenderedState,He=ae(Ce,_);if($.hasEagerState=!0,$.eagerState=He,Fn(He,Ce))return Lk(h,w,$,0),yn===null&&Fk(),!1}catch{}if(_=jj(h,w,$,M),_!==null)return Uo(_,h,M),$U(_,w,M),!0}return!1}function pM(h,w,_,M){if(M={lane:2,revertLane:GM(),gesture:null,action:M,hasEagerState:!1,eagerState:null,next:null},t_(h)){if(w)throw Error(n(479))}else w=jj(h,_,M,2),w!==null&&Uo(w,h,2)}function t_(h){var w=h.alternate;return h===ir||w!==null&&w===ir}function GU(h,w){qb=Kk=!0;var _=h.pending;_===null?w.next=w:(w.next=_.next,_.next=w),h.pending=w}function $U(h,w,_){if((_&4194048)!==0){var M=w.lanes;M&=h.pendingLanes,_|=M,w.lanes=_,Lt(h,_)}}var Hv={readContext:_s,use:Xk,useCallback:Jn,useContext:Jn,useEffect:Jn,useImperativeHandle:Jn,useLayoutEffect:Jn,useInsertionEffect:Jn,useMemo:Jn,useReducer:Jn,useRef:Jn,useState:Jn,useDebugValue:Jn,useDeferredValue:Jn,useTransition:Jn,useSyncExternalStore:Jn,useId:Jn,useHostTransitionStatus:Jn,useFormState:Jn,useActionState:Jn,useOptimistic:Jn,useMemoCache:Jn,useCacheRefresh:Jn};Hv.useEffectEvent=Jn;var WU={readContext:_s,use:Xk,useCallback:function(h,w){return po().memoizedState=[h,w===void 0?null:w],h},useContext:_s,useEffect:jU,useImperativeHandle:function(h,w,_){_=_!=null?_.concat([h]):null,Jk(4194308,4,FU.bind(null,w,h),_)},useLayoutEffect:function(h,w){return Jk(4194308,4,h,w)},useInsertionEffect:function(h,w){Jk(4,2,h,w)},useMemo:function(h,w){var _=po();w=w===void 0?null:w;var M=h();if(zf){Re(!0);try{h()}finally{Re(!1)}}return _.memoizedState=[M,w],M},useReducer:function(h,w,_){var M=po();if(_!==void 0){var $=_(w);if(zf){Re(!0);try{_(w)}finally{Re(!1)}}}else $=w;return M.memoizedState=M.baseState=$,h={pending:null,lanes:0,dispatch:null,lastRenderedReducer:h,lastRenderedState:$},M.queue=h,h=h.dispatch=Tue.bind(null,ir,h),[M.memoizedState,h]},useRef:function(h){var w=po();return h={current:h},w.memoizedState=h},useState:function(h){h=sM(h);var w=h.queue,_=VU.bind(null,ir,w);return w.dispatch=_,[h.memoizedState,_]},useDebugValue:cM,useDeferredValue:function(h,w){var _=po();return dM(_,h,w)},useTransition:function(){var h=sM(!1);return h=OU.bind(null,ir,h.queue,!0,!1),po().memoizedState=h,[!1,h]},useSyncExternalStore:function(h,w,_){var M=ir,$=po();if(Dr){if(_===void 0)throw Error(n(407));_=_()}else{if(_=w(),yn===null)throw Error(n(349));(Ar&127)!==0||hU(M,w,_)}$.memoizedState=_;var ae={value:_,getSnapshot:w};return $.queue=ae,jU(gU.bind(null,M,ae,h),[h]),M.flags|=2048,Gb(9,{destroy:void 0},fU.bind(null,M,ae,_,w),null),_},useId:function(){var h=po(),w=yn.identifierPrefix;if(Dr){var _=Rd,M=Ld;_=(M&~(1<<32-Ge(M)-1)).toString(32)+_,w="_"+w+"R_"+_,_=Yk++,0<_&&(w+="H"+_.toString(32)),w+="_"}else _=wue++,w="_"+w+"r_"+_.toString(32)+"_";return h.memoizedState=w},useHostTransitionStatus:mM,useFormState:CU,useActionState:CU,useOptimistic:function(h){var w=po();w.memoizedState=w.baseState=h;var _={pending:null,lanes:0,dispatch:null,lastRenderedReducer:null,lastRenderedState:null};return w.queue=_,w=pM.bind(null,ir,!0,_),_.dispatch=w,[h,w]},useMemoCache:rM,useCacheRefresh:function(){return po().memoizedState=Nue.bind(null,ir)},useEffectEvent:function(h){var w=po(),_={impl:h};return w.memoizedState=_,function(){if(($r&2)!==0)throw Error(n(440));return _.impl.apply(void 0,arguments)}}},hM={readContext:_s,use:Xk,useCallback:RU,useContext:_s,useEffect:lM,useImperativeHandle:LU,useInsertionEffect:EU,useLayoutEffect:DU,useMemo:zU,useReducer:Zk,useRef:AU,useState:function(){return Zk($u)},useDebugValue:cM,useDeferredValue:function(h,w){var _=bi();return IU(_,sn.memoizedState,h,w)},useTransition:function(){var h=Zk($u)[0],w=bi().memoizedState;return[typeof h=="boolean"?h:Bv(h),w]},useSyncExternalStore:pU,useId:HU,useHostTransitionStatus:mM,useFormState:PU,useActionState:PU,useOptimistic:function(h,w){var _=bi();return yU(_,sn,h,w)},useMemoCache:rM,useCacheRefresh:qU};hM.useEffectEvent=MU;var KU={readContext:_s,use:Xk,useCallback:RU,useContext:_s,useEffect:lM,useImperativeHandle:LU,useInsertionEffect:EU,useLayoutEffect:DU,useMemo:zU,useReducer:iM,useRef:AU,useState:function(){return iM($u)},useDebugValue:cM,useDeferredValue:function(h,w){var _=bi();return sn===null?dM(_,h,w):IU(_,sn.memoizedState,h,w)},useTransition:function(){var h=iM($u)[0],w=bi().memoizedState;return[typeof h=="boolean"?h:Bv(h),w]},useSyncExternalStore:pU,useId:HU,useHostTransitionStatus:mM,useFormState:TU,useActionState:TU,useOptimistic:function(h,w){var _=bi();return sn!==null?yU(_,sn,h,w):(_.baseState=h,[h,_.queue.dispatch])},useMemoCache:rM,useCacheRefresh:qU};KU.useEffectEvent=MU;function fM(h,w,_,M){w=h.memoizedState,_=_(M,w),_=_==null?w:m({},w,_),h.memoizedState=_,h.lanes===0&&(h.updateQueue.baseState=_)}var gM={enqueueSetState:function(h,w,_){h=h._reactInternals;var M=xl(),$=Mp(M);$.payload=w,_!=null&&($.callback=_),w=Ep(h,$,M),w!==null&&(Uo(w,h,M),Rv(w,h,M))},enqueueReplaceState:function(h,w,_){h=h._reactInternals;var M=xl(),$=Mp(M);$.tag=1,$.payload=w,_!=null&&($.callback=_),w=Ep(h,$,M),w!==null&&(Uo(w,h,M),Rv(w,h,M))},enqueueForceUpdate:function(h,w){h=h._reactInternals;var _=xl(),M=Mp(_);M.tag=2,w!=null&&(M.callback=w),w=Ep(h,M,_),w!==null&&(Uo(w,h,_),Rv(w,h,_))}};function YU(h,w,_,M,$,ae,Ce){return h=h.stateNode,typeof h.shouldComponentUpdate=="function"?h.shouldComponentUpdate(M,ae,Ce):w.prototype&&w.prototype.isPureReactComponent?!Ss(_,M)||!Ss($,ae):!0}function QU(h,w,_,M){h=w.state,typeof w.componentWillReceiveProps=="function"&&w.componentWillReceiveProps(_,M),typeof w.UNSAFE_componentWillReceiveProps=="function"&&w.UNSAFE_componentWillReceiveProps(_,M),w.state!==h&&gM.enqueueReplaceState(w,w.state,null)}function If(h,w){var _=w;if("ref"in w){_={};for(var M in w)M!=="ref"&&(_[M]=w[M])}if(h=h.defaultProps){_===w&&(_=m({},_));for(var $ in h)_[$]===void 0&&(_[$]=h[$])}return _}function XU(h){Dk(h)}function ZU(h){console.error(h)}function JU(h){Dk(h)}function a_(h,w){try{var _=h.onUncaughtError;_(w.value,{componentStack:w.stack})}catch(M){setTimeout(function(){throw M})}}function eH(h,w,_){try{var M=h.onCaughtError;M(_.value,{componentStack:_.stack,errorBoundary:w.tag===1?w.stateNode:null})}catch($){setTimeout(function(){throw $})}}function bM(h,w,_){return _=Mp(_),_.tag=3,_.payload={element:null},_.callback=function(){a_(h,w)},_}function tH(h){return h=Mp(h),h.tag=3,h}function aH(h,w,_,M){var $=_.type.getDerivedStateFromError;if(typeof $=="function"){var ae=M.value;h.payload=function(){return $(ae)},h.callback=function(){eH(w,_,M)}}var Ce=_.stateNode;Ce!==null&&typeof Ce.componentDidCatch=="function"&&(h.callback=function(){eH(w,_,M),typeof $!="function"&&(Ip===null?Ip=new Set([this]):Ip.add(this));var He=M.stack;this.componentDidCatch(M.value,{componentStack:He!==null?He:""})})}function Aue(h,w,_,M,$){if(_.flags|=32768,M!==null&&typeof M=="object"&&typeof M.then=="function"){if(w=_.alternate,w!==null&&zb(w,_,$,!0),_=hl.current,_!==null){switch(_.tag){case 31:case 13:return Jl===null?h_():_.alternate===null&&ei===0&&(ei=3),_.flags&=-257,_.flags|=65536,_.lanes=$,M===qk?_.flags|=16384:(w=_.updateQueue,w===null?_.updateQueue=new Set([M]):w.add(M),HM(h,M,$)),!1;case 22:return _.flags|=65536,M===qk?_.flags|=16384:(w=_.updateQueue,w===null?(w={transitions:null,markerInstances:null,retryQueue:new Set([M])},_.updateQueue=w):(_=w.retryQueue,_===null?w.retryQueue=new Set([M]):_.add(M)),HM(h,M,$)),!1}throw Error(n(435,_.tag))}return HM(h,M,$),h_(),!1}if(Dr)return w=hl.current,w!==null?((w.flags&65536)===0&&(w.flags|=256),w.flags|=65536,w.lanes=$,M!==Rj&&(h=Error(n(422),{cause:M}),Mv(Yl(h,_)))):(M!==Rj&&(w=Error(n(423),{cause:M}),Mv(Yl(w,_))),h=h.current.alternate,h.flags|=65536,$&=-$,h.lanes|=$,M=Yl(M,_),$=bM(h.stateNode,M,$),Wj(h,$),ei!==4&&(ei=2)),!1;var ae=Error(n(520),{cause:M});if(ae=Yl(ae,_),Qv===null?Qv=[ae]:Qv.push(ae),ei!==4&&(ei=2),w===null)return!0;M=Yl(M,_),_=w;do{switch(_.tag){case 3:return _.flags|=65536,h=$&-$,_.lanes|=h,h=bM(_.stateNode,M,h),Wj(_,h),!1;case 1:if(w=_.type,ae=_.stateNode,(_.flags&128)===0&&(typeof w.getDerivedStateFromError=="function"||ae!==null&&typeof ae.componentDidCatch=="function"&&(Ip===null||!Ip.has(ae))))return _.flags|=65536,$&=-$,_.lanes|=$,$=tH($),aH($,h,_,M),Wj(_,$),!1}_=_.return}while(_!==null);return!1}var xM=Error(n(461)),Ti=!1;function Cs(h,w,_,M){w.child=h===null?sU(w,null,_,M):Rf(w,h.child,_,M)}function rH(h,w,_,M,$){_=_.render;var ae=w.ref;if("ref"in M){var Ce={};for(var He in M)He!=="ref"&&(Ce[He]=M[He])}else Ce=M;return Ef(w),M=Jj(h,w,_,Ce,ae,$),He=eM(),h!==null&&!Ti?(tM(h,w,$),Wu(h,w,$)):(Dr&&He&&Fj(w),w.flags|=1,Cs(h,w,M,$),w.child)}function nH(h,w,_,M,$){if(h===null){var ae=_.type;return typeof ae=="function"&&!Mj(ae)&&ae.defaultProps===void 0&&_.compare===null?(w.tag=15,w.type=ae,iH(h,w,ae,M,$)):(h=zk(_.type,null,M,w,w.mode,$),h.ref=w.ref,h.return=w,w.child=h)}if(ae=h.child,!PM(h,$)){var Ce=ae.memoizedProps;if(_=_.compare,_=_!==null?_:Ss,_(Ce,M)&&h.ref===w.ref)return Wu(h,w,$)}return w.flags|=1,h=Uu(ae,M),h.ref=w.ref,h.return=w,w.child=h}function iH(h,w,_,M,$){if(h!==null){var ae=h.memoizedProps;if(Ss(ae,M)&&h.ref===w.ref)if(Ti=!1,w.pendingProps=M=ae,PM(h,$))(h.flags&131072)!==0&&(Ti=!0);else return w.lanes=h.lanes,Wu(h,w,$)}return yM(h,w,_,M,$)}function sH(h,w,_,M){var $=M.children,ae=h!==null?h.memoizedState:null;if(h===null&&w.stateNode===null&&(w.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),M.mode==="hidden"){if((w.flags&128)!==0){if(ae=ae!==null?ae.baseLanes|_:_,h!==null){for(M=w.child=h.child,$=0;M!==null;)$=$|M.lanes|M.childLanes,M=M.sibling;M=$&~ae}else M=0,w.child=null;return oH(h,w,ae,_,M)}if((_&536870912)!==0)w.memoizedState={baseLanes:0,cachePool:null},h!==null&&Uk(w,ae!==null?ae.cachePool:null),ae!==null?cU(w,ae):Yj(),dU(w);else return M=w.lanes=536870912,oH(h,w,ae!==null?ae.baseLanes|_:_,_,M)}else ae!==null?(Uk(w,ae.cachePool),cU(w,ae),Fp(),w.memoizedState=null):(h!==null&&Uk(w,null),Yj(),Fp());return Cs(h,w,$,_),w.child}function qv(h,w){return h!==null&&h.tag===22||w.stateNode!==null||(w.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),w.sibling}function oH(h,w,_,M,$){var ae=qj();return ae=ae===null?null:{parent:Pi._currentValue,pool:ae},w.memoizedState={baseLanes:_,cachePool:ae},h!==null&&Uk(w,null),Yj(),dU(w),h!==null&&zb(h,w,M,!0),w.childLanes=$,null}function r_(h,w){return w=i_({mode:w.mode,children:w.children},h.mode),w.ref=h.ref,h.child=w,w.return=h,w}function lH(h,w,_){return Rf(w,h.child,null,_),h=r_(w,w.pendingProps),h.flags|=2,fl(w),w.memoizedState=null,h}function jue(h,w,_){var M=w.pendingProps,$=(w.flags&128)!==0;if(w.flags&=-129,h===null){if(Dr){if(M.mode==="hidden")return h=r_(w,M),w.lanes=536870912,qv(null,h);if(Xj(w),(h=Nn)?(h=vq(h,Zl),h=h!==null&&h.data==="&"?h:null,h!==null&&(w.memoizedState={dehydrated:h,treeContext:Pp!==null?{id:Ld,overflow:Rd}:null,retryLane:536870912,hydrationErrors:null},_=G5(h),_.return=w,w.child=_,ks=w,Nn=null)):h=null,h===null)throw Tp(w);return w.lanes=536870912,null}return r_(w,M)}var ae=h.memoizedState;if(ae!==null){var Ce=ae.dehydrated;if(Xj(w),$)if(w.flags&256)w.flags&=-257,w=lH(h,w,_);else if(w.memoizedState!==null)w.child=h.child,w.flags|=128,w=null;else throw Error(n(558));else if(Ti||zb(h,w,_,!1),$=(_&h.childLanes)!==0,Ti||$){if(M=yn,M!==null&&(Ce=dt(M,_),Ce!==0&&Ce!==ae.retryLane))throw ae.retryLane=Ce,Tf(h,Ce),Uo(M,h,Ce),xM;h_(),w=lH(h,w,_)}else h=ae.treeContext,Nn=ec(Ce.nextSibling),ks=w,Dr=!0,Np=null,Zl=!1,h!==null&&K5(w,h),w=r_(w,M),w.flags|=4096;return w}return h=Uu(h.child,{mode:M.mode,children:M.children}),h.ref=w.ref,w.child=h,h.return=w,h}function n_(h,w){var _=w.ref;if(_===null)h!==null&&h.ref!==null&&(w.flags|=4194816);else{if(typeof _!="function"&&typeof _!="object")throw Error(n(284));(h===null||h.ref!==_)&&(w.flags|=4194816)}}function yM(h,w,_,M,$){return Ef(w),_=Jj(h,w,_,M,void 0,$),M=eM(),h!==null&&!Ti?(tM(h,w,$),Wu(h,w,$)):(Dr&&M&&Fj(w),w.flags|=1,Cs(h,w,_,$),w.child)}function cH(h,w,_,M,$,ae){return Ef(w),w.updateQueue=null,_=mU(w,M,_,$),uU(h),M=eM(),h!==null&&!Ti?(tM(h,w,ae),Wu(h,w,ae)):(Dr&&M&&Fj(w),w.flags|=1,Cs(h,w,_,ae),w.child)}function dH(h,w,_,M,$){if(Ef(w),w.stateNode===null){var ae=Db,Ce=_.contextType;typeof Ce=="object"&&Ce!==null&&(ae=_s(Ce)),ae=new _(M,ae),w.memoizedState=ae.state!==null&&ae.state!==void 0?ae.state:null,ae.updater=gM,w.stateNode=ae,ae._reactInternals=w,ae=w.stateNode,ae.props=M,ae.state=w.memoizedState,ae.refs={},Gj(w),Ce=_.contextType,ae.context=typeof Ce=="object"&&Ce!==null?_s(Ce):Db,ae.state=w.memoizedState,Ce=_.getDerivedStateFromProps,typeof Ce=="function"&&(fM(w,_,Ce,M),ae.state=w.memoizedState),typeof _.getDerivedStateFromProps=="function"||typeof ae.getSnapshotBeforeUpdate=="function"||typeof ae.UNSAFE_componentWillMount!="function"&&typeof ae.componentWillMount!="function"||(Ce=ae.state,typeof ae.componentWillMount=="function"&&ae.componentWillMount(),typeof ae.UNSAFE_componentWillMount=="function"&&ae.UNSAFE_componentWillMount(),Ce!==ae.state&&gM.enqueueReplaceState(ae,ae.state,null),Iv(w,M,ae,$),zv(),ae.state=w.memoizedState),typeof ae.componentDidMount=="function"&&(w.flags|=4194308),M=!0}else if(h===null){ae=w.stateNode;var He=w.memoizedProps,ft=If(_,He);ae.props=ft;var Et=ae.context,qt=_.contextType;Ce=Db,typeof qt=="object"&&qt!==null&&(Ce=_s(qt));var Wt=_.getDerivedStateFromProps;qt=typeof Wt=="function"||typeof ae.getSnapshotBeforeUpdate=="function",He=w.pendingProps!==He,qt||typeof ae.UNSAFE_componentWillReceiveProps!="function"&&typeof ae.componentWillReceiveProps!="function"||(He||Et!==Ce)&&QU(w,ae,M,Ce),jp=!1;var Dt=w.memoizedState;ae.state=Dt,Iv(w,M,ae,$),zv(),Et=w.memoizedState,He||Dt!==Et||jp?(typeof Wt=="function"&&(fM(w,_,Wt,M),Et=w.memoizedState),(ft=jp||YU(w,_,ft,M,Dt,Et,Ce))?(qt||typeof ae.UNSAFE_componentWillMount!="function"&&typeof ae.componentWillMount!="function"||(typeof ae.componentWillMount=="function"&&ae.componentWillMount(),typeof ae.UNSAFE_componentWillMount=="function"&&ae.UNSAFE_componentWillMount()),typeof ae.componentDidMount=="function"&&(w.flags|=4194308)):(typeof ae.componentDidMount=="function"&&(w.flags|=4194308),w.memoizedProps=M,w.memoizedState=Et),ae.props=M,ae.state=Et,ae.context=Ce,M=ft):(typeof ae.componentDidMount=="function"&&(w.flags|=4194308),M=!1)}else{ae=w.stateNode,$j(h,w),Ce=w.memoizedProps,qt=If(_,Ce),ae.props=qt,Wt=w.pendingProps,Dt=ae.context,Et=_.contextType,ft=Db,typeof Et=="object"&&Et!==null&&(ft=_s(Et)),He=_.getDerivedStateFromProps,(Et=typeof He=="function"||typeof ae.getSnapshotBeforeUpdate=="function")||typeof ae.UNSAFE_componentWillReceiveProps!="function"&&typeof ae.componentWillReceiveProps!="function"||(Ce!==Wt||Dt!==ft)&&QU(w,ae,M,ft),jp=!1,Dt=w.memoizedState,ae.state=Dt,Iv(w,M,ae,$),zv();var Ot=w.memoizedState;Ce!==Wt||Dt!==Ot||jp||h!==null&&h.dependencies!==null&&Ok(h.dependencies)?(typeof He=="function"&&(fM(w,_,He,M),Ot=w.memoizedState),(qt=jp||YU(w,_,qt,M,Dt,Ot,ft)||h!==null&&h.dependencies!==null&&Ok(h.dependencies))?(Et||typeof ae.UNSAFE_componentWillUpdate!="function"&&typeof ae.componentWillUpdate!="function"||(typeof ae.componentWillUpdate=="function"&&ae.componentWillUpdate(M,Ot,ft),typeof ae.UNSAFE_componentWillUpdate=="function"&&ae.UNSAFE_componentWillUpdate(M,Ot,ft)),typeof ae.componentDidUpdate=="function"&&(w.flags|=4),typeof ae.getSnapshotBeforeUpdate=="function"&&(w.flags|=1024)):(typeof ae.componentDidUpdate!="function"||Ce===h.memoizedProps&&Dt===h.memoizedState||(w.flags|=4),typeof ae.getSnapshotBeforeUpdate!="function"||Ce===h.memoizedProps&&Dt===h.memoizedState||(w.flags|=1024),w.memoizedProps=M,w.memoizedState=Ot),ae.props=M,ae.state=Ot,ae.context=ft,M=qt):(typeof ae.componentDidUpdate!="function"||Ce===h.memoizedProps&&Dt===h.memoizedState||(w.flags|=4),typeof ae.getSnapshotBeforeUpdate!="function"||Ce===h.memoizedProps&&Dt===h.memoizedState||(w.flags|=1024),M=!1)}return ae=M,n_(h,w),M=(w.flags&128)!==0,ae||M?(ae=w.stateNode,_=M&&typeof _.getDerivedStateFromError!="function"?null:ae.render(),w.flags|=1,h!==null&&M?(w.child=Rf(w,h.child,null,$),w.child=Rf(w,null,_,$)):Cs(h,w,_,$),w.memoizedState=ae.state,h=w.child):h=Wu(h,w,$),h}function uH(h,w,_,M){return jf(),w.flags|=256,Cs(h,w,_,M),w.child}var vM={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function wM(h){return{baseLanes:h,cachePool:eU()}}function SM(h,w,_){return h=h!==null?h.childLanes&~_:0,w&&(h|=bl),h}function mH(h,w,_){var M=w.pendingProps,$=!1,ae=(w.flags&128)!==0,Ce;if((Ce=ae)||(Ce=h!==null&&h.memoizedState===null?!1:(gi.current&2)!==0),Ce&&($=!0,w.flags&=-129),Ce=(w.flags&32)!==0,w.flags&=-33,h===null){if(Dr){if($?Dp(w):Fp(),(h=Nn)?(h=vq(h,Zl),h=h!==null&&h.data!=="&"?h:null,h!==null&&(w.memoizedState={dehydrated:h,treeContext:Pp!==null?{id:Ld,overflow:Rd}:null,retryLane:536870912,hydrationErrors:null},_=G5(h),_.return=w,w.child=_,ks=w,Nn=null)):h=null,h===null)throw Tp(w);return nE(h)?w.lanes=32:w.lanes=536870912,null}var He=M.children;return M=M.fallback,$?(Fp(),$=w.mode,He=i_({mode:"hidden",children:He},$),M=Af(M,$,_,null),He.return=w,M.return=w,He.sibling=M,w.child=He,M=w.child,M.memoizedState=wM(_),M.childLanes=SM(h,Ce,_),w.memoizedState=vM,qv(null,M)):(Dp(w),kM(w,He))}var ft=h.memoizedState;if(ft!==null&&(He=ft.dehydrated,He!==null)){if(ae)w.flags&256?(Dp(w),w.flags&=-257,w=_M(h,w,_)):w.memoizedState!==null?(Fp(),w.child=h.child,w.flags|=128,w=null):(Fp(),He=M.fallback,$=w.mode,M=i_({mode:"visible",children:M.children},$),He=Af(He,$,_,null),He.flags|=2,M.return=w,He.return=w,M.sibling=He,w.child=M,Rf(w,h.child,null,_),M=w.child,M.memoizedState=wM(_),M.childLanes=SM(h,Ce,_),w.memoizedState=vM,w=qv(null,M));else if(Dp(w),nE(He)){if(Ce=He.nextSibling&&He.nextSibling.dataset,Ce)var Et=Ce.dgst;Ce=Et,M=Error(n(419)),M.stack="",M.digest=Ce,Mv({value:M,source:null,stack:null}),w=_M(h,w,_)}else if(Ti||zb(h,w,_,!1),Ce=(_&h.childLanes)!==0,Ti||Ce){if(Ce=yn,Ce!==null&&(M=dt(Ce,_),M!==0&&M!==ft.retryLane))throw ft.retryLane=M,Tf(h,M),Uo(Ce,h,M),xM;rE(He)||h_(),w=_M(h,w,_)}else rE(He)?(w.flags|=192,w.child=h.child,w=null):(h=ft.treeContext,Nn=ec(He.nextSibling),ks=w,Dr=!0,Np=null,Zl=!1,h!==null&&K5(w,h),w=kM(w,M.children),w.flags|=4096);return w}return $?(Fp(),He=M.fallback,$=w.mode,ft=h.child,Et=ft.sibling,M=Uu(ft,{mode:"hidden",children:M.children}),M.subtreeFlags=ft.subtreeFlags&65011712,Et!==null?He=Uu(Et,He):(He=Af(He,$,_,null),He.flags|=2),He.return=w,M.return=w,M.sibling=He,w.child=M,qv(null,M),M=w.child,He=h.child.memoizedState,He===null?He=wM(_):($=He.cachePool,$!==null?(ft=Pi._currentValue,$=$.parent!==ft?{parent:ft,pool:ft}:$):$=eU(),He={baseLanes:He.baseLanes|_,cachePool:$}),M.memoizedState=He,M.childLanes=SM(h,Ce,_),w.memoizedState=vM,qv(h.child,M)):(Dp(w),_=h.child,h=_.sibling,_=Uu(_,{mode:"visible",children:M.children}),_.return=w,_.sibling=null,h!==null&&(Ce=w.deletions,Ce===null?(w.deletions=[h],w.flags|=16):Ce.push(h)),w.child=_,w.memoizedState=null,_)}function kM(h,w){return w=i_({mode:"visible",children:w},h.mode),w.return=h,h.child=w}function i_(h,w){return h=pl(22,h,null,w),h.lanes=0,h}function _M(h,w,_){return Rf(w,h.child,null,_),h=kM(w,w.pendingProps.children),h.flags|=2,w.memoizedState=null,h}function pH(h,w,_){h.lanes|=w;var M=h.alternate;M!==null&&(M.lanes|=w),Oj(h.return,w,_)}function CM(h,w,_,M,$,ae){var Ce=h.memoizedState;Ce===null?h.memoizedState={isBackwards:w,rendering:null,renderingStartTime:0,last:M,tail:_,tailMode:$,treeForkCount:ae}:(Ce.isBackwards=w,Ce.rendering=null,Ce.renderingStartTime=0,Ce.last=M,Ce.tail=_,Ce.tailMode=$,Ce.treeForkCount=ae)}function hH(h,w,_){var M=w.pendingProps,$=M.revealOrder,ae=M.tail;M=M.children;var Ce=gi.current,He=(Ce&2)!==0;if(He?(Ce=Ce&1|2,w.flags|=128):Ce&=1,me(gi,Ce),Cs(h,w,M,_),M=Dr?jv:0,!He&&h!==null&&(h.flags&128)!==0)e:for(h=w.child;h!==null;){if(h.tag===13)h.memoizedState!==null&&pH(h,_,w);else if(h.tag===19)pH(h,_,w);else if(h.child!==null){h.child.return=h,h=h.child;continue}if(h===w)break e;for(;h.sibling===null;){if(h.return===null||h.return===w)break e;h=h.return}h.sibling.return=h.return,h=h.sibling}switch($){case"forwards":for(_=w.child,$=null;_!==null;)h=_.alternate,h!==null&&Wk(h)===null&&($=_),_=_.sibling;_=$,_===null?($=w.child,w.child=null):($=_.sibling,_.sibling=null),CM(w,!1,$,_,ae,M);break;case"backwards":case"unstable_legacy-backwards":for(_=null,$=w.child,w.child=null;$!==null;){if(h=$.alternate,h!==null&&Wk(h)===null){w.child=$;break}h=$.sibling,$.sibling=_,_=$,$=h}CM(w,!0,_,null,ae,M);break;case"together":CM(w,!1,null,null,void 0,M);break;default:w.memoizedState=null}return w.child}function Wu(h,w,_){if(h!==null&&(w.dependencies=h.dependencies),zp|=w.lanes,(_&w.childLanes)===0)if(h!==null){if(zb(h,w,_,!1),(_&w.childLanes)===0)return null}else return null;if(h!==null&&w.child!==h.child)throw Error(n(153));if(w.child!==null){for(h=w.child,_=Uu(h,h.pendingProps),w.child=_,_.return=w;h.sibling!==null;)h=h.sibling,_=_.sibling=Uu(h,h.pendingProps),_.return=w;_.sibling=null}return w.child}function PM(h,w){return(h.lanes&w)!==0?!0:(h=h.dependencies,!!(h!==null&&Ok(h)))}function Mue(h,w,_){switch(w.tag){case 3:A(w,w.stateNode.containerInfo),Ap(w,Pi,h.memoizedState.cache),jf();break;case 27:case 5:H(w);break;case 4:A(w,w.stateNode.containerInfo);break;case 10:Ap(w,w.type,w.memoizedProps.value);break;case 31:if(w.memoizedState!==null)return w.flags|=128,Xj(w),null;break;case 13:var M=w.memoizedState;if(M!==null)return M.dehydrated!==null?(Dp(w),w.flags|=128,null):(_&w.child.childLanes)!==0?mH(h,w,_):(Dp(w),h=Wu(h,w,_),h!==null?h.sibling:null);Dp(w);break;case 19:var $=(h.flags&128)!==0;if(M=(_&w.childLanes)!==0,M||(zb(h,w,_,!1),M=(_&w.childLanes)!==0),$){if(M)return hH(h,w,_);w.flags|=128}if($=w.memoizedState,$!==null&&($.rendering=null,$.tail=null,$.lastEffect=null),me(gi,gi.current),M)break;return null;case 22:return w.lanes=0,sH(h,w,_,w.pendingProps);case 24:Ap(w,Pi,h.memoizedState.cache)}return Wu(h,w,_)}function fH(h,w,_){if(h!==null)if(h.memoizedProps!==w.pendingProps)Ti=!0;else{if(!PM(h,_)&&(w.flags&128)===0)return Ti=!1,Mue(h,w,_);Ti=(h.flags&131072)!==0}else Ti=!1,Dr&&(w.flags&1048576)!==0&&W5(w,jv,w.index);switch(w.lanes=0,w.tag){case 16:e:{var M=w.pendingProps;if(h=Ff(w.elementType),w.type=h,typeof h=="function")Mj(h)?(M=If(h,M),w.tag=1,w=dH(null,w,h,M,_)):(w.tag=0,w=yM(null,w,h,M,_));else{if(h!=null){var $=h.$$typeof;if($===C){w.tag=11,w=rH(null,w,h,M,_);break e}else if($===j){w.tag=14,w=nH(null,w,h,M,_);break e}}throw w=z(h)||h,Error(n(306,w,""))}}return w;case 0:return yM(h,w,w.type,w.pendingProps,_);case 1:return M=w.type,$=If(M,w.pendingProps),dH(h,w,M,$,_);case 3:e:{if(A(w,w.stateNode.containerInfo),h===null)throw Error(n(387));M=w.pendingProps;var ae=w.memoizedState;$=ae.element,$j(h,w),Iv(w,M,null,_);var Ce=w.memoizedState;if(M=Ce.cache,Ap(w,Pi,M),M!==ae.cache&&Bj(w,[Pi],_,!0),zv(),M=Ce.element,ae.isDehydrated)if(ae={element:M,isDehydrated:!1,cache:Ce.cache},w.updateQueue.baseState=ae,w.memoizedState=ae,w.flags&256){w=uH(h,w,M,_);break e}else if(M!==$){$=Yl(Error(n(424)),w),Mv($),w=uH(h,w,M,_);break e}else for(h=w.stateNode.containerInfo,h.nodeType===9?h=h.body:h=h.nodeName==="HTML"?h.ownerDocument.body:h,Nn=ec(h.firstChild),ks=w,Dr=!0,Np=null,Zl=!0,_=sU(w,null,M,_),w.child=_;_;)_.flags=_.flags&-3|4096,_=_.sibling;else{if(jf(),M===$){w=Wu(h,w,_);break e}Cs(h,w,M,_)}w=w.child}return w;case 26:return n_(h,w),h===null?(_=Pq(w.type,null,w.pendingProps,null))?w.memoizedState=_:Dr||(_=w.type,h=w.pendingProps,M=w_(U.current).createElement(_),M[gt]=w,M[Pt]=h,Ps(M,_,h),it(M),w.stateNode=M):w.memoizedState=Pq(w.type,h.memoizedProps,w.pendingProps,h.memoizedState),null;case 27:return H(w),h===null&&Dr&&(M=w.stateNode=kq(w.type,w.pendingProps,U.current),ks=w,Zl=!0,$=Nn,Hp(w.type)?(iE=$,Nn=ec(M.firstChild)):Nn=$),Cs(h,w,w.pendingProps.children,_),n_(h,w),h===null&&(w.flags|=4194304),w.child;case 5:return h===null&&Dr&&(($=M=Nn)&&(M=ome(M,w.type,w.pendingProps,Zl),M!==null?(w.stateNode=M,ks=w,Nn=ec(M.firstChild),Zl=!1,$=!0):$=!1),$||Tp(w)),H(w),$=w.type,ae=w.pendingProps,Ce=h!==null?h.memoizedProps:null,M=ae.children,eE($,ae)?M=null:Ce!==null&&eE($,Ce)&&(w.flags|=32),w.memoizedState!==null&&($=Jj(h,w,Sue,null,null,_),n0._currentValue=$),n_(h,w),Cs(h,w,M,_),w.child;case 6:return h===null&&Dr&&((h=_=Nn)&&(_=lme(_,w.pendingProps,Zl),_!==null?(w.stateNode=_,ks=w,Nn=null,h=!0):h=!1),h||Tp(w)),null;case 13:return mH(h,w,_);case 4:return A(w,w.stateNode.containerInfo),M=w.pendingProps,h===null?w.child=Rf(w,null,M,_):Cs(h,w,M,_),w.child;case 11:return rH(h,w,w.type,w.pendingProps,_);case 7:return Cs(h,w,w.pendingProps,_),w.child;case 8:return Cs(h,w,w.pendingProps.children,_),w.child;case 12:return Cs(h,w,w.pendingProps.children,_),w.child;case 10:return M=w.pendingProps,Ap(w,w.type,M.value),Cs(h,w,M.children,_),w.child;case 9:return $=w.type._context,M=w.pendingProps.children,Ef(w),$=_s($),M=M($),w.flags|=1,Cs(h,w,M,_),w.child;case 14:return nH(h,w,w.type,w.pendingProps,_);case 15:return iH(h,w,w.type,w.pendingProps,_);case 19:return hH(h,w,_);case 31:return jue(h,w,_);case 22:return sH(h,w,_,w.pendingProps);case 24:return Ef(w),M=_s(Pi),h===null?($=qj(),$===null&&($=yn,ae=Uj(),$.pooledCache=ae,ae.refCount++,ae!==null&&($.pooledCacheLanes|=_),$=ae),w.memoizedState={parent:M,cache:$},Gj(w),Ap(w,Pi,$)):((h.lanes&_)!==0&&($j(h,w),Iv(w,null,null,_),zv()),$=h.memoizedState,ae=w.memoizedState,$.parent!==M?($={parent:M,cache:M},w.memoizedState=$,w.lanes===0&&(w.memoizedState=w.updateQueue.baseState=$),Ap(w,Pi,M)):(M=ae.cache,Ap(w,Pi,M),M!==$.cache&&Bj(w,[Pi],_,!0))),Cs(h,w,w.pendingProps.children,_),w.child;case 29:throw w.pendingProps}throw Error(n(156,w.tag))}function Ku(h){h.flags|=4}function NM(h,w,_,M,$){if((w=(h.mode&32)!==0)&&(w=!1),w){if(h.flags|=16777216,($&335544128)===$)if(h.stateNode.complete)h.flags|=8192;else if(HH())h.flags|=8192;else throw Lf=qk,Vj}else h.flags&=-16777217}function gH(h,w){if(w.type!=="stylesheet"||(w.state.loading&4)!==0)h.flags&=-16777217;else if(h.flags|=16777216,!Mq(w))if(HH())h.flags|=8192;else throw Lf=qk,Vj}function s_(h,w){w!==null&&(h.flags|=4),h.flags&16384&&(w=h.tag!==22?$e():536870912,h.lanes|=w,Yb|=w)}function Vv(h,w){if(!Dr)switch(h.tailMode){case"hidden":w=h.tail;for(var _=null;w!==null;)w.alternate!==null&&(_=w),w=w.sibling;_===null?h.tail=null:_.sibling=null;break;case"collapsed":_=h.tail;for(var M=null;_!==null;)_.alternate!==null&&(M=_),_=_.sibling;M===null?w||h.tail===null?h.tail=null:h.tail.sibling=null:M.sibling=null}}function Tn(h){var w=h.alternate!==null&&h.alternate.child===h.child,_=0,M=0;if(w)for(var $=h.child;$!==null;)_|=$.lanes|$.childLanes,M|=$.subtreeFlags&65011712,M|=$.flags&65011712,$.return=h,$=$.sibling;else for($=h.child;$!==null;)_|=$.lanes|$.childLanes,M|=$.subtreeFlags,M|=$.flags,$.return=h,$=$.sibling;return h.subtreeFlags|=M,h.childLanes=_,w}function Eue(h,w,_){var M=w.pendingProps;switch(Lj(w),w.tag){case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return Tn(w),null;case 1:return Tn(w),null;case 3:return _=w.stateNode,M=null,h!==null&&(M=h.memoizedState.cache),w.memoizedState.cache!==M&&(w.flags|=2048),Vu(Pi),D(),_.pendingContext&&(_.context=_.pendingContext,_.pendingContext=null),(h===null||h.child===null)&&(Rb(w)?Ku(w):h===null||h.memoizedState.isDehydrated&&(w.flags&256)===0||(w.flags|=1024,zj())),Tn(w),null;case 26:var $=w.type,ae=w.memoizedState;return h===null?(Ku(w),ae!==null?(Tn(w),gH(w,ae)):(Tn(w),NM(w,$,null,M,_))):ae?ae!==h.memoizedState?(Ku(w),Tn(w),gH(w,ae)):(Tn(w),w.flags&=-16777217):(h=h.memoizedProps,h!==M&&Ku(w),Tn(w),NM(w,$,h,M,_)),null;case 27:if(W(w),_=U.current,$=w.type,h!==null&&w.stateNode!=null)h.memoizedProps!==M&&Ku(w);else{if(!M){if(w.stateNode===null)throw Error(n(166));return Tn(w),null}h=pe.current,Rb(w)?Y5(w):(h=kq($,M,_),w.stateNode=h,Ku(w))}return Tn(w),null;case 5:if(W(w),$=w.type,h!==null&&w.stateNode!=null)h.memoizedProps!==M&&Ku(w);else{if(!M){if(w.stateNode===null)throw Error(n(166));return Tn(w),null}if(ae=pe.current,Rb(w))Y5(w);else{var Ce=w_(U.current);switch(ae){case 1:ae=Ce.createElementNS("http://www.w3.org/2000/svg",$);break;case 2:ae=Ce.createElementNS("http://www.w3.org/1998/Math/MathML",$);break;default:switch($){case"svg":ae=Ce.createElementNS("http://www.w3.org/2000/svg",$);break;case"math":ae=Ce.createElementNS("http://www.w3.org/1998/Math/MathML",$);break;case"script":ae=Ce.createElement("div"),ae.innerHTML=" +