diff --git a/CHANGELOG.md b/CHANGELOG.md index 549e90189..4dfe5750b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ All notable changes to Bambuddy will be documented in this file. ## [0.2.4b2] - Unreleased ### Added +- **Home Assistant addon detection — Settings → Updates and the in-app update banner now defer to the HA Supervisor** ([#1167](https://github.com/maziggy/bambuddy/issues/1167), reported by @Spegeli) — Bambuddy already shipped `HA_URL`/`HA_TOKEN` env-var support specifically labelled "for HA Add-on deployments" ([#283](https://github.com/maziggy/bambuddy/issues/283)) and a community-maintained HA addon (`hobbypunk90/homeassistant-addon-bambuddy`) exists upstream, so an HA-supervised installation is a real first-class deployment shape. Until now though, the update UI didn't know about it: HA addon users got the same "Update available!" banner as everyone else and, if they clicked through to Settings, saw the docker-compose snippet ("`docker compose pull && docker compose up -d`") which they cannot run from inside an HA addon container — that's the Supervisor's job. Detection uses the canonical signal: HA Supervisor injects `SUPERVISOR_TOKEN` into every addon container, and that variable is not set in any other environment. A new `_is_ha_addon()` helper in `backend/app/api/routes/updates.py` flips a request-level boolean which `/updates/check` surfaces as `is_ha_addon: bool` + an extended `update_method: 'git' | 'docker' | 'ha_addon'` enum. The check is checked **before** Docker on `/updates/apply` because HA addons *are* Docker containers — checking docker first would mis-classify them and serve the wrong message; the response also keeps `is_docker: true` alongside `is_ha_addon: true` so older frontend bundles still hit a managed-deployment branch (degrading to the Docker UX) instead of rendering an in-app Install button that can't work. Frontend branches identically: `SettingsPage.tsx`'s update card checks `is_ha_addon` first and renders "Updates are managed by the Home Assistant Supervisor. Open Settings → Add-ons → Bambuddy in Home Assistant to install the new version." in place of the docker-compose hint; `Layout.tsx`'s update banner is suppressed entirely for HA addons since the HA Supervisor's own update notification already surfaces the new version natively in the HA UI and a duplicate Bambuddy banner would just be noise that links to a page that says "go to HA". Plain Docker deployments are unaffected — the existing docker-compose hint and the in-app banner still render the same way they did. Localised across all 8 UI languages (en/de/fr/it/ja/pt-BR/zh-CN/zh-TW) with full translations of the new `settings.updateViaHomeAssistant` string. 6 new tests pin the contract: 3 backend unit tests for `_is_ha_addon()` (env var present → true, absent → false, empty string treated as unset to guard against shells that export it empty), 1 backend integration test for the HA-precedes-Docker rejection on `/updates/apply` (asserts the message says "Home Assistant" and not "Docker Compose"), 2 backend integration tests for `/updates/check` covering the HA-addon branch (`update_method == "ha_addon"`, both flags true) and the plain-Docker branch (`is_ha_addon: false`, `update_method == "docker"`); 2 frontend SettingsPage tests pin the mutually-exclusive UI rendering (HA branch shows the HA copy and not the docker-compose snippet; Docker branch shows the snippet and not the HA copy, neither shows the Install button); 2 frontend Layout tests pin the banner suppression for HA and its retention for plain Docker. + - **Filament Track Switch (FTS) support — print modal filament dropdown is no longer empty when an X2D / H2D has the FTS accessory installed** ([#1162](https://github.com/maziggy/bambuddy/issues/1162), reported by @mkavalecz) — When the FTS accessory is installed the printer's MQTT changes one nibble of the per-AMS `info` bitmask: bits 8-11 flip from a fixed extruder ID (0x0 / 0x1) to `0xE` ("uninitialized"), because the AMS is no longer wired to a single nozzle — the FTS dynamically routes any slot to either extruder. Bambuddy's MQTT parser already skipped 0xE entries when building `ams_extruder_map` (matching BambuStudio's reading for boot-time transient state), so with the FTS installed the map ended up empty and the print modal's filament dropdown — which filters by `extruderId === nozzle_id` to prevent cross-nozzle assignment ("position of left hotend is abnormal" failures) — filtered out *every* loaded slot. Net effect: empty Filament Mapping dropdown on every dual-nozzle print with the FTS, even when the AMS was fully loaded with the right material. Detection comes from a new MQTT field — `print.device.fila_switch` — which is non-null only when the accessory is installed; it carries the routing topology as two arrays: `in[track] = currently fed slot (-1 = empty)` and `out[track] = extruder this track terminates at`. The fix surfaces this through a new `FilaSwitchState` dataclass on `PrinterState` (`installed`, `in_slots`, `out_extruders`, `stat`, `info`) and the equivalent `FilaSwitchResponse` Pydantic schema on the `GET /printers/{id}/status` route. Frontend (`useFilamentMapping.ts` + `FilamentMapping.tsx`) skips the per-extruder filter when `printerStatus.fila_switch?.installed === true` so any compatible AMS slot can satisfy any nozzle's filament requirement, since the FTS handles the routing. Slots currently fed into a track also get a routing badge in the dropdown — `[L]` or `[R]` — so the user can tell at a glance which slot the FTS is currently routing where (idle slots get no badge: they can be routed to either extruder on demand). The hard "no cross-nozzle assignment" filter on real dual-nozzle printers without the FTS stays untouched (still trips the same way it always has — `fila_switch == null` keeps the existing behaviour). 4 backend tests in `test_bambu_mqtt.py::TestFilamentTrackSwitchDetection` (default-not-installed, detect-from-MQTT-using-the-reporter's-bundle, no-fila_switch-field-stays-not-installed, missing-in-out-arrays-don't-crash) and 2 frontend tests in `useFilamentMapping.test.ts` (FTS-active drops the nozzle filter; explicit `fila_switch: null` keeps the filter applied). Upstream fila_switch payloads with anything other than the documented shape are tolerated — `installed` flips on the *presence* of the field, the routing arrays default to empty lists if missing, and the dropdown skips the badge for slots not currently in `in_slots`. ### Fixed diff --git a/backend/app/api/routes/updates.py b/backend/app/api/routes/updates.py index f940e27d4..ba2459a54 100644 --- a/backend/app/api/routes/updates.py +++ b/backend/app/api/routes/updates.py @@ -54,6 +54,16 @@ def _is_docker_environment() -> bool: return False +def _is_ha_addon() -> bool: + """Detect if running as a Home Assistant Supervisor addon. + + HA Supervisor injects ``SUPERVISOR_TOKEN`` into every addon container; + the variable is not set in any other environment, so a single env-var + check is sufficient with no false-positive surface. + """ + return bool(os.environ.get("SUPERVISOR_TOKEN")) + + def _find_executable(name: str) -> str | None: """Find an executable in PATH or common locations.""" # Try standard PATH first @@ -355,6 +365,13 @@ async def check_for_updates( } is_docker = _is_docker_environment() + is_ha_addon = _is_ha_addon() + if is_ha_addon: + update_method = "ha_addon" + elif is_docker: + update_method = "docker" + else: + update_method = "git" return { "update_available": update_available, "current_version": APP_VERSION, @@ -364,7 +381,8 @@ async def check_for_updates( "release_url": release_url, "published_at": published_at, "is_docker": is_docker, - "update_method": "docker" if is_docker else "git", + "is_ha_addon": is_ha_addon, + "update_method": update_method, } except httpx.HTTPError as e: @@ -669,7 +687,20 @@ async def apply_update( "status": _update_status, } - # Check if running in Docker + # Check for managed deployment shapes that own the update lifecycle. + # HA addons are also Docker, so check HA first to surface the more + # specific message. + if _is_ha_addon(): + return { + "success": False, + "is_ha_addon": True, + "is_docker": True, + "message": ( + "Bambuddy is running as a Home Assistant addon. " + "Updates are managed by the Home Assistant Supervisor " + "(Settings → Add-ons → Bambuddy → Update)." + ), + } if _is_docker_environment(): return { "success": False, diff --git a/backend/tests/integration/test_updates_api.py b/backend/tests/integration/test_updates_api.py index 17d490794..fc8c41069 100644 --- a/backend/tests/integration/test_updates_api.py +++ b/backend/tests/integration/test_updates_api.py @@ -15,11 +15,34 @@ class TestUpdatesAPI: @pytest.mark.asyncio async def test_apply_update_docker_rejection(self, async_client: AsyncClient): - with patch("backend.app.api.routes.updates._is_docker_environment", return_value=True): + with ( + patch("backend.app.api.routes.updates._is_ha_addon", return_value=False), + patch("backend.app.api.routes.updates._is_docker_environment", return_value=True), + ): response = await async_client.post("/api/v1/updates/apply") result = response.json() assert result["success"] is False assert result["is_docker"] is True + assert result.get("is_ha_addon") is not True + # Docker message tells the user to docker compose, not HA. + assert "Docker Compose" in result["message"] + + @pytest.mark.asyncio + async def test_apply_update_ha_addon_rejection(self, async_client: AsyncClient): + """HA addons are also Docker, so the route must check HA first and + return the HA-specific message — otherwise users see "run docker + compose" advice they can't follow.""" + with ( + patch("backend.app.api.routes.updates._is_ha_addon", return_value=True), + patch("backend.app.api.routes.updates._is_docker_environment", return_value=True), + ): + response = await async_client.post("/api/v1/updates/apply") + result = response.json() + assert result["success"] is False + assert result["is_ha_addon"] is True + assert result["is_docker"] is True + assert "Home Assistant" in result["message"] + assert "Docker Compose" not in result["message"] @pytest.mark.asyncio async def test_apply_update_non_docker(self, async_client: AsyncClient): @@ -27,6 +50,7 @@ class TestUpdatesAPI: to prevent side effects (network call to GitHub releases API + actual git/pip subprocesses).""" with ( + patch("backend.app.api.routes.updates._is_ha_addon", return_value=False), patch("backend.app.api.routes.updates._is_docker_environment", return_value=False), patch( "backend.app.api.routes.updates._discover_target_release", @@ -44,6 +68,119 @@ class TestUpdatesAPI: with patch("os.path.exists", return_value=True): assert _is_docker_environment() is True + def test_is_ha_addon_detects_supervisor_token(self): + """HA Supervisor sets SUPERVISOR_TOKEN on every addon container. + That env-var alone is the canonical HA-addon signal.""" + from backend.app.api.routes.updates import _is_ha_addon + + with patch.dict("os.environ", {"SUPERVISOR_TOKEN": "abc123"}, clear=False): + assert _is_ha_addon() is True + + def test_is_ha_addon_false_outside_supervisor(self): + from backend.app.api.routes.updates import _is_ha_addon + + with patch.dict("os.environ", {}, clear=True): + assert _is_ha_addon() is False + + def test_is_ha_addon_empty_token_treated_as_unset(self): + """An empty string is not a real token — guard against shells that + export the variable empty.""" + from backend.app.api.routes.updates import _is_ha_addon + + with patch.dict("os.environ", {"SUPERVISOR_TOKEN": ""}, clear=False): + assert _is_ha_addon() is False + + @pytest.mark.asyncio + async def test_check_returns_ha_addon_flag_and_method(self, async_client: AsyncClient): + """`/updates/check` must surface the deployment shape so the frontend + can pick the right CTA. HA must take precedence over Docker because + HA addons run *inside* a Docker container — checking docker first + would mis-classify them.""" + import httpx as _httpx + + fake_release = { + "tag_name": "v999.9.9", + "name": "Far Future Release", + "body": "", + "html_url": "https://example.invalid/r", + "published_at": "2099-01-01T00:00:00Z", + } + + class _Resp: + status_code = 200 + + def raise_for_status(self): + return None + + def json(self): + return [fake_release] + + class _FakeClient: + async def __aenter__(self): + return self + + async def __aexit__(self, *_): + return None + + async def get(self, *_, **__): + return _Resp() + + with ( + patch.object(_httpx, "AsyncClient", _FakeClient), + patch("backend.app.api.routes.updates._is_ha_addon", return_value=True), + patch("backend.app.api.routes.updates._is_docker_environment", return_value=True), + ): + response = await async_client.get("/api/v1/updates/check") + body = response.json() + assert body["is_ha_addon"] is True + assert body["update_method"] == "ha_addon" + # is_docker is preserved alongside so older frontend bundles still + # hit a managed-deployment branch (degrades to Docker UX) instead of + # rendering the in-app Install button. + assert body["is_docker"] is True + + @pytest.mark.asyncio + async def test_check_docker_only_returns_docker_method(self, async_client: AsyncClient): + import httpx as _httpx + + fake_release = { + "tag_name": "v999.9.9", + "name": "Far Future Release", + "body": "", + "html_url": "https://example.invalid/r", + "published_at": "2099-01-01T00:00:00Z", + } + + class _Resp: + status_code = 200 + + def raise_for_status(self): + return None + + def json(self): + return [fake_release] + + class _FakeClient: + async def __aenter__(self): + return self + + async def __aexit__(self, *_): + return None + + async def get(self, *_, **__): + return _Resp() + + with ( + patch.object(_httpx, "AsyncClient", _FakeClient), + patch("backend.app.api.routes.updates._is_ha_addon", return_value=False), + patch("backend.app.api.routes.updates._is_docker_environment", return_value=True), + ): + response = await async_client.get("/api/v1/updates/check") + body = response.json() + assert body["is_ha_addon"] is False + assert body["is_docker"] is True + assert body["update_method"] == "docker" + def test_parse_version(self): from backend.app.api.routes.updates import parse_version @@ -261,6 +398,7 @@ class TestUpdatesAPI: return "v0.2.4b1" with ( + patch.object(updates_module, "_is_ha_addon", return_value=False), patch.object(updates_module, "_is_docker_environment", return_value=False), patch.object(updates_module, "_perform_update", side_effect=fake_perform_update), patch.object(updates_module, "_discover_target_release", side_effect=fake_discover), @@ -289,6 +427,7 @@ class TestUpdatesAPI: updates_module._update_status = {"status": "idle", "progress": 0, "message": "", "error": None} with ( + patch.object(updates_module, "_is_ha_addon", return_value=False), patch.object(updates_module, "_is_docker_environment", return_value=False), patch.object(updates_module, "_discover_target_release", side_effect=fake_discover), ): diff --git a/frontend/src/__tests__/components/Layout.test.tsx b/frontend/src/__tests__/components/Layout.test.tsx index 4615fa490..0d8a1c6ff 100644 --- a/frontend/src/__tests__/components/Layout.test.tsx +++ b/frontend/src/__tests__/components/Layout.test.tsx @@ -255,4 +255,54 @@ describe('Layout', () => { }); }); }); + + describe('update banner suppression for HA addon', () => { + // HA Supervisor surfaces its own update notification natively in the HA + // UI, so the in-app banner would be duplicate noise that links to a page + // that just says "update via HA". Suppress it for HA addon deployments. + it('hides the update-available banner when running as an HA addon', async () => { + server.use( + http.get('/api/v1/updates/check', () => { + return HttpResponse.json({ + update_available: true, + current_version: '0.2.4', + latest_version: '0.2.5', + is_docker: true, + is_ha_addon: true, + update_method: 'ha_addon', + }); + }), + ); + + render(); + + await waitFor(() => { + const sidebar = document.querySelector('aside'); + expect(sidebar).toBeInTheDocument(); + }); + + expect(document.body.textContent).not.toContain('Update available'); + }); + + it('still shows the update-available banner for plain Docker deployments', async () => { + server.use( + http.get('/api/v1/updates/check', () => { + return HttpResponse.json({ + update_available: true, + current_version: '0.2.4', + latest_version: '0.2.5', + is_docker: true, + is_ha_addon: false, + update_method: 'docker', + }); + }), + ); + + render(); + + await waitFor(() => { + expect(document.body.textContent).toContain('0.2.5'); + }); + }); + }); }); diff --git a/frontend/src/__tests__/pages/SettingsPage.test.tsx b/frontend/src/__tests__/pages/SettingsPage.test.tsx index b1422c68b..2afc6ff7c 100644 --- a/frontend/src/__tests__/pages/SettingsPage.test.tsx +++ b/frontend/src/__tests__/pages/SettingsPage.test.tsx @@ -174,6 +174,73 @@ describe('SettingsPage', () => { }); }); + describe('update CTA per deployment shape', () => { + // The update card branches on the deployment shape returned by + // /updates/check. Each branch is mutually exclusive — verify the right + // one wins so HA addon users never see the docker-compose snippet + // (which they can't run from inside an HA addon container) and Docker + // users never see the in-app Install button (which would no-op). + const renderWithUpdateCheck = async ( + checkBody: Record, + ) => { + server.use( + http.get('/api/v1/settings/', () => + HttpResponse.json({ ...mockSettings, check_updates: true }), + ), + http.get('/api/v1/updates/check', () => HttpResponse.json(checkBody)), + ); + render(); + await waitFor(() => { + expect(screen.getByText('Updates')).toBeInTheDocument(); + }); + }; + + it('shows the HA Supervisor message when running as an HA addon', async () => { + await renderWithUpdateCheck({ + update_available: true, + current_version: '0.2.4', + latest_version: '0.2.5', + release_name: '0.2.5', + release_notes: '', + release_url: 'https://example.invalid/r', + published_at: '2099-01-01T00:00:00Z', + is_docker: true, + is_ha_addon: true, + update_method: 'ha_addon', + }); + + await waitFor(() => { + expect( + screen.getByText(/Home Assistant Supervisor/i), + ).toBeInTheDocument(); + }); + // Docker hint must NOT render — HA branch wins. + expect(screen.queryByText('docker compose pull && docker compose up -d')).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: /install update/i })).not.toBeInTheDocument(); + }); + + it('shows the docker-compose snippet for Docker (non-HA) deployments', async () => { + await renderWithUpdateCheck({ + update_available: true, + current_version: '0.2.4', + latest_version: '0.2.5', + release_name: '0.2.5', + release_notes: '', + release_url: 'https://example.invalid/r', + published_at: '2099-01-01T00:00:00Z', + is_docker: true, + is_ha_addon: false, + update_method: 'docker', + }); + + await waitFor(() => { + expect(screen.getByText('docker compose pull && docker compose up -d')).toBeInTheDocument(); + }); + expect(screen.queryByText(/Home Assistant Supervisor/i)).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: /install update/i })).not.toBeInTheDocument(); + }); + }); + describe('tabs navigation', () => { it('can switch to Network tab', async () => { const user = userEvent.setup(); diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index a605bde3d..1bd127ca2 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -2354,7 +2354,8 @@ export interface UpdateCheckResult { error?: string; message?: string; is_docker?: boolean; - update_method?: 'docker' | 'git'; + is_ha_addon?: boolean; + update_method?: 'docker' | 'git' | 'ha_addon'; } export interface UpdateStatus { @@ -4417,7 +4418,7 @@ export const api = { getVersion: () => request('/updates/version'), checkForUpdates: () => request('/updates/check'), applyUpdate: () => - request<{ success: boolean; message: string; status?: UpdateStatus; is_docker?: boolean }>('/updates/apply', { + request<{ success: boolean; message: string; status?: UpdateStatus; is_docker?: boolean; is_ha_addon?: boolean }>('/updates/apply', { method: 'POST', }), getUpdateStatus: () => request('/updates/status'), diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index 1172cd968..0086c26c7 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -377,10 +377,14 @@ export function Layout() { setDragOverId(null); }; - // Show update banner if update available and not dismissed for this version + // Show update banner if update available and not dismissed for this version. + // Suppressed when running as a Home Assistant addon — HA Supervisor surfaces + // its own update notification in the HA UI, so the in-app banner is duplicate + // noise that links to a page that just says "update via HA." const showUpdateBanner = updateCheck?.update_available && updateCheck.latest_version && - updateCheck.latest_version !== dismissedUpdateVersion; + updateCheck.latest_version !== dismissedUpdateVersion && + !updateCheck.is_ha_addon; const dismissUpdateBanner = () => { if (updateCheck?.latest_version) { diff --git a/frontend/src/i18n/locales/de.ts b/frontend/src/i18n/locales/de.ts index d65de0e5a..de33b5430 100644 --- a/frontend/src/i18n/locales/de.ts +++ b/frontend/src/i18n/locales/de.ts @@ -2068,6 +2068,7 @@ export default { updateAvailableVersion: 'Update verfügbar: v{{version}}', releaseNotes: 'Versionshinweise', updateViaDocker: 'Update über Docker Compose:', + updateViaHomeAssistant: 'Updates werden vom Home Assistant Supervisor verwaltet. Öffne Einstellungen → Add-ons → Bambuddy in Home Assistant, um die neue Version zu installieren.', installUpdate: 'Update installieren', latestVersionRunning: 'Sie verwenden die neueste Version', failedToCheckUpdates: 'Update-Prüfung fehlgeschlagen: {{error}}', diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index 68571dc6e..c5395e890 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -2071,6 +2071,7 @@ export default { updateAvailableVersion: 'Update available: v{{version}}', releaseNotes: 'Release Notes', updateViaDocker: 'Update via Docker Compose:', + updateViaHomeAssistant: 'Updates are managed by the Home Assistant Supervisor. Open Settings → Add-ons → Bambuddy in Home Assistant to install the new version.', installUpdate: 'Install Update', latestVersionRunning: "You're running the latest version", failedToCheckUpdates: 'Failed to check for updates: {{error}}', diff --git a/frontend/src/i18n/locales/fr.ts b/frontend/src/i18n/locales/fr.ts index 1a0b69d50..0aae0ecdb 100644 --- a/frontend/src/i18n/locales/fr.ts +++ b/frontend/src/i18n/locales/fr.ts @@ -2019,6 +2019,7 @@ export default { updateAvailableVersion: 'Mise à jour disponible : v{{version}}', releaseNotes: 'Notes de version', updateViaDocker: 'Mettre à jour via Docker Compose :', + updateViaHomeAssistant: 'Les mises à jour sont gérées par le superviseur Home Assistant. Ouvrez Paramètres → Modules complémentaires → Bambuddy dans Home Assistant pour installer la nouvelle version.', installUpdate: 'Installer la mise à jour', latestVersionRunning: 'Vous utilisez la dernière version', failedToCheckUpdates: 'Échec de la vérification des mises à jour : {{error}}', diff --git a/frontend/src/i18n/locales/it.ts b/frontend/src/i18n/locales/it.ts index 06b5fcd54..96f9e9f3f 100644 --- a/frontend/src/i18n/locales/it.ts +++ b/frontend/src/i18n/locales/it.ts @@ -2018,6 +2018,7 @@ export default { updateAvailableVersion: 'Aggiornamento disponibile: v{{version}}', releaseNotes: 'Note di rilascio', updateViaDocker: 'Aggiorna tramite Docker Compose:', + updateViaHomeAssistant: 'Gli aggiornamenti sono gestiti dal Supervisor di Home Assistant. Apri Impostazioni → Add-on → Bambuddy in Home Assistant per installare la nuova versione.', installUpdate: 'Installa aggiornamento', latestVersionRunning: 'Stai usando l\'ultima versione', failedToCheckUpdates: 'Controllo aggiornamenti fallito: {{error}}', diff --git a/frontend/src/i18n/locales/ja.ts b/frontend/src/i18n/locales/ja.ts index 76a0dd9ec..4099316ca 100644 --- a/frontend/src/i18n/locales/ja.ts +++ b/frontend/src/i18n/locales/ja.ts @@ -2067,6 +2067,7 @@ export default { updateAvailableVersion: 'アップデート利用可能: v{{version}}', releaseNotes: 'リリースノート', updateViaDocker: 'Docker Composeでアップデート:', + updateViaHomeAssistant: 'アップデートはHome Assistant Supervisorによって管理されます。Home Assistantの設定→アドオン→Bambuddyを開いて新しいバージョンをインストールしてください。', installUpdate: 'アップデートをインストール', latestVersionRunning: '最新バージョンを使用しています', failedToCheckUpdates: 'アップデートの確認に失敗しました: {{error}}', diff --git a/frontend/src/i18n/locales/pt-BR.ts b/frontend/src/i18n/locales/pt-BR.ts index 012eec9ae..2a0dc7bf9 100644 --- a/frontend/src/i18n/locales/pt-BR.ts +++ b/frontend/src/i18n/locales/pt-BR.ts @@ -2018,6 +2018,7 @@ export default { updateAvailableVersion: 'Atualização disponível: v{{version}}', releaseNotes: 'Notas da versão', updateViaDocker: 'Atualizar via Docker Compose:', + updateViaHomeAssistant: 'As atualizações são gerenciadas pelo Supervisor do Home Assistant. Abra Configurações → Complementos → Bambuddy no Home Assistant para instalar a nova versão.', installUpdate: 'Instalar atualização', latestVersionRunning: 'Você está usando a versão mais recente', failedToCheckUpdates: 'Falha ao verificar atualizações: {{error}}', diff --git a/frontend/src/i18n/locales/zh-CN.ts b/frontend/src/i18n/locales/zh-CN.ts index e06d80296..4d66ecaf0 100644 --- a/frontend/src/i18n/locales/zh-CN.ts +++ b/frontend/src/i18n/locales/zh-CN.ts @@ -2062,6 +2062,7 @@ export default { updateAvailableVersion: '可用更新:v{{version}}', releaseNotes: '发布说明', updateViaDocker: '通过 Docker Compose 更新:', + updateViaHomeAssistant: '更新由 Home Assistant Supervisor 管理。请在 Home Assistant 中打开 设置 → 加载项 → Bambuddy 以安装新版本。', installUpdate: '安装更新', latestVersionRunning: '您正在运行最新版本', failedToCheckUpdates: '检查更新失败:{{error}}', diff --git a/frontend/src/i18n/locales/zh-TW.ts b/frontend/src/i18n/locales/zh-TW.ts index 9ddbeb438..9e6e33cb6 100644 --- a/frontend/src/i18n/locales/zh-TW.ts +++ b/frontend/src/i18n/locales/zh-TW.ts @@ -2062,6 +2062,7 @@ export default { updateAvailableVersion: '可用更新:v{{version}}', releaseNotes: '發布說明', updateViaDocker: '透過 Docker Compose 更新:', + updateViaHomeAssistant: '更新由 Home Assistant Supervisor 管理。請在 Home Assistant 中開啟 設定 → 附加元件 → Bambuddy 以安裝新版本。', installUpdate: '安裝更新', latestVersionRunning: '您正在執行最新版本', failedToCheckUpdates: '檢查更新失敗:{{error}}', diff --git a/frontend/src/pages/SettingsPage.tsx b/frontend/src/pages/SettingsPage.tsx index 18e21f02d..023badd60 100644 --- a/frontend/src/pages/SettingsPage.tsx +++ b/frontend/src/pages/SettingsPage.tsx @@ -769,7 +769,7 @@ export function SettingsPage() { const applyUpdateMutation = useMutation({ mutationFn: api.applyUpdate, onSuccess: (data) => { - if (data.is_docker) { + if (data.is_ha_addon || data.is_docker) { showToast(data.message, 'error'); } else { refetchUpdateStatus(); @@ -2271,6 +2271,12 @@ export function SettingsPage() {
{updateStatus.error || updateStatus.message}
+ ) : updateCheck?.is_ha_addon ? ( +
+

+ {t('settings.updateViaHomeAssistant')} +

+
) : updateCheck?.is_docker ? (

diff --git a/static/assets/index-CTxVw43p.js b/static/assets/index-D4GSGeyj.js similarity index 84% rename from static/assets/index-CTxVw43p.js rename to static/assets/index-D4GSGeyj.js index cc09f4659..d6ae4399f 100644 --- a/static/assets/index-CTxVw43p.js +++ b/static/assets/index-D4GSGeyj.js @@ -5,34 +5,34 @@ function cue(t,e){for(var n=0;nV||lt[j]!==vt[V]){var Rt=` `+lt[j].replace(" at new "," at ");return h.displayName&&Rt.includes("")&&(Rt=Rt.replace("",h.displayName)),Rt}while(1<=j&&0<=V);break}}}finally{G=!1,Error.prepareStackTrace=_}return(_=h?h.displayName||h.name:"")?I(_):""}function W(h,y){switch(h.tag){case 26:case 27:case 5:return I(h.type);case 16:return I("Lazy");case 13:return h.child!==y&&y!==null?I("Suspense Fallback"):I("Suspense");case 19:return I("SuspenseList");case 0:case 15:return q(h.type,!1);case 11:return q(h.type.render,!1);case 1:return q(h.type,!0);case 31:return I("Activity");default:return""}}function te(h){try{var y="",_=null;do y+=W(h,_),_=h,h=h.return;while(h);return y}catch(j){return` Error generating stack: `+j.message+` -`+j.stack}}var le=Object.prototype.hasOwnProperty,ye=t.unstable_scheduleCallback,he=t.unstable_cancelCallback,me=t.unstable_shouldYield,H=t.unstable_requestPaint,L=t.unstable_now,ee=t.unstable_getCurrentPriorityLevel,ge=t.unstable_ImmediatePriority,ke=t.unstable_UserBlockingPriority,je=t.unstable_NormalPriority,Te=t.unstable_LowPriority,xe=t.unstable_IdlePriority,Fe=t.log,Be=t.unstable_setDisableYieldValue,Ce=null,ze=null;function Me(h){if(typeof Fe=="function"&&Be(h),ze&&typeof ze.setStrictMode=="function")try{ze.setStrictMode(Ce,h)}catch{}}var Oe=Math.clz32?Math.clz32:ve,Ge=Math.log,et=Math.LN2;function ve(h){return h>>>=0,h===0?32:31-(Ge(h)/et|0)|0}var Xe=256,Pe=262144,Ve=4194304;function we(h){var y=h&42;if(y!==0)return y;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 de(h,y,_){var j=h.pendingLanes;if(j===0)return 0;var V=0,Z=h.suspendedLanes,Ne=h.pingedLanes;h=h.warmLanes;var qe=j&134217727;return qe!==0?(j=qe&~Z,j!==0?V=we(j):(Ne&=qe,Ne!==0?V=we(Ne):_||(_=qe&~h,_!==0&&(V=we(_))))):(qe=j&~Z,qe!==0?V=we(qe):Ne!==0?V=we(Ne):_||(_=j&~h,_!==0&&(V=we(_)))),V===0?0:y!==0&&y!==V&&(y&Z)===0&&(Z=V&-V,_=y&-y,Z>=_||Z===32&&(_&4194048)!==0)?y:V}function Se(h,y){return(h.pendingLanes&~(h.suspendedLanes&~h.pingedLanes)&y)===0}function $e(h,y){switch(h){case 1:case 2:case 4:case 8:case 64:return y+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 y+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 st(){var h=Ve;return Ve<<=1,(Ve&62914560)===0&&(Ve=4194304),h}function He(h){for(var y=[],_=0;31>_;_++)y.push(h);return y}function at(h,y){h.pendingLanes|=y,y!==268435456&&(h.suspendedLanes=0,h.pingedLanes=0,h.warmLanes=0)}function ht(h,y,_,j,V,Z){var Ne=h.pendingLanes;h.pendingLanes=_,h.suspendedLanes=0,h.pingedLanes=0,h.warmLanes=0,h.expiredLanes&=_,h.entangledLanes&=_,h.errorRecoveryDisabledLanes&=_,h.shellSuspendCounter=0;var qe=h.entanglements,lt=h.expirationTimes,vt=h.hiddenUpdates;for(_=Ne&~_;0<_;){var Rt=31-Oe(_),Ut=1<"u")return null;try{return h.activeElement||h.body}catch{return h.body}}var Tr=/[\n"\\]/g;function On(h){return h.replace(Tr,function(y){return"\\"+y.charCodeAt(0).toString(16)+" "})}function Ra(h,y,_,j,V,Z,Ne,qe){h.name="",Ne!=null&&typeof Ne!="function"&&typeof Ne!="symbol"&&typeof Ne!="boolean"?h.type=Ne:h.removeAttribute("type"),y!=null?Ne==="number"?(y===0&&h.value===""||h.value!=y)&&(h.value=""+on(y)):h.value!==""+on(y)&&(h.value=""+on(y)):Ne!=="submit"&&Ne!=="reset"||h.removeAttribute("value"),y!=null?Br(h,Ne,on(y)):_!=null?Br(h,Ne,on(_)):j!=null&&h.removeAttribute("value"),V==null&&Z!=null&&(h.defaultChecked=!!Z),V!=null&&(h.checked=V&&typeof V!="function"&&typeof V!="symbol"),qe!=null&&typeof qe!="function"&&typeof qe!="symbol"&&typeof qe!="boolean"?h.name=""+on(qe):h.removeAttribute("name")}function $n(h,y,_,j,V,Z,Ne,qe){if(Z!=null&&typeof Z!="function"&&typeof Z!="symbol"&&typeof Z!="boolean"&&(h.type=Z),y!=null||_!=null){if(!(Z!=="submit"&&Z!=="reset"||y!=null)){_n(h);return}_=_!=null?""+on(_):"",y=y!=null?""+on(y):_,qe||y===h.value||(h.value=y),h.defaultValue=y}j=j??V,j=typeof j!="function"&&typeof j!="symbol"&&!!j,h.checked=qe?h.checked:!!j,h.defaultChecked=!!j,Ne!=null&&typeof Ne!="function"&&typeof Ne!="symbol"&&typeof Ne!="boolean"&&(h.name=Ne),_n(h)}function Br(h,y,_){y==="number"&&br(h.ownerDocument)===h||h.defaultValue===""+_||(h.defaultValue=""+_)}function Kr(h,y,_,j){if(h=h.options,y){y={};for(var V=0;V<_.length;V++)y["$"+_[V]]=!0;for(_=0;_"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),zi=!1;if($o)try{var wl={};Object.defineProperty(wl,"passive",{get:function(){zi=!0}}),window.addEventListener("test",wl,wl),window.removeEventListener("test",wl,wl)}catch{zi=!1}var wo=null,ha=null,vc=null;function Im(){if(vc)return vc;var h,y=ha,_=y.length,j,V="value"in wo?wo.value:wo.textContent,Z=V.length;for(h=0;h<_&&y[h]===V[h];h++);var Ne=_-h;for(j=1;j<=Ne&&y[_-j]===V[Z-j];j++);return vc=V.slice(h,1=it),ct=" ",Mn=!1;function xr(h,y){switch(h){case"keyup":return Fn.indexOf(y.keyCode)!==-1;case"keydown":return y.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function lr(h){return h=h.detail,typeof h=="object"&&"data"in h?h.data:null}var Or=!1;function La(h,y){switch(h){case"compositionend":return lr(y);case"keypress":return y.which!==32?null:(Mn=!0,ct);case"textInput":return h=y.data,h===ct&&Mn?null:h;default:return null}}function un(h,y){if(Or)return h==="compositionend"||!kn&&xr(h,y)?(h=Im(),vc=ha=wo=null,Or=!1,h):null;switch(h){case"paste":return null;case"keypress":if(!(y.ctrlKey||y.altKey||y.metaKey)||y.ctrlKey&&y.altKey){if(y.char&&1=y)return{node:_,offset:y-h};h=j}e:{for(;_;){if(_.nextSibling){_=_.nextSibling;break e}_=_.parentNode}_=void 0}_=Yp(_)}}function Jy(h,y){return h&&y?h===y?!0:h&&h.nodeType===3?!1:y&&y.nodeType===3?Jy(h,y.parentNode):"contains"in h?h.contains(y):h.compareDocumentPosition?!!(h.compareDocumentPosition(y)&16):!1:!1}function YS(h){h=h!=null&&h.ownerDocument!=null&&h.ownerDocument.defaultView!=null?h.ownerDocument.defaultView:window;for(var y=br(h.document);y instanceof h.HTMLIFrameElement;){try{var _=typeof y.contentWindow.location.href=="string"}catch{_=!1}if(_)h=y.contentWindow;else break;y=br(h.document)}return y}function DA(h){var y=h&&h.nodeName&&h.nodeName.toLowerCase();return y&&(y==="input"&&(h.type==="text"||h.type==="search"||h.type==="tel"||h.type==="url"||h.type==="password")||y==="textarea"||h.contentEditable==="true")}var Hce=$o&&"documentMode"in document&&11>=document.documentMode,tb=null,FA=null,Zy=null,RA=!1;function U5(h,y,_){var j=_.window===_?_.document:_.nodeType===9?_:_.ownerDocument;RA||tb==null||tb!==br(j)||(j=tb,"selectionStart"in j&&DA(j)?j={start:j.selectionStart,end:j.selectionEnd}:(j=(j.ownerDocument&&j.ownerDocument.defaultView||window).getSelection(),j={anchorNode:j.anchorNode,anchorOffset:j.anchorOffset,focusNode:j.focusNode,focusOffset:j.focusOffset}),Zy&&Vm(Zy,j)||(Zy=j,j=O_(FA,"onSelect"),0>=Ne,V-=Ne,ld=1<<32-Oe(y)+V|_<Qn?(vr=vn,vn=null):vr=vn.sibling;var Dr=kt(gt,vn,yt[Qn],It);if(Dr===null){vn===null&&(vn=vr);break}h&&vn&&Dr.alternate===null&&y(gt,vn),pt=Z(Dr,pt,Qn),Er===null?An=Dr:Er.sibling=Dr,Er=Dr,vn=vr}if(Qn===yt.length)return _(gt,vn),Sr&&du(gt,Qn),An;if(vn===null){for(;QnQn?(vr=vn,vn=null):vr=vn.sibling;var hh=kt(gt,vn,Dr.value,It);if(hh===null){vn===null&&(vn=vr);break}h&&vn&&hh.alternate===null&&y(gt,vn),pt=Z(hh,pt,Qn),Er===null?An=hh:Er.sibling=hh,Er=hh,vn=vr}if(Dr.done)return _(gt,vn),Sr&&du(gt,Qn),An;if(vn===null){for(;!Dr.done;Qn++,Dr=yt.next())Dr=Ut(gt,Dr.value,It),Dr!==null&&(pt=Z(Dr,pt,Qn),Er===null?An=Dr:Er.sibling=Dr,Er=Dr);return Sr&&du(gt,Qn),An}for(vn=j(vn);!Dr.done;Qn++,Dr=yt.next())Dr=At(vn,gt,Qn,Dr.value,It),Dr!==null&&(h&&Dr.alternate!==null&&vn.delete(Dr.key===null?Qn:Dr.key),pt=Z(Dr,pt,Qn),Er===null?An=Dr:Er.sibling=Dr,Er=Dr);return h&&vn.forEach(function(lue){return y(gt,lue)}),Sr&&du(gt,Qn),An}function Qr(gt,pt,yt,It){if(typeof yt=="object"&&yt!==null&&yt.type===v&&yt.key===null&&(yt=yt.props.children),typeof yt=="object"&&yt!==null){switch(yt.$$typeof){case f:e:{for(var An=yt.key;pt!==null;){if(pt.key===An){if(An=yt.type,An===v){if(pt.tag===7){_(gt,pt.sibling),It=V(pt,yt.props.children),It.return=gt,gt=It;break e}}else if(pt.elementType===An||typeof An=="object"&&An!==null&&An.$$typeof===F&&sf(An)===pt.type){_(gt,pt.sibling),It=V(pt,yt.props),iv(It,yt),It.return=gt,gt=It;break e}_(gt,pt);break}else y(gt,pt);pt=pt.sibling}yt.type===v?(It=ef(yt.props.children,gt.mode,It,yt.key),It.return=gt,gt=It):(It=t_(yt.type,yt.key,yt.props,null,gt.mode,It),iv(It,yt),It.return=gt,gt=It)}return Ne(gt);case x:e:{for(An=yt.key;pt!==null;){if(pt.key===An)if(pt.tag===4&&pt.stateNode.containerInfo===yt.containerInfo&&pt.stateNode.implementation===yt.implementation){_(gt,pt.sibling),It=V(pt,yt.children||[]),It.return=gt,gt=It;break e}else{_(gt,pt);break}else y(gt,pt);pt=pt.sibling}It=HA(yt,gt.mode,It),It.return=gt,gt=It}return Ne(gt);case F:return yt=sf(yt),Qr(gt,pt,yt,It)}if(ne(yt))return bn(gt,pt,yt,It);if(z(yt)){if(An=z(yt),typeof An!="function")throw Error(r(150));return yt=An.call(yt),In(gt,pt,yt,It)}if(typeof yt.then=="function")return Qr(gt,pt,l_(yt),It);if(yt.$$typeof===N)return Qr(gt,pt,a_(gt,yt),It);c_(gt,yt)}return typeof yt=="string"&&yt!==""||typeof yt=="number"||typeof yt=="bigint"?(yt=""+yt,pt!==null&&pt.tag===6?(_(gt,pt.sibling),It=V(pt,yt),It.return=gt,gt=It):(_(gt,pt),It=BA(yt,gt.mode,It),It.return=gt,gt=It),Ne(gt)):_(gt,pt)}return function(gt,pt,yt,It){try{av=0;var An=Qr(gt,pt,yt,It);return mb=null,An}catch(vn){if(vn===ub||vn===s_)throw vn;var Er=Ko(29,vn,null,gt.mode);return Er.lanes=It,Er.return=gt,Er}}}var lf=cU(!0),dU=cU(!1),Ym=!1;function ej(h){h.updateQueue={baseState:h.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function tj(h,y){h=h.updateQueue,y.updateQueue===h&&(y.updateQueue={baseState:h.baseState,firstBaseUpdate:h.firstBaseUpdate,lastBaseUpdate:h.lastBaseUpdate,shared:h.shared,callbacks:null})}function Qm(h){return{lane:h,tag:0,payload:null,callback:null,next:null}}function Jm(h,y,_){var j=h.updateQueue;if(j===null)return null;if(j=j.shared,(Lr&2)!==0){var V=j.pending;return V===null?y.next=y:(y.next=V.next,V.next=y),j.pending=y,y=e_(h),W5(h,null,_),y}return ZS(h,j,y,_),e_(h)}function sv(h,y,_){if(y=y.updateQueue,y!==null&&(y=y.shared,(_&4194048)!==0)){var j=y.lanes;j&=h.pendingLanes,_|=j,y.lanes=_,qt(h,_)}}function nj(h,y){var _=h.updateQueue,j=h.alternate;if(j!==null&&(j=j.updateQueue,_===j)){var V=null,Z=null;if(_=_.firstBaseUpdate,_!==null){do{var Ne={lane:_.lane,tag:_.tag,payload:_.payload,callback:null,next:null};Z===null?V=Z=Ne:Z=Z.next=Ne,_=_.next}while(_!==null);Z===null?V=Z=y:Z=Z.next=y}else V=Z=y;_={baseState:j.baseState,firstBaseUpdate:V,lastBaseUpdate:Z,shared:j.shared,callbacks:j.callbacks},h.updateQueue=_;return}h=_.lastBaseUpdate,h===null?_.firstBaseUpdate=y:h.next=y,_.lastBaseUpdate=y}var rj=!1;function ov(){if(rj){var h=db;if(h!==null)throw h}}function lv(h,y,_,j){rj=!1;var V=h.updateQueue;Ym=!1;var Z=V.firstBaseUpdate,Ne=V.lastBaseUpdate,qe=V.shared.pending;if(qe!==null){V.shared.pending=null;var lt=qe,vt=lt.next;lt.next=null,Ne===null?Z=vt:Ne.next=vt,Ne=lt;var Rt=h.alternate;Rt!==null&&(Rt=Rt.updateQueue,qe=Rt.lastBaseUpdate,qe!==Ne&&(qe===null?Rt.firstBaseUpdate=vt:qe.next=vt,Rt.lastBaseUpdate=lt))}if(Z!==null){var Ut=V.baseState;Ne=0,Rt=vt=lt=null,qe=Z;do{var kt=qe.lane&-536870913,At=kt!==qe.lane;if(At?(yr&kt)===kt:(j&kt)===kt){kt!==0&&kt===cb&&(rj=!0),Rt!==null&&(Rt=Rt.next={lane:0,tag:qe.tag,payload:qe.payload,callback:null,next:null});e:{var bn=h,In=qe;kt=y;var Qr=_;switch(In.tag){case 1:if(bn=In.payload,typeof bn=="function"){Ut=bn.call(Qr,Ut,kt);break e}Ut=bn;break e;case 3:bn.flags=bn.flags&-65537|128;case 0:if(bn=In.payload,kt=typeof bn=="function"?bn.call(Qr,Ut,kt):bn,kt==null)break e;Ut=m({},Ut,kt);break e;case 2:Ym=!0}}kt=qe.callback,kt!==null&&(h.flags|=64,At&&(h.flags|=8192),At=V.callbacks,At===null?V.callbacks=[kt]:At.push(kt))}else At={lane:kt,tag:qe.tag,payload:qe.payload,callback:qe.callback,next:null},Rt===null?(vt=Rt=At,lt=Ut):Rt=Rt.next=At,Ne|=kt;if(qe=qe.next,qe===null){if(qe=V.shared.pending,qe===null)break;At=qe,qe=At.next,At.next=null,V.lastBaseUpdate=At,V.shared.pending=null}}while(!0);Rt===null&&(lt=Ut),V.baseState=lt,V.firstBaseUpdate=vt,V.lastBaseUpdate=Rt,Z===null&&(V.shared.lanes=0),rh|=Ne,h.lanes=Ne,h.memoizedState=Ut}}function uU(h,y){if(typeof h!="function")throw Error(r(191,h));h.call(y)}function mU(h,y){var _=h.callbacks;if(_!==null)for(h.callbacks=null,h=0;h<_.length;h++)uU(_[h],y)}var hb=K(null),d_=K(0);function hU(h,y){h=wu,pe(d_,h),pe(hb,y),wu=h|y.baseLanes}function aj(){pe(d_,wu),pe(hb,hb.current)}function ij(){wu=d_.current,ae(hb),ae(d_)}var Xo=K(null),Dl=null;function Zm(h){var y=h.alternate;pe(ai,ai.current&1),pe(Xo,h),Dl===null&&(y===null||hb.current!==null||y.memoizedState!==null)&&(Dl=h)}function sj(h){pe(ai,ai.current),pe(Xo,h),Dl===null&&(Dl=h)}function pU(h){h.tag===22?(pe(ai,ai.current),pe(Xo,h),Dl===null&&(Dl=h)):eh()}function eh(){pe(ai,ai.current),pe(Xo,Xo.current)}function Yo(h){ae(Xo),Dl===h&&(Dl=null),ae(ai)}var ai=K(0);function u_(h){for(var y=h;y!==null;){if(y.tag===13){var _=y.memoizedState;if(_!==null&&(_=_.dehydrated,_===null||mM(_)||hM(_)))return y}else if(y.tag===19&&(y.memoizedProps.revealOrder==="forwards"||y.memoizedProps.revealOrder==="backwards"||y.memoizedProps.revealOrder==="unstable_legacy-backwards"||y.memoizedProps.revealOrder==="together")){if((y.flags&128)!==0)return y}else if(y.child!==null){y.child.return=y,y=y.child;continue}if(y===h)break;for(;y.sibling===null;){if(y.return===null||y.return===h)return null;y=y.return}y.sibling.return=y.return,y=y.sibling}return null}var hu=0,Xn=null,Xr=null,mi=null,m_=!1,pb=!1,cf=!1,h_=0,cv=0,fb=null,Jce=0;function $a(){throw Error(r(321))}function oj(h,y){if(y===null)return!1;for(var _=0;_Z?Z:8;var Ne=re.T,qe={};re.T=qe,Sj(h,!1,y,_);try{var lt=V(),vt=re.S;if(vt!==null&&vt(qe,lt),lt!==null&&typeof lt=="object"&&typeof lt.then=="function"){var Rt=Qce(lt,j);uv(h,y,Rt,Zo(h))}else uv(h,y,j,Zo(h))}catch(Ut){uv(h,y,{then:function(){},status:"rejected",reason:Ut},Zo())}finally{J.p=Z,Ne!==null&&qe.types!==null&&(Ne.types=qe.types),re.T=Ne}}function rde(){}function vj(h,y,_,j){if(h.tag!==5)throw Error(r(476));var V=$U(h).queue;qU(h,V,y,ue,_===null?rde:function(){return VU(h),_(j)})}function $U(h){var y=h.memoizedState;if(y!==null)return y;y={memoizedState:ue,baseState:ue,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:pu,lastRenderedState:ue},next:null};var _={};return y.next={memoizedState:_,baseState:_,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:pu,lastRenderedState:_},next:null},h.memoizedState=y,h=h.alternate,h!==null&&(h.memoizedState=y),y}function VU(h){var y=$U(h);y.next===null&&(y=h.alternate.memoizedState),uv(h,y.next.queue,{},Zo())}function wj(){return ds(Tv)}function GU(){return ii().memoizedState}function WU(){return ii().memoizedState}function ade(h){for(var y=h.return;y!==null;){switch(y.tag){case 24:case 3:var _=Zo();h=Qm(_);var j=Jm(y,h,_);j!==null&&(To(j,y,_),sv(j,y,_)),y={cache:YA()},h.payload=y;return}y=y.return}}function ide(h,y,_){var j=Zo();_={lane:j,revertLane:0,gesture:null,action:_,hasEagerState:!1,eagerState:null,next:null},y_(h)?XU(y,_):(_=zA(h,y,_,j),_!==null&&(To(_,h,j),YU(_,y,j)))}function KU(h,y,_){var j=Zo();uv(h,y,_,j)}function uv(h,y,_,j){var V={lane:j,revertLane:0,gesture:null,action:_,hasEagerState:!1,eagerState:null,next:null};if(y_(h))XU(y,V);else{var Z=h.alternate;if(h.lanes===0&&(Z===null||Z.lanes===0)&&(Z=y.lastRenderedReducer,Z!==null))try{var Ne=y.lastRenderedState,qe=Z(Ne,_);if(V.hasEagerState=!0,V.eagerState=qe,_o(qe,Ne))return ZS(h,y,V,0),oa===null&&JS(),!1}catch{}if(_=zA(h,y,V,j),_!==null)return To(_,h,j),YU(_,y,j),!0}return!1}function Sj(h,y,_,j){if(j={lane:2,revertLane:eM(),gesture:null,action:j,hasEagerState:!1,eagerState:null,next:null},y_(h)){if(y)throw Error(r(479))}else y=zA(h,_,j,2),y!==null&&To(y,h,2)}function y_(h){var y=h.alternate;return h===Xn||y!==null&&y===Xn}function XU(h,y){pb=m_=!0;var _=h.pending;_===null?y.next=y:(y.next=_.next,_.next=y),h.pending=y}function YU(h,y,_){if((_&4194048)!==0){var j=y.lanes;j&=h.pendingLanes,_|=j,y.lanes=_,qt(h,_)}}var mv={readContext:ds,use:f_,useCallback:$a,useContext:$a,useEffect:$a,useImperativeHandle:$a,useLayoutEffect:$a,useInsertionEffect:$a,useMemo:$a,useReducer:$a,useRef:$a,useState:$a,useDebugValue:$a,useDeferredValue:$a,useTransition:$a,useSyncExternalStore:$a,useId:$a,useHostTransitionStatus:$a,useFormState:$a,useActionState:$a,useOptimistic:$a,useMemoCache:$a,useCacheRefresh:$a};mv.useEffectEvent=$a;var QU={readContext:ds,use:f_,useCallback:function(h,y){return Zs().memoizedState=[h,y===void 0?null:y],h},useContext:ds,useEffect:FU,useImperativeHandle:function(h,y,_){_=_!=null?_.concat([h]):null,b_(4194308,4,OU.bind(null,y,h),_)},useLayoutEffect:function(h,y){return b_(4194308,4,h,y)},useInsertionEffect:function(h,y){b_(4,2,h,y)},useMemo:function(h,y){var _=Zs();y=y===void 0?null:y;var j=h();if(cf){Me(!0);try{h()}finally{Me(!1)}}return _.memoizedState=[j,y],j},useReducer:function(h,y,_){var j=Zs();if(_!==void 0){var V=_(y);if(cf){Me(!0);try{_(y)}finally{Me(!1)}}}else V=y;return j.memoizedState=j.baseState=V,h={pending:null,lanes:0,dispatch:null,lastRenderedReducer:h,lastRenderedState:V},j.queue=h,h=h.dispatch=ide.bind(null,Xn,h),[j.memoizedState,h]},useRef:function(h){var y=Zs();return h={current:h},y.memoizedState=h},useState:function(h){h=fj(h);var y=h.queue,_=KU.bind(null,Xn,y);return y.dispatch=_,[h.memoizedState,_]},useDebugValue:xj,useDeferredValue:function(h,y){var _=Zs();return yj(_,h,y)},useTransition:function(){var h=fj(!1);return h=qU.bind(null,Xn,h.queue,!0,!1),Zs().memoizedState=h,[!1,h]},useSyncExternalStore:function(h,y,_){var j=Xn,V=Zs();if(Sr){if(_===void 0)throw Error(r(407));_=_()}else{if(_=y(),oa===null)throw Error(r(349));(yr&127)!==0||xU(j,y,_)}V.memoizedState=_;var Z={value:_,getSnapshot:y};return V.queue=Z,FU(vU.bind(null,j,Z,h),[h]),j.flags|=2048,gb(9,{destroy:void 0},yU.bind(null,j,Z,_,y),null),_},useId:function(){var h=Zs(),y=oa.identifierPrefix;if(Sr){var _=cd,j=ld;_=(j&~(1<<32-Oe(j)-1)).toString(32)+_,y="_"+y+"R_"+_,_=h_++,0<_&&(y+="H"+_.toString(32)),y+="_"}else _=Jce++,y="_"+y+"r_"+_.toString(32)+"_";return h.memoizedState=y},useHostTransitionStatus:wj,useFormState:AU,useActionState:AU,useOptimistic:function(h){var y=Zs();y.memoizedState=y.baseState=h;var _={pending:null,lanes:0,dispatch:null,lastRenderedReducer:null,lastRenderedState:null};return y.queue=_,y=Sj.bind(null,Xn,!0,_),_.dispatch=y,[h,y]},useMemoCache:mj,useCacheRefresh:function(){return Zs().memoizedState=ade.bind(null,Xn)},useEffectEvent:function(h){var y=Zs(),_={impl:h};return y.memoizedState=_,function(){if((Lr&2)!==0)throw Error(r(440));return _.impl.apply(void 0,arguments)}}},_j={readContext:ds,use:f_,useCallback:UU,useContext:ds,useEffect:bj,useImperativeHandle:zU,useInsertionEffect:LU,useLayoutEffect:IU,useMemo:BU,useReducer:g_,useRef:DU,useState:function(){return g_(pu)},useDebugValue:xj,useDeferredValue:function(h,y){var _=ii();return HU(_,Xr.memoizedState,h,y)},useTransition:function(){var h=g_(pu)[0],y=ii().memoizedState;return[typeof h=="boolean"?h:dv(h),y]},useSyncExternalStore:bU,useId:GU,useHostTransitionStatus:wj,useFormState:jU,useActionState:jU,useOptimistic:function(h,y){var _=ii();return _U(_,Xr,h,y)},useMemoCache:mj,useCacheRefresh:WU};_j.useEffectEvent=RU;var JU={readContext:ds,use:f_,useCallback:UU,useContext:ds,useEffect:bj,useImperativeHandle:zU,useInsertionEffect:LU,useLayoutEffect:IU,useMemo:BU,useReducer:pj,useRef:DU,useState:function(){return pj(pu)},useDebugValue:xj,useDeferredValue:function(h,y){var _=ii();return Xr===null?yj(_,h,y):HU(_,Xr.memoizedState,h,y)},useTransition:function(){var h=pj(pu)[0],y=ii().memoizedState;return[typeof h=="boolean"?h:dv(h),y]},useSyncExternalStore:bU,useId:GU,useHostTransitionStatus:wj,useFormState:EU,useActionState:EU,useOptimistic:function(h,y){var _=ii();return Xr!==null?_U(_,Xr,h,y):(_.baseState=h,[h,_.queue.dispatch])},useMemoCache:mj,useCacheRefresh:WU};JU.useEffectEvent=RU;function kj(h,y,_,j){y=h.memoizedState,_=_(j,y),_=_==null?y:m({},y,_),h.memoizedState=_,h.lanes===0&&(h.updateQueue.baseState=_)}var Nj={enqueueSetState:function(h,y,_){h=h._reactInternals;var j=Zo(),V=Qm(j);V.payload=y,_!=null&&(V.callback=_),y=Jm(h,V,j),y!==null&&(To(y,h,j),sv(y,h,j))},enqueueReplaceState:function(h,y,_){h=h._reactInternals;var j=Zo(),V=Qm(j);V.tag=1,V.payload=y,_!=null&&(V.callback=_),y=Jm(h,V,j),y!==null&&(To(y,h,j),sv(y,h,j))},enqueueForceUpdate:function(h,y){h=h._reactInternals;var _=Zo(),j=Qm(_);j.tag=2,y!=null&&(j.callback=y),y=Jm(h,j,_),y!==null&&(To(y,h,_),sv(y,h,_))}};function ZU(h,y,_,j,V,Z,Ne){return h=h.stateNode,typeof h.shouldComponentUpdate=="function"?h.shouldComponentUpdate(j,Z,Ne):y.prototype&&y.prototype.isPureReactComponent?!Vm(_,j)||!Vm(V,Z):!0}function eB(h,y,_,j){h=y.state,typeof y.componentWillReceiveProps=="function"&&y.componentWillReceiveProps(_,j),typeof y.UNSAFE_componentWillReceiveProps=="function"&&y.UNSAFE_componentWillReceiveProps(_,j),y.state!==h&&Nj.enqueueReplaceState(y,y.state,null)}function df(h,y){var _=y;if("ref"in y){_={};for(var j in y)j!=="ref"&&(_[j]=y[j])}if(h=h.defaultProps){_===y&&(_=m({},_));for(var V in h)_[V]===void 0&&(_[V]=h[V])}return _}function tB(h){QS(h)}function nB(h){console.error(h)}function rB(h){QS(h)}function v_(h,y){try{var _=h.onUncaughtError;_(y.value,{componentStack:y.stack})}catch(j){setTimeout(function(){throw j})}}function aB(h,y,_){try{var j=h.onCaughtError;j(_.value,{componentStack:_.stack,errorBoundary:y.tag===1?y.stateNode:null})}catch(V){setTimeout(function(){throw V})}}function Cj(h,y,_){return _=Qm(_),_.tag=3,_.payload={element:null},_.callback=function(){v_(h,y)},_}function iB(h){return h=Qm(h),h.tag=3,h}function sB(h,y,_,j){var V=_.type.getDerivedStateFromError;if(typeof V=="function"){var Z=j.value;h.payload=function(){return V(Z)},h.callback=function(){aB(y,_,j)}}var Ne=_.stateNode;Ne!==null&&typeof Ne.componentDidCatch=="function"&&(h.callback=function(){aB(y,_,j),typeof V!="function"&&(ah===null?ah=new Set([this]):ah.add(this));var qe=j.stack;this.componentDidCatch(j.value,{componentStack:qe!==null?qe:""})})}function sde(h,y,_,j,V){if(_.flags|=32768,j!==null&&typeof j=="object"&&typeof j.then=="function"){if(y=_.alternate,y!==null&&lb(y,_,V,!0),_=Xo.current,_!==null){switch(_.tag){case 31:case 13:return Dl===null?E_():_.alternate===null&&Va===0&&(Va=3),_.flags&=-257,_.flags|=65536,_.lanes=V,j===o_?_.flags|=16384:(y=_.updateQueue,y===null?_.updateQueue=new Set([j]):y.add(j),Qj(h,j,V)),!1;case 22:return _.flags|=65536,j===o_?_.flags|=16384:(y=_.updateQueue,y===null?(y={transitions:null,markerInstances:null,retryQueue:new Set([j])},_.updateQueue=y):(_=y.retryQueue,_===null?y.retryQueue=new Set([j]):_.add(j)),Qj(h,j,V)),!1}throw Error(r(435,_.tag))}return Qj(h,j,V),E_(),!1}if(Sr)return y=Xo.current,y!==null?((y.flags&65536)===0&&(y.flags|=256),y.flags|=65536,y.lanes=V,j!==VA&&(h=Error(r(422),{cause:j}),tv(Al(h,_)))):(j!==VA&&(y=Error(r(423),{cause:j}),tv(Al(y,_))),h=h.current.alternate,h.flags|=65536,V&=-V,h.lanes|=V,j=Al(j,_),V=Cj(h.stateNode,j,V),nj(h,V),Va!==4&&(Va=2)),!1;var Z=Error(r(520),{cause:j});if(Z=Al(Z,_),vv===null?vv=[Z]:vv.push(Z),Va!==4&&(Va=2),y===null)return!0;j=Al(j,_),_=y;do{switch(_.tag){case 3:return _.flags|=65536,h=V&-V,_.lanes|=h,h=Cj(_.stateNode,j,h),nj(_,h),!1;case 1:if(y=_.type,Z=_.stateNode,(_.flags&128)===0&&(typeof y.getDerivedStateFromError=="function"||Z!==null&&typeof Z.componentDidCatch=="function"&&(ah===null||!ah.has(Z))))return _.flags|=65536,V&=-V,_.lanes|=V,V=iB(V),sB(V,h,_,j),nj(_,V),!1}_=_.return}while(_!==null);return!1}var Pj=Error(r(461)),hi=!1;function us(h,y,_,j){y.child=h===null?dU(y,null,_,j):lf(y,h.child,_,j)}function oB(h,y,_,j,V){_=_.render;var Z=y.ref;if("ref"in j){var Ne={};for(var qe in j)qe!=="ref"&&(Ne[qe]=j[qe])}else Ne=j;return rf(y),j=lj(h,y,_,Ne,Z,V),qe=cj(),h!==null&&!hi?(dj(h,y,V),fu(h,y,V)):(Sr&&qe&&qA(y),y.flags|=1,us(h,y,j,V),y.child)}function lB(h,y,_,j,V){if(h===null){var Z=_.type;return typeof Z=="function"&&!UA(Z)&&Z.defaultProps===void 0&&_.compare===null?(y.tag=15,y.type=Z,cB(h,y,Z,j,V)):(h=t_(_.type,null,j,y,y.mode,V),h.ref=y.ref,h.return=y,y.child=h)}if(Z=h.child,!Rj(h,V)){var Ne=Z.memoizedProps;if(_=_.compare,_=_!==null?_:Vm,_(Ne,j)&&h.ref===y.ref)return fu(h,y,V)}return y.flags|=1,h=cu(Z,j),h.ref=y.ref,h.return=y,y.child=h}function cB(h,y,_,j,V){if(h!==null){var Z=h.memoizedProps;if(Vm(Z,j)&&h.ref===y.ref)if(hi=!1,y.pendingProps=j=Z,Rj(h,V))(h.flags&131072)!==0&&(hi=!0);else return y.lanes=h.lanes,fu(h,y,V)}return Tj(h,y,_,j,V)}function dB(h,y,_,j){var V=j.children,Z=h!==null?h.memoizedState:null;if(h===null&&y.stateNode===null&&(y.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),j.mode==="hidden"){if((y.flags&128)!==0){if(Z=Z!==null?Z.baseLanes|_:_,h!==null){for(j=y.child=h.child,V=0;j!==null;)V=V|j.lanes|j.childLanes,j=j.sibling;j=V&~Z}else j=0,y.child=null;return uB(h,y,Z,_,j)}if((_&536870912)!==0)y.memoizedState={baseLanes:0,cachePool:null},h!==null&&i_(y,Z!==null?Z.cachePool:null),Z!==null?hU(y,Z):aj(),pU(y);else return j=y.lanes=536870912,uB(h,y,Z!==null?Z.baseLanes|_:_,_,j)}else Z!==null?(i_(y,Z.cachePool),hU(y,Z),eh(),y.memoizedState=null):(h!==null&&i_(y,null),aj(),eh());return us(h,y,V,_),y.child}function hv(h,y){return h!==null&&h.tag===22||y.stateNode!==null||(y.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),y.sibling}function uB(h,y,_,j,V){var Z=JA();return Z=Z===null?null:{parent:ui._currentValue,pool:Z},y.memoizedState={baseLanes:_,cachePool:Z},h!==null&&i_(y,null),aj(),pU(y),h!==null&&lb(h,y,j,!0),y.childLanes=V,null}function w_(h,y){return y=__({mode:y.mode,children:y.children},h.mode),y.ref=h.ref,h.child=y,y.return=h,y}function mB(h,y,_){return lf(y,h.child,null,_),h=w_(y,y.pendingProps),h.flags|=2,Yo(y),y.memoizedState=null,h}function ode(h,y,_){var j=y.pendingProps,V=(y.flags&128)!==0;if(y.flags&=-129,h===null){if(Sr){if(j.mode==="hidden")return h=w_(y,j),y.lanes=536870912,hv(null,h);if(sj(y),(h=xa)?(h=k8(h,El),h=h!==null&&h.data==="&"?h:null,h!==null&&(y.memoizedState={dehydrated:h,treeContext:Gm!==null?{id:ld,overflow:cd}:null,retryLane:536870912,hydrationErrors:null},_=X5(h),_.return=y,y.child=_,cs=y,xa=null)):h=null,h===null)throw Km(y);return y.lanes=536870912,null}return w_(y,j)}var Z=h.memoizedState;if(Z!==null){var Ne=Z.dehydrated;if(sj(y),V)if(y.flags&256)y.flags&=-257,y=mB(h,y,_);else if(y.memoizedState!==null)y.child=h.child,y.flags|=128,y=null;else throw Error(r(558));else if(hi||lb(h,y,_,!1),V=(_&h.childLanes)!==0,hi||V){if(j=oa,j!==null&&(Ne=Vt(j,_),Ne!==0&&Ne!==Z.retryLane))throw Z.retryLane=Ne,Zp(h,Ne),To(j,h,Ne),Pj;E_(),y=mB(h,y,_)}else h=Z.treeContext,xa=Fl(Ne.nextSibling),cs=y,Sr=!0,Wm=null,El=!1,h!==null&&J5(y,h),y=w_(y,j),y.flags|=4096;return y}return h=cu(h.child,{mode:j.mode,children:j.children}),h.ref=y.ref,y.child=h,h.return=y,h}function S_(h,y){var _=y.ref;if(_===null)h!==null&&h.ref!==null&&(y.flags|=4194816);else{if(typeof _!="function"&&typeof _!="object")throw Error(r(284));(h===null||h.ref!==_)&&(y.flags|=4194816)}}function Tj(h,y,_,j,V){return rf(y),_=lj(h,y,_,j,void 0,V),j=cj(),h!==null&&!hi?(dj(h,y,V),fu(h,y,V)):(Sr&&j&&qA(y),y.flags|=1,us(h,y,_,V),y.child)}function hB(h,y,_,j,V,Z){return rf(y),y.updateQueue=null,_=gU(y,j,_,V),fU(h),j=cj(),h!==null&&!hi?(dj(h,y,Z),fu(h,y,Z)):(Sr&&j&&qA(y),y.flags|=1,us(h,y,_,Z),y.child)}function pB(h,y,_,j,V){if(rf(y),y.stateNode===null){var Z=ab,Ne=_.contextType;typeof Ne=="object"&&Ne!==null&&(Z=ds(Ne)),Z=new _(j,Z),y.memoizedState=Z.state!==null&&Z.state!==void 0?Z.state:null,Z.updater=Nj,y.stateNode=Z,Z._reactInternals=y,Z=y.stateNode,Z.props=j,Z.state=y.memoizedState,Z.refs={},ej(y),Ne=_.contextType,Z.context=typeof Ne=="object"&&Ne!==null?ds(Ne):ab,Z.state=y.memoizedState,Ne=_.getDerivedStateFromProps,typeof Ne=="function"&&(kj(y,_,Ne,j),Z.state=y.memoizedState),typeof _.getDerivedStateFromProps=="function"||typeof Z.getSnapshotBeforeUpdate=="function"||typeof Z.UNSAFE_componentWillMount!="function"&&typeof Z.componentWillMount!="function"||(Ne=Z.state,typeof Z.componentWillMount=="function"&&Z.componentWillMount(),typeof Z.UNSAFE_componentWillMount=="function"&&Z.UNSAFE_componentWillMount(),Ne!==Z.state&&Nj.enqueueReplaceState(Z,Z.state,null),lv(y,j,Z,V),ov(),Z.state=y.memoizedState),typeof Z.componentDidMount=="function"&&(y.flags|=4194308),j=!0}else if(h===null){Z=y.stateNode;var qe=y.memoizedProps,lt=df(_,qe);Z.props=lt;var vt=Z.context,Rt=_.contextType;Ne=ab,typeof Rt=="object"&&Rt!==null&&(Ne=ds(Rt));var Ut=_.getDerivedStateFromProps;Rt=typeof Ut=="function"||typeof Z.getSnapshotBeforeUpdate=="function",qe=y.pendingProps!==qe,Rt||typeof Z.UNSAFE_componentWillReceiveProps!="function"&&typeof Z.componentWillReceiveProps!="function"||(qe||vt!==Ne)&&eB(y,Z,j,Ne),Ym=!1;var kt=y.memoizedState;Z.state=kt,lv(y,j,Z,V),ov(),vt=y.memoizedState,qe||kt!==vt||Ym?(typeof Ut=="function"&&(kj(y,_,Ut,j),vt=y.memoizedState),(lt=Ym||ZU(y,_,lt,j,kt,vt,Ne))?(Rt||typeof Z.UNSAFE_componentWillMount!="function"&&typeof Z.componentWillMount!="function"||(typeof Z.componentWillMount=="function"&&Z.componentWillMount(),typeof Z.UNSAFE_componentWillMount=="function"&&Z.UNSAFE_componentWillMount()),typeof Z.componentDidMount=="function"&&(y.flags|=4194308)):(typeof Z.componentDidMount=="function"&&(y.flags|=4194308),y.memoizedProps=j,y.memoizedState=vt),Z.props=j,Z.state=vt,Z.context=Ne,j=lt):(typeof Z.componentDidMount=="function"&&(y.flags|=4194308),j=!1)}else{Z=y.stateNode,tj(h,y),Ne=y.memoizedProps,Rt=df(_,Ne),Z.props=Rt,Ut=y.pendingProps,kt=Z.context,vt=_.contextType,lt=ab,typeof vt=="object"&&vt!==null&&(lt=ds(vt)),qe=_.getDerivedStateFromProps,(vt=typeof qe=="function"||typeof Z.getSnapshotBeforeUpdate=="function")||typeof Z.UNSAFE_componentWillReceiveProps!="function"&&typeof Z.componentWillReceiveProps!="function"||(Ne!==Ut||kt!==lt)&&eB(y,Z,j,lt),Ym=!1,kt=y.memoizedState,Z.state=kt,lv(y,j,Z,V),ov();var At=y.memoizedState;Ne!==Ut||kt!==At||Ym||h!==null&&h.dependencies!==null&&r_(h.dependencies)?(typeof qe=="function"&&(kj(y,_,qe,j),At=y.memoizedState),(Rt=Ym||ZU(y,_,Rt,j,kt,At,lt)||h!==null&&h.dependencies!==null&&r_(h.dependencies))?(vt||typeof Z.UNSAFE_componentWillUpdate!="function"&&typeof Z.componentWillUpdate!="function"||(typeof Z.componentWillUpdate=="function"&&Z.componentWillUpdate(j,At,lt),typeof Z.UNSAFE_componentWillUpdate=="function"&&Z.UNSAFE_componentWillUpdate(j,At,lt)),typeof Z.componentDidUpdate=="function"&&(y.flags|=4),typeof Z.getSnapshotBeforeUpdate=="function"&&(y.flags|=1024)):(typeof Z.componentDidUpdate!="function"||Ne===h.memoizedProps&&kt===h.memoizedState||(y.flags|=4),typeof Z.getSnapshotBeforeUpdate!="function"||Ne===h.memoizedProps&&kt===h.memoizedState||(y.flags|=1024),y.memoizedProps=j,y.memoizedState=At),Z.props=j,Z.state=At,Z.context=lt,j=Rt):(typeof Z.componentDidUpdate!="function"||Ne===h.memoizedProps&&kt===h.memoizedState||(y.flags|=4),typeof Z.getSnapshotBeforeUpdate!="function"||Ne===h.memoizedProps&&kt===h.memoizedState||(y.flags|=1024),j=!1)}return Z=j,S_(h,y),j=(y.flags&128)!==0,Z||j?(Z=y.stateNode,_=j&&typeof _.getDerivedStateFromError!="function"?null:Z.render(),y.flags|=1,h!==null&&j?(y.child=lf(y,h.child,null,V),y.child=lf(y,null,_,V)):us(h,y,_,V),y.memoizedState=Z.state,h=y.child):h=fu(h,y,V),h}function fB(h,y,_,j){return tf(),y.flags|=256,us(h,y,_,j),y.child}var Aj={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function jj(h){return{baseLanes:h,cachePool:aU()}}function Mj(h,y,_){return h=h!==null?h.childLanes&~_:0,y&&(h|=Jo),h}function gB(h,y,_){var j=y.pendingProps,V=!1,Z=(y.flags&128)!==0,Ne;if((Ne=Z)||(Ne=h!==null&&h.memoizedState===null?!1:(ai.current&2)!==0),Ne&&(V=!0,y.flags&=-129),Ne=(y.flags&32)!==0,y.flags&=-33,h===null){if(Sr){if(V?Zm(y):eh(),(h=xa)?(h=k8(h,El),h=h!==null&&h.data!=="&"?h:null,h!==null&&(y.memoizedState={dehydrated:h,treeContext:Gm!==null?{id:ld,overflow:cd}:null,retryLane:536870912,hydrationErrors:null},_=X5(h),_.return=y,y.child=_,cs=y,xa=null)):h=null,h===null)throw Km(y);return hM(h)?y.lanes=32:y.lanes=536870912,null}var qe=j.children;return j=j.fallback,V?(eh(),V=y.mode,qe=__({mode:"hidden",children:qe},V),j=ef(j,V,_,null),qe.return=y,j.return=y,qe.sibling=j,y.child=qe,j=y.child,j.memoizedState=jj(_),j.childLanes=Mj(h,Ne,_),y.memoizedState=Aj,hv(null,j)):(Zm(y),Ej(y,qe))}var lt=h.memoizedState;if(lt!==null&&(qe=lt.dehydrated,qe!==null)){if(Z)y.flags&256?(Zm(y),y.flags&=-257,y=Dj(h,y,_)):y.memoizedState!==null?(eh(),y.child=h.child,y.flags|=128,y=null):(eh(),qe=j.fallback,V=y.mode,j=__({mode:"visible",children:j.children},V),qe=ef(qe,V,_,null),qe.flags|=2,j.return=y,qe.return=y,j.sibling=qe,y.child=j,lf(y,h.child,null,_),j=y.child,j.memoizedState=jj(_),j.childLanes=Mj(h,Ne,_),y.memoizedState=Aj,y=hv(null,j));else if(Zm(y),hM(qe)){if(Ne=qe.nextSibling&&qe.nextSibling.dataset,Ne)var vt=Ne.dgst;Ne=vt,j=Error(r(419)),j.stack="",j.digest=Ne,tv({value:j,source:null,stack:null}),y=Dj(h,y,_)}else if(hi||lb(h,y,_,!1),Ne=(_&h.childLanes)!==0,hi||Ne){if(Ne=oa,Ne!==null&&(j=Vt(Ne,_),j!==0&&j!==lt.retryLane))throw lt.retryLane=j,Zp(h,j),To(Ne,h,j),Pj;mM(qe)||E_(),y=Dj(h,y,_)}else mM(qe)?(y.flags|=192,y.child=h.child,y=null):(h=lt.treeContext,xa=Fl(qe.nextSibling),cs=y,Sr=!0,Wm=null,El=!1,h!==null&&J5(y,h),y=Ej(y,j.children),y.flags|=4096);return y}return V?(eh(),qe=j.fallback,V=y.mode,lt=h.child,vt=lt.sibling,j=cu(lt,{mode:"hidden",children:j.children}),j.subtreeFlags=lt.subtreeFlags&65011712,vt!==null?qe=cu(vt,qe):(qe=ef(qe,V,_,null),qe.flags|=2),qe.return=y,j.return=y,j.sibling=qe,y.child=j,hv(null,j),j=y.child,qe=h.child.memoizedState,qe===null?qe=jj(_):(V=qe.cachePool,V!==null?(lt=ui._currentValue,V=V.parent!==lt?{parent:lt,pool:lt}:V):V=aU(),qe={baseLanes:qe.baseLanes|_,cachePool:V}),j.memoizedState=qe,j.childLanes=Mj(h,Ne,_),y.memoizedState=Aj,hv(h.child,j)):(Zm(y),_=h.child,h=_.sibling,_=cu(_,{mode:"visible",children:j.children}),_.return=y,_.sibling=null,h!==null&&(Ne=y.deletions,Ne===null?(y.deletions=[h],y.flags|=16):Ne.push(h)),y.child=_,y.memoizedState=null,_)}function Ej(h,y){return y=__({mode:"visible",children:y},h.mode),y.return=h,h.child=y}function __(h,y){return h=Ko(22,h,null,y),h.lanes=0,h}function Dj(h,y,_){return lf(y,h.child,null,_),h=Ej(y,y.pendingProps.children),h.flags|=2,y.memoizedState=null,h}function bB(h,y,_){h.lanes|=y;var j=h.alternate;j!==null&&(j.lanes|=y),KA(h.return,y,_)}function Fj(h,y,_,j,V,Z){var Ne=h.memoizedState;Ne===null?h.memoizedState={isBackwards:y,rendering:null,renderingStartTime:0,last:j,tail:_,tailMode:V,treeForkCount:Z}:(Ne.isBackwards=y,Ne.rendering=null,Ne.renderingStartTime=0,Ne.last=j,Ne.tail=_,Ne.tailMode=V,Ne.treeForkCount=Z)}function xB(h,y,_){var j=y.pendingProps,V=j.revealOrder,Z=j.tail;j=j.children;var Ne=ai.current,qe=(Ne&2)!==0;if(qe?(Ne=Ne&1|2,y.flags|=128):Ne&=1,pe(ai,Ne),us(h,y,j,_),j=Sr?ev:0,!qe&&h!==null&&(h.flags&128)!==0)e:for(h=y.child;h!==null;){if(h.tag===13)h.memoizedState!==null&&bB(h,_,y);else if(h.tag===19)bB(h,_,y);else if(h.child!==null){h.child.return=h,h=h.child;continue}if(h===y)break e;for(;h.sibling===null;){if(h.return===null||h.return===y)break e;h=h.return}h.sibling.return=h.return,h=h.sibling}switch(V){case"forwards":for(_=y.child,V=null;_!==null;)h=_.alternate,h!==null&&u_(h)===null&&(V=_),_=_.sibling;_=V,_===null?(V=y.child,y.child=null):(V=_.sibling,_.sibling=null),Fj(y,!1,V,_,Z,j);break;case"backwards":case"unstable_legacy-backwards":for(_=null,V=y.child,y.child=null;V!==null;){if(h=V.alternate,h!==null&&u_(h)===null){y.child=V;break}h=V.sibling,V.sibling=_,_=V,V=h}Fj(y,!0,_,null,Z,j);break;case"together":Fj(y,!1,null,null,void 0,j);break;default:y.memoizedState=null}return y.child}function fu(h,y,_){if(h!==null&&(y.dependencies=h.dependencies),rh|=y.lanes,(_&y.childLanes)===0)if(h!==null){if(lb(h,y,_,!1),(_&y.childLanes)===0)return null}else return null;if(h!==null&&y.child!==h.child)throw Error(r(153));if(y.child!==null){for(h=y.child,_=cu(h,h.pendingProps),y.child=_,_.return=y;h.sibling!==null;)h=h.sibling,_=_.sibling=cu(h,h.pendingProps),_.return=y;_.sibling=null}return y.child}function Rj(h,y){return(h.lanes&y)!==0?!0:(h=h.dependencies,!!(h!==null&&r_(h)))}function lde(h,y,_){switch(y.tag){case 3:D(y,y.stateNode.containerInfo),Xm(y,ui,h.memoizedState.cache),tf();break;case 27:case 5:U(y);break;case 4:D(y,y.stateNode.containerInfo);break;case 10:Xm(y,y.type,y.memoizedProps.value);break;case 31:if(y.memoizedState!==null)return y.flags|=128,sj(y),null;break;case 13:var j=y.memoizedState;if(j!==null)return j.dehydrated!==null?(Zm(y),y.flags|=128,null):(_&y.child.childLanes)!==0?gB(h,y,_):(Zm(y),h=fu(h,y,_),h!==null?h.sibling:null);Zm(y);break;case 19:var V=(h.flags&128)!==0;if(j=(_&y.childLanes)!==0,j||(lb(h,y,_,!1),j=(_&y.childLanes)!==0),V){if(j)return xB(h,y,_);y.flags|=128}if(V=y.memoizedState,V!==null&&(V.rendering=null,V.tail=null,V.lastEffect=null),pe(ai,ai.current),j)break;return null;case 22:return y.lanes=0,dB(h,y,_,y.pendingProps);case 24:Xm(y,ui,h.memoizedState.cache)}return fu(h,y,_)}function yB(h,y,_){if(h!==null)if(h.memoizedProps!==y.pendingProps)hi=!0;else{if(!Rj(h,_)&&(y.flags&128)===0)return hi=!1,lde(h,y,_);hi=(h.flags&131072)!==0}else hi=!1,Sr&&(y.flags&1048576)!==0&&Q5(y,ev,y.index);switch(y.lanes=0,y.tag){case 16:e:{var j=y.pendingProps;if(h=sf(y.elementType),y.type=h,typeof h=="function")UA(h)?(j=df(h,j),y.tag=1,y=pB(null,y,h,j,_)):(y.tag=0,y=Tj(null,y,h,j,_));else{if(h!=null){var V=h.$$typeof;if(V===P){y.tag=11,y=oB(null,y,h,j,_);break e}else if(V===T){y.tag=14,y=lB(null,y,h,j,_);break e}}throw y=R(h)||h,Error(r(306,y,""))}}return y;case 0:return Tj(h,y,y.type,y.pendingProps,_);case 1:return j=y.type,V=df(j,y.pendingProps),pB(h,y,j,V,_);case 3:e:{if(D(y,y.stateNode.containerInfo),h===null)throw Error(r(387));j=y.pendingProps;var Z=y.memoizedState;V=Z.element,tj(h,y),lv(y,j,null,_);var Ne=y.memoizedState;if(j=Ne.cache,Xm(y,ui,j),j!==Z.cache&&XA(y,[ui],_,!0),ov(),j=Ne.element,Z.isDehydrated)if(Z={element:j,isDehydrated:!1,cache:Ne.cache},y.updateQueue.baseState=Z,y.memoizedState=Z,y.flags&256){y=fB(h,y,j,_);break e}else if(j!==V){V=Al(Error(r(424)),y),tv(V),y=fB(h,y,j,_);break e}else for(h=y.stateNode.containerInfo,h.nodeType===9?h=h.body:h=h.nodeName==="HTML"?h.ownerDocument.body:h,xa=Fl(h.firstChild),cs=y,Sr=!0,Wm=null,El=!0,_=dU(y,null,j,_),y.child=_;_;)_.flags=_.flags&-3|4096,_=_.sibling;else{if(tf(),j===V){y=fu(h,y,_);break e}us(h,y,j,_)}y=y.child}return y;case 26:return S_(h,y),h===null?(_=j8(y.type,null,y.pendingProps,null))?y.memoizedState=_:Sr||(_=y.type,h=y.pendingProps,j=z_(B.current).createElement(_),j[jt]=y,j[Jt]=h,ms(j,_,h),nt(j),y.stateNode=j):y.memoizedState=j8(y.type,h.memoizedProps,y.pendingProps,h.memoizedState),null;case 27:return U(y),h===null&&Sr&&(j=y.stateNode=P8(y.type,y.pendingProps,B.current),cs=y,El=!0,V=xa,lh(y.type)?(pM=V,xa=Fl(j.firstChild)):xa=V),us(h,y,y.pendingProps.children,_),S_(h,y),h===null&&(y.flags|=4194304),y.child;case 5:return h===null&&Sr&&((V=j=xa)&&(j=Ode(j,y.type,y.pendingProps,El),j!==null?(y.stateNode=j,cs=y,xa=Fl(j.firstChild),El=!1,V=!0):V=!1),V||Km(y)),U(y),V=y.type,Z=y.pendingProps,Ne=h!==null?h.memoizedProps:null,j=Z.children,cM(V,Z)?j=null:Ne!==null&&cM(V,Ne)&&(y.flags|=32),y.memoizedState!==null&&(V=lj(h,y,Zce,null,null,_),Tv._currentValue=V),S_(h,y),us(h,y,j,_),y.child;case 6:return h===null&&Sr&&((h=_=xa)&&(_=zde(_,y.pendingProps,El),_!==null?(y.stateNode=_,cs=y,xa=null,h=!0):h=!1),h||Km(y)),null;case 13:return gB(h,y,_);case 4:return D(y,y.stateNode.containerInfo),j=y.pendingProps,h===null?y.child=lf(y,null,j,_):us(h,y,j,_),y.child;case 11:return oB(h,y,y.type,y.pendingProps,_);case 7:return us(h,y,y.pendingProps,_),y.child;case 8:return us(h,y,y.pendingProps.children,_),y.child;case 12:return us(h,y,y.pendingProps.children,_),y.child;case 10:return j=y.pendingProps,Xm(y,y.type,j.value),us(h,y,j.children,_),y.child;case 9:return V=y.type._context,j=y.pendingProps.children,rf(y),V=ds(V),j=j(V),y.flags|=1,us(h,y,j,_),y.child;case 14:return lB(h,y,y.type,y.pendingProps,_);case 15:return cB(h,y,y.type,y.pendingProps,_);case 19:return xB(h,y,_);case 31:return ode(h,y,_);case 22:return dB(h,y,_,y.pendingProps);case 24:return rf(y),j=ds(ui),h===null?(V=JA(),V===null&&(V=oa,Z=YA(),V.pooledCache=Z,Z.refCount++,Z!==null&&(V.pooledCacheLanes|=_),V=Z),y.memoizedState={parent:j,cache:V},ej(y),Xm(y,ui,V)):((h.lanes&_)!==0&&(tj(h,y),lv(y,null,null,_),ov()),V=h.memoizedState,Z=y.memoizedState,V.parent!==j?(V={parent:j,cache:j},y.memoizedState=V,y.lanes===0&&(y.memoizedState=y.updateQueue.baseState=V),Xm(y,ui,j)):(j=Z.cache,Xm(y,ui,j),j!==V.cache&&XA(y,[ui],_,!0))),us(h,y,y.pendingProps.children,_),y.child;case 29:throw y.pendingProps}throw Error(r(156,y.tag))}function gu(h){h.flags|=4}function Lj(h,y,_,j,V){if((y=(h.mode&32)!==0)&&(y=!1),y){if(h.flags|=16777216,(V&335544128)===V)if(h.stateNode.complete)h.flags|=8192;else if(GB())h.flags|=8192;else throw of=o_,ZA}else h.flags&=-16777217}function vB(h,y){if(y.type!=="stylesheet"||(y.state.loading&4)!==0)h.flags&=-16777217;else if(h.flags|=16777216,!R8(y))if(GB())h.flags|=8192;else throw of=o_,ZA}function k_(h,y){y!==null&&(h.flags|=4),h.flags&16384&&(y=h.tag!==22?st():536870912,h.lanes|=y,vb|=y)}function pv(h,y){if(!Sr)switch(h.tailMode){case"hidden":y=h.tail;for(var _=null;y!==null;)y.alternate!==null&&(_=y),y=y.sibling;_===null?h.tail=null:_.sibling=null;break;case"collapsed":_=h.tail;for(var j=null;_!==null;)_.alternate!==null&&(j=_),_=_.sibling;j===null?y||h.tail===null?h.tail=null:h.tail.sibling=null:j.sibling=null}}function ya(h){var y=h.alternate!==null&&h.alternate.child===h.child,_=0,j=0;if(y)for(var V=h.child;V!==null;)_|=V.lanes|V.childLanes,j|=V.subtreeFlags&65011712,j|=V.flags&65011712,V.return=h,V=V.sibling;else for(V=h.child;V!==null;)_|=V.lanes|V.childLanes,j|=V.subtreeFlags,j|=V.flags,V.return=h,V=V.sibling;return h.subtreeFlags|=j,h.childLanes=_,y}function cde(h,y,_){var j=y.pendingProps;switch($A(y),y.tag){case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return ya(y),null;case 1:return ya(y),null;case 3:return _=y.stateNode,j=null,h!==null&&(j=h.memoizedState.cache),y.memoizedState.cache!==j&&(y.flags|=2048),mu(ui),M(),_.pendingContext&&(_.context=_.pendingContext,_.pendingContext=null),(h===null||h.child===null)&&(ob(y)?gu(y):h===null||h.memoizedState.isDehydrated&&(y.flags&256)===0||(y.flags|=1024,GA())),ya(y),null;case 26:var V=y.type,Z=y.memoizedState;return h===null?(gu(y),Z!==null?(ya(y),vB(y,Z)):(ya(y),Lj(y,V,null,j,_))):Z?Z!==h.memoizedState?(gu(y),ya(y),vB(y,Z)):(ya(y),y.flags&=-16777217):(h=h.memoizedProps,h!==j&&gu(y),ya(y),Lj(y,V,h,j,_)),null;case 27:if(Q(y),_=B.current,V=y.type,h!==null&&y.stateNode!=null)h.memoizedProps!==j&&gu(y);else{if(!j){if(y.stateNode===null)throw Error(r(166));return ya(y),null}h=be.current,ob(y)?Z5(y):(h=P8(V,j,_),y.stateNode=h,gu(y))}return ya(y),null;case 5:if(Q(y),V=y.type,h!==null&&y.stateNode!=null)h.memoizedProps!==j&&gu(y);else{if(!j){if(y.stateNode===null)throw Error(r(166));return ya(y),null}if(Z=be.current,ob(y))Z5(y);else{var Ne=z_(B.current);switch(Z){case 1:Z=Ne.createElementNS("http://www.w3.org/2000/svg",V);break;case 2:Z=Ne.createElementNS("http://www.w3.org/1998/Math/MathML",V);break;default:switch(V){case"svg":Z=Ne.createElementNS("http://www.w3.org/2000/svg",V);break;case"math":Z=Ne.createElementNS("http://www.w3.org/1998/Math/MathML",V);break;case"script":Z=Ne.createElement("div"),Z.innerHTML=" +