diff --git a/CHANGELOG.md b/CHANGELOG.md index ebaf13f8d..596ed3146 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -85,6 +85,13 @@ All notable changes to Bambuddy will be documented in this file. - Link archives to Printables, Thingiverse, or any other URL - Globe button opens external link when set, falls back to auto-detected MakerWorld URL - Edit via archive edit modal +- **Tag Management** - Centralized tag management for print archives (Issue #183): + - View all tags with usage counts in a dedicated modal + - Rename tags across all archives with one action + - Delete tags from all archives with confirmation + - Search and filter tags by name + - Sort by usage count or alphabetically + - Access via gear icon next to tag filter dropdown on Archives page - Included in backup/restore - **External Network Camera Support** - Add external cameras (MJPEG, RTSP, HTTP snapshot) to replace built-in printer cameras (Issue #143): - Configure per-printer external camera URL and type in Settings → Camera diff --git a/README.md b/README.md index 1ed95f862..a07b2028b 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ - Re-print to any connected printer with AMS mapping (auto-match or manual slot selection, multi-plate support) - Plate thumbnail browsing for multi-plate archives (hover to navigate between plates) - Archive comparison (side-by-side diff) +- Tag management (rename/delete across all archives) ### 📊 Monitoring & Control - Real-time printer status via WebSocket diff --git a/backend/app/api/routes/archives.py b/backend/app/api/routes/archives.py index 85241c422..112588d12 100644 --- a/backend/app/api/routes/archives.py +++ b/backend/app/api/routes/archives.py @@ -552,6 +552,103 @@ async def get_archive_stats(db: AsyncSession = Depends(get_db)): ) +@router.get("/tags") +async def get_all_tags(db: AsyncSession = Depends(get_db)): + """List all unique tags with usage counts. + + Returns a list of tags sorted by count (descending), then by name. + """ + # Query all archives with non-null tags + result = await db.execute(select(PrintArchive.tags).where(PrintArchive.tags.isnot(None))) + all_tags_rows = result.all() + + # Count occurrences of each tag + tag_counts: dict[str, int] = {} + for (tags_str,) in all_tags_rows: + if tags_str: + for tag in tags_str.split(","): + tag = tag.strip() + if tag: + tag_counts[tag] = tag_counts.get(tag, 0) + 1 + + # Convert to list and sort by count (desc), then name (asc) + tags_list = [{"name": name, "count": count} for name, count in tag_counts.items()] + tags_list.sort(key=lambda x: (-x["count"], x["name"].lower())) + + return tags_list + + +@router.put("/tags/{tag_name}") +async def rename_tag( + tag_name: str, + request: Request, + db: AsyncSession = Depends(get_db), +): + """Rename a tag across all archives. + + Request body should contain {"new_name": "new tag name"}. + Returns the count of affected archives. + """ + body = await request.json() + new_name = body.get("new_name", "").strip() + + if not new_name: + raise HTTPException(400, "new_name is required") + + if new_name == tag_name: + return {"affected": 0} + + # Find all archives containing the old tag + result = await db.execute(select(PrintArchive).where(PrintArchive.tags.isnot(None))) + archives = list(result.scalars().all()) + + affected = 0 + for archive in archives: + if not archive.tags: + continue + tags = [t.strip() for t in archive.tags.split(",")] + if tag_name in tags: + # Replace old tag with new tag + new_tags = [new_name if t == tag_name else t for t in tags] + # Remove duplicates while preserving order + seen = set() + unique_tags = [] + for t in new_tags: + if t not in seen: + seen.add(t) + unique_tags.append(t) + archive.tags = ", ".join(unique_tags) + affected += 1 + + await db.commit() + return {"affected": affected} + + +@router.delete("/tags/{tag_name}") +async def delete_tag(tag_name: str, db: AsyncSession = Depends(get_db)): + """Delete a tag from all archives. + + Returns the count of affected archives. + """ + # Find all archives containing the tag + result = await db.execute(select(PrintArchive).where(PrintArchive.tags.isnot(None))) + archives = list(result.scalars().all()) + + affected = 0 + for archive in archives: + if not archive.tags: + continue + tags = [t.strip() for t in archive.tags.split(",")] + if tag_name in tags: + # Remove the tag + new_tags = [t for t in tags if t != tag_name] + archive.tags = ", ".join(new_tags) if new_tags else None + affected += 1 + + await db.commit() + return {"affected": affected} + + @router.get("/{archive_id}", response_model=ArchiveResponse) async def get_archive(archive_id: int, db: AsyncSession = Depends(get_db)): """Get a specific archive.""" diff --git a/backend/tests/integration/test_archives_api.py b/backend/tests/integration/test_archives_api.py index 6d8510f17..59c8a30d4 100644 --- a/backend/tests/integration/test_archives_api.py +++ b/backend/tests/integration/test_archives_api.py @@ -434,3 +434,127 @@ class TestArchiveF3DEndpoints: """Verify filament-requirements with plate_id returns 404 for non-existent archive.""" response = await async_client.get("/api/v1/archives/999999/filament-requirements?plate_id=1") assert response.status_code == 404 + + # ======================================================================== + # Tag Management endpoints (Issue #183) + # ======================================================================== + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_get_tags_empty(self, async_client: AsyncClient): + """Verify empty list when no tags exist.""" + response = await async_client.get("/api/v1/archives/tags") + assert response.status_code == 200 + data = response.json() + assert isinstance(data, list) + assert len(data) == 0 + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_get_tags_with_data(self, async_client: AsyncClient, archive_factory, printer_factory, db_session): + """Verify tags are returned with counts.""" + printer = await printer_factory() + await archive_factory(printer.id, print_name="Archive 1", tags="functional, test") + await archive_factory(printer.id, print_name="Archive 2", tags="functional, calibration") + await archive_factory(printer.id, print_name="Archive 3", tags="test") + + response = await async_client.get("/api/v1/archives/tags") + assert response.status_code == 200 + data = response.json() + assert isinstance(data, list) + + # Convert to dict for easier lookup + tags_dict = {t["name"]: t["count"] for t in data} + assert tags_dict.get("functional") == 2 + assert tags_dict.get("test") == 2 + assert tags_dict.get("calibration") == 1 + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_get_tags_sorted_by_count( + self, async_client: AsyncClient, archive_factory, printer_factory, db_session + ): + """Verify tags are sorted by count descending, then by name.""" + printer = await printer_factory() + await archive_factory(printer.id, tags="alpha") + await archive_factory(printer.id, tags="beta, alpha") + await archive_factory(printer.id, tags="gamma, beta, alpha") + + response = await async_client.get("/api/v1/archives/tags") + assert response.status_code == 200 + data = response.json() + + # alpha=3, beta=2, gamma=1 + assert data[0]["name"] == "alpha" + assert data[0]["count"] == 3 + assert data[1]["name"] == "beta" + assert data[1]["count"] == 2 + assert data[2]["name"] == "gamma" + assert data[2]["count"] == 1 + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_rename_tag(self, async_client: AsyncClient, archive_factory, printer_factory, db_session): + """Verify renaming a tag updates all archives.""" + printer = await printer_factory() + a1 = await archive_factory(printer.id, print_name="Archive 1", tags="old-tag, other") + a2 = await archive_factory(printer.id, print_name="Archive 2", tags="old-tag") + await archive_factory(printer.id, print_name="Archive 3", tags="different") + + response = await async_client.put("/api/v1/archives/tags/old-tag", json={"new_name": "new-tag"}) + assert response.status_code == 200 + data = response.json() + assert data["affected"] == 2 + + # Verify the archives were updated + response = await async_client.get(f"/api/v1/archives/{a1.id}") + assert "new-tag" in response.json()["tags"] + assert "old-tag" not in response.json()["tags"] + + response = await async_client.get(f"/api/v1/archives/{a2.id}") + assert response.json()["tags"] == "new-tag" + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_rename_tag_no_change(self, async_client: AsyncClient): + """Verify renaming to same name returns 0 affected.""" + response = await async_client.put("/api/v1/archives/tags/some-tag", json={"new_name": "some-tag"}) + assert response.status_code == 200 + assert response.json()["affected"] == 0 + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_rename_tag_empty_name_error(self, async_client: AsyncClient): + """Verify renaming to empty name returns error.""" + response = await async_client.put("/api/v1/archives/tags/some-tag", json={"new_name": ""}) + assert response.status_code == 400 + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_delete_tag(self, async_client: AsyncClient, archive_factory, printer_factory, db_session): + """Verify deleting a tag removes it from all archives.""" + printer = await printer_factory() + a1 = await archive_factory(printer.id, print_name="Archive 1", tags="delete-me, keep") + a2 = await archive_factory(printer.id, print_name="Archive 2", tags="delete-me") + await archive_factory(printer.id, print_name="Archive 3", tags="different") + + response = await async_client.delete("/api/v1/archives/tags/delete-me") + assert response.status_code == 200 + data = response.json() + assert data["affected"] == 2 + + # Verify the archives were updated + response = await async_client.get(f"/api/v1/archives/{a1.id}") + assert response.json()["tags"] == "keep" + + response = await async_client.get(f"/api/v1/archives/{a2.id}") + # Should be None or empty when last tag is removed + assert response.json()["tags"] is None or response.json()["tags"] == "" + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_delete_tag_not_found(self, async_client: AsyncClient): + """Verify deleting non-existent tag returns 0 affected.""" + response = await async_client.delete("/api/v1/archives/tags/nonexistent-tag") + assert response.status_code == 200 + assert response.json()["affected"] == 0 diff --git a/frontend/src/__tests__/components/EditArchiveModal.test.tsx b/frontend/src/__tests__/components/EditArchiveModal.test.tsx index d852bcf66..73718efca 100644 --- a/frontend/src/__tests__/components/EditArchiveModal.test.tsx +++ b/frontend/src/__tests__/components/EditArchiveModal.test.tsx @@ -38,7 +38,11 @@ describe('EditArchiveModal', () => { return HttpResponse.json(mockProjects); }), http.get('/api/v1/archives/tags', () => { - return HttpResponse.json(['test', 'calibration', 'functional']); + return HttpResponse.json([ + { name: 'test', count: 2 }, + { name: 'calibration', count: 1 }, + { name: 'functional', count: 3 }, + ]); }), http.patch('/api/v1/archives/:id', async ({ request }) => { const body = await request.json(); diff --git a/frontend/src/__tests__/components/TagManagementModal.test.tsx b/frontend/src/__tests__/components/TagManagementModal.test.tsx new file mode 100644 index 000000000..db064d193 --- /dev/null +++ b/frontend/src/__tests__/components/TagManagementModal.test.tsx @@ -0,0 +1,300 @@ +/** + * Tests for the TagManagementModal component. + */ + +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { screen, waitFor, within } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { render } from '../utils'; +import { TagManagementModal } from '../../components/TagManagementModal'; +import { http, HttpResponse } from 'msw'; +import { server } from '../mocks/server'; + +const mockTags = [ + { name: 'functional', count: 5 }, + { name: 'calibration', count: 3 }, + { name: 'test', count: 2 }, + { name: 'art', count: 1 }, +]; + +describe('TagManagementModal', () => { + const mockOnClose = vi.fn(); + + beforeEach(() => { + vi.clearAllMocks(); + server.use( + http.get('/api/v1/archives/tags', () => { + return HttpResponse.json(mockTags); + }), + http.put('/api/v1/archives/tags/:tagName', async () => { + return HttpResponse.json({ affected: 2 }); + }), + http.delete('/api/v1/archives/tags/:tagName', () => { + return HttpResponse.json({ affected: 1 }); + }) + ); + }); + + describe('rendering', () => { + it('renders the modal title', async () => { + render(); + + expect(screen.getByText('Manage Tags')).toBeInTheDocument(); + }); + + it('shows loading state initially', () => { + render(); + + // Should show loading spinner before data loads + expect(screen.getByRole('button', { name: /close/i })).toBeInTheDocument(); + }); + + it('displays tags with counts', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText('functional')).toBeInTheDocument(); + expect(screen.getByText('5')).toBeInTheDocument(); + expect(screen.getByText('calibration')).toBeInTheDocument(); + expect(screen.getByText('3')).toBeInTheDocument(); + }); + }); + + it('shows total tag count and usage', async () => { + render(); + + await waitFor(() => { + // 4 tags, 11 total usages + expect(screen.getByText(/4 tags across 11 usages/i)).toBeInTheDocument(); + }); + }); + }); + + describe('search functionality', () => { + it('filters tags by search input', async () => { + const user = userEvent.setup(); + render(); + + await waitFor(() => { + expect(screen.getByText('functional')).toBeInTheDocument(); + }); + + const searchInput = screen.getByPlaceholderText('Search tags...'); + await user.type(searchInput, 'cal'); + + await waitFor(() => { + expect(screen.getByText('calibration')).toBeInTheDocument(); + expect(screen.queryByText('functional')).not.toBeInTheDocument(); + expect(screen.queryByText('art')).not.toBeInTheDocument(); + }); + }); + + it('shows no results message when search has no matches', async () => { + const user = userEvent.setup(); + render(); + + await waitFor(() => { + expect(screen.getByText('functional')).toBeInTheDocument(); + }); + + const searchInput = screen.getByPlaceholderText('Search tags...'); + await user.type(searchInput, 'nonexistent'); + + await waitFor(() => { + expect(screen.getByText('No tags match your search')).toBeInTheDocument(); + }); + }); + }); + + describe('sorting', () => { + it('sorts by count by default', async () => { + render(); + + await waitFor(() => { + const tagElements = screen.getAllByText(/functional|calibration|test|art/); + // First should be functional (count 5) + expect(tagElements[0]).toHaveTextContent('functional'); + }); + }); + + it('can sort by name', async () => { + const user = userEvent.setup(); + render(); + + await waitFor(() => { + expect(screen.getByText('functional')).toBeInTheDocument(); + }); + + const sortSelect = screen.getByDisplayValue('Sort by Count'); + await user.selectOptions(sortSelect, 'name'); + + await waitFor(() => { + const tagElements = screen.getAllByText(/functional|calibration|test|art/); + // First should be 'art' alphabetically + expect(tagElements[0]).toHaveTextContent('art'); + }); + }); + }); + + describe('rename functionality', () => { + it('enters edit mode when clicking edit button', async () => { + const user = userEvent.setup(); + render(); + + await waitFor(() => { + expect(screen.getByText('functional')).toBeInTheDocument(); + }); + + // Find the tag row and click its edit button + const tagRow = screen.getByText('functional').closest('div'); + const editButton = within(tagRow!).getByTitle('Rename tag'); + await user.click(editButton); + + // Should show input with current value + await waitFor(() => { + expect(screen.getByDisplayValue('functional')).toBeInTheDocument(); + }); + }); + + it('submits rename on Enter key', async () => { + const user = userEvent.setup(); + render(); + + await waitFor(() => { + expect(screen.getByText('functional')).toBeInTheDocument(); + }); + + const tagRow = screen.getByText('functional').closest('div'); + const editButton = within(tagRow!).getByTitle('Rename tag'); + await user.click(editButton); + + const input = screen.getByDisplayValue('functional'); + await user.clear(input); + await user.type(input, 'new-name{Enter}'); + + // Should show success (mutation called) + await waitFor(() => { + // After successful rename, edit mode should close + expect(screen.queryByDisplayValue('new-name')).not.toBeInTheDocument(); + }); + }); + + it('cancels edit on Escape key', async () => { + const user = userEvent.setup(); + render(); + + await waitFor(() => { + expect(screen.getByText('functional')).toBeInTheDocument(); + }); + + const tagRow = screen.getByText('functional').closest('div'); + const editButton = within(tagRow!).getByTitle('Rename tag'); + await user.click(editButton); + + const input = screen.getByDisplayValue('functional'); + await user.type(input, '-modified{Escape}'); + + // Should exit edit mode without saving + await waitFor(() => { + expect(screen.queryByDisplayValue('functional-modified')).not.toBeInTheDocument(); + expect(screen.getByText('functional')).toBeInTheDocument(); + }); + }); + }); + + describe('delete functionality', () => { + it('shows delete confirmation when clicking delete button', async () => { + const user = userEvent.setup(); + render(); + + await waitFor(() => { + expect(screen.getByText('functional')).toBeInTheDocument(); + }); + + const tagRow = screen.getByText('functional').closest('div'); + const deleteButton = within(tagRow!).getByTitle('Delete tag'); + await user.click(deleteButton); + + await waitFor(() => { + expect(screen.getByText(/Delete "functional" from 5 archives?/i)).toBeInTheDocument(); + }); + }); + + it('cancels delete confirmation on X button', async () => { + const user = userEvent.setup(); + render(); + + await waitFor(() => { + expect(screen.getByText('functional')).toBeInTheDocument(); + }); + + const tagRow = screen.getByText('functional').closest('div'); + const deleteButton = within(tagRow!).getByTitle('Delete tag'); + await user.click(deleteButton); + + await waitFor(() => { + expect(screen.getByText(/Delete "functional"/i)).toBeInTheDocument(); + }); + + // Find the confirmation row and click the cancel (X) button within it + const confirmationText = screen.getByText(/Delete "functional"/i); + const confirmationRow = confirmationText.closest('div'); + // The X button is the last button in the confirmation row + const buttons = within(confirmationRow!.parentElement!).getAllByRole('button'); + const cancelButton = buttons[buttons.length - 1]; // X button is last + await user.click(cancelButton); + + await waitFor(() => { + // Should return to normal display - the tag name should be visible again + expect(screen.getByText('functional')).toBeInTheDocument(); + }); + }); + }); + + describe('modal behavior', () => { + it('calls onClose when close button is clicked', async () => { + const user = userEvent.setup(); + render(); + + await waitFor(() => { + expect(screen.getByText('Manage Tags')).toBeInTheDocument(); + }); + + // Find close button in header (X icon) + const headerCloseButton = screen.getAllByRole('button')[0]; + await user.click(headerCloseButton); + + expect(mockOnClose).toHaveBeenCalledTimes(1); + }); + + it('calls onClose when Close button is clicked', async () => { + const user = userEvent.setup(); + render(); + + await waitFor(() => { + expect(screen.getByText('Manage Tags')).toBeInTheDocument(); + }); + + const closeButton = screen.getByRole('button', { name: /close/i }); + await user.click(closeButton); + + expect(mockOnClose).toHaveBeenCalledTimes(1); + }); + }); + + describe('empty state', () => { + it('shows empty message when no tags exist', async () => { + server.use( + http.get('/api/v1/archives/tags', () => { + return HttpResponse.json([]); + }) + ); + + render(); + + await waitFor(() => { + expect(screen.getByText('No tags found')).toBeInTheDocument(); + }); + }); + }); +}); diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 0c371769e..80a95fc60 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -335,6 +335,11 @@ export interface ArchiveStats { total_energy_cost: number; } +export interface TagInfo { + name: string; + count: number; +} + export interface FailureAnalysis { period_days: number; total_prints: number; @@ -1934,6 +1939,17 @@ export const api = { deleteArchive: (id: number) => request(`/archives/${id}`, { method: 'DELETE' }), getArchiveStats: () => request('/archives/stats'), + // Tag management + getTags: () => request('/archives/tags'), + renameTag: (oldName: string, newName: string) => + request<{ affected: number }>(`/archives/tags/${encodeURIComponent(oldName)}`, { + method: 'PUT', + body: JSON.stringify({ new_name: newName }), + }), + deleteTag: (name: string) => + request<{ affected: number }>(`/archives/tags/${encodeURIComponent(name)}`, { + method: 'DELETE', + }), recalculateCosts: () => request<{ message: string; updated: number }>('/archives/recalculate-costs', { method: 'POST' }), getFailureAnalysis: (options?: { days?: number; printerId?: number; projectId?: number }) => { diff --git a/frontend/src/components/EditArchiveModal.tsx b/frontend/src/components/EditArchiveModal.tsx index 2de8e3ac7..22f6ddc88 100644 --- a/frontend/src/components/EditArchiveModal.tsx +++ b/frontend/src/components/EditArchiveModal.tsx @@ -68,30 +68,49 @@ export function EditArchiveModal({ archive, onClose, existingTags = [] }: EditAr queryFn: () => api.getProjects(), }); - // Get all archives to extract existing tags if not provided - const { data: archives } = useQuery({ - queryKey: ['archives'], - queryFn: () => api.getArchives(undefined, 1000, 0), + // Fetch all tags using the dedicated API + const { data: tagsData } = useQuery({ + queryKey: ['tags'], + queryFn: api.getTags, enabled: existingTags.length === 0, }); - // Extract unique tags from all archives + // Use existing tags prop if provided, otherwise use fetched tags const allTags = existingTags.length > 0 ? existingTags - : [...new Set( - archives?.flatMap(a => a.tags?.split(',').map(t => t.trim()) || []).filter(Boolean) || [] - )].sort(); + : (tagsData?.map(t => t.name) || []); // Get current tags as array const currentTags = tags.split(',').map(t => t.trim()).filter(Boolean); - // Filter suggestions based on what's not already added - const tagSuggestions = allTags.filter(t => !currentTags.includes(t)); + // Get the text being typed after the last comma (for autocomplete filtering) + const currentInput = tags.includes(',') + ? tags.substring(tags.lastIndexOf(',') + 1).trim().toLowerCase() + : tags.trim().toLowerCase(); - // Add a tag + // Filter suggestions: not already added AND matches current input (if any) + const tagSuggestions = allTags.filter(t => + !currentTags.includes(t) && + (currentInput === '' || t.toLowerCase().includes(currentInput)) + ); + + // Add a tag (replaces any partial input with the selected tag) const addTag = (tag: string) => { - if (!currentTags.includes(tag)) { - const newTags = [...currentTags, tag].join(', '); + // If there's partial input being typed, replace it with the selected tag + // Otherwise, just append the tag + let baseTags: string[]; + if (currentInput && !allTags.includes(currentInput)) { + // User is typing a partial tag - replace it with the selected one + baseTags = tags.includes(',') + ? tags.substring(0, tags.lastIndexOf(',')).split(',').map(t => t.trim()).filter(Boolean) + : []; + } else { + // No partial input or input is already a complete tag - append + baseTags = currentTags; + } + + if (!baseTags.includes(tag)) { + const newTags = [...baseTags, tag].join(', '); setTags(newTags); } // Clear any pending blur timeout to prevent hiding suggestions @@ -342,7 +361,7 @@ export function EditArchiveModal({ archive, onClose, existingTags = [] }: EditAr {showTagSuggestions && tagSuggestions.length > 0 && (
- Existing tags (click to add) + {currentInput ? `Matching "${currentInput}"` : 'Existing tags'} (click to add)
{tagSuggestions.map((tag) => ( diff --git a/frontend/src/components/TagManagementModal.tsx b/frontend/src/components/TagManagementModal.tsx new file mode 100644 index 000000000..0f4cbe7f3 --- /dev/null +++ b/frontend/src/components/TagManagementModal.tsx @@ -0,0 +1,294 @@ +import { useState, useEffect } from 'react'; +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; +import { X, Tag, Pencil, Trash2, Loader2, Search, Check, AlertTriangle } from 'lucide-react'; +import { api } from '../api/client'; +import type { TagInfo } from '../api/client'; +import { Card, CardContent } from './Card'; +import { Button } from './Button'; +import { useToast } from '../contexts/ToastContext'; + +interface TagManagementModalProps { + onClose: () => void; +} + +export function TagManagementModal({ onClose }: TagManagementModalProps) { + const queryClient = useQueryClient(); + const { showToast } = useToast(); + const [search, setSearch] = useState(''); + const [editingTag, setEditingTag] = useState(null); + const [editValue, setEditValue] = useState(''); + const [deleteConfirm, setDeleteConfirm] = useState(null); + const [sortBy, setSortBy] = useState<'count' | 'name'>('count'); + + // Close on Escape key + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + if (editingTag) { + setEditingTag(null); + } else if (deleteConfirm) { + setDeleteConfirm(null); + } else { + onClose(); + } + } + }; + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, [onClose, editingTag, deleteConfirm]); + + const { data: tags, isLoading } = useQuery({ + queryKey: ['tags'], + queryFn: api.getTags, + }); + + const renameMutation = useMutation({ + mutationFn: ({ oldName, newName }: { oldName: string; newName: string }) => + api.renameTag(oldName, newName), + onSuccess: (data, { oldName, newName }) => { + queryClient.invalidateQueries({ queryKey: ['tags'] }); + queryClient.invalidateQueries({ queryKey: ['archives'] }); + showToast(`Renamed "${oldName}" to "${newName}" in ${data.affected} archive${data.affected !== 1 ? 's' : ''}`); + setEditingTag(null); + }, + onError: (error: Error) => { + showToast(error.message || 'Failed to rename tag', 'error'); + }, + }); + + const deleteMutation = useMutation({ + mutationFn: (name: string) => api.deleteTag(name), + onSuccess: (data, name) => { + queryClient.invalidateQueries({ queryKey: ['tags'] }); + queryClient.invalidateQueries({ queryKey: ['archives'] }); + showToast(`Deleted "${name}" from ${data.affected} archive${data.affected !== 1 ? 's' : ''}`); + setDeleteConfirm(null); + }, + onError: (error: Error) => { + showToast(error.message || 'Failed to delete tag', 'error'); + }, + }); + + const startEdit = (tag: TagInfo) => { + setEditingTag(tag.name); + setEditValue(tag.name); + setDeleteConfirm(null); + }; + + const cancelEdit = () => { + setEditingTag(null); + setEditValue(''); + }; + + const submitEdit = () => { + if (!editingTag || !editValue.trim()) return; + const newName = editValue.trim(); + if (newName === editingTag) { + cancelEdit(); + return; + } + renameMutation.mutate({ oldName: editingTag, newName }); + }; + + const handleEditKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + e.preventDefault(); + submitEdit(); + } else if (e.key === 'Escape') { + e.preventDefault(); + cancelEdit(); + } + }; + + const confirmDelete = (name: string) => { + setDeleteConfirm(name); + setEditingTag(null); + }; + + const executeDelete = () => { + if (deleteConfirm) { + deleteMutation.mutate(deleteConfirm); + } + }; + + // Filter and sort tags + const filteredTags = tags + ?.filter(t => t.name.toLowerCase().includes(search.toLowerCase())) + .sort((a, b) => { + if (sortBy === 'count') { + return b.count - a.count || a.name.localeCompare(b.name); + } + return a.name.localeCompare(b.name); + }); + + const totalUsage = tags?.reduce((sum, t) => sum + t.count, 0) || 0; + + return ( +
+ + + {/* Header */} +
+
+ +

Manage Tags

+
+ +
+ + {/* Search and sort */} +
+
+
+ + setSearch(e.target.value)} + /> +
+ +
+ {tags && ( +

+ {tags.length} tag{tags.length !== 1 ? 's' : ''} across {totalUsage} usage{totalUsage !== 1 ? 's' : ''} +

+ )} +
+ + {/* Tags list */} +
+ {isLoading ? ( +
+ +
+ ) : !filteredTags?.length ? ( +
+ {search ? 'No tags match your search' : 'No tags found'} +
+ ) : ( +
+ {filteredTags.map((tag) => ( +
+ {editingTag === tag.name ? ( + // Edit mode +
+ setEditValue(e.target.value)} + onKeyDown={handleEditKeyDown} + autoFocus + /> + + +
+ ) : deleteConfirm === tag.name ? ( + // Delete confirmation +
+ + + Delete "{tag.name}" from {tag.count} archive{tag.count !== 1 ? 's' : ''}? + + + +
+ ) : ( + // Normal display + <> + + {tag.name} + + {tag.count} + +
+ + +
+ + )} +
+ ))} +
+ )} +
+ + {/* Footer */} +
+ +
+
+
+
+ ); +} diff --git a/frontend/src/pages/ArchivesPage.tsx b/frontend/src/pages/ArchivesPage.tsx index 59673ab1a..0aef08234 100644 --- a/frontend/src/pages/ArchivesPage.tsx +++ b/frontend/src/pages/ArchivesPage.tsx @@ -43,6 +43,7 @@ import { FolderKanban, ChevronLeft, ChevronRight, + Settings, } from 'lucide-react'; import { api } from '../api/client'; import { openInSlicer } from '../utils/slicer'; @@ -66,6 +67,7 @@ import { ProjectPageModal } from '../components/ProjectPageModal'; import { TimelapseViewer } from '../components/TimelapseViewer'; import { CompareArchivesModal } from '../components/CompareArchivesModal'; import { PendingUploadsPanel } from '../components/PendingUploadsPanel'; +import { TagManagementModal } from '../components/TagManagementModal'; import { useToast } from '../contexts/ToastContext'; function formatFileSize(bytes: number): string { @@ -2006,6 +2008,7 @@ export function ArchivesPage() { const [showExportMenu, setShowExportMenu] = useState(false); const [isExporting, setIsExporting] = useState(false); const [showCompareModal, setShowCompareModal] = useState(false); + const [showTagManagement, setShowTagManagement] = useState(false); const [highlightedArchiveId, setHighlightedArchiveId] = useState(null); // Clear highlight after 5 seconds and scroll to highlighted element @@ -2637,6 +2640,13 @@ export function ArchivesPage() { ))} +
)}
@@ -2865,6 +2875,11 @@ export function ArchivesPage() { }} /> )} + + {/* Tag Management Modal */} + {showTagManagement && ( + setShowTagManagement(false)} /> + )}
); } diff --git a/static/assets/index-Dm1kl7bT.js b/static/assets/index-Dm1kl7bT.js new file mode 100644 index 000000000..2b7200ed6 --- /dev/null +++ b/static/assets/index-Dm1kl7bT.js @@ -0,0 +1,8665 @@ +function jae(t,e){for(var n=0;nr[i]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))r(i);new MutationObserver(i=>{for(const a of i)if(a.type==="childList")for(const o of a.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&r(o)}).observe(document,{childList:!0,subtree:!0});function n(i){const a={};return i.integrity&&(a.integrity=i.integrity),i.referrerPolicy&&(a.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?a.credentials="include":i.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function r(i){if(i.ep)return;i.ep=!0;const a=n(i);fetch(i.href,a)}})();var e_=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function Fo(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var _T={exports:{}},Xx={};var _U;function Pae(){if(_U)return Xx;_U=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function n(r,i,a){var o=null;if(a!==void 0&&(o=""+a),i.key!==void 0&&(o=""+i.key),"key"in i){a={};for(var l in i)l!=="key"&&(a[l]=i[l])}else a=i;return i=a.ref,{$$typeof:t,type:r,key:o,ref:i!==void 0?i:null,props:a}}return Xx.Fragment=e,Xx.jsx=n,Xx.jsxs=n,Xx}var wU;function Rae(){return wU||(wU=1,_T.exports=Pae()),_T.exports}var s=Rae(),wT={exports:{}},xn={};var SU;function Dae(){if(SU)return xn;SU=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),r=Symbol.for("react.strict_mode"),i=Symbol.for("react.profiler"),a=Symbol.for("react.consumer"),o=Symbol.for("react.context"),l=Symbol.for("react.forward_ref"),c=Symbol.for("react.suspense"),u=Symbol.for("react.memo"),d=Symbol.for("react.lazy"),f=Symbol.for("react.activity"),m=Symbol.iterator;function g(z){return z===null||typeof z!="object"?null:(z=m&&z[m]||z["@@iterator"],typeof z=="function"?z:null)}var x={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},y=Object.assign,b={};function v(z,q,oe){this.props=z,this.context=q,this.refs=b,this.updater=oe||x}v.prototype.isReactComponent={},v.prototype.setState=function(z,q){if(typeof z!="object"&&typeof z!="function"&&z!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,z,q,"setState")},v.prototype.forceUpdate=function(z){this.updater.enqueueForceUpdate(this,z,"forceUpdate")};function N(){}N.prototype=v.prototype;function S(z,q,oe){this.props=z,this.context=q,this.refs=b,this.updater=oe||x}var E=S.prototype=new N;E.constructor=S,y(E,v.prototype),E.isPureReactComponent=!0;var T=Array.isArray;function k(){}var C={H:null,A:null,T:null,S:null},D=Object.prototype.hasOwnProperty;function A(z,q,oe){var ae=oe.ref;return{$$typeof:t,type:z,key:q,ref:ae!==void 0?ae:null,props:oe}}function R(z,q){return A(z.type,q,z.props)}function F(z){return typeof z=="object"&&z!==null&&z.$$typeof===t}function O(z){var q={"=":"=0",":":"=2"};return"$"+z.replace(/[=:]/g,function(oe){return q[oe]})}var W=/\/+/g;function j(z,q){return typeof z=="object"&&z!==null&&z.key!=null?O(""+z.key):q.toString(36)}function G(z){switch(z.status){case"fulfilled":return z.value;case"rejected":throw z.reason;default:switch(typeof z.status=="string"?z.then(k,k):(z.status="pending",z.then(function(q){z.status==="pending"&&(z.status="fulfilled",z.value=q)},function(q){z.status==="pending"&&(z.status="rejected",z.reason=q)})),z.status){case"fulfilled":return z.value;case"rejected":throw z.reason}}throw z}function X(z,q,oe,ae,ue){var me=typeof z;(me==="undefined"||me==="boolean")&&(z=null);var ge=!1;if(z===null)ge=!0;else switch(me){case"bigint":case"string":case"number":ge=!0;break;case"object":switch(z.$$typeof){case t:case e:ge=!0;break;case d:return ge=z._init,X(ge(z._payload),q,oe,ae,ue)}}if(ge)return ue=ue(z),ge=ae===""?"."+j(z,0):ae,T(ue)?(oe="",ge!=null&&(oe=ge.replace(W,"$&/")+"/"),X(ue,q,oe,"",function(Me){return Me})):ue!=null&&(F(ue)&&(ue=R(ue,oe+(ue.key==null||z&&z.key===ue.key?"":(""+ue.key).replace(W,"$&/")+"/")+ge)),q.push(ue)),1;ge=0;var xe=ae===""?".":ae+":";if(T(z))for(var fe=0;fe>>1,ne=X[te];if(0>>1;tei(oe,Z))aei(ue,oe)?(X[te]=ue,X[ae]=Z,te=ae):(X[te]=oe,X[q]=Z,te=q);else if(aei(ue,Z))X[te]=ue,X[ae]=Z,te=ae;else break e}}return K}function i(X,K){var Z=X.sortIndex-K.sortIndex;return Z!==0?Z:X.id-K.id}if(t.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var a=performance;t.unstable_now=function(){return a.now()}}else{var o=Date,l=o.now();t.unstable_now=function(){return o.now()-l}}var c=[],u=[],d=1,f=null,m=3,g=!1,x=!1,y=!1,b=!1,v=typeof setTimeout=="function"?setTimeout:null,N=typeof clearTimeout=="function"?clearTimeout:null,S=typeof setImmediate<"u"?setImmediate:null;function E(X){for(var K=n(u);K!==null;){if(K.callback===null)r(u);else if(K.startTime<=X)r(u),K.sortIndex=K.expirationTime,e(c,K);else break;K=n(u)}}function T(X){if(y=!1,E(X),!x)if(n(c)!==null)x=!0,k||(k=!0,O());else{var K=n(u);K!==null&&G(T,K.startTime-X)}}var k=!1,C=-1,D=5,A=-1;function R(){return b?!0:!(t.unstable_now()-AX&&R());){var te=f.callback;if(typeof te=="function"){f.callback=null,m=f.priorityLevel;var ne=te(f.expirationTime<=X);if(X=t.unstable_now(),typeof ne=="function"){f.callback=ne,E(X),K=!0;break t}f===n(c)&&r(c),E(X)}else r(c);f=n(c)}if(f!==null)K=!0;else{var z=n(u);z!==null&&G(T,z.startTime-X),K=!1}}break e}finally{f=null,m=Z,g=!1}K=void 0}}finally{K?O():k=!1}}}var O;if(typeof S=="function")O=function(){S(F)};else if(typeof MessageChannel<"u"){var W=new MessageChannel,j=W.port2;W.port1.onmessage=F,O=function(){j.postMessage(null)}}else O=function(){v(F,0)};function G(X,K){C=v(function(){X(t.unstable_now())},K)}t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(X){X.callback=null},t.unstable_forceFrameRate=function(X){0>X||125te?(X.sortIndex=Z,e(u,X),n(c)===null&&X===n(u)&&(y?(N(C),C=-1):y=!0,G(T,Z-te))):(X.sortIndex=ne,e(c,X),x||g||(x=!0,k||(k=!0,O()))),X},t.unstable_shouldYield=R,t.unstable_wrapCallback=function(X){var K=m;return function(){var Z=m;m=K;try{return X.apply(this,arguments)}finally{m=Z}}}})(MT)),MT}var EU;function Iae(){return EU||(EU=1,NT.exports=Lae()),NT.exports}var ET={exports:{}},Na={};var TU;function Fae(){if(TU)return Na;TU=1;var t=Og();function e(c){var u="https://react.dev/errors/"+c;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(e){console.error(e)}}return t(),ET.exports=Fae(),ET.exports}var AU;function Uae(){if(AU)return Yx;AU=1;var t=Iae(),e=Og(),n=aW();function r(h){var p="https://react.dev/errors/"+h;if(1ne||(h.current=te[ne],te[ne]=null,ne--)}function oe(h,p){ne++,te[ne]=h.current,h.current=p}var ae=z(null),ue=z(null),me=z(null),ge=z(null);function xe(h,p){switch(oe(me,p),oe(ue,h),oe(ae,null),p.nodeType){case 9:case 11:h=(h=p.documentElement)&&(h=h.namespaceURI)?VF(h):0;break;default:if(h=p.tagName,p=p.namespaceURI)p=VF(p),h=GF(p,h);else switch(h){case"svg":h=1;break;case"math":h=2;break;default:h=0}}q(ae),oe(ae,h)}function fe(){q(ae),q(ue),q(me)}function Me(h){h.memoizedState!==null&&oe(ge,h);var p=ae.current,_=GF(p,h.type);p!==_&&(oe(ue,h),oe(ae,_))}function ve(h){ue.current===h&&(q(ae),q(ue)),ge.current===h&&(q(ge),Gx._currentValue=Z)}var le,Fe;function V(h){if(le===void 0)try{throw Error()}catch(_){var p=_.stack.trim().match(/\n( *(at )?)/);le=p&&p[1]||"",Fe=-1<_.stack.indexOf(` + at`)?" ()":-1<_.stack.indexOf("@")?"@unknown:0:0":""}return` +`+le+h+Fe}var B=!1;function Y(h,p){if(!h||B)return"";B=!0;var _=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var M={DetermineComponentFrameRoot:function(){try{if(p){var _t=function(){throw Error()};if(Object.defineProperty(_t.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(_t,[])}catch(ht){var at=ht}Reflect.construct(h,[],_t)}else{try{_t.call()}catch(ht){at=ht}h.call(_t.prototype)}}else{try{throw Error()}catch(ht){at=ht}(_t=h())&&typeof _t.catch=="function"&&_t.catch(function(){})}}catch(ht){if(ht&&at&&typeof ht.stack=="string")return[ht.stack,at.stack]}return[null,null]}};M.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var P=Object.getOwnPropertyDescriptor(M.DetermineComponentFrameRoot,"name");P&&P.configurable&&Object.defineProperty(M.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var L=M.DetermineComponentFrameRoot(),J=L[0],pe=L[1];if(J&&pe){var Ue=J.split(` +`),nt=pe.split(` +`);for(P=M=0;MP||Ue[M]!==nt[P]){var bt=` +`+Ue[M].replace(" at new "," at ");return h.displayName&&bt.includes("")&&(bt=bt.replace("",h.displayName)),bt}while(1<=M&&0<=P);break}}}finally{B=!1,Error.prepareStackTrace=_}return(_=h?h.displayName||h.name:"")?V(_):""}function U(h,p){switch(h.tag){case 26:case 27:case 5:return V(h.type);case 16:return V("Lazy");case 13:return h.child!==p&&p!==null?V("Suspense Fallback"):V("Suspense");case 19:return V("SuspenseList");case 0:case 15:return Y(h.type,!1);case 11:return Y(h.type.render,!1);case 1:return Y(h.type,!0);case 31:return V("Activity");default:return""}}function Q(h){try{var p="",_=null;do p+=U(h,_),_=h,h=h.return;while(h);return p}catch(M){return` +Error generating stack: `+M.message+` +`+M.stack}}var ce=Object.prototype.hasOwnProperty,Ae=t.unstable_scheduleCallback,De=t.unstable_cancelCallback,ke=t.unstable_shouldYield,$=t.unstable_requestPaint,I=t.unstable_now,H=t.unstable_getCurrentPriorityLevel,re=t.unstable_ImmediatePriority,we=t.unstable_UserBlockingPriority,be=t.unstable_NormalPriority,Be=t.unstable_LowPriority,Ie=t.unstable_IdlePriority,Ke=t.log,We=t.unstable_setDisableYieldValue,Xe=null,ie=null;function ye(h){if(typeof Ke=="function"&&We(h),ie&&typeof ie.setStrictMode=="function")try{ie.setStrictMode(Xe,h)}catch{}}var Le=Math.clz32?Math.clz32:ee,qe=Math.log,ot=Math.LN2;function ee(h){return h>>>=0,h===0?32:31-(qe(h)/ot|0)|0}var Te=256,se=262144,Ee=4194304;function Re(h){var p=h&42;if(p!==0)return p;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 Ce(h,p,_){var M=h.pendingLanes;if(M===0)return 0;var P=0,L=h.suspendedLanes,J=h.pingedLanes;h=h.warmLanes;var pe=M&134217727;return pe!==0?(M=pe&~L,M!==0?P=Re(M):(J&=pe,J!==0?P=Re(J):_||(_=pe&~h,_!==0&&(P=Re(_))))):(pe=M&~L,pe!==0?P=Re(pe):J!==0?P=Re(J):_||(_=M&~h,_!==0&&(P=Re(_)))),P===0?0:p!==0&&p!==P&&(p&L)===0&&(L=P&-P,_=p&-p,L>=_||L===32&&(_&4194048)!==0)?p:P}function Je(h,p){return(h.pendingLanes&~(h.suspendedLanes&~h.pingedLanes)&p)===0}function yt(h,p){switch(h){case 1:case 2:case 4:case 8:case 64:return p+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 p+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 kt(){var h=Ee;return Ee<<=1,(Ee&62914560)===0&&(Ee=4194304),h}function Ot(h){for(var p=[],_=0;31>_;_++)p.push(h);return p}function cn(h,p){h.pendingLanes|=p,p!==268435456&&(h.suspendedLanes=0,h.pingedLanes=0,h.warmLanes=0)}function Bt(h,p,_,M,P,L){var J=h.pendingLanes;h.pendingLanes=_,h.suspendedLanes=0,h.pingedLanes=0,h.warmLanes=0,h.expiredLanes&=_,h.entangledLanes&=_,h.errorRecoveryDisabledLanes&=_,h.shellSuspendCounter=0;var pe=h.entanglements,Ue=h.expirationTimes,nt=h.hiddenUpdates;for(_=J&~_;0<_;){var bt=31-Le(_),_t=1<"u")return null;try{return h.activeElement||h.body}catch{return h.body}}var Zt=/[\n"\\]/g;function $t(h){return h.replace(Zt,function(p){return"\\"+p.charCodeAt(0).toString(16)+" "})}function Vn(h,p,_,M,P,L,J,pe){h.name="",J!=null&&typeof J!="function"&&typeof J!="symbol"&&typeof J!="boolean"?h.type=J:h.removeAttribute("type"),p!=null?J==="number"?(p===0&&h.value===""||h.value!=p)&&(h.value=""+bn(p)):h.value!==""+bn(p)&&(h.value=""+bn(p)):J!=="submit"&&J!=="reset"||h.removeAttribute("value"),p!=null?wr(h,J,bn(p)):_!=null?wr(h,J,bn(_)):M!=null&&h.removeAttribute("value"),P==null&&L!=null&&(h.defaultChecked=!!L),P!=null&&(h.checked=P&&typeof P!="function"&&typeof P!="symbol"),pe!=null&&typeof pe!="function"&&typeof pe!="symbol"&&typeof pe!="boolean"?h.name=""+bn(pe):h.removeAttribute("name")}function un(h,p,_,M,P,L,J,pe){if(L!=null&&typeof L!="function"&&typeof L!="symbol"&&typeof L!="boolean"&&(h.type=L),p!=null||_!=null){if(!(L!=="submit"&&L!=="reset"||p!=null)){xt(h);return}_=_!=null?""+bn(_):"",p=p!=null?""+bn(p):_,pe||p===h.value||(h.value=p),h.defaultValue=p}M=M??P,M=typeof M!="function"&&typeof M!="symbol"&&!!M,h.checked=pe?h.checked:!!M,h.defaultChecked=!!M,J!=null&&typeof J!="function"&&typeof J!="symbol"&&typeof J!="boolean"&&(h.name=J),xt(h)}function wr(h,p,_){p==="number"&&mt(h.ownerDocument)===h||h.defaultValue===""+_||(h.defaultValue=""+_)}function Wt(h,p,_,M){if(h=h.options,p){p={};for(var P=0;P<_.length;P++)p["$"+_[P]]=!0;for(_=0;_"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),_M=!1;if(rc)try{var cx={};Object.defineProperty(cx,"passive",{get:function(){_M=!0}}),window.addEventListener("test",cx,cx),window.removeEventListener("test",cx,cx)}catch{_M=!1}var Su=null,wM=null,Wy=null;function GL(){if(Wy)return Wy;var h,p=wM,_=p.length,M,P="value"in Su?Su.value:Su.textContent,L=P.length;for(h=0;h<_&&p[h]===P[h];h++);var J=_-h;for(M=1;M<=J&&p[_-M]===P[L-M];M++);return Wy=P.slice(h,1=hx),QL=" ",ZL=!1;function JL(h,p){switch(h){case"keyup":return Jre.indexOf(p.keyCode)!==-1;case"keydown":return p.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function eI(h){return h=h.detail,typeof h=="object"&&"data"in h?h.data:null}var um=!1;function tie(h,p){switch(h){case"compositionend":return eI(p);case"keypress":return p.which!==32?null:(ZL=!0,QL);case"textInput":return h=p.data,h===QL&&ZL?null:h;default:return null}}function nie(h,p){if(um)return h==="compositionend"||!TM&&JL(h,p)?(h=GL(),Wy=wM=Su=null,um=!1,h):null;switch(h){case"paste":return null;case"keypress":if(!(p.ctrlKey||p.altKey||p.metaKey)||p.ctrlKey&&p.altKey){if(p.char&&1=p)return{node:_,offset:p-h};h=M}e:{for(;_;){if(_.nextSibling){_=_.nextSibling;break e}_=_.parentNode}_=void 0}_=lI(_)}}function uI(h,p){return h&&p?h===p?!0:h&&h.nodeType===3?!1:p&&p.nodeType===3?uI(h,p.parentNode):"contains"in h?h.contains(p):h.compareDocumentPosition?!!(h.compareDocumentPosition(p)&16):!1:!1}function dI(h){h=h!=null&&h.ownerDocument!=null&&h.ownerDocument.defaultView!=null?h.ownerDocument.defaultView:window;for(var p=mt(h.document);p instanceof h.HTMLIFrameElement;){try{var _=typeof p.contentWindow.location.href=="string"}catch{_=!1}if(_)h=p.contentWindow;else break;p=mt(h.document)}return p}function kM(h){var p=h&&h.nodeName&&h.nodeName.toLowerCase();return p&&(p==="input"&&(h.type==="text"||h.type==="search"||h.type==="tel"||h.type==="url"||h.type==="password")||p==="textarea"||h.contentEditable==="true")}var uie=rc&&"documentMode"in document&&11>=document.documentMode,dm=null,jM=null,gx=null,PM=!1;function hI(h,p,_){var M=_.window===_?_.document:_.nodeType===9?_:_.ownerDocument;PM||dm==null||dm!==mt(M)||(M=dm,"selectionStart"in M&&kM(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}),gx&&px(gx,M)||(gx=M,M=Uv(jM,"onSelect"),0>=J,P-=J,xl=1<<32-Le(p)+P|_<wn?(In=Yt,Yt=null):In=Yt.sibling;var qn=at(Ze,Yt,tt[wn],vt);if(qn===null){Yt===null&&(Yt=In);break}h&&Yt&&qn.alternate===null&&p(Ze,Yt),Ve=L(qn,Ve,wn),Wn===null?Jt=qn:Wn.sibling=qn,Wn=qn,Yt=In}if(wn===tt.length)return _(Ze,Yt),Un&&ac(Ze,wn),Jt;if(Yt===null){for(;wnwn?(In=Yt,Yt=null):In=Yt.sibling;var Gu=at(Ze,Yt,qn.value,vt);if(Gu===null){Yt===null&&(Yt=In);break}h&&Yt&&Gu.alternate===null&&p(Ze,Yt),Ve=L(Gu,Ve,wn),Wn===null?Jt=Gu:Wn.sibling=Gu,Wn=Gu,Yt=In}if(qn.done)return _(Ze,Yt),Un&&ac(Ze,wn),Jt;if(Yt===null){for(;!qn.done;wn++,qn=tt.next())qn=_t(Ze,qn.value,vt),qn!==null&&(Ve=L(qn,Ve,wn),Wn===null?Jt=qn:Wn.sibling=qn,Wn=qn);return Un&&ac(Ze,wn),Jt}for(Yt=M(Yt);!qn.done;wn++,qn=tt.next())qn=ht(Yt,Ze,wn,qn.value,vt),qn!==null&&(h&&qn.alternate!==null&&Yt.delete(qn.key===null?wn:qn.key),Ve=L(qn,Ve,wn),Wn===null?Jt=qn:Wn.sibling=qn,Wn=qn);return h&&Yt.forEach(function(kae){return p(Ze,kae)}),Un&&ac(Ze,wn),Jt}function lr(Ze,Ve,tt,vt){if(typeof tt=="object"&&tt!==null&&tt.type===y&&tt.key===null&&(tt=tt.props.children),typeof tt=="object"&&tt!==null){switch(tt.$$typeof){case g:e:{for(var Jt=tt.key;Ve!==null;){if(Ve.key===Jt){if(Jt=tt.type,Jt===y){if(Ve.tag===7){_(Ze,Ve.sibling),vt=P(Ve,tt.props.children),vt.return=Ze,Ze=vt;break e}}else if(Ve.elementType===Jt||typeof Jt=="object"&&Jt!==null&&Jt.$$typeof===D&&Sh(Jt)===Ve.type){_(Ze,Ve.sibling),vt=P(Ve,tt.props),wx(vt,tt),vt.return=Ze,Ze=vt;break e}_(Ze,Ve);break}else p(Ze,Ve);Ve=Ve.sibling}tt.type===y?(vt=bh(tt.props.children,Ze.mode,vt,tt.key),vt.return=Ze,Ze=vt):(vt=nv(tt.type,tt.key,tt.props,null,Ze.mode,vt),wx(vt,tt),vt.return=Ze,Ze=vt)}return J(Ze);case x:e:{for(Jt=tt.key;Ve!==null;){if(Ve.key===Jt)if(Ve.tag===4&&Ve.stateNode.containerInfo===tt.containerInfo&&Ve.stateNode.implementation===tt.implementation){_(Ze,Ve.sibling),vt=P(Ve,tt.children||[]),vt.return=Ze,Ze=vt;break e}else{_(Ze,Ve);break}else p(Ze,Ve);Ve=Ve.sibling}vt=UM(tt,Ze.mode,vt),vt.return=Ze,Ze=vt}return J(Ze);case D:return tt=Sh(tt),lr(Ze,Ve,tt,vt)}if(G(tt))return qt(Ze,Ve,tt,vt);if(O(tt)){if(Jt=O(tt),typeof Jt!="function")throw Error(r(150));return tt=Jt.call(tt),an(Ze,Ve,tt,vt)}if(typeof tt.then=="function")return lr(Ze,Ve,cv(tt),vt);if(tt.$$typeof===S)return lr(Ze,Ve,av(Ze,tt),vt);uv(Ze,tt)}return typeof tt=="string"&&tt!==""||typeof tt=="number"||typeof tt=="bigint"?(tt=""+tt,Ve!==null&&Ve.tag===6?(_(Ze,Ve.sibling),vt=P(Ve,tt),vt.return=Ze,Ze=vt):(_(Ze,Ve),vt=FM(tt,Ze.mode,vt),vt.return=Ze,Ze=vt),J(Ze)):_(Ze,Ve)}return function(Ze,Ve,tt,vt){try{_x=0;var Jt=lr(Ze,Ve,tt,vt);return wm=null,Jt}catch(Yt){if(Yt===_m||Yt===ov)throw Yt;var Wn=As(29,Yt,null,Ze.mode);return Wn.lanes=vt,Wn.return=Ze,Wn}finally{}}}var Mh=OI(!0),LI=OI(!1),Cu=!1;function QM(h){h.updateQueue={baseState:h.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function ZM(h,p){h=h.updateQueue,p.updateQueue===h&&(p.updateQueue={baseState:h.baseState,firstBaseUpdate:h.firstBaseUpdate,lastBaseUpdate:h.lastBaseUpdate,shared:h.shared,callbacks:null})}function Au(h){return{lane:h,tag:0,payload:null,callback:null,next:null}}function ku(h,p,_){var M=h.updateQueue;if(M===null)return null;if(M=M.shared,(Yn&2)!==0){var P=M.pending;return P===null?p.next=p:(p.next=P.next,P.next=p),M.pending=p,p=tv(h),yI(h,null,_),p}return ev(h,M,p,_),tv(h)}function Sx(h,p,_){if(p=p.updateQueue,p!==null&&(p=p.shared,(_&4194048)!==0)){var M=p.lanes;M&=h.pendingLanes,_|=M,p.lanes=_,gt(h,_)}}function JM(h,p){var _=h.updateQueue,M=h.alternate;if(M!==null&&(M=M.updateQueue,_===M)){var P=null,L=null;if(_=_.firstBaseUpdate,_!==null){do{var J={lane:_.lane,tag:_.tag,payload:_.payload,callback:null,next:null};L===null?P=L=J:L=L.next=J,_=_.next}while(_!==null);L===null?P=L=p:L=L.next=p}else P=L=p;_={baseState:M.baseState,firstBaseUpdate:P,lastBaseUpdate:L,shared:M.shared,callbacks:M.callbacks},h.updateQueue=_;return}h=_.lastBaseUpdate,h===null?_.firstBaseUpdate=p:h.next=p,_.lastBaseUpdate=p}var eE=!1;function Nx(){if(eE){var h=vm;if(h!==null)throw h}}function Mx(h,p,_,M){eE=!1;var P=h.updateQueue;Cu=!1;var L=P.firstBaseUpdate,J=P.lastBaseUpdate,pe=P.shared.pending;if(pe!==null){P.shared.pending=null;var Ue=pe,nt=Ue.next;Ue.next=null,J===null?L=nt:J.next=nt,J=Ue;var bt=h.alternate;bt!==null&&(bt=bt.updateQueue,pe=bt.lastBaseUpdate,pe!==J&&(pe===null?bt.firstBaseUpdate=nt:pe.next=nt,bt.lastBaseUpdate=Ue))}if(L!==null){var _t=P.baseState;J=0,bt=nt=Ue=null,pe=L;do{var at=pe.lane&-536870913,ht=at!==pe.lane;if(ht?(Ln&at)===at:(M&at)===at){at!==0&&at===ym&&(eE=!0),bt!==null&&(bt=bt.next={lane:0,tag:pe.tag,payload:pe.payload,callback:null,next:null});e:{var qt=h,an=pe;at=p;var lr=_;switch(an.tag){case 1:if(qt=an.payload,typeof qt=="function"){_t=qt.call(lr,_t,at);break e}_t=qt;break e;case 3:qt.flags=qt.flags&-65537|128;case 0:if(qt=an.payload,at=typeof qt=="function"?qt.call(lr,_t,at):qt,at==null)break e;_t=f({},_t,at);break e;case 2:Cu=!0}}at=pe.callback,at!==null&&(h.flags|=64,ht&&(h.flags|=8192),ht=P.callbacks,ht===null?P.callbacks=[at]:ht.push(at))}else ht={lane:at,tag:pe.tag,payload:pe.payload,callback:pe.callback,next:null},bt===null?(nt=bt=ht,Ue=_t):bt=bt.next=ht,J|=at;if(pe=pe.next,pe===null){if(pe=P.shared.pending,pe===null)break;ht=pe,pe=ht.next,ht.next=null,P.lastBaseUpdate=ht,P.shared.pending=null}}while(!0);bt===null&&(Ue=_t),P.baseState=Ue,P.firstBaseUpdate=nt,P.lastBaseUpdate=bt,L===null&&(P.shared.lanes=0),Ou|=J,h.lanes=J,h.memoizedState=_t}}function II(h,p){if(typeof h!="function")throw Error(r(191,h));h.call(p)}function FI(h,p){var _=h.callbacks;if(_!==null)for(h.callbacks=null,h=0;h<_.length;h++)II(_[h],p)}var Sm=z(null),dv=z(0);function UI(h,p){h=gc,oe(dv,h),oe(Sm,p),gc=h|p.baseLanes}function tE(){oe(dv,gc),oe(Sm,Sm.current)}function nE(){gc=dv.current,q(Sm),q(dv)}var ks=z(null),ao=null;function ju(h){var p=h.alternate;oe(li,li.current&1),oe(ks,h),ao===null&&(p===null||Sm.current!==null||p.memoizedState!==null)&&(ao=h)}function rE(h){oe(li,li.current),oe(ks,h),ao===null&&(ao=h)}function zI(h){h.tag===22?(oe(li,li.current),oe(ks,h),ao===null&&(ao=h)):Pu()}function Pu(){oe(li,li.current),oe(ks,ks.current)}function js(h){q(ks),ao===h&&(ao=null),q(li)}var li=z(0);function hv(h){for(var p=h;p!==null;){if(p.tag===13){var _=p.memoizedState;if(_!==null&&(_=_.dehydrated,_===null||cT(_)||uT(_)))return p}else if(p.tag===19&&(p.memoizedProps.revealOrder==="forwards"||p.memoizedProps.revealOrder==="backwards"||p.memoizedProps.revealOrder==="unstable_legacy-backwards"||p.memoizedProps.revealOrder==="together")){if((p.flags&128)!==0)return p}else if(p.child!==null){p.child.return=p,p=p.child;continue}if(p===h)break;for(;p.sibling===null;){if(p.return===null||p.return===h)return null;p=p.return}p.sibling.return=p.return,p=p.sibling}return null}var lc=0,vn=null,sr=null,pi=null,fv=!1,Nm=!1,Eh=!1,mv=0,Ex=0,Mm=null,vie=0;function Zr(){throw Error(r(321))}function iE(h,p){if(p===null)return!1;for(var _=0;_L?L:8;var J=X.T,pe={};X.T=pe,yE(h,!1,p,_);try{var Ue=P(),nt=X.S;if(nt!==null&&nt(pe,Ue),Ue!==null&&typeof Ue=="object"&&typeof Ue.then=="function"){var bt=yie(Ue,M);Cx(h,p,bt,Ds(h))}else Cx(h,p,M,Ds(h))}catch(_t){Cx(h,p,{then:function(){},status:"rejected",reason:_t},Ds())}finally{K.p=L,J!==null&&pe.types!==null&&(J.types=pe.types),X.T=J}}function Mie(){}function xE(h,p,_,M){if(h.tag!==5)throw Error(r(476));var P=g5(h).queue;p5(h,P,p,Z,_===null?Mie:function(){return x5(h),_(M)})}function g5(h){var p=h.memoizedState;if(p!==null)return p;p={memoizedState:Z,baseState:Z,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:cc,lastRenderedState:Z},next:null};var _={};return p.next={memoizedState:_,baseState:_,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:cc,lastRenderedState:_},next:null},h.memoizedState=p,h=h.alternate,h!==null&&(h.memoizedState=p),p}function x5(h){var p=g5(h);p.next===null&&(p=h.alternate.memoizedState),Cx(h,p.next.queue,{},Ds())}function bE(){return ca(Gx)}function b5(){return ci().memoizedState}function y5(){return ci().memoizedState}function Eie(h){for(var p=h.return;p!==null;){switch(p.tag){case 24:case 3:var _=Ds();h=Au(_);var M=ku(p,h,_);M!==null&&(us(M,p,_),Sx(M,p,_)),p={cache:qM()},h.payload=p;return}p=p.return}}function Tie(h,p,_){var M=Ds();_={lane:M,revertLane:0,gesture:null,action:_,hasEagerState:!1,eagerState:null,next:null},vv(h)?_5(p,_):(_=LM(h,p,_,M),_!==null&&(us(_,h,M),w5(_,p,M)))}function v5(h,p,_){var M=Ds();Cx(h,p,_,M)}function Cx(h,p,_,M){var P={lane:M,revertLane:0,gesture:null,action:_,hasEagerState:!1,eagerState:null,next:null};if(vv(h))_5(p,P);else{var L=h.alternate;if(h.lanes===0&&(L===null||L.lanes===0)&&(L=p.lastRenderedReducer,L!==null))try{var J=p.lastRenderedState,pe=L(J,_);if(P.hasEagerState=!0,P.eagerState=pe,Cs(pe,J))return ev(h,p,P,0),br===null&&Jy(),!1}catch{}finally{}if(_=LM(h,p,P,M),_!==null)return us(_,h,M),w5(_,p,M),!0}return!1}function yE(h,p,_,M){if(M={lane:2,revertLane:QE(),gesture:null,action:M,hasEagerState:!1,eagerState:null,next:null},vv(h)){if(p)throw Error(r(479))}else p=LM(h,_,M,2),p!==null&&us(p,h,2)}function vv(h){var p=h.alternate;return h===vn||p!==null&&p===vn}function _5(h,p){Nm=fv=!0;var _=h.pending;_===null?p.next=p:(p.next=_.next,_.next=p),h.pending=p}function w5(h,p,_){if((_&4194048)!==0){var M=p.lanes;M&=h.pendingLanes,_|=M,p.lanes=_,gt(h,_)}}var Ax={readContext:ca,use:gv,useCallback:Zr,useContext:Zr,useEffect:Zr,useImperativeHandle:Zr,useLayoutEffect:Zr,useInsertionEffect:Zr,useMemo:Zr,useReducer:Zr,useRef:Zr,useState:Zr,useDebugValue:Zr,useDeferredValue:Zr,useTransition:Zr,useSyncExternalStore:Zr,useId:Zr,useHostTransitionStatus:Zr,useFormState:Zr,useActionState:Zr,useOptimistic:Zr,useMemoCache:Zr,useCacheRefresh:Zr};Ax.useEffectEvent=Zr;var S5={readContext:ca,use:gv,useCallback:function(h,p){return Ba().memoizedState=[h,p===void 0?null:p],h},useContext:ca,useEffect:s5,useImperativeHandle:function(h,p,_){_=_!=null?_.concat([h]):null,bv(4194308,4,u5.bind(null,p,h),_)},useLayoutEffect:function(h,p){return bv(4194308,4,h,p)},useInsertionEffect:function(h,p){bv(4,2,h,p)},useMemo:function(h,p){var _=Ba();p=p===void 0?null:p;var M=h();if(Eh){ye(!0);try{h()}finally{ye(!1)}}return _.memoizedState=[M,p],M},useReducer:function(h,p,_){var M=Ba();if(_!==void 0){var P=_(p);if(Eh){ye(!0);try{_(p)}finally{ye(!1)}}}else P=p;return M.memoizedState=M.baseState=P,h={pending:null,lanes:0,dispatch:null,lastRenderedReducer:h,lastRenderedState:P},M.queue=h,h=h.dispatch=Tie.bind(null,vn,h),[M.memoizedState,h]},useRef:function(h){var p=Ba();return h={current:h},p.memoizedState=h},useState:function(h){h=hE(h);var p=h.queue,_=v5.bind(null,vn,p);return p.dispatch=_,[h.memoizedState,_]},useDebugValue:pE,useDeferredValue:function(h,p){var _=Ba();return gE(_,h,p)},useTransition:function(){var h=hE(!1);return h=p5.bind(null,vn,h.queue,!0,!1),Ba().memoizedState=h,[!1,h]},useSyncExternalStore:function(h,p,_){var M=vn,P=Ba();if(Un){if(_===void 0)throw Error(r(407));_=_()}else{if(_=p(),br===null)throw Error(r(349));(Ln&127)!==0||VI(M,p,_)}P.memoizedState=_;var L={value:_,getSnapshot:p};return P.queue=L,s5(WI.bind(null,M,L,h),[h]),M.flags|=2048,Em(9,{destroy:void 0},GI.bind(null,M,L,_,p),null),_},useId:function(){var h=Ba(),p=br.identifierPrefix;if(Un){var _=bl,M=xl;_=(M&~(1<<32-Le(M)-1)).toString(32)+_,p="_"+p+"R_"+_,_=mv++,0<_&&(p+="H"+_.toString(32)),p+="_"}else _=vie++,p="_"+p+"r_"+_.toString(32)+"_";return h.memoizedState=p},useHostTransitionStatus:bE,useFormState:t5,useActionState:t5,useOptimistic:function(h){var p=Ba();p.memoizedState=p.baseState=h;var _={pending:null,lanes:0,dispatch:null,lastRenderedReducer:null,lastRenderedState:null};return p.queue=_,p=yE.bind(null,vn,!0,_),_.dispatch=p,[h,p]},useMemoCache:cE,useCacheRefresh:function(){return Ba().memoizedState=Eie.bind(null,vn)},useEffectEvent:function(h){var p=Ba(),_={impl:h};return p.memoizedState=_,function(){if((Yn&2)!==0)throw Error(r(440));return _.impl.apply(void 0,arguments)}}},vE={readContext:ca,use:gv,useCallback:h5,useContext:ca,useEffect:mE,useImperativeHandle:d5,useInsertionEffect:l5,useLayoutEffect:c5,useMemo:f5,useReducer:xv,useRef:a5,useState:function(){return xv(cc)},useDebugValue:pE,useDeferredValue:function(h,p){var _=ci();return m5(_,sr.memoizedState,h,p)},useTransition:function(){var h=xv(cc)[0],p=ci().memoizedState;return[typeof h=="boolean"?h:Tx(h),p]},useSyncExternalStore:HI,useId:b5,useHostTransitionStatus:bE,useFormState:n5,useActionState:n5,useOptimistic:function(h,p){var _=ci();return XI(_,sr,h,p)},useMemoCache:cE,useCacheRefresh:y5};vE.useEffectEvent=o5;var N5={readContext:ca,use:gv,useCallback:h5,useContext:ca,useEffect:mE,useImperativeHandle:d5,useInsertionEffect:l5,useLayoutEffect:c5,useMemo:f5,useReducer:dE,useRef:a5,useState:function(){return dE(cc)},useDebugValue:pE,useDeferredValue:function(h,p){var _=ci();return sr===null?gE(_,h,p):m5(_,sr.memoizedState,h,p)},useTransition:function(){var h=dE(cc)[0],p=ci().memoizedState;return[typeof h=="boolean"?h:Tx(h),p]},useSyncExternalStore:HI,useId:b5,useHostTransitionStatus:bE,useFormState:i5,useActionState:i5,useOptimistic:function(h,p){var _=ci();return sr!==null?XI(_,sr,h,p):(_.baseState=h,[h,_.queue.dispatch])},useMemoCache:cE,useCacheRefresh:y5};N5.useEffectEvent=o5;function _E(h,p,_,M){p=h.memoizedState,_=_(M,p),_=_==null?p:f({},p,_),h.memoizedState=_,h.lanes===0&&(h.updateQueue.baseState=_)}var wE={enqueueSetState:function(h,p,_){h=h._reactInternals;var M=Ds(),P=Au(M);P.payload=p,_!=null&&(P.callback=_),p=ku(h,P,M),p!==null&&(us(p,h,M),Sx(p,h,M))},enqueueReplaceState:function(h,p,_){h=h._reactInternals;var M=Ds(),P=Au(M);P.tag=1,P.payload=p,_!=null&&(P.callback=_),p=ku(h,P,M),p!==null&&(us(p,h,M),Sx(p,h,M))},enqueueForceUpdate:function(h,p){h=h._reactInternals;var _=Ds(),M=Au(_);M.tag=2,p!=null&&(M.callback=p),p=ku(h,M,_),p!==null&&(us(p,h,_),Sx(p,h,_))}};function M5(h,p,_,M,P,L,J){return h=h.stateNode,typeof h.shouldComponentUpdate=="function"?h.shouldComponentUpdate(M,L,J):p.prototype&&p.prototype.isPureReactComponent?!px(_,M)||!px(P,L):!0}function E5(h,p,_,M){h=p.state,typeof p.componentWillReceiveProps=="function"&&p.componentWillReceiveProps(_,M),typeof p.UNSAFE_componentWillReceiveProps=="function"&&p.UNSAFE_componentWillReceiveProps(_,M),p.state!==h&&wE.enqueueReplaceState(p,p.state,null)}function Th(h,p){var _=p;if("ref"in p){_={};for(var M in p)M!=="ref"&&(_[M]=p[M])}if(h=h.defaultProps){_===p&&(_=f({},_));for(var P in h)_[P]===void 0&&(_[P]=h[P])}return _}function T5(h){Zy(h)}function C5(h){console.error(h)}function A5(h){Zy(h)}function _v(h,p){try{var _=h.onUncaughtError;_(p.value,{componentStack:p.stack})}catch(M){setTimeout(function(){throw M})}}function k5(h,p,_){try{var M=h.onCaughtError;M(_.value,{componentStack:_.stack,errorBoundary:p.tag===1?p.stateNode:null})}catch(P){setTimeout(function(){throw P})}}function SE(h,p,_){return _=Au(_),_.tag=3,_.payload={element:null},_.callback=function(){_v(h,p)},_}function j5(h){return h=Au(h),h.tag=3,h}function P5(h,p,_,M){var P=_.type.getDerivedStateFromError;if(typeof P=="function"){var L=M.value;h.payload=function(){return P(L)},h.callback=function(){k5(p,_,M)}}var J=_.stateNode;J!==null&&typeof J.componentDidCatch=="function"&&(h.callback=function(){k5(p,_,M),typeof P!="function"&&(Lu===null?Lu=new Set([this]):Lu.add(this));var pe=M.stack;this.componentDidCatch(M.value,{componentStack:pe!==null?pe:""})})}function Cie(h,p,_,M,P){if(_.flags|=32768,M!==null&&typeof M=="object"&&typeof M.then=="function"){if(p=_.alternate,p!==null&&bm(p,_,P,!0),_=ks.current,_!==null){switch(_.tag){case 31:case 13:return ao===null?Rv():_.alternate===null&&Jr===0&&(Jr=3),_.flags&=-257,_.flags|=65536,_.lanes=P,M===lv?_.flags|=16384:(p=_.updateQueue,p===null?_.updateQueue=new Set([M]):p.add(M),KE(h,M,P)),!1;case 22:return _.flags|=65536,M===lv?_.flags|=16384:(p=_.updateQueue,p===null?(p={transitions:null,markerInstances:null,retryQueue:new Set([M])},_.updateQueue=p):(_=p.retryQueue,_===null?p.retryQueue=new Set([M]):_.add(M)),KE(h,M,P)),!1}throw Error(r(435,_.tag))}return KE(h,M,P),Rv(),!1}if(Un)return p=ks.current,p!==null?((p.flags&65536)===0&&(p.flags|=256),p.flags|=65536,p.lanes=P,M!==$M&&(h=Error(r(422),{cause:M}),bx(to(h,_)))):(M!==$M&&(p=Error(r(423),{cause:M}),bx(to(p,_))),h=h.current.alternate,h.flags|=65536,P&=-P,h.lanes|=P,M=to(M,_),P=SE(h.stateNode,M,P),JM(h,P),Jr!==4&&(Jr=2)),!1;var L=Error(r(520),{cause:M});if(L=to(L,_),Ix===null?Ix=[L]:Ix.push(L),Jr!==4&&(Jr=2),p===null)return!0;M=to(M,_),_=p;do{switch(_.tag){case 3:return _.flags|=65536,h=P&-P,_.lanes|=h,h=SE(_.stateNode,M,h),JM(_,h),!1;case 1:if(p=_.type,L=_.stateNode,(_.flags&128)===0&&(typeof p.getDerivedStateFromError=="function"||L!==null&&typeof L.componentDidCatch=="function"&&(Lu===null||!Lu.has(L))))return _.flags|=65536,P&=-P,_.lanes|=P,P=j5(P),P5(P,h,_,M),JM(_,P),!1}_=_.return}while(_!==null);return!1}var NE=Error(r(461)),gi=!1;function ua(h,p,_,M){p.child=h===null?LI(p,null,_,M):Mh(p,h.child,_,M)}function R5(h,p,_,M,P){_=_.render;var L=p.ref;if("ref"in M){var J={};for(var pe in M)pe!=="ref"&&(J[pe]=M[pe])}else J=M;return _h(p),M=aE(h,p,_,J,L,P),pe=sE(),h!==null&&!gi?(oE(h,p,P),uc(h,p,P)):(Un&&pe&&zM(p),p.flags|=1,ua(h,p,M,P),p.child)}function D5(h,p,_,M,P){if(h===null){var L=_.type;return typeof L=="function"&&!IM(L)&&L.defaultProps===void 0&&_.compare===null?(p.tag=15,p.type=L,O5(h,p,L,M,P)):(h=nv(_.type,null,M,p,p.mode,P),h.ref=p.ref,h.return=p,p.child=h)}if(L=h.child,!PE(h,P)){var J=L.memoizedProps;if(_=_.compare,_=_!==null?_:px,_(J,M)&&h.ref===p.ref)return uc(h,p,P)}return p.flags|=1,h=ic(L,M),h.ref=p.ref,h.return=p,p.child=h}function O5(h,p,_,M,P){if(h!==null){var L=h.memoizedProps;if(px(L,M)&&h.ref===p.ref)if(gi=!1,p.pendingProps=M=L,PE(h,P))(h.flags&131072)!==0&&(gi=!0);else return p.lanes=h.lanes,uc(h,p,P)}return ME(h,p,_,M,P)}function L5(h,p,_,M){var P=M.children,L=h!==null?h.memoizedState:null;if(h===null&&p.stateNode===null&&(p.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),M.mode==="hidden"){if((p.flags&128)!==0){if(L=L!==null?L.baseLanes|_:_,h!==null){for(M=p.child=h.child,P=0;M!==null;)P=P|M.lanes|M.childLanes,M=M.sibling;M=P&~L}else M=0,p.child=null;return I5(h,p,L,_,M)}if((_&536870912)!==0)p.memoizedState={baseLanes:0,cachePool:null},h!==null&&sv(p,L!==null?L.cachePool:null),L!==null?UI(p,L):tE(),zI(p);else return M=p.lanes=536870912,I5(h,p,L!==null?L.baseLanes|_:_,_,M)}else L!==null?(sv(p,L.cachePool),UI(p,L),Pu(),p.memoizedState=null):(h!==null&&sv(p,null),tE(),Pu());return ua(h,p,P,_),p.child}function kx(h,p){return h!==null&&h.tag===22||p.stateNode!==null||(p.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),p.sibling}function I5(h,p,_,M,P){var L=XM();return L=L===null?null:{parent:mi._currentValue,pool:L},p.memoizedState={baseLanes:_,cachePool:L},h!==null&&sv(p,null),tE(),zI(p),h!==null&&bm(h,p,M,!0),p.childLanes=P,null}function wv(h,p){return p=Nv({mode:p.mode,children:p.children},h.mode),p.ref=h.ref,h.child=p,p.return=h,p}function F5(h,p,_){return Mh(p,h.child,null,_),h=wv(p,p.pendingProps),h.flags|=2,js(p),p.memoizedState=null,h}function Aie(h,p,_){var M=p.pendingProps,P=(p.flags&128)!==0;if(p.flags&=-129,h===null){if(Un){if(M.mode==="hidden")return h=wv(p,M),p.lanes=536870912,kx(null,h);if(rE(p),(h=Ar)?(h=YF(h,io),h=h!==null&&h.data==="&"?h:null,h!==null&&(p.memoizedState={dehydrated:h,treeContext:Nu!==null?{id:xl,overflow:bl}:null,retryLane:536870912,hydrationErrors:null},_=_I(h),_.return=p,p.child=_,la=p,Ar=null)):h=null,h===null)throw Eu(p);return p.lanes=536870912,null}return wv(p,M)}var L=h.memoizedState;if(L!==null){var J=L.dehydrated;if(rE(p),P)if(p.flags&256)p.flags&=-257,p=F5(h,p,_);else if(p.memoizedState!==null)p.child=h.child,p.flags|=128,p=null;else throw Error(r(558));else if(gi||bm(h,p,_,!1),P=(_&h.childLanes)!==0,gi||P){if(M=br,M!==null&&(J=Xt(M,_),J!==0&&J!==L.retryLane))throw L.retryLane=J,xh(h,J),us(M,h,J),NE;Rv(),p=F5(h,p,_)}else h=L.treeContext,Ar=so(J.nextSibling),la=p,Un=!0,Mu=null,io=!1,h!==null&&NI(p,h),p=wv(p,M),p.flags|=4096;return p}return h=ic(h.child,{mode:M.mode,children:M.children}),h.ref=p.ref,p.child=h,h.return=p,h}function Sv(h,p){var _=p.ref;if(_===null)h!==null&&h.ref!==null&&(p.flags|=4194816);else{if(typeof _!="function"&&typeof _!="object")throw Error(r(284));(h===null||h.ref!==_)&&(p.flags|=4194816)}}function ME(h,p,_,M,P){return _h(p),_=aE(h,p,_,M,void 0,P),M=sE(),h!==null&&!gi?(oE(h,p,P),uc(h,p,P)):(Un&&M&&zM(p),p.flags|=1,ua(h,p,_,P),p.child)}function U5(h,p,_,M,P,L){return _h(p),p.updateQueue=null,_=$I(p,M,_,P),BI(h),M=sE(),h!==null&&!gi?(oE(h,p,L),uc(h,p,L)):(Un&&M&&zM(p),p.flags|=1,ua(h,p,_,L),p.child)}function z5(h,p,_,M,P){if(_h(p),p.stateNode===null){var L=mm,J=_.contextType;typeof J=="object"&&J!==null&&(L=ca(J)),L=new _(M,L),p.memoizedState=L.state!==null&&L.state!==void 0?L.state:null,L.updater=wE,p.stateNode=L,L._reactInternals=p,L=p.stateNode,L.props=M,L.state=p.memoizedState,L.refs={},QM(p),J=_.contextType,L.context=typeof J=="object"&&J!==null?ca(J):mm,L.state=p.memoizedState,J=_.getDerivedStateFromProps,typeof J=="function"&&(_E(p,_,J,M),L.state=p.memoizedState),typeof _.getDerivedStateFromProps=="function"||typeof L.getSnapshotBeforeUpdate=="function"||typeof L.UNSAFE_componentWillMount!="function"&&typeof L.componentWillMount!="function"||(J=L.state,typeof L.componentWillMount=="function"&&L.componentWillMount(),typeof L.UNSAFE_componentWillMount=="function"&&L.UNSAFE_componentWillMount(),J!==L.state&&wE.enqueueReplaceState(L,L.state,null),Mx(p,M,L,P),Nx(),L.state=p.memoizedState),typeof L.componentDidMount=="function"&&(p.flags|=4194308),M=!0}else if(h===null){L=p.stateNode;var pe=p.memoizedProps,Ue=Th(_,pe);L.props=Ue;var nt=L.context,bt=_.contextType;J=mm,typeof bt=="object"&&bt!==null&&(J=ca(bt));var _t=_.getDerivedStateFromProps;bt=typeof _t=="function"||typeof L.getSnapshotBeforeUpdate=="function",pe=p.pendingProps!==pe,bt||typeof L.UNSAFE_componentWillReceiveProps!="function"&&typeof L.componentWillReceiveProps!="function"||(pe||nt!==J)&&E5(p,L,M,J),Cu=!1;var at=p.memoizedState;L.state=at,Mx(p,M,L,P),Nx(),nt=p.memoizedState,pe||at!==nt||Cu?(typeof _t=="function"&&(_E(p,_,_t,M),nt=p.memoizedState),(Ue=Cu||M5(p,_,Ue,M,at,nt,J))?(bt||typeof L.UNSAFE_componentWillMount!="function"&&typeof L.componentWillMount!="function"||(typeof L.componentWillMount=="function"&&L.componentWillMount(),typeof L.UNSAFE_componentWillMount=="function"&&L.UNSAFE_componentWillMount()),typeof L.componentDidMount=="function"&&(p.flags|=4194308)):(typeof L.componentDidMount=="function"&&(p.flags|=4194308),p.memoizedProps=M,p.memoizedState=nt),L.props=M,L.state=nt,L.context=J,M=Ue):(typeof L.componentDidMount=="function"&&(p.flags|=4194308),M=!1)}else{L=p.stateNode,ZM(h,p),J=p.memoizedProps,bt=Th(_,J),L.props=bt,_t=p.pendingProps,at=L.context,nt=_.contextType,Ue=mm,typeof nt=="object"&&nt!==null&&(Ue=ca(nt)),pe=_.getDerivedStateFromProps,(nt=typeof pe=="function"||typeof L.getSnapshotBeforeUpdate=="function")||typeof L.UNSAFE_componentWillReceiveProps!="function"&&typeof L.componentWillReceiveProps!="function"||(J!==_t||at!==Ue)&&E5(p,L,M,Ue),Cu=!1,at=p.memoizedState,L.state=at,Mx(p,M,L,P),Nx();var ht=p.memoizedState;J!==_t||at!==ht||Cu||h!==null&&h.dependencies!==null&&iv(h.dependencies)?(typeof pe=="function"&&(_E(p,_,pe,M),ht=p.memoizedState),(bt=Cu||M5(p,_,bt,M,at,ht,Ue)||h!==null&&h.dependencies!==null&&iv(h.dependencies))?(nt||typeof L.UNSAFE_componentWillUpdate!="function"&&typeof L.componentWillUpdate!="function"||(typeof L.componentWillUpdate=="function"&&L.componentWillUpdate(M,ht,Ue),typeof L.UNSAFE_componentWillUpdate=="function"&&L.UNSAFE_componentWillUpdate(M,ht,Ue)),typeof L.componentDidUpdate=="function"&&(p.flags|=4),typeof L.getSnapshotBeforeUpdate=="function"&&(p.flags|=1024)):(typeof L.componentDidUpdate!="function"||J===h.memoizedProps&&at===h.memoizedState||(p.flags|=4),typeof L.getSnapshotBeforeUpdate!="function"||J===h.memoizedProps&&at===h.memoizedState||(p.flags|=1024),p.memoizedProps=M,p.memoizedState=ht),L.props=M,L.state=ht,L.context=Ue,M=bt):(typeof L.componentDidUpdate!="function"||J===h.memoizedProps&&at===h.memoizedState||(p.flags|=4),typeof L.getSnapshotBeforeUpdate!="function"||J===h.memoizedProps&&at===h.memoizedState||(p.flags|=1024),M=!1)}return L=M,Sv(h,p),M=(p.flags&128)!==0,L||M?(L=p.stateNode,_=M&&typeof _.getDerivedStateFromError!="function"?null:L.render(),p.flags|=1,h!==null&&M?(p.child=Mh(p,h.child,null,P),p.child=Mh(p,null,_,P)):ua(h,p,_,P),p.memoizedState=L.state,h=p.child):h=uc(h,p,P),h}function B5(h,p,_,M){return yh(),p.flags|=256,ua(h,p,_,M),p.child}var EE={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function TE(h){return{baseLanes:h,cachePool:kI()}}function CE(h,p,_){return h=h!==null?h.childLanes&~_:0,p&&(h|=Rs),h}function $5(h,p,_){var M=p.pendingProps,P=!1,L=(p.flags&128)!==0,J;if((J=L)||(J=h!==null&&h.memoizedState===null?!1:(li.current&2)!==0),J&&(P=!0,p.flags&=-129),J=(p.flags&32)!==0,p.flags&=-33,h===null){if(Un){if(P?ju(p):Pu(),(h=Ar)?(h=YF(h,io),h=h!==null&&h.data!=="&"?h:null,h!==null&&(p.memoizedState={dehydrated:h,treeContext:Nu!==null?{id:xl,overflow:bl}:null,retryLane:536870912,hydrationErrors:null},_=_I(h),_.return=p,p.child=_,la=p,Ar=null)):h=null,h===null)throw Eu(p);return uT(h)?p.lanes=32:p.lanes=536870912,null}var pe=M.children;return M=M.fallback,P?(Pu(),P=p.mode,pe=Nv({mode:"hidden",children:pe},P),M=bh(M,P,_,null),pe.return=p,M.return=p,pe.sibling=M,p.child=pe,M=p.child,M.memoizedState=TE(_),M.childLanes=CE(h,J,_),p.memoizedState=EE,kx(null,M)):(ju(p),AE(p,pe))}var Ue=h.memoizedState;if(Ue!==null&&(pe=Ue.dehydrated,pe!==null)){if(L)p.flags&256?(ju(p),p.flags&=-257,p=kE(h,p,_)):p.memoizedState!==null?(Pu(),p.child=h.child,p.flags|=128,p=null):(Pu(),pe=M.fallback,P=p.mode,M=Nv({mode:"visible",children:M.children},P),pe=bh(pe,P,_,null),pe.flags|=2,M.return=p,pe.return=p,M.sibling=pe,p.child=M,Mh(p,h.child,null,_),M=p.child,M.memoizedState=TE(_),M.childLanes=CE(h,J,_),p.memoizedState=EE,p=kx(null,M));else if(ju(p),uT(pe)){if(J=pe.nextSibling&&pe.nextSibling.dataset,J)var nt=J.dgst;J=nt,M=Error(r(419)),M.stack="",M.digest=J,bx({value:M,source:null,stack:null}),p=kE(h,p,_)}else if(gi||bm(h,p,_,!1),J=(_&h.childLanes)!==0,gi||J){if(J=br,J!==null&&(M=Xt(J,_),M!==0&&M!==Ue.retryLane))throw Ue.retryLane=M,xh(h,M),us(J,h,M),NE;cT(pe)||Rv(),p=kE(h,p,_)}else cT(pe)?(p.flags|=192,p.child=h.child,p=null):(h=Ue.treeContext,Ar=so(pe.nextSibling),la=p,Un=!0,Mu=null,io=!1,h!==null&&NI(p,h),p=AE(p,M.children),p.flags|=4096);return p}return P?(Pu(),pe=M.fallback,P=p.mode,Ue=h.child,nt=Ue.sibling,M=ic(Ue,{mode:"hidden",children:M.children}),M.subtreeFlags=Ue.subtreeFlags&65011712,nt!==null?pe=ic(nt,pe):(pe=bh(pe,P,_,null),pe.flags|=2),pe.return=p,M.return=p,M.sibling=pe,p.child=M,kx(null,M),M=p.child,pe=h.child.memoizedState,pe===null?pe=TE(_):(P=pe.cachePool,P!==null?(Ue=mi._currentValue,P=P.parent!==Ue?{parent:Ue,pool:Ue}:P):P=kI(),pe={baseLanes:pe.baseLanes|_,cachePool:P}),M.memoizedState=pe,M.childLanes=CE(h,J,_),p.memoizedState=EE,kx(h.child,M)):(ju(p),_=h.child,h=_.sibling,_=ic(_,{mode:"visible",children:M.children}),_.return=p,_.sibling=null,h!==null&&(J=p.deletions,J===null?(p.deletions=[h],p.flags|=16):J.push(h)),p.child=_,p.memoizedState=null,_)}function AE(h,p){return p=Nv({mode:"visible",children:p},h.mode),p.return=h,h.child=p}function Nv(h,p){return h=As(22,h,null,p),h.lanes=0,h}function kE(h,p,_){return Mh(p,h.child,null,_),h=AE(p,p.pendingProps.children),h.flags|=2,p.memoizedState=null,h}function H5(h,p,_){h.lanes|=p;var M=h.alternate;M!==null&&(M.lanes|=p),GM(h.return,p,_)}function jE(h,p,_,M,P,L){var J=h.memoizedState;J===null?h.memoizedState={isBackwards:p,rendering:null,renderingStartTime:0,last:M,tail:_,tailMode:P,treeForkCount:L}:(J.isBackwards=p,J.rendering=null,J.renderingStartTime=0,J.last=M,J.tail=_,J.tailMode=P,J.treeForkCount=L)}function V5(h,p,_){var M=p.pendingProps,P=M.revealOrder,L=M.tail;M=M.children;var J=li.current,pe=(J&2)!==0;if(pe?(J=J&1|2,p.flags|=128):J&=1,oe(li,J),ua(h,p,M,_),M=Un?xx:0,!pe&&h!==null&&(h.flags&128)!==0)e:for(h=p.child;h!==null;){if(h.tag===13)h.memoizedState!==null&&H5(h,_,p);else if(h.tag===19)H5(h,_,p);else if(h.child!==null){h.child.return=h,h=h.child;continue}if(h===p)break e;for(;h.sibling===null;){if(h.return===null||h.return===p)break e;h=h.return}h.sibling.return=h.return,h=h.sibling}switch(P){case"forwards":for(_=p.child,P=null;_!==null;)h=_.alternate,h!==null&&hv(h)===null&&(P=_),_=_.sibling;_=P,_===null?(P=p.child,p.child=null):(P=_.sibling,_.sibling=null),jE(p,!1,P,_,L,M);break;case"backwards":case"unstable_legacy-backwards":for(_=null,P=p.child,p.child=null;P!==null;){if(h=P.alternate,h!==null&&hv(h)===null){p.child=P;break}h=P.sibling,P.sibling=_,_=P,P=h}jE(p,!0,_,null,L,M);break;case"together":jE(p,!1,null,null,void 0,M);break;default:p.memoizedState=null}return p.child}function uc(h,p,_){if(h!==null&&(p.dependencies=h.dependencies),Ou|=p.lanes,(_&p.childLanes)===0)if(h!==null){if(bm(h,p,_,!1),(_&p.childLanes)===0)return null}else return null;if(h!==null&&p.child!==h.child)throw Error(r(153));if(p.child!==null){for(h=p.child,_=ic(h,h.pendingProps),p.child=_,_.return=p;h.sibling!==null;)h=h.sibling,_=_.sibling=ic(h,h.pendingProps),_.return=p;_.sibling=null}return p.child}function PE(h,p){return(h.lanes&p)!==0?!0:(h=h.dependencies,!!(h!==null&&iv(h)))}function kie(h,p,_){switch(p.tag){case 3:xe(p,p.stateNode.containerInfo),Tu(p,mi,h.memoizedState.cache),yh();break;case 27:case 5:Me(p);break;case 4:xe(p,p.stateNode.containerInfo);break;case 10:Tu(p,p.type,p.memoizedProps.value);break;case 31:if(p.memoizedState!==null)return p.flags|=128,rE(p),null;break;case 13:var M=p.memoizedState;if(M!==null)return M.dehydrated!==null?(ju(p),p.flags|=128,null):(_&p.child.childLanes)!==0?$5(h,p,_):(ju(p),h=uc(h,p,_),h!==null?h.sibling:null);ju(p);break;case 19:var P=(h.flags&128)!==0;if(M=(_&p.childLanes)!==0,M||(bm(h,p,_,!1),M=(_&p.childLanes)!==0),P){if(M)return V5(h,p,_);p.flags|=128}if(P=p.memoizedState,P!==null&&(P.rendering=null,P.tail=null,P.lastEffect=null),oe(li,li.current),M)break;return null;case 22:return p.lanes=0,L5(h,p,_,p.pendingProps);case 24:Tu(p,mi,h.memoizedState.cache)}return uc(h,p,_)}function G5(h,p,_){if(h!==null)if(h.memoizedProps!==p.pendingProps)gi=!0;else{if(!PE(h,_)&&(p.flags&128)===0)return gi=!1,kie(h,p,_);gi=(h.flags&131072)!==0}else gi=!1,Un&&(p.flags&1048576)!==0&&SI(p,xx,p.index);switch(p.lanes=0,p.tag){case 16:e:{var M=p.pendingProps;if(h=Sh(p.elementType),p.type=h,typeof h=="function")IM(h)?(M=Th(h,M),p.tag=1,p=z5(null,p,h,M,_)):(p.tag=0,p=ME(null,p,h,M,_));else{if(h!=null){var P=h.$$typeof;if(P===E){p.tag=11,p=R5(null,p,h,M,_);break e}else if(P===C){p.tag=14,p=D5(null,p,h,M,_);break e}}throw p=j(h)||h,Error(r(306,p,""))}}return p;case 0:return ME(h,p,p.type,p.pendingProps,_);case 1:return M=p.type,P=Th(M,p.pendingProps),z5(h,p,M,P,_);case 3:e:{if(xe(p,p.stateNode.containerInfo),h===null)throw Error(r(387));M=p.pendingProps;var L=p.memoizedState;P=L.element,ZM(h,p),Mx(p,M,null,_);var J=p.memoizedState;if(M=J.cache,Tu(p,mi,M),M!==L.cache&&WM(p,[mi],_,!0),Nx(),M=J.element,L.isDehydrated)if(L={element:M,isDehydrated:!1,cache:J.cache},p.updateQueue.baseState=L,p.memoizedState=L,p.flags&256){p=B5(h,p,M,_);break e}else if(M!==P){P=to(Error(r(424)),p),bx(P),p=B5(h,p,M,_);break e}else{switch(h=p.stateNode.containerInfo,h.nodeType){case 9:h=h.body;break;default:h=h.nodeName==="HTML"?h.ownerDocument.body:h}for(Ar=so(h.firstChild),la=p,Un=!0,Mu=null,io=!0,_=LI(p,null,M,_),p.child=_;_;)_.flags=_.flags&-3|4096,_=_.sibling}else{if(yh(),M===P){p=uc(h,p,_);break e}ua(h,p,M,_)}p=p.child}return p;case 26:return Sv(h,p),h===null?(_=nU(p.type,null,p.pendingProps,null))?p.memoizedState=_:Un||(_=p.type,h=p.pendingProps,M=zv(me.current).createElement(_),M[Kt]=p,M[Tr]=h,da(M,_,h),$e(M),p.stateNode=M):p.memoizedState=nU(p.type,h.memoizedProps,p.pendingProps,h.memoizedState),null;case 27:return Me(p),h===null&&Un&&(M=p.stateNode=JF(p.type,p.pendingProps,me.current),la=p,io=!0,P=Ar,zu(p.type)?(dT=P,Ar=so(M.firstChild)):Ar=P),ua(h,p,p.pendingProps.children,_),Sv(h,p),h===null&&(p.flags|=4194304),p.child;case 5:return h===null&&Un&&((P=M=Ar)&&(M=sae(M,p.type,p.pendingProps,io),M!==null?(p.stateNode=M,la=p,Ar=so(M.firstChild),io=!1,P=!0):P=!1),P||Eu(p)),Me(p),P=p.type,L=p.pendingProps,J=h!==null?h.memoizedProps:null,M=L.children,sT(P,L)?M=null:J!==null&&sT(P,J)&&(p.flags|=32),p.memoizedState!==null&&(P=aE(h,p,_ie,null,null,_),Gx._currentValue=P),Sv(h,p),ua(h,p,M,_),p.child;case 6:return h===null&&Un&&((h=_=Ar)&&(_=oae(_,p.pendingProps,io),_!==null?(p.stateNode=_,la=p,Ar=null,h=!0):h=!1),h||Eu(p)),null;case 13:return $5(h,p,_);case 4:return xe(p,p.stateNode.containerInfo),M=p.pendingProps,h===null?p.child=Mh(p,null,M,_):ua(h,p,M,_),p.child;case 11:return R5(h,p,p.type,p.pendingProps,_);case 7:return ua(h,p,p.pendingProps,_),p.child;case 8:return ua(h,p,p.pendingProps.children,_),p.child;case 12:return ua(h,p,p.pendingProps.children,_),p.child;case 10:return M=p.pendingProps,Tu(p,p.type,M.value),ua(h,p,M.children,_),p.child;case 9:return P=p.type._context,M=p.pendingProps.children,_h(p),P=ca(P),M=M(P),p.flags|=1,ua(h,p,M,_),p.child;case 14:return D5(h,p,p.type,p.pendingProps,_);case 15:return O5(h,p,p.type,p.pendingProps,_);case 19:return V5(h,p,_);case 31:return Aie(h,p,_);case 22:return L5(h,p,_,p.pendingProps);case 24:return _h(p),M=ca(mi),h===null?(P=XM(),P===null&&(P=br,L=qM(),P.pooledCache=L,L.refCount++,L!==null&&(P.pooledCacheLanes|=_),P=L),p.memoizedState={parent:M,cache:P},QM(p),Tu(p,mi,P)):((h.lanes&_)!==0&&(ZM(h,p),Mx(p,null,null,_),Nx()),P=h.memoizedState,L=p.memoizedState,P.parent!==M?(P={parent:M,cache:M},p.memoizedState=P,p.lanes===0&&(p.memoizedState=p.updateQueue.baseState=P),Tu(p,mi,M)):(M=L.cache,Tu(p,mi,M),M!==P.cache&&WM(p,[mi],_,!0))),ua(h,p,p.pendingProps.children,_),p.child;case 29:throw p.pendingProps}throw Error(r(156,p.tag))}function dc(h){h.flags|=4}function RE(h,p,_,M,P){if((p=(h.mode&32)!==0)&&(p=!1),p){if(h.flags|=16777216,(P&335544128)===P)if(h.stateNode.complete)h.flags|=8192;else if(bF())h.flags|=8192;else throw Nh=lv,YM}else h.flags&=-16777217}function W5(h,p){if(p.type!=="stylesheet"||(p.state.loading&4)!==0)h.flags&=-16777217;else if(h.flags|=16777216,!oU(p))if(bF())h.flags|=8192;else throw Nh=lv,YM}function Mv(h,p){p!==null&&(h.flags|=4),h.flags&16384&&(p=h.tag!==22?kt():536870912,h.lanes|=p,km|=p)}function jx(h,p){if(!Un)switch(h.tailMode){case"hidden":p=h.tail;for(var _=null;p!==null;)p.alternate!==null&&(_=p),p=p.sibling;_===null?h.tail=null:_.sibling=null;break;case"collapsed":_=h.tail;for(var M=null;_!==null;)_.alternate!==null&&(M=_),_=_.sibling;M===null?p||h.tail===null?h.tail=null:h.tail.sibling=null:M.sibling=null}}function kr(h){var p=h.alternate!==null&&h.alternate.child===h.child,_=0,M=0;if(p)for(var P=h.child;P!==null;)_|=P.lanes|P.childLanes,M|=P.subtreeFlags&65011712,M|=P.flags&65011712,P.return=h,P=P.sibling;else for(P=h.child;P!==null;)_|=P.lanes|P.childLanes,M|=P.subtreeFlags,M|=P.flags,P.return=h,P=P.sibling;return h.subtreeFlags|=M,h.childLanes=_,p}function jie(h,p,_){var M=p.pendingProps;switch(BM(p),p.tag){case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return kr(p),null;case 1:return kr(p),null;case 3:return _=p.stateNode,M=null,h!==null&&(M=h.memoizedState.cache),p.memoizedState.cache!==M&&(p.flags|=2048),oc(mi),fe(),_.pendingContext&&(_.context=_.pendingContext,_.pendingContext=null),(h===null||h.child===null)&&(xm(p)?dc(p):h===null||h.memoizedState.isDehydrated&&(p.flags&256)===0||(p.flags|=1024,HM())),kr(p),null;case 26:var P=p.type,L=p.memoizedState;return h===null?(dc(p),L!==null?(kr(p),W5(p,L)):(kr(p),RE(p,P,null,M,_))):L?L!==h.memoizedState?(dc(p),kr(p),W5(p,L)):(kr(p),p.flags&=-16777217):(h=h.memoizedProps,h!==M&&dc(p),kr(p),RE(p,P,h,M,_)),null;case 27:if(ve(p),_=me.current,P=p.type,h!==null&&p.stateNode!=null)h.memoizedProps!==M&&dc(p);else{if(!M){if(p.stateNode===null)throw Error(r(166));return kr(p),null}h=ae.current,xm(p)?MI(p):(h=JF(P,M,_),p.stateNode=h,dc(p))}return kr(p),null;case 5:if(ve(p),P=p.type,h!==null&&p.stateNode!=null)h.memoizedProps!==M&&dc(p);else{if(!M){if(p.stateNode===null)throw Error(r(166));return kr(p),null}if(L=ae.current,xm(p))MI(p);else{var J=zv(me.current);switch(L){case 1:L=J.createElementNS("http://www.w3.org/2000/svg",P);break;case 2:L=J.createElementNS("http://www.w3.org/1998/Math/MathML",P);break;default:switch(P){case"svg":L=J.createElementNS("http://www.w3.org/2000/svg",P);break;case"math":L=J.createElementNS("http://www.w3.org/1998/Math/MathML",P);break;case"script":L=J.createElement("div"),L.innerHTML="