completamente vazia, em seguida clique em Calibrar.',
+ calibrationDescription: 'A calibração captura uma imagem de referência da placa vazia. Verificações futuras compararão com esta referência para detectar objetos.',
+ calibrationTip: '
Dica: Você pode armazenar até 5 calibrações para diferentes placas. O sistema usa automaticamente a melhor correspondência ao verificar.',
+ plateEmpty: 'A placa parece vazia',
+ objectsDetected: 'Objetos detectados na placa',
+ confidence: 'Confiança',
+ difference: 'Diferença',
+ analysisPreview: 'Pré-visualização da análise:',
+ analysisLegend: 'Caixa verde = área de detecção, Sobreposição vermelha = diferenças em relação à calibração',
+ savedReferences: 'Referências salvas ({{count}}/{{max}})',
+ deleteReference: 'Excluir referência',
+ labelPlaceholder: 'Etiqueta...',
+ clickToEdit: '{{label}} - Clique para editar',
+ clickToAddLabel: 'Clique para adicionar etiqueta',
+ },
+ // Fans
+ fans: {
+ partCooling: 'Ventilador de resfriamento da peça',
+ auxiliary: 'Ventilador auxiliar',
+ chamber: 'Ventilador da câmara',
+ },
+ // HMS errors
+ clickToViewHmsErrors: 'Clique para ver erros do HMS',
+ estimatedCompletion: 'Tempo estimado de conclusão',
+ slotOptions: 'Opções de slot',
+ // Firmware modal
+ firmwareModal: {
+ title: 'Atualização de Firmware',
+ titleUpToDate: 'Informações do Firmware',
+ currentVersion: 'Atual:',
+ latestVersion: 'Última:',
+ releaseNotes: 'Notas de Lançamento',
+ checkingPrereqs: 'Verificando pré-requisitos...',
+ sdCardReady: 'Cartão SD pronto. Clique abaixo para enviar o firmware.',
+ uploadedSuccess: 'Firmware enviado para o cartão SD!',
+ applyInstructions: 'Para aplicar a atualização na sua impressora:',
+ step1: 'Na tela sensível ao toque da impressora, vá para
Configurações',
+ step2: 'Navegue até
Firmware',
+ step3: 'Selecione
Atualizar a partir do cartão SD',
+ step4: 'A atualização levará de 10 a 20 minutos',
+ done: 'Concluído',
+ starting: 'Iniciando...',
+ uploadFirmware: 'Enviar Firmware',
+ uploadFailed: 'Falha ao iniciar o envio: {{error}}',
+ uploadedToast: 'Firmware enviado! Inicie a atualização na tela da impressora.',
+ },
+ accessCodePlaceholder: 'Deixe vazio para manter o atual',
+ // ROI editor
+ roi: {
+ title: 'Área de Detecção (ROI)',
+ xStart: 'Início X',
+ yStart: 'Início Y',
+ width: 'Largura',
+ height: 'Altura',
+ instruction: 'Ajuste a área de detecção para focar na placa de construção. A caixa verde na pré-visualização mostra a área atual.',
+ },
+ },
+
+ // Archives page
+ archives: {
+ title: 'Arquivos de Impressão',
+ searchPlaceholder: 'Pesquisar arquivos...',
+ filterByPrinter: 'Filtrar por impressora',
+ filterByStatus: 'Filtrar por status',
+ sortBy: 'Ordenar por',
+ sortNewest: 'Mais recentes primeiro',
+ sortOldest: 'Mais antigos primeiro',
+ sortName: 'Nome',
+ sortDuration: 'Duração',
+ sortLargest: 'Maiores primeiro',
+ sortSmallest: 'Menores primeiro',
+ sortSize: 'Tamanho',
+ noArchives: 'Nenhum arquivo encontrado',
+ noArchivesSearch: 'Nenhum arquivo corresponde à sua pesquisa',
+ noArchivesYet: 'Ainda não há arquivos',
+ loadingArchives: 'Carregando arquivos...',
+ releaseToUpload: 'Solte para enviar',
+ showAll: 'Mostrar todos',
+ showFavoritesOnly: 'Mostrar apenas favoritos',
+ gridView: 'Visualização em grade',
+ listView: 'Visualização em lista',
+ calendarView: 'Visualização em calendário',
+ logView: 'Registro de impressão',
+ manageTags: 'Gerenciar etiquetas',
+ showFailedPrints: 'Mostrar impressões falhas',
+ hideFailedPrints: 'Ocultar impressões falhas',
+ printTime: 'Tempo de impressão',
+ filamentUsed: 'Filamento usado',
+ cost: 'Custo',
+ reprint: 'Reimprimir',
+ preview: 'Pré-visualizar',
+ deleteArchive: 'Excluir arquivo',
+ deleteConfirm: 'Tem certeza de que deseja excluir este arquivo?',
+ favorite: 'Favorito',
+ unfavorite: 'Remover dos favoritos',
+ viewDetails: 'Ver detalhes',
+ status: {
+ completed: 'Concluído',
+ failed: 'Falhou',
+ stopped: 'Parado',
+ },
+ toast: {
+ source3mfAttached: 'Arquivo de origem 3MF anexado: {{filename}}',
+ failedUploadSource3mf: 'Falha ao enviar arquivo de origem 3MF',
+ source3mfRemoved: 'Arquivo de origem 3MF removido',
+ failedRemoveSource3mf: 'Falha ao remover arquivo de origem 3MF',
+ f3dAttached: 'F3D anexado: {{filename}}',
+ failedUploadF3d: 'Falha ao enviar F3D',
+ f3dRemoved: 'F3D removido',
+ failedRemoveF3d: 'Falha ao remover F3D',
+ timelapseAttached: 'Timelapse anexado: {{filename}}',
+ timelapseAlreadyAttached: 'Timelapse já anexado',
+ noMatchingTimelapse: 'Nenhum timelapse correspondente encontrado',
+ failedScanTimelapse: 'Falha ao escanear timelapse',
+ failedAttachTimelapse: 'Falha ao anexar timelapse',
+ timelapseRemoved: 'Timelapse removido',
+ failedRemoveTimelapse: 'Falha ao remover timelapse',
+ timelapseUploaded: 'Timelapse enviado: {{filename}}',
+ failedUploadTimelapse: 'Falha ao enviar timelapse',
+ archiveDeleted: 'Arquivo excluído',
+ failedDeleteArchive: 'Falha ao excluir arquivo',
+ addedToFavorites: 'Adicionado aos favoritos',
+ removedFromFavorites: 'Removido dos favoritos',
+ projectUpdated: 'Projeto atualizado',
+ failedUpdateProject: 'Falha ao atualizar projeto',
+ linkCopied: 'Link copiado para a área de transferência',
+ failedCopyLink: 'Falha ao copiar link',
+ photoDeleted: 'Foto excluída',
+ failedDeletePhoto: 'Falha ao excluir foto',
+ failedDeleteArchives: 'Falha ao excluir arquivos',
+ failedUpdateFavorites: 'Falha ao atualizar favoritos',
+ exportDownloaded: 'Exportação baixada',
+ exportFailed: 'Falha na exportação',
+ },
+ menu: {
+ print: 'Imprimir',
+ schedule: 'Agendar',
+ openInBambuStudio: 'Abrir no Slicer',
+ slice: 'Fatiar',
+ externalLink: 'Link externo',
+ viewOnMakerWorld: 'Ver no MakerWorld',
+ preview3d: 'Pré-visualização 3D',
+ viewTimelapse: 'Ver Timelapse',
+ scanForTimelapse: 'Escanear Timelapse',
+ uploadTimelapse: 'Enviar Timelapse',
+ removeTimelapse: 'Remover Timelapse',
+ downloadSource3mf: 'Baixar Source 3MF',
+ uploadSource3mf: 'Enviar Source 3MF',
+ replaceSource3mf: 'Substituir Source 3MF',
+ removeSource3mf: 'Remover Source 3MF',
+ uploadF3d: 'Enviar F3D',
+ replaceF3d: 'Substituir F3D',
+ downloadF3d: 'Baixar F3D',
+ removeF3d: 'Remover F3D',
+ download: 'Baixar',
+ copyDownloadLink: 'Copiar link de download',
+ qrCode: 'Qr Code',
+ viewPhotos: 'Ver fotos',
+ viewPhotosCount: 'Ver fotos ({{count}})',
+ projectPage: 'Página do projeto',
+ addToFavorites: 'Adicionar aos favoritos',
+ removeFromFavorites: 'Remover dos favoritos',
+ edit: 'Editar',
+ goToProject: 'Ir para o projeto: {{name}}',
+ addToProject: 'Adicionar ao projeto',
+ removeFromProject: 'Remover do projeto',
+ loading: 'Carregando...',
+ noProjectsAvailable: 'Nenhum projeto disponível',
+ select: 'Selecionar',
+ deselect: 'Desmarcar',
+ delete: 'Excluir',
+ },
+ permission: {
+ noReprint: 'Você não tem permissão para reimprimir este arquivo',
+ noAddToQueue: 'Você não tem permissão para adicionar à fila',
+ noUpdateArchives: 'Você não tem permissão para atualizar arquivos',
+ noUploadFiles: 'Você não tem permissão para enviar arquivos',
+ noDownload: 'Você não tem permissão para baixar arquivos',
+ noCopyLink: 'Você não tem permissão para copiar links de download',
+ noDelete: 'Você não tem permissão para excluir este arquivo',
+ noCreate: 'Você não tem permissão para criar arquivos',
+ },
+ card: {
+ previousPlate: 'Placa anterior',
+ nextPlate: 'Próxima placa',
+ plateNumber: 'Placa {{index}}',
+ moreOptions: 'Clique com o botão direito para mais opções',
+ addToFavorites: 'Adicionar aos favoritos',
+ removeFromFavorites: 'Remover dos favoritos',
+ cancelled: 'cancelado',
+ failed: 'falha',
+ duplicate: 'duplicado',
+ duplicateTitle: 'Este modelo já foi impresso antes',
+ openSource3mf: 'Abrir source 3MF no Bambu Studio (clique com o botão direito para mais opções)',
+ downloadF3d: 'Baixar arquivo de design do Fusion 360',
+ viewTimelapse: 'Ver timelapse',
+ viewPhoto: 'Ver 1 foto',
+ viewPhotos: 'Ver {{count}} fotos',
+ openFolder: 'Abrir pasta: {{name}}',
+ slicedFile: 'Arquivo fatiado - pronto para imprimir',
+ sourceFile: 'Apenas arquivo fonte - nenhum mapeamento AMS disponível',
+ gcode: 'GCODE',
+ source: 'SOURCE',
+ project: 'Projeto: {{name}}',
+ estimated: 'Estimado: {{time}}',
+ actual: 'Real: {{time}}',
+ accuracy: 'Precisão: {{percent}}%',
+ filament: '{{weight}}g',
+ layer: '{{count}} camada',
+ layers: '{{count}} camadas',
+ object: '{{count}} objeto',
+ objects: '{{count}} objetos',
+ slicedFor: 'Fatiado para {{model}}',
+ uploadedBy: 'Enviado por',
+ noPermissionReprint: 'Você não tem permissão para reimprimir',
+ noFileForReprint: 'Nenhum arquivo 3MF disponível — o arquivo não pôde ser baixado da impressora quando a impressão foi registrada',
+ noPermissionEdit: 'Você não tem permissão para editar arquivos',
+ noPermissionDelete: 'Você não tem permissão para excluir arquivos',
+ reprint: 'Reimprimir',
+ schedulePrint: 'Agendar impressão',
+ schedule: 'Agendar',
+ openInBambuStudio: 'Abrir no Bambu Studio',
+ openInBambuStudioToSlice: 'Abrir no Bambu Studio para fatiar',
+ slice: 'Fatiar',
+ externalLink: 'Link externo',
+ makerWorld: 'MakerWorld: {{designer}}',
+ viewProject: 'Ver projeto',
+ noExternalLink: 'Nenhum link externo',
+ preview3d: 'Visualização 3D',
+ download: 'Baixar',
+ edit: 'Editar',
+ delete: 'Excluir',
+ },
+ modal: {
+ deleteArchive: 'Excluir Arquivo',
+ deleteConfirm: 'Tem certeza de que deseja excluir "{{name}}"? Esta ação não pode ser desfeita.',
+ deleteButton: 'Excluir',
+ removeSource3mf: 'Remover Source 3MF',
+ removeSource3mfConfirm: 'Tem certeza de que deseja remover o arquivo source 3MF de "{{name}}"? Isso excluirá o arquivo original do projeto do fatiador.',
+ removeButton: 'Remover',
+ removeF3d: 'Remover F3D',
+ removeF3dConfirm: 'Tem certeza de que deseja remover o arquivo de design do Fusion 360 de "{{name}}"?',
+ removeTimelapse: 'Remover Timelapse',
+ removeTimelapseConfirm: 'Tem certeza de que deseja remover o vídeo timelapse de "{{name}}"?',
+ timelapse: '{{name}} - Timelapse',
+ selectTimelapse: 'Selecionar Timelapse',
+ selectTimelapseDesc: 'Nenhuma correspondência automática encontrada. Selecione o timelapse para esta impressão:',
+ deleteArchives: 'Excluir Arquivos',
+ deleteArchivesConfirm: 'Tem certeza de que deseja excluir {{count}} arquivo(s)? Esta ação não pode ser desfeita.',
+ deleteCount: 'Excluir {{count}}',
+ },
+ page: {
+ title: 'Arquivos',
+ printsCount: '{{filtered}} de {{total}} impressões',
+ dropFilesHere: 'Solte arquivos .3mf aqui',
+ releaseToUpload: 'Solte para enviar',
+ only3mfSupported: 'Apenas arquivos .3mf são suportados',
+ close: 'Fechar',
+ selected: '{{count}} selecionado(s)',
+ selectAll: 'Selecionar Todos',
+ tags: 'Tags',
+ project: 'Projeto',
+ favorite: 'Favorito',
+ delete: 'Excluir',
+ toggledFavorites: 'Favoritos alternados para {{count}} arquivo(s)',
+ failedUpdateFavorites: 'Falha ao atualizar favoritos',
+ archivesDeleted: '{{count}} arquivo(s) excluído(s)',
+ failedDeleteArchives: 'Falha ao excluir arquivos',
+ photoDeleted: 'Foto excluída',
+ failedDeletePhoto: 'Falha ao excluir foto',
+ },
+ list: {
+ name: 'Nome',
+ printer: 'Impressora',
+ date: 'Data',
+ size: 'Tamanho',
+ actions: 'Ações',
+ hasTimelapse: 'Possui timelapse',
+ },
+ log: {
+ date: 'Data',
+ printName: 'Nome da Impressão',
+ printer: 'Impressora',
+ user: 'Usuário',
+ status: 'Status',
+ duration: 'Duração',
+ filament: 'Filamento',
+ allPrinters: 'Todas as Impressoras',
+ allUsers: 'Todos os Usuários',
+ allStatuses: 'Todos os Status',
+ cancelled: 'Cancelado',
+ skipped: 'Ignorado',
+ dateFrom: 'De',
+ dateTo: 'Até',
+ noEntries: 'Nenhuma entrada de registro de impressão encontrada',
+ showing: 'Mostrando {{count}} de {{total}} entradas',
+ rowsPerPage: 'Linhas',
+ page: 'Página',
+ prev: 'Anterior',
+ next: 'Próxima',
+ clearLog: 'Limpar Registro',
+ clearLogTitle: 'Limpar Registro de Impressão',
+ clearLogConfirm: 'Todas as entradas do registro de impressão serão permanentemente excluídas. Arquivos e itens da fila não serão afetados. Esta ação não pode ser desfeita. Tem certeza?',
+ clearLogButton: 'Limpar Tudo',
+ cleared: '{{count}} entradas do registro de impressão limpas',
+ clearFailed: 'Falha ao limpar o registro de impressão',
+ },
+ },
+
+ // Queue page
+ queue: {
+ title: 'Fila de Impressão',
+ subtitle: 'Agende e gerencie seus trabalhos de impressão',
+ addToQueue: 'Adicionar à Fila',
+ // Print modal
+ print: 'Imprimir',
+ reprint: 'Reimprimir',
+ schedulePrint: 'Agendar Impressão',
+ editQueueItem: 'Editar Item da Fila',
+ printToPrinters: 'Imprimir para {{count}} Impressoras',
+ queueToPrinters: 'Adicionar à Fila para {{count}} Impressoras',
+ sending: 'Enviando...',
+ sendingProgress: 'Enviando {{current}}/{{total}}...',
+ adding: 'Adicionando...',
+ addingProgress: 'Adicionando {{current}}/{{total}}...',
+ savingProgress: 'Salvando {{current}}/{{total}}...',
+ clearQueue: 'Limpar Fila',
+ clearHistory: 'Limpar Histórico',
+ emptyQueue: 'Fila vazia',
+ position: 'Posição',
+ scheduledTime: 'Hora Agendada',
+ moveUp: 'Mover para Cima',
+ moveDown: 'Mover para Baixo',
+ startNow: 'Iniciar Agora',
+ printingInProgress: 'Impressão em andamento...',
+ viewArchive: 'Ver Arquivo',
+ viewInFileManager: 'Ver no Gerenciador de Arquivos',
+ itemCount: '{{count}} item',
+ itemCount_plural: '{{count}} itens',
+ dragToReorder: 'Arraste para reordenar (apenas ASAP)',
+ reorderHint: 'A posição afeta apenas itens ASAP. Itens agendados são executados no horário definido.',
+ addedBy: 'Adicionado por {{name}}',
+ nextInQueue: 'Próximo na fila',
+ clearPlate: 'Limpar Placa e Iniciar Próximo',
+ clearPlateSuccess: 'Placa limpa — pronta para a próxima impressão',
+ plateReady: 'Placa limpa — pronta para a próxima impressão',
+ plateNumber: 'Placa {{index}}',
+ // Sections
+ sections: {
+ currentlyPrinting: 'Imprimindo Atualmente',
+ queued: 'Na Fila',
+ history: 'Histórico',
+ },
+ // Status
+ status: {
+ pending: 'Pendente',
+ waiting: 'Aguardando',
+ printing: 'Imprimindo',
+ paused: 'Pausado',
+ completed: 'Concluído',
+ failed: 'Falhou',
+ skipped: 'Ignorado',
+ cancelled: 'Cancelado',
+ },
+ // Summary cards
+ summary: {
+ printing: 'Imprimindo',
+ queued: 'Na Fila',
+ totalTime: 'Tempo Total da Fila',
+ totalWeight: 'Peso Total da Fila',
+ history: 'Histórico',
+ },
+ // Filters
+ filter: {
+ allPrinters: 'Todas as Impressoras',
+ unassigned: 'Não Atribuído',
+ allStatus: 'Todos os Status',
+ allLocations: 'Todos os Locais',
+ any: 'Qualquer',
+ },
+ // Sort
+ sort: {
+ byPosition: 'Ordenar por Posição',
+ byName: 'Ordenar por Nome',
+ byPrinter: 'Ordenar por Impressora',
+ bySchedule: 'Ordenar por Agendamento',
+ byDate: 'Ordenar por Data',
+ ascendingOldest: 'Crescente (mais antigo primeiro)',
+ descendingNewest: 'Decrescente (mais recente primeiro)',
+ },
+ // Badges
+ badges: {
+ staged: 'Preparado (início manual)',
+ requiresPrevious: 'Requer sucesso anterior',
+ autoPowerOff: 'Desligamento automático',
+ },
+ // Empty state
+ empty: {
+ title: 'Nenhuma impressão agendada',
+ description: 'Agende uma impressão a partir da página de Arquivos usando a opção "Agendar" no menu de contexto, ou arraste e solte arquivos para começar.',
+ },
+ // Time
+ time: {
+ asap: 'ASAP',
+ overdue: 'Atrasado',
+ now: 'Agora',
+ lessThanMinute: 'Em menos de um minuto',
+ inMinutes: 'Em {{count}} min',
+ inHours: 'Em {{count}} horas',
+ },
+ // Actions
+ actions: {
+ stopPrint: 'Parar Impressão',
+ startPrint: 'Iniciar Impressão',
+ requeue: 'Reenfileirar',
+ },
+ // Bulk edit
+ bulkEdit: {
+ title: 'Editar {{count}} Item',
+ title_plural: 'Editar {{count}} Itens',
+ description: 'Apenas as configurações alteradas serão aplicadas aos itens selecionados.',
+ printer: 'Impressora',
+ noChange: '— Sem alterações —',
+ queueOptions: 'Opções de Fila',
+ staged: 'Preparado (início manual)',
+ autoPowerOff: 'Desligamento automático após impressão',
+ requirePrevious: 'Requer sucesso anterior',
+ printOptions: 'Opções de Impressão',
+ bedLevelling: 'Nivelamento da Mesa',
+ flowCalibration: 'Calibração de Fluxo',
+ vibrationCalibration: 'Calibração de Vibração',
+ layerInspection: 'Inspeção da Primeira Camada',
+ timelapse: 'Timelapse',
+ useAms: 'Usar AMS',
+ applyChanges: 'Aplicar Alterações',
+ selectAll: 'Selecionar Todos',
+ deselectAll: 'Desmarcar Todos',
+ selected: '{{count}} selecionado(s)',
+ editSelected: 'Editar Selecionados',
+ cancelSelected: 'Cancelar Selecionados',
+ },
+ // Confirmations
+ confirm: {
+ cancelTitle: 'Cancelar Impressão Agendada',
+ cancelMessage: 'Tem certeza de que deseja cancelar "{{name}}"?',
+ stopTitle: 'Parar Impressão',
+ stopMessage: 'Tem certeza de que deseja parar a impressão atual "{{name}}"? Isso cancelará o trabalho de impressão na impressora.',
+ removeTitle: 'Remover do Histórico',
+ removeMessage: 'Tem certeza de que deseja remover "{{name}}" do histórico da fila?',
+ clearHistoryTitle: 'Limpar Histórico',
+ clearHistoryMessage: 'Tem certeza de que deseja remover todos os {{count}} itens do histórico?',
+ cancelButton: 'Cancelar Impressão',
+ stopButton: 'Parar Impressão',
+ thisPrint: 'esta impressão',
+ thisItem: 'este item',
+ },
+ // Toast messages
+ toast: {
+ cancelled: 'Item da fila cancelado',
+ cancelFailed: 'Falha ao cancelar item',
+ removed: 'Item da fila removido',
+ removeFailed: 'Falha ao remover item',
+ stopped: 'Impressão parada',
+ stopFailed: 'Falha ao parar impressão',
+ released: 'Impressão liberada para a fila',
+ startFailed: 'Falha ao iniciar impressão',
+ reorderFailed: 'Falha ao reordenar fila',
+ historyCleared: 'Limpar {{count}} item(s) do histórico',
+ clearHistoryFailed: 'Falha ao limpar histórico',
+ updateFailed: 'Falha ao atualizar itens',
+ bulkCancelled: 'Cancelado {{count}} item(s)',
+ bulkCancelFailed: 'Falha ao cancelar itens',
+ },
+ // Permissions
+ permissions: {
+ noStopPrint: 'Você não tem permissão para parar impressões',
+ noStartPrint: 'Você não tem permissão para iniciar impressões',
+ noEdit: 'Você não tem permissão para editar este item da fila',
+ noCancel: 'Você não tem permissão para cancelar este item da fila',
+ noRequeue: 'Você não tem permissão para reenfileirar itens',
+ noRemove: 'Você não tem permissão para remover este item da fila',
+ noClearHistory: 'Você não tem permissão para limpar todo o histórico',
+ noEditItems: 'Você não tem permissão para editar itens da fila',
+ noCancelItems: 'Você não tem permissão para cancelar itens da fila',
+ },
+ },
+
+ // Statistics page
+ stats: {
+ title: 'Dashboard',
+ subtitle: 'Arraste os widgets para reorganizar. Clique no ícone de olho para ocultar.',
+ overview: 'Visão Geral',
+ totalPrints: 'Total de Impressões',
+ successRate: 'Taxa de Sucesso',
+ totalPrintTime: 'Tempo Total de Impressão',
+ printTime: 'Tempo de Impressão',
+ totalFilament: 'Filamento Total Utilizado',
+ filamentUsed: 'Filamento Utilizado',
+ filamentCost: 'Custo do Filamento',
+ totalCost: 'Custo Total',
+ energyUsed: 'Energia Utilizada',
+ energyCost: 'Custo da Energia',
+ averagePrintTime: 'Tempo Médio de Impressão',
+ printsPerDay: 'Impressões por Dia',
+ byPrinter: 'Por Impressora',
+ printsByPrinter: 'Impressões por Impressora',
+ byMaterial: 'Por Material',
+ byMonth: 'Por Mês',
+ last7Days: 'Últimos 7 Dias',
+ last30Days: 'Últimos 30 Dias',
+ last90Days: 'Últimos 90 Dias',
+ allTime: 'Todo o Tempo',
+ // Widgets
+ quickStats: 'Estatísticas Rápidas',
+ printActivity: 'Atividade de Impressão',
+ filamentTypes: 'Tipos de Filamento',
+ filamentTrends: 'Tendências de Filamento',
+ failureAnalysis: 'Análise de Falhas',
+ timeAccuracy: 'Precisão do Tempo',
+ successful: 'Bem-sucedido:',
+ failed: 'Falhou:',
+ perfectEstimate: '100% = estimativa perfeita',
+ noTimeAccuracyData: 'Nenhum dado de precisão de tempo disponível',
+ noFilamentData: 'Nenhum dado de filamento disponível',
+ noPrinterData: 'Nenhum dado de impressora disponível',
+ noPrintData: 'Nenhum dado de impressão disponível',
+ noPrintDataLast30Days: 'Nenhum dado de impressão nos últimos 30 dias',
+ failureReasons: 'Razões de Falha',
+ topFailureReasons: 'Principais Razões de Falha',
+ failedPrintsCount: '{{failed}} / {{total}} impressões falharam',
+ lastWeekRate: 'Última semana: {{rate}}%',
+ // Actions
+ resetLayout: 'Redefinir Layout',
+ recalculateCosts: 'Recalcular Custos',
+ recalculateCostsHint: 'Recalcular todos os custos do arquivo usando os preços atuais do filamento',
+ exportStats: 'Exportar Estatísticas',
+ exportAsCsv: 'Exportar como CSV',
+ exportAsExcel: 'Exportar como Excel',
+ hiddenCount: '{{count}} Oculto',
+ // Toast
+ exportDownloaded: 'Exportação baixada',
+ exportFailed: 'Falha na exportação',
+ layoutReset: 'Layout redefinido',
+ recalculatedCosts: 'Custos recalculados para {{count}} arquivos',
+ recalculateFailed: 'Falha ao recalcular custos',
+ // Loading
+ loadingStats: 'Carregando estatísticas...',
+ // Permissions
+ noPermissionResetLayout: 'Você não tem permissão para redefinir o layout',
+ noPermissionRecalculate: 'Você não tem permissão para recalcular custos',
+ },
+
+ // Maintenance page
+ maintenance: {
+ title: 'Manutenção',
+ overview: 'Visão Geral',
+ allOk: 'Todas as manutenções estão em dia',
+ dueCount: '{{count}} item pendente',
+ dueCount_plural: '{{count}} itens pendentes',
+ warningCount: '{{count}} aviso',
+ warningCount_plural: '{{count}} avisos',
+ totalPrintTime: 'Tempo Total de Impressão',
+ nextMaintenance: 'Próxima Manutenção',
+ nothingDue: 'Nada pendente',
+ tasks: 'Tarefas',
+ lastPerformed: 'Última execução',
+ interval: 'Intervalo',
+ hoursRemaining: '{{hours}}h restantes',
+ hoursOverdue: '{{hours}}h atrasadas',
+ markDone: 'Marcar como Concluída',
+ performMaintenance: 'Realizar Manutenção',
+ history: 'Histórico',
+ noHistory: 'Nenhum histórico de manutenção',
+ editPrintHours: 'Editar Horas de Impressão',
+ currentHours: 'Horas Atuais',
+ // Tabs
+ statusTab: 'Status',
+ settingsTab: 'Configurações',
+ // Status
+ overdueCount: '{{count}} atrasado',
+ dueSoonCount: '{{count}} prestes a vencer',
+ dueSoon: 'Prestes a vencer',
+ allGood: 'Tudo certo',
+ overdueBy: 'Atrasado por {{duration}}',
+ dueIn: 'Vence em {{duration}}',
+ timeLeft: '{{duration}} restantes',
+ // Duration formats
+ day: '1 dia',
+ days: '{{count}} dias',
+ week: '1 semana',
+ weeks: '{{count}} semanas',
+ month: '1 mês',
+ months: '{{count}} meses',
+ year: '1 ano',
+ // Settings
+ maintenanceTypes: 'Tipos de Manutenção',
+ maintenanceTypesDescription: 'Tipos de sistema e suas tarefas de manutenção personalizadas',
+ addCustomType: 'Adicionar Tipo Personalizado',
+ restoreDefaults: 'Restaurar Tarefas Padrão',
+ intervalType: 'Tipo de Intervalo',
+ intervalValue: 'Intervalo ({{type}})',
+ icon: 'Icon',
+ documentationLink: 'Link da Documentação (opcional)',
+ assignToPrinters: 'Atribuir a Impressoras',
+ selectAtLeastOnePrinter: 'Selecione pelo menos uma impressora',
+ addType: 'Adicionar Tipo',
+ custom: 'Personalizado',
+ printHours: 'Horas de Impressão',
+ calendarDays: 'Dias de Calendário',
+ exampleName: 'ex., Substituir Filtro HEPA',
+ viewDocumentation: 'Ver documentação',
+ timeBasedInterval: 'Intervalo baseado em tempo',
+ // Interval overrides
+ intervalOverrides: 'Substituições de Intervalo',
+ intervalOverridesDescription: 'Personalize os intervalos para impressoras específicas',
+ // Printer assignment
+ assignedToPrinters: 'Atribuído a impressoras:',
+ noPrintersAssigned: 'Nenhuma impressora atribuída',
+ addPrinterShort: 'Adicionar:',
+ printersAssignedClick: '{{count}} impressora(s) atribuída(s) - clique para gerenciar',
+ removeFromPrinter: 'Remover desta impressora',
+ // Types
+ types: {
+ lubricateCarbonRods: 'Lubricar Barras de Carbono',
+ lubricateRails: 'Lubricar Trilhos Lineares',
+ cleanNozzle: 'Limpar Bico/Hotend',
+ checkBelts: 'Verificar Tensão das Correias',
+ cleanBuildPlate: 'Limpar Plataforma de Impressão',
+ checkExtruder: 'Verificar Engrenagens do Extrusor',
+ checkCooling: 'Verificar Ventiladores de Resfriamento',
+ generalInspection: 'Inspeção Geral',
+ cleanCarbonRods: 'Limpar Barras de Carbono',
+ cleanLinearRails: 'Limpar Trilhos Lineares',
+ checkPtfeTube: 'Verificar Tubo PTFE',
+ replaceHepaFilter: 'Substituir Filtro HEPA',
+ replaceCarbonFilter: 'Substituir Filtro de Carbono',
+ lubricateLeftNozzleRail: 'Lubrificar Trilho do Bico Esquerdo',
+ },
+ // Toast
+ maintenanceComplete: 'Manutenção marcada como concluída',
+ typeUpdated: 'Tipo de manutenção atualizado',
+ typeDeleted: 'Tipo de manutenção excluído',
+ defaultsRestored: 'Restauradas {{count}} tarefa(s) padrão',
+ printHoursUpdated: 'Horas de impressão atualizadas',
+ printerAssigned: 'Impressora atribuída',
+ printerRemoved: 'Impressora removida',
+ // Confirmation
+ deleteTypeConfirm: 'Excluir "{{name}}"?',
+ deleteSystemTypeTitle: 'Excluir tarefa de manutenção padrão?',
+ deleteSystemTypeMessage: 'Tem certeza de que deseja excluir a tarefa de manutenção padrão "{{name}}"?',
+ // Permissions
+ noPermissionUpdate: 'Você não tem permissão para atualizar itens de manutenção',
+ noPermissionPerform: 'Você não tem permissão para realizar manutenção',
+ noPermissionEditTypes: 'Você não tem permissão para editar tipos de manutenção',
+ noPermissionDeleteTypes: 'Você não tem permissão para excluir tipos de manutenção',
+ noPermissionEditHours: 'Você não tem permissão para editar horas de impressão',
+ noPermissionRemovePrinter: 'Você não tem permissão para remover atribuições de impressora',
+ noPermissionAssignPrinter: 'Você não tem permissão para atribuir impressoras',
+ noPermissionEditIntervals: 'Você não tem permissão para editar intervalos',
+ // Configure link
+ configureSettings: 'Configure tipos de manutenção e intervalos',
+ },
+
+ // Settings page
+ settings: {
+ title: 'Configurações',
+ general: 'Geral',
+ // Tab names
+ tabs: {
+ general: 'Geral',
+ smartPlugs: 'Tomadas Inteligentes',
+ notifications: 'Notificações',
+ filament: 'Filamento',
+ network: 'Rede',
+ apiKeys: 'Chaves API',
+ virtualPrinter: 'Impressora Virtual',
+ users: 'Autenticação',
+ backup: 'Backup',
+ emailAuth: 'Autenticação por Email',
+ },
+ // Email settings
+ email: {
+ smtpSettings: 'Configuração SMTP',
+ smtpHost: 'Servidor SMTP',
+ smtpPort: 'Porta SMTP',
+ security: 'Segurança',
+ authentication: 'Autenticação',
+ username: 'Nome de Usuário',
+ password: 'Senha',
+ fromEmail: 'Email de Remetente',
+ fromName: 'Nome de Remetente',
+ testConnection: 'Testar Conexão SMTP',
+ testRecipient: 'Email de Teste',
+ sendTest: 'Enviar Email de Teste',
+ sending: 'Enviando...',
+ save: 'Salvar Configurações',
+ saving: 'Salvando...',
+ advancedAuth: 'Autenticação Avançada',
+ advancedAuthEnabled: 'Autenticação Avançada está habilitada',
+ advancedAuthEnabledDesc: 'Recursos de gerenciamento de usuários baseados em email estão ativos. Novos usuários receberão senhas geradas automaticamente por email, e os usuários podem redefinir suas senhas através do recurso de esqueci minha senha.',
+ advancedAuthDisabled: 'Autenticação Avançada está desabilitada',
+ advancedAuthDisabledDesc: 'Habilite a autenticação avançada para ativar recursos baseados em email para gerenciamento de usuários.',
+ enable: 'Habilitar',
+ disable: 'Desabilitar',
+ feature1: 'Senhas são geradas automaticamente e enviadas por email para novos usuários',
+ feature2: 'Usuários podem fazer login com nome de usuário ou email',
+ feature3: 'Recurso de esqueci minha senha está disponível',
+ feature4: 'Administradores podem redefinir senhas de usuários via email',
+ // Error messages
+ errors: {
+ requiredFields: 'Por favor, preencha todos os campos obrigatórios',
+ usernameRequired: 'Nome de usuário é obrigatório quando a autenticação está habilitada',
+ enterTestEmail: 'Por favor, insira um endereço de email de teste',
+ smtpServerAndEmail: 'Por favor, preencha o servidor SMTP e o email de remetente antes de testar',
+ usernamePasswordRequired: 'Nome de usuário e senha são obrigatórios quando a autenticação está habilitada',
+ configureSmtpFirst: 'Por favor, configure e teste as configurações SMTP primeiro',
+ },
+ // Success messages
+ success: {
+ settingsSaved: 'Configurações SMTP salvas com sucesso',
+ },
+ // Security options
+ securityOptions: {
+ starttls: 'STARTTLS (Porta 587)',
+ ssl: 'SSL/TLS (Porta 465)',
+ none: 'Nenhuma (Porta 25)',
+ },
+ // Authentication options
+ authOptions: {
+ enabled: 'Habilitado',
+ disabled: 'Desabilitado',
+ },
+ },
+ appearance: 'Aparência',
+ notifications: 'Notificações',
+ smartPlugs: 'Tomadas Inteligentes',
+ spoolman: 'Spoolman',
+ updates: 'Atualizações',
+ language: 'Idioma',
+ languageDescription: 'Selecione seu idioma preferido',
+ theme: 'Tema',
+ themeLight: 'Claro',
+ themeDark: 'Escuro',
+ themeSystem: 'Sistema',
+ defaultView: 'Visualização Padrão',
+ defaultViewDescription: 'Página a ser exibida ao abrir o aplicativo',
+ checkForUpdates: 'Verificar Atualizações',
+ autoUpdate: 'Atualização Automática',
+ currentVersion: 'Versão Atual',
+ latestVersion: 'Última Versão',
+ upToDate: 'Você está atualizado',
+ updateAvailable: 'Atualização disponível',
+ // Notifications
+ notificationLanguage: 'Idioma das Notificações',
+ notificationLanguageDescription: 'Idioma para notificações push',
+ bedCooledThreshold: 'Limite de Resfriamento da Cama',
+ bedCooledThresholdDescription: 'Temperatura abaixo da qual a cama é considerada resfriada após uma impressão',
+ notificationProviders: 'Provedores de Notificação',
+ addProvider: 'Adicionar Provedor',
+ editProvider: 'Editar Provedor',
+ providerType: 'Tipo de Provedor',
+ testNotification: 'Testar Notificação',
+ testSuccess: 'Notificação de teste enviada com sucesso',
+ testFailed: 'Falha ao enviar notificação de teste',
+ quietHours: 'Horas de Silêncio',
+ quietHoursDescription: 'Não perturbe durante essas horas',
+ quietHoursStart: 'Início',
+ quietHoursEnd: 'Fim',
+ events: {
+ title: 'Eventos de Notificação',
+ printStart: 'Impressão Iniciada',
+ printComplete: 'Impressão Concluída',
+ printFailed: 'Falha na Impressão',
+ printStopped: 'Impressão Interrompida',
+ printProgress: 'Marcos de Progresso',
+ printProgressDescription: 'Notificar em 25%, 50%, 75%',
+ printerOffline: 'Impressora Offline',
+ printerError: 'Erro na Impressora',
+ filamentLow: 'Filamento Baixo',
+ maintenanceDue: 'Manutenção Pendente',
+ maintenanceDueDescription: 'Notificar quando a manutenção for necessária',
+ },
+ // Smart Plugs
+ smartPlug: {
+ title: 'Tomadas Inteligentes',
+ add: 'Adicionar Tomada Inteligente',
+ edit: 'Editar Tomada Inteligente',
+ name: 'Nome',
+ ipAddress: 'Endereço IP',
+ linkedPrinter: 'Impressora Vinculada',
+ autoOn: 'Ligar Automaticamente',
+ autoOnDescription: 'Ligar quando a impressão começar',
+ autoOff: 'Desligar Automaticamente',
+ autoOffDescription: 'Desligar após a conclusão da impressão',
+ offDelay: 'Atraso para Desligar',
+ offDelayMinutes: 'Minutos após a impressão',
+ offDelayTemp: 'Quando o bico estiver abaixo da temperatura',
+ currentState: 'Estado Atual',
+ turnOn: 'Ligar',
+ turnOff: 'Desligar',
+ },
+ // Filament Tracking Mode
+ filamentTracking: 'Rastreamento de Filamento',
+ filamentTrackingDesc: 'Escolha como rastrear seus rolos de filamento. Você pode usar o inventário interno ou conectar a um servidor Spoolman externo.',
+ trackingModeBuiltIn: 'Inventário Interno',
+ trackingModeBuiltInDesc: 'Correspondência automática de RFID e rastreamento de uso incluídos',
+ trackingModeSpoolmanDesc: 'Servidor de gerenciamento de filamento externo',
+ builtInFeatureRfid: 'Detecta automaticamente rolos RFID da Bambu Lab no AMS',
+ builtInFeatureUsage: 'Rastreia o consumo de filamento por impressão',
+ builtInFeatureCatalog: 'Gerencia rolos, cores e perfis de fator K',
+ builtInFeatureThirdParty: 'Rolos de terceiros podem ser atribuídos aos rolos do inventário',
+ amsSyncButton: 'Sincronizar Pesos do AMS',
+ amsSyncTitle: 'Sincronizar Pesos dos Rolos do AMS',
+ amsSyncMessage: 'Isso substituirá todos os pesos dos rolos do inventário pelos valores atuais de % restante do AMS das impressoras conectadas. Use isso para recuperar dados de peso corrompidos. As impressoras devem estar online.',
+ amsSyncing: 'Sincronizando...',
+ amsSyncSuccess: '{{synced}} rolo(s) sincronizado(s), {{skipped}} ignorado(s)',
+ amsSyncError: 'Falha ao sincronizar pesos do AMS',
+ // Spoolman settings
+ spoolmanUrl: 'Spoolman URL',
+ spoolmanUrlHint: 'URL do seu servidor Spoolman (por exemplo, http://localhost:7912)',
+ spoolmanConnected: 'Conectado',
+ spoolmanDisconnected: 'Desconectado',
+ status: 'Status',
+ connect: 'Conectar',
+ disconnect: 'Desconectar',
+ howSyncWorks: 'Como a Sincronização Funciona',
+ syncInfoRfidOnly: 'Apenas rolos oficiais da Bambu Lab com RFID são sincronizados',
+ syncInfoAutoCreate: 'Novos rolos são criados automaticamente no Spoolman na primeira sincronização',
+ syncInfoThirdPartySkipped: 'Rolos não oficiais da Bambu Lab (terceiros, reabastecidos) são ignorados',
+ linkingExistingSpools: 'Vinculando Rolos Existentes',
+ linkingExistingSpoolsDesc: 'Para vincular rolos existentes do Spoolman ao seu AMS, passe o mouse sobre um slot do AMS e clique em "Vincular ao Spoolman".',
+ syncMode: 'Modo de Sincronização',
+ syncModeAuto: 'Automático',
+ syncModeManual: 'Apenas Manual',
+ syncModeAutoDesc: 'Os dados do AMS são sincronizados automaticamente quando alterações são detectadas',
+ syncModeManualDesc: 'Somente sincronize quando acionado manualmente',
+ syncAmsData: 'Sincronizar Dados do AMS',
+ syncAmsDataDesc: 'Sincronize manualmente os dados do AMS da impressora com o Spoolman',
+ allPrinters: 'Todas as Impressoras',
+ // Default printer
+ noDefaultPrinter: 'Sem padrão (perguntar a cada vez)',
+ // Sidebar
+ sidebarOrder: 'Ordem da barra lateral',
+ // Camera
+ saveThumbnails: 'Salvar miniaturas',
+ captureFinishPhoto: 'Capturar foto de conclusão',
+ noPrintersConfigured: 'Nenhuma impressora configurada',
+ // Archive settings
+ archiveMode: {
+ always: 'Sempre criar entrada de arquivo',
+ never: 'Nunca criar entrada de arquivo',
+ ask: 'Perguntar a cada vez',
+ },
+ // Updates
+ checkForUpdatesLabel: 'Verificar atualizações',
+ checkPrinterFirmware: 'Verificar firmware da impressora',
+ // Queue
+ enableRetry: 'Habilitar tentativa',
+ // Home Assistant
+ homeAssistantDescription: 'Controlar tomadas inteligentes via Home Assistant',
+ environmentManagedLabel: '(Gerenciado pelo Ambiente)',
+ autoEnabledViaEnv: 'Habilitado automaticamente via variáveis de ambiente',
+ urlFromEnvReadOnly: 'Valor definido pela variável de ambiente HA_URL (somente leitura)',
+ tokenFromEnvReadOnly: 'Valor definido pela variável de ambiente HA_TOKEN (somente leitura)',
+ // MQTT
+ mqttConnectedTo: 'Conectado a',
+ // Prometheus
+ prometheusDescription: 'Expor dados da impressora no formato Prometheus',
+ // Smart plugs empty state
+ noSmartPlugsTitle: 'Nenhuma tomada inteligente configurada',
+ noSmartPlugsDescription: 'Adicione uma tomada inteligente baseada em Tasmota para monitorar o consumo de energia e automatizar o controle de energia.',
+ // Notifications empty state
+ noProvidersTitle: 'Nenhum provedor configurado',
+ noProvidersDescription: 'Adicione um provedor para receber alertas.',
+ noTemplatesAvailable: 'Nenhum modelo disponível. Reinicie o backend para gerar os modelos padrão.',
+ // API permissions
+ apiPermissionView: 'Visualizar status da impressora e fila',
+ apiPermissionEdit: 'Adicionar e remover itens da fila de impressão',
+ // API keys
+ apiKeysEmptyTitle: 'Nenhuma chave API',
+ apiKeysEmptyDescription: 'Crie uma chave API para integrar com serviços externos.',
+ // Users
+ noUsersFound: 'Nenhum usuário encontrado',
+ noGroupsFound: 'Nenhum grupo encontrado',
+ noGroupsAvailable: 'Nenhum grupo disponível',
+ passwordsDoNotMatch: 'As senhas não coincidem',
+ systemGroupWarning: 'Os nomes dos grupos do sistema não podem ser alterados',
+ // Auth disabled
+ authDisabledTitle: 'Autenticação Desativada',
+ authDisabledFeature1: 'Exigir login para acessar o sistema',
+ authDisabledFeature2: 'Criar múltiplos usuários com permissões baseadas em grupos',
+ authDisabledFeature3: 'Controlar acesso com mais de 50 permissões granulares',
+ // User deletion
+ userHasCreated: 'Este usuário criou:',
+ userItemsQuestion: 'O que você gostaria de fazer com esses itens?',
+ deleteUserConfirm: 'Tem certeza de que deseja excluir este usuário?',
+ actionCannotBeUndone: 'Esta ação não pode ser desfeita.',
+ // Smart plugs
+ addFirstSmartPlug: 'Adicione sua primeira tomada inteligente',
+ // Notifications
+ providers: 'Provedores',
+ log: 'Registro',
+ testAll: 'Testar tudo',
+ testResults: 'Resultados do teste',
+ testPassedCount: '{{count}} aprovado',
+ testFailedCount: '{{count}} falhou',
+ messageTemplates: 'Modelos de mensagem',
+ messageTemplatesDescription: 'Personalize as mensagens de notificação para cada evento.',
+ // API Keys section
+ apiKeys: 'Chaves API',
+ apiKeysDescription: 'Crie chaves API para integrações externas e webhooks.',
+ createKey: 'Criar Chave',
+ apiKeyCreated: 'Chave API criada com sucesso',
+ apiKeyCopyWarning: "Copie esta chave agora - ela não será exibida novamente!",
+ useInApiBrowser: 'Usar no Navegador API',
+ createNewApiKey: 'Criar Nova Chave API',
+ keyName: 'Nome da Chave',
+ keyNamePlaceholder: 'e.g., Home Assistant, OctoPrint',
+ readStatus: 'Status de Leitura',
+ readStatusDescription: 'Visualizar status da impressora e fila',
+ manageQueue: 'Gerenciar Fila',
+ manageQueueDescription: 'Adicionar e remover itens da fila de impressão',
+ controlPrinter: 'Controlar Impressora',
+ controlPrinterDescription: 'Pausar, retomar e parar impressões',
+ unnamedKey: 'Chave Sem Nome',
+ lastUsed: 'Último uso',
+ read: 'Ler',
+ control: 'Controlar',
+ createFirstKey: 'Crie sua primeira chave',
+ webhookEndpoints: 'Endpoints de Webhook',
+ webhookApiKeyHint: 'Use sua chave API no cabeçalho X-API-Key.',
+ webhook: {
+ getAllStatus: 'Obter status de todas as impressoras',
+ getSpecificStatus: 'Obter status de uma impressora específica',
+ addToQueue: 'Adicionar à fila de impressão',
+ pausePrint: 'Pausar impressão',
+ resumePrint: 'Retomar impressão',
+ stopPrint: 'Parar impressão',
+ },
+ apiBrowser: 'Navegador API',
+ apiBrowserDescription: 'Explore e teste todos os endpoints de API disponíveis.',
+ apiKeyForTesting: 'Chave API para Teste',
+ apiKeyPlaceholder: 'Cole sua chave API aqui para testar endpoints autenticados...',
+ apiKeyHint: 'Esta chave será enviada como cabeçalho X-API-Key nas solicitações.',
+ deleteApiKeyTitle: 'Excluir Chave API',
+ deleteApiKeyMessage: 'Tem certeza de que deseja excluir esta chave API? Quaisquer integrações usando esta chave deixarão de funcionar.',
+ deleteKey: 'Excluir Chave',
+ // Filament tab
+ amsDisplayThresholds: 'Limiares de Exibição AMS',
+ amsThresholdsDescription: 'Configure os limiares de cores para os indicadores de umidade e temperatura do AMS.',
+ humidity: 'Umidade',
+ goodGreen: 'Bom (verde)',
+ fairOrange: 'Razoável (laranja)',
+ aboveFairBad: 'Acima do limiar razoável mostra como vermelho (ruim)',
+ temperature: 'Temperatura',
+ goodBlue: 'Bom (azul)',
+ aboveFairHot: 'Acima do limiar razoável mostra como vermelho (quente)',
+ historyRetention: 'Retenção de Histórico',
+ keepSensorHistory: 'Manter histórico do sensor por',
+ historyRetentionDescription: 'Dados antigos de umidade e temperatura serão automaticamente excluídos',
+ printModal: 'Modal de Impressão',
+ expandCustomMapping: 'Expandir mapeamento personalizado por padrão',
+ expandCustomMappingDescription: 'Ao imprimir em várias impressoras, mostrar o mapeamento AMS por impressora expandido',
+ // User management
+ authentication: 'Autenticação',
+ authEnabledDescription: 'Sua instância está protegida com autenticação de usuário',
+ authDisabledDescription: 'Ative para exigir login e gerenciar o acesso dos usuários',
+ authDisabledMessage: 'Ative a autenticação para criar contas de usuário, gerenciar permissões e proteger sua instância do Bambuddy.',
+ enableAuthentication: 'Ativar Autenticação',
+ currentUser: 'Usuário Atual',
+ changePassword: 'Alterar Senha',
+ admin: 'Administrador',
+ users: 'Usuários',
+ addUser: 'Adicionar Usuário',
+ groups: 'Grupos',
+ addGroup: 'Adicionar Grupo',
+ system: 'Sistema',
+ noDescription: 'Sem descrição',
+ userCount: '{{count}} usuários',
+ permissionCount: '{{count}} permissões',
+ createUser: 'Criar Usuário',
+ username: 'Nome de Usuário',
+ enterUsername: 'Digite o nome de usuário',
+ password: 'Senha',
+ enterPassword: 'Digite a senha (mínimo 6 caracteres)',
+ confirmPassword: 'Confirmar Senha',
+ confirmPasswordPlaceholder: 'Confirme a senha',
+ // Title tooltips
+ viewReleaseOnGitHub: 'Ver lançamento no GitHub',
+ turnAllPlugsOn: 'Ligar todas as tomadas',
+ turnAllPlugsOff: 'Desligar todas as tomadas',
+ // Modal: Clear logs
+ clearNotificationLogs: 'Limpar Logs de Notificação',
+ clearLogsMessage: 'Isso excluirá permanentemente todos os logs de notificação com mais de 30 dias. Esta ação não pode ser desfeita.',
+ clearLogs: 'Limpar Logs',
+ // Modal: Reset UI
+ resetUiPreferences: 'Redefinir Preferências de UI',
+ resetUiPreferencesMessage: 'Isso redefinirá todas as preferências de UI para os padrões: ordem da barra lateral, tema, layout do painel, modos de exibição e preferências de classificação. Suas impressoras, arquivos e configurações do servidor NÃO serão afetados. A página será recarregada após a limpeza.',
+ resetPreferences: 'Redefinir Preferências',
+ // Modal: Delete group
+ deleteGroupTitle: 'Excluir Grupo',
+ deleteGroupMessage: 'Tem certeza de que deseja excluir este grupo? Usuários neste grupo perderão essas permissões.',
+ deleteGroup: 'Excluir Grupo',
+ // Modal: Disable auth
+ disableAuthenticationTitle: 'Desativar Autenticação',
+ disableAuthenticationMessage: 'Tem certeza de que deseja desativar a autenticação? Isso tornará sua instância do Bambuddy acessível sem login. Todos os usuários permanecerão no banco de dados, mas a autenticação será desativada.',
+ disableAuthentication: 'Desativar Autenticação',
+ // Additional settings
+ configureBambuddy: 'Configurar Bambuddy',
+ systemDefault: 'Padrão do Sistema',
+ archiveSettings: 'Configurações de Arquivo',
+ newWindow: 'Nova Janela',
+ embeddedOverlay: 'Sobreposição Incorporada',
+ preferredSlicer: 'Fatiador Preferido',
+ preferredSlicerDescription: 'Escolha qual aplicativo de fatiamento abrirá os arquivos',
+ externalCameras: 'Câmeras Externas',
+ costTracking: 'Rastreamento de Custos',
+ printsOnly: 'Apenas Impressões',
+ totalConsumption: 'Consumo Total',
+ dataManagement: 'Gerenciamento de Dados',
+ storageUsage: 'Uso de Armazenamento',
+ storageUsageDescription: 'Detalhamento do uso de dados por categoria',
+ storageUsageTotal: 'Total',
+ storageUsageErrors: 'Erros',
+ storageUsageOtherBreakdown: 'Outros (inclui ativos estáticos, scripts e arquivos de configuração)',
+ storageUsageSystem: 'Sistema',
+ storageUsageData: 'Dados',
+ storageUsageUnavailable: 'Informações de uso de armazenamento indisponíveis',
+ clearNotificationLogsDescription: 'Excluir logs de notificação com mais de 30 dias',
+ resetUiPreferencesDescription: 'Redefinir ordem da barra lateral, tema, modos de exibição e preferências de layout. Impressoras, arquivos e configurações não são afetados.',
+ enableHomeAssistant: 'Ativar Home Assistant',
+ enableMqtt: 'Ativar MQTT',
+ useTls: 'Usar TLS',
+ enableMetricsEndpoint: 'Ativar Endpoint de Métricas',
+ availableMetrics: 'Métricas Disponíveis',
+ editUser: 'Editar Usuário',
+ deleteUserTitle: 'Excluir Usuário',
+ groupName: 'Nome do Grupo',
+ // Placeholders
+ leaveEmptyForAnonymous: 'Deixe vazio para anônimo',
+ leaveEmptyForNoAuth: 'Deixe vazio para sem autenticação',
+ enterNewPassword: 'Digite a nova senha',
+ confirmNewPassword: 'Confirme a nova senha',
+ enterGroupName: 'Digite o nome do grupo',
+ enterDescriptionOptional: 'Digite a descrição (opcional)',
+ enterCurrentPassword: 'Digite a senha atual',
+ enterNewPasswordMin6: 'Digite a nova senha (mínimo 6 caracteres)',
+ toast: {
+ keyCopied: 'Chave copiada para a área de transferência',
+ copyFailed: 'Falha ao copiar a chave',
+ keyAddedToBrowser: 'Chave adicionada ao Navegador de API',
+ clearLogsFailed: 'Falha ao limpar logs',
+ uiPreferencesReset: 'Preferências de UI redefinidas. Atualizando...',
+ authDisabled: 'Autenticação desativada com sucesso',
+ authDisableFailed: 'Falha ao desativar a autenticação',
+ apiKeyCreated: 'Chave de API criada',
+ apiKeyDeleted: 'Chave de API excluída',
+ userCreated: 'Usuário criado com sucesso',
+ userUpdated: 'Usuário atualizado com sucesso',
+ userDeleted: 'Usuário excluído com sucesso',
+ groupCreated: 'Grupo criado com sucesso',
+ groupUpdated: 'Grupo atualizado com sucesso',
+ groupDeleted: 'Grupo excluído com sucesso',
+ fillRequiredFields: 'Por favor, preencha todos os campos obrigatórios',
+ passwordsDoNotMatch: 'As senhas não coincidem',
+ passwordTooShort: 'A senha deve ter pelo menos 6 caracteres',
+ enterGroupName: 'Por favor, insira um nome de grupo',
+ settingsSaved: 'Configurações salvas',
+ cameraSettingsSaved: 'Configurações da câmera salvas',
+ enterCameraUrl: 'Por favor, insira a URL da câmera',
+ passwordChanged: 'Senha alterada com sucesso',
+ connectionFailed: 'Falha na conexão',
+ testFailed: 'Falha no teste',
+ cameraConnected: 'Câmera conectada{{resolution}}',
+ },
+ testConnection: 'Testar Conexão',
+ catalog: {
+ spoolCatalog: 'Catálogo de Carretéis',
+ spoolCatalogDescription: 'Pesos de carretéis vazios por marca/tipo. Usado para pesquisa automática de peso ao adicionar carretéis.',
+ searchCatalog: 'Pesquisar no catálogo...',
+ addNewEntry: 'Adicionar Nova Entrada',
+ namePlaceholder: 'Nome (ex.: Bambu Lab - Plástico)',
+ weight: 'Peso',
+ type: 'Tipo',
+ default: 'Padrão',
+ custom: 'Personalizado',
+ noMatch: 'Nenhuma entrada corresponde à sua pesquisa',
+ empty: 'Nenhuma entrada no catálogo',
+ deleteEntry: 'Excluir Entrada',
+ deleteConfirm: 'Tem certeza de que deseja excluir "{{name}}"?',
+ resetCatalog: 'Redefinir Catálogo',
+ resetConfirm: 'Redefinir catálogo para os padrões? Isso removerá todas as entradas personalizadas.',
+ loadFailed: 'Falha ao carregar o catálogo de carretéis',
+ nameWeightRequired: 'Nome e peso são obrigatórios',
+ entryAdded: 'Entrada adicionada',
+ addFailed: 'Falha ao adicionar entrada',
+ entryUpdated: 'Entrada atualizada',
+ updateFailed: 'Falha ao atualizar entrada',
+ entryDeleted: 'Entrada excluída',
+ deleteFailed: 'Falha ao excluir entrada',
+ resetSuccess: 'Catálogo redefinido para os padrões',
+ resetFailed: 'Falha ao redefinir catálogo',
+ exported: 'Exportadas {{count}} entradas',
+ imported: 'Importadas {{added}} entradas ({{skipped}} ignoradas)',
+ importFailed: 'Falha ao importar: formato JSON inválido',
+ exportTooltip: 'Exportar catálogo para JSON',
+ importTooltip: 'Importar catálogo de JSON',
+ resetTooltip: 'Redefinir para os padrões',
+ },
+ colorCatalog: {
+ title: 'Catálogo de Cores',
+ description: 'Cores de filamento por fabricante/material. Usado para pesquisa automática de cores ao adicionar carretéis.',
+ searchColors: 'Pesquisar cores...',
+ allManufacturers: 'Todos os fabricantes',
+ addNewColor: 'Adicionar Nova Cor',
+ manufacturer: 'Fabricante',
+ colorName: 'Nome da Cor',
+ hex: 'Hex',
+ materialOptional: 'Material (opcional)',
+ showing: 'Mostrando {{filtered}} de {{total}} cores',
+ noMatch: 'Nenhuma cor corresponde à sua pesquisa',
+ empty: 'Nenhuma cor no catálogo',
+ deleteColor: 'Excluir Cor',
+ deleteConfirm: 'Tem certeza de que deseja excluir "{{name}}"?',
+ resetCatalog: 'Redefinir Catálogo de Cores',
+ resetConfirm: 'Redefinir catálogo para os padrões? Isso removerá todas as cores personalizadas.',
+ sync: 'Sincronizar',
+ starting: 'Iniciando...',
+ syncTooltip: 'Sincronizar do FilamentColors.xyz (2000+ cores, pode levar um minuto)',
+ loadFailed: 'Falha ao carregar o catálogo de cores',
+ fieldsRequired: 'Fabricante, nome da cor e cor hex são obrigatórios',
+ colorAdded: 'Cor adicionada',
+ addFailed: 'Falha ao adicionar cor',
+ colorUpdated: 'Cor atualizada',
+ updateFailed: 'Falha ao atualizar cor',
+ colorDeleted: 'Cor excluída',
+ deleteFailed: 'Falha ao excluir cor',
+ resetSuccess: 'Catálogo de cores redefinido para os padrões',
+ resetFailed: 'Falha ao redefinir catálogo',
+ syncUpToDate: 'Já está atualizado ({{count}} cores verificadas)',
+ syncComplete: 'Adicionadas {{added}} novas cores ({{skipped}} já existiam)',
+ syncError: 'Erro de sincronização',
+ syncFailed: 'Falha ao sincronizar do FilamentColors.xyz',
+ exported: 'Exportadas {{count}} cores',
+ imported: 'Importadas {{added}} cores ({{skipped}} ignoradas)',
+ importFailed: 'Falha ao importar: formato JSON inválido',
+ },
+ },
+
+ // Notifications (for push notifications)
+ notification: {
+ printStarted: {
+ title: 'Impressão Iniciada',
+ body: '{{printer}}: {{filename}} iniciou a impressão',
+ },
+ printCompleted: {
+ title: 'Impressão Concluída',
+ body: '{{printer}}: {{filename}} foi concluída com sucesso',
+ },
+ printFailed: {
+ title: 'Falha na Impressão',
+ body: '{{printer}}: {{filename}} falhou',
+ },
+ printStopped: {
+ title: 'Impressão Interrompida',
+ body: '{{printer}}: {{filename}} foi interrompida',
+ },
+ printProgress: {
+ title: 'Progresso da Impressão',
+ body: '{{printer}}: {{filename}} está {{percent}}% concluída',
+ },
+ printerOffline: {
+ title: 'Impressora Offline',
+ body: '{{printer}} está offline',
+ },
+ printerError: {
+ title: 'Erro na Impressora',
+ body: '{{printer}}: {{error}}',
+ },
+ filamentLow: {
+ title: 'Filamento Baixo',
+ body: '{{printer}}: O filamento está acabando',
+ },
+ maintenanceDue: {
+ title: 'Manutenção Pendente',
+ body: '{{printer}}: {{items}} precisam de atenção',
+ },
+ },
+
+ // Errors
+ errors: {
+ generic: 'Algo deu errado',
+ networkError: 'Erro de rede. Por favor, verifique sua conexão.',
+ notFound: 'Não encontrado',
+ unauthorized: 'Não autorizado',
+ serverError: 'Erro no servidor',
+ validationError: 'Por favor, verifique sua entrada',
+ printerConnectionFailed: 'Falha ao conectar à impressora',
+ saveFailed: 'Falha ao salvar alterações',
+ deleteFailed: 'Falha ao excluir',
+ loadFailed: 'Falha ao carregar dados',
+ },
+
+ // HMS Errors modal
+ hmsErrors: {
+ title: 'Erros - {{name}}',
+ noErrors: 'Nenhum erro',
+ viewOnWiki: 'Ver no Bambu Lab Wiki',
+ clearInstructions: 'Limpe os erros na impressora para descartá-los aqui.',
+ clearErrors: 'Limpar Erros',
+ clearSuccess: 'Erros HMS limpos',
+ clearFailed: 'Falha ao limpar erros HMS',
+ },
+
+ // MQTT Debug modal
+ mqttDebug: {
+ title: 'MQTT Log de Depuração',
+ searchPlaceholder: 'Pesquisar tópico ou payload...',
+ noMessages: 'Nenhuma mensagem registrada ainda',
+ startLoggingHint: 'Clique em "Iniciar Registro" para começar a capturar mensagens MQTT',
+ noMessagesMatch: 'Nenhuma mensagem corresponde ao seu filtro',
+ adjustFilterHint: 'Tente ajustar seus critérios de pesquisa ou filtro',
+ incoming: 'Entrada',
+ outgoing: 'Saída',
+ loggingStopped: 'Registro interrompido',
+ loggingActive: 'Registro ativo - as mensagens serão atualizadas automaticamente',
+ startLogging: 'Iniciar Registro',
+ stopLogging: 'Parar Registro',
+ clearLog: 'Limpar Registro',
+ topic: 'ópico',
+ timestamp: 'Carimbo de Data/Hora',
+ direction: 'Direção',
+ all: 'Todos',
+ },
+
+ // Printer File Manager modal (printer internal storage)
+ printerFiles: {
+ title: 'Gerenciador de Arquivos',
+ storageUsed: 'Usado:',
+ storageFree: 'Livre:',
+ filterPlaceholder: 'Filtrar arquivos...',
+ deleteButton: 'Excluir',
+ deleteFiles: 'Excluir {{count}} arquivos',
+ deleteFileConfirm: 'Excluir "{{name}}"? Isso não pode ser desfeito.',
+ deleteFilesConfirm: 'Excluir {{count}} arquivos selecionados? Isso não pode ser desfeito.',
+ noFiles: 'Nenhum arquivo na impressora',
+ loadingFiles: 'Carregando arquivos...',
+ failedToLoad: 'Falha ao carregar arquivos',
+ toast: {
+ filesDeleted: 'Arquivos excluídos: {{count}}',
+ deleteFailed: 'Falha ao excluir: {{error}}',
+ },
+ },
+
+ // Confirmations
+ confirm: {
+ delete: 'Tem certeza de que deseja excluir isso?',
+ unsavedChanges: 'Você tem alterações não salvas. Tem certeza de que deseja sair?',
+ clearQueue: 'Tem certeza de que deseja limpar a fila?',
+ },
+
+ // Login page
+ login: {
+ title: 'Bambuddy Login',
+ subtitle: 'Faça login na sua conta',
+ username: 'Nome de usuário',
+ usernamePlaceholder: 'Digite seu nome de usuário',
+ usernameOrEmail: 'Nome de usuário ou Email',
+ usernameOrEmailPlaceholder: 'Nome de usuário ou Email',
+ password: 'Senha',
+ passwordPlaceholder: 'Digite sua senha',
+ signIn: 'Entrar',
+ signingIn: 'Entrando...',
+ forgotPassword: 'Esqueceu sua senha?',
+ loginSuccess: 'Login realizado com sucesso',
+ loginFailed: 'Falha no login',
+ enterCredentials: 'Por favor, insira nome de usuário e senha',
+ forgotPasswordTitle: 'Esqueceu a Senha',
+ forgotPasswordMessage: "Se você esqueceu sua senha, entre em contato com o administrador do sistema para redefini-la.",
+ forgotPasswordEmailMessage: "Digite seu endereço de email e enviaremos uma nova senha.",
+ emailAddress: 'Endereço de Email',
+ emailPlaceholder: 'seu.email@exemplo.com',
+ cancel: 'Cancelar',
+ sending: 'Enviando...',
+ sendResetEmail: 'Enviar Email de Redefinição',
+ howToReset: 'Como redefinir sua senha:',
+ resetStep1: 'Entre em contato com o administrador do Bambuddy',
+ resetStep2: 'Peça para redefinir sua senha na Gestão de Usuários',
+ resetStep3: 'Eles podem definir uma nova senha temporária para você',
+ resetStep4: 'Faça login com a nova senha e altere-a nas Configurações',
+ gotIt: 'Entendi',
+ },
+
+ // Setup page
+ setup: {
+ title: 'Bambuddy Configuração',
+ subtitle: 'Configure a autenticação para sua instância do Bambuddy',
+ enableAuth: 'Ativar Autenticação',
+ adminAccount: 'Conta de Administrador',
+ adminAccountDesc: 'Se usuários administradores já existirem, a autenticação será ativada usando as contas de administrador existentes. Deixe os campos abaixo vazios para usar os administradores existentes ou insira novas credenciais para criar um novo usuário administrador.',
+ adminUsername: 'Nome de usuário do administrador',
+ adminPassword: 'Senha do administrador',
+ optionalIfAdminExists: '(opcional se usuários administradores existirem)',
+ adminUsernamePlaceholder: 'Digite o nome de usuário do administrador (opcional)',
+ adminPasswordPlaceholder: 'Digite a senha do administrador (opcional)',
+ confirmPassword: 'Confirmar Senha',
+ confirmPasswordPlaceholder: 'Confirme a senha do administrador',
+ settingUp: 'Configurando...',
+ completeSetup: 'Concluir Configuração',
+ toast: {
+ authEnabledAdminCreated: 'Autenticação ativada e usuário administrador criado',
+ authEnabledExistingAdmins: 'Autenticação ativada usando usuários administradores existentes',
+ setupCompleted: 'Configuração concluída',
+ enterBothCredentials: 'Por favor, insira o nome de usuário e a senha do administrador, ou deixe ambos vazios para usar os usuários administradores existentes',
+ passwordsDoNotMatch: 'As senhas não coincidem',
+ passwordTooShort: 'A senha deve ter pelo menos 6 caracteres',
+ },
+ },
+
+ // Password change
+ changePassword: {
+ title: 'Alterar Senha',
+ currentPassword: 'Senha Atual',
+ currentPasswordPlaceholder: 'Digite a senha atual',
+ newPassword: 'Nova Senha',
+ newPasswordPlaceholder: 'Digite a nova senha (mínimo 6 caracteres)',
+ confirmPassword: 'Confirmar Senha',
+ confirmPasswordPlaceholder: 'Confirme a nova senha',
+ passwordsDoNotMatch: 'As senhas não coincidem',
+ passwordTooShort: 'A senha deve ter pelo menos 6 caracteres',
+ changing: 'Alterando...',
+ success: 'Senha alterada com sucesso',
+ failed: 'Falha ao alterar a senha',
+ },
+
+ // Plate detection alert
+ plateAlert: {
+ title: 'Impressão Pausada!',
+ message: 'Objetos detectados na mesa de impressão. A impressão foi automaticamente pausada. Por favor, limpe a mesa e retome a impressão.',
+ understand: 'Entendi',
+ },
+
+ // Camera page
+ camera: {
+ title: 'Visualização da Câmera',
+ invalidPrinterId: 'ID da impressora inválido',
+ live: 'Ao Vivo',
+ snapshot: 'Captura',
+ restartStream: 'Reiniciar transmissão',
+ refreshSnapshot: 'Atualizar captura',
+ fullscreen: 'Tela Cheia',
+ exitFullscreen: 'Sair da Tela Cheia',
+ connectingToCamera: 'Conectando à câmera...',
+ capturingSnapshot: 'Capturando imagem...',
+ connectionLost: 'Conexão perdida',
+ connectionFailed: 'Falha na conexão com a câmera',
+ reconnecting: 'Reconectando em {{countdown}}s... (tentativa {{attempt}}/{{max}})',
+ reconnectNow: 'Reconectar agora',
+ cameraUnavailable: 'Câmera indisponível',
+ cameraUnavailableDesc: 'Certifique-se de que a impressora está ligada e conectada.',
+ noCamera: 'Nenhuma câmera disponível',
+ retry: 'Tentar novamente',
+ cameraStream: 'Transmissão da câmera',
+ zoomOut: 'Reduzir zoom',
+ zoomIn: 'Aumentar zoom',
+ resetZoom: 'Redefinir zoom',
+ recording: 'Gravando',
+ startRecording: 'Iniciar gravação',
+ stopRecording: 'Parar gravação',
+ chamberLight: 'Alternar luz da câmara',
+ },
+
+ // Groups management
+ groups: {
+ title: 'Gerenciamento de Grupos',
+ subtitle: 'Gerenciar grupos de permissão para controle de acesso',
+ backToSettings: 'Voltar para Configurações',
+ createGroup: 'Criar Grupo',
+ noPermission: 'Você não tem permissão para acessar esta página.',
+ system: 'Sistema',
+ noDescription: 'Sem descrição',
+ usersCount: '{{count}} usuários',
+ permissionsCount: '{{count}} permissões',
+ edit: 'Editar',
+ delete: 'Excluir',
+ toast: {
+ created: 'Grupo criado com sucesso',
+ updated: 'Grupo atualizado com sucesso',
+ deleted: 'Grupo excluído com sucesso',
+ enterGroupName: 'Por favor, insira um nome para o grupo',
+ },
+ modal: {
+ editGroup: 'Editar Grupo',
+ createGroup: 'Criar Grupo',
+ cancel: 'Cancelar',
+ saving: 'Salvando...',
+ creating: 'Criando...',
+ saveChanges: 'Salvar Alterações',
+ },
+ form: {
+ groupName: 'Nome do Grupo',
+ groupNamePlaceholder: 'Insira o nome do grupo',
+ systemGroupWarning: 'Os nomes dos grupos do sistema não podem ser alterados',
+ description: 'Descrição',
+ descriptionPlaceholder: 'Insira a descrição (opcional)',
+ permissions: 'Permissões ({{count}} selecionadas)',
+ },
+ deleteModal: {
+ title: 'Excluir Grupo',
+ message: 'Tem certeza de que deseja excluir este grupo? Os usuários deste grupo perderão essas permissões.',
+ confirm: 'Excluir Grupo',
+ },
+ },
+
+ // Users management
+ users: {
+ title: 'Gerenciamento de Usuários',
+ subtitle: 'Gerenciar usuários e seu acesso à sua instância do Bambuddy',
+ backToSettings: 'Voltar para Configurações',
+ createUser: 'Criar Usuário',
+ noPermission: 'Você não tem permissão para acessar esta página.',
+ admin: 'Admin',
+ noGroups: 'Sem grupos',
+ active: 'Ativo',
+ inactive: 'Inativo',
+ edit: 'Editar',
+ delete: 'Excluir',
+ system: 'Sistema',
+ noGroupsAvailable: 'Nenhum grupo disponível',
+ table: {
+ username: 'Nome de Usuário',
+ groups: 'Grupos',
+ status: 'Status',
+ actions: 'Ações',
+ },
+ toast: {
+ created: 'Usuário criado com sucesso',
+ updated: 'Usuário atualizado com sucesso',
+ deleted: 'Usuário excluído com sucesso',
+ fillRequired: 'Por favor, preencha todos os campos obrigatórios',
+ passwordsDoNotMatch: 'As senhas não coincidem',
+ passwordTooShort: 'A senha deve ter pelo menos 6 caracteres',
+ },
+ modal: {
+ createUser: 'Criar Usuário',
+ editUser: 'Editar Usuário',
+ cancel: 'Cancelar',
+ creating: 'Criando...',
+ saving: 'Salvando...',
+ saveChanges: 'Salvar Alterações',
+ advancedAuthSubtitle: 'com Autenticação Avançada',
+ },
+ form: {
+ username: 'Nome de Usuário',
+ usernamePlaceholder: 'Insira o nome de usuário',
+ email: 'Email',
+ emailPlaceholder: 'user@example.com',
+ password: 'Senha',
+ passwordPlaceholder: 'Insira a senha',
+ confirmPassword: 'Confirmar Senha',
+ confirmPasswordPlaceholder: 'Confirme a senha',
+ newPasswordPlaceholder: 'Insira a nova senha',
+ confirmNewPasswordPlaceholder: 'Confirme a nova senha',
+ leaveBlankToKeep: 'deixe em branco para manter a atual',
+ groups: 'Grupos',
+ optional: 'opcional',
+ autoGeneratedPassword: 'Uma senha segura será gerada automaticamente e enviada por e-mail ao usuário.',
+ passwordManagedByAdvancedAuth: 'A senha é gerenciada pela Autenticação Avançada. Use "Redefinir Senha" para enviar uma nova senha ao usuário por e-mail.',
+ resetPassword: 'Redefinir Senha',
+ resettingPassword: 'Redefinindo Senha...',
+ },
+ deleteModal: {
+ title: 'Excluir Usuário',
+ message: 'Tem certeza de que deseja excluir este usuário? Esta ação não pode ser desfeita.',
+ confirm: 'Excluir Usuário',
+ },
+ },
+
+ // Stream overlay
+ streamOverlay: {
+ title: 'Stream Overlay',
+ invalidPrinterId: 'ID da impressora inválido',
+ cameraStream: 'Transmissão da câmera',
+ progress: 'Progresso da impressão',
+ eta: 'ETA',
+ printerIdle: 'Impressora ociosa',
+ printerOffline: 'Impressora offline',
+ status: {
+ printing: 'Imprimindo',
+ paused: 'Pausado',
+ finished: 'Concluído',
+ failed: 'Falhou',
+ idle: 'Ocioso',
+ unknown: 'Desconhecido',
+ },
+ },
+
+ // Profiles
+ profiles: {
+ title: 'Perfis',
+ subtitle: 'Gerencie seus presets de fatiador e calibrações de avanço de pressão',
+ tabs: {
+ cloud: 'Perfis na Nuvem',
+ local: 'Perfis Locais',
+ kprofiles: 'K-Perfis',
+ },
+ localProfiles: {
+ title: 'Perfis Locais',
+ subtitle: 'Importe e gerencie presets de fatiador do OrcaSlicer',
+ import: 'Importar Perfis',
+ importDesc: 'Solte arquivos .bbscfg, .bbsflmt, .orca_filament, .zip ou .json aqui',
+ importing: 'Importando...',
+ search: 'Pesquisar presets locais...',
+ noPresets: 'Nenhum preset local ainda',
+ badge: 'Local',
+ edit: 'Editar',
+ delete: 'Excluir',
+ cancel: 'Cancelar',
+ deleteConfirmTitle: 'Excluir Preset',
+ deleteConfirm: 'Tem certeza de que deseja excluir este preset? Esta ação não pode ser desfeita.',
+ source: 'Fonte',
+ inheritsFrom: 'Herdado de',
+ filamentType: 'Tipo',
+ vendor: 'Fornecedor',
+ compatiblePrinters: 'Impressoras Compatíveis',
+ nozzleTemp: 'Temperatura do Bico',
+ cost: 'Custo',
+ density: 'Densidade',
+ pressureAdvance: 'Avanço de Pressão',
+ filament: 'Filamento',
+ process: 'Processo',
+ printer: 'Impressora',
+ toast: {
+ importSuccess: '{{count}} preset(s) importada(s)',
+ importSkipped: '{{count}} preset(s) ignorada(s) (duplicadas)',
+ importError: '{{count}} erro(s) durante a importação',
+ deleted: 'Preset excluído',
+ updated: 'Preset atualizado',
+ },
+ },
+ connectedAs: 'Conectado como',
+ logout: 'Sair',
+ noLogoutPermission: 'Você não tem permissão para sair',
+ failedToLoad: 'Falha ao carregar perfis',
+ retry: 'Tentar novamente',
+ time: {
+ justNow: 'Agora mesmo',
+ minsAgo: 'há {{count}} minutos',
+ hoursAgo: 'há {{count}} horas',
+ daysAgo: 'há {{count}} dias',
+ },
+ toast: {
+ loggedOut: 'Desconectado',
+ },
+ login: {
+ title: 'Conectar ao Bambu Cloud',
+ subtitle: 'Sincronize seus presets de fatiador entre dispositivos',
+ email: 'Email',
+ password: 'Senha',
+ region: 'Região',
+ regionGlobal: 'Global',
+ regionChina: 'China',
+ verificationCode: 'Código de Verificação',
+ totpCode: 'Código do Autenticador',
+ checkEmail: 'Verifique seu email ({{email}}) para um código de 6 dígitos',
+ enterTotpHint: 'Digite o código de 6 dígitos do seu aplicativo autenticador',
+ accessToken: 'Token de Acesso',
+ accessTokenHint: 'Cole seu token de acesso Bambu Lab (do Bambu Studio)',
+ back: 'Voltar',
+ loginButton: 'Entrar',
+ verifyButton: 'Verificar',
+ setTokenButton: 'Definir Token',
+ useToken: 'Usar token de acesso em vez disso',
+ useEmail: 'Entrar com email em vez disso',
+ toast: {
+ loggedIn: 'Conectado com sucesso',
+ codeSent: 'Código de verificação enviado para seu email',
+ enterTotp: 'Digite o código do seu aplicativo autenticador',
+ tokenSet: 'Token definido com sucesso',
+ },
+ },
+ presets: {
+ myPreset: 'Meu preset (editável)',
+ duplicate: 'Duplicar',
+ editable: 'Editável',
+ failedToLoadDetails: 'Falha ao carregar detalhes do preset',
+ deleteConfirm: 'Excluir este preset?',
+ deleteWarning: 'Isso excluirá permanentemente "{{name}}" do Bambu Cloud. Esta ação não pode ser desfeita.',
+ noDuplicatePermission: 'Você não tem permissão para duplicar presets',
+ noEditPermission: 'Você não tem permissão para editar presets',
+ noDeletePermission: 'Você não tem permissão para excluir presets',
+ types: {
+ filament: 'Preset de filamento',
+ printer: 'Preset de impressora',
+ process: 'Preset de processo',
+ },
+ toast: {
+ deleted: 'Preset excluído',
+ created: 'Preset criado',
+ updated: 'Preset atualizado',
+ duplicated: 'Preset duplicado',
+ fieldAdded: 'Campo "{{key}}" adicionado',
+ exported: 'Preset exportado',
+ },
+ baseLabel: 'Base: {{name}}',
+ currentLabel: 'Atual: {{name}}',
+ newPreset: 'Novo Preset',
+ editPreset: 'Editar Preset',
+ duplicatePreset: 'Duplicar Preset',
+ createNewPreset: 'Criar Novo Preset',
+ customizeSettings: 'Personalizar configurações para seu novo preset',
+ compareWithBase: 'Comparar com o preset base',
+ compare: 'Comparar',
+ // CreatePresetModal - Basic Info
+ basePreset: 'Preset Base',
+ selectBasePreset: 'Selecionar preset base...',
+ presetName: 'Nome do Preset',
+ myCustomPreset: 'Meu preset personalizado',
+ inheritsFrom: 'Herdado de',
+ dropJsonToImport: 'Solte o arquivo JSON para importar',
+ // CreatePresetModal - Tabs
+ tabs: {
+ common: 'Comum',
+ allFields: 'Todos os Campos',
+ },
+ // CreatePresetModal - All Fields Tab
+ availableFields: 'Campos Disponíveis',
+ searchFieldsPlaceholder: 'Pesquisar campos...',
+ noMatchingFields: 'Nenhum campo correspondente',
+ allFieldsAdded: 'Todos os campos adicionados',
+ addCustomField: 'Adicionar campo personalizado',
+ yourOverrides: 'Suas Substituições',
+ noOverridesYet: 'Nenhuma substituição ainda',
+ clickFieldsToAdd: 'Clique nos campos à esquerda para adicioná-los',
+ saveAsTemplate: 'Salvar como modelo',
+ jsonTip: 'Dica: Arraste e solte um arquivo .json em qualquer lugar deste modal para importar configurações',
+ },
+ cloudView: {
+ searchPlaceholder: 'Pesquisar presets...',
+ templates: 'Modelos',
+ refresh: 'Atualizar',
+ newPreset: 'Novo Preset',
+ clearFilters: 'Limpar filtros',
+ // Compare mode
+ compareMode: 'Modo de Comparação',
+ selectAnotherPreset: 'Selecionar outro preset {{type}}',
+ clickTwoPresets: 'Clique em dois presets do mesmo tipo para comparar',
+ selectFirst: '1. Selecionar primeiro',
+ selectSecond: '2. Selecionar segundo',
+ compareNow: 'Comparar Agora',
+ // Status row
+ lastSynced: 'Última sincronização:',
+ showingCount: 'Mostrando {{showing}} de {{total}} presets',
+ noPresetsFound: 'Nenhum preset encontrado',
+ // Column headers
+ columns: {
+ filament: 'Filamento',
+ process: 'Processo',
+ printer: 'Impressora',
+ },
+ noFilamentPresets: 'Nenhum preset de filamento',
+ noProcessPresets: 'Nenhum preset de processo',
+ noPrinterPresets: 'Nenhum preset de impressora',
+ // Filters
+ filters: {
+ type: 'Tipo',
+ owner: 'Proprietário',
+ printer: 'Impressora',
+ nozzle: 'Bico',
+ filament: 'Filamento',
+ layer: 'Camada',
+ all: 'Todos',
+ myPresets: 'Meus Presets',
+ builtIn: 'Integrado',
+ process: 'Processo',
+ },
+ // Permissions
+ noTemplatesPermission: 'Você não tem permissão para gerenciar modelos',
+ noRefreshPermission: 'Você não tem permissão para atualizar perfis',
+ noCreatePermission: 'Você não tem permissão para criar presets',
+ },
+ templates: {
+ title: 'Modelos Rápidos',
+ noTemplates: 'Nenhum modelo ainda',
+ createFirst: 'Crie modelos a partir do editor de presets',
+ typeFilter: 'Tipo:',
+ deleteTitle: 'Excluir Modelo',
+ deleteWarning: 'Esta ação não pode ser desfeita',
+ deleteConfirm: 'Tem certeza de que deseja excluir "{{name}}"?',
+ namePlaceholder: 'Nome do modelo',
+ descriptionPlaceholder: 'Descrição',
+ settingsJson: 'Configurações (JSON)',
+ fieldsCount: '{{count}} campos',
+ shownInModals: 'Exibido em modais',
+ hiddenInModals: 'Oculto em modais',
+ apply: 'Aplicar',
+ toast: {
+ deleted: 'Modelo excluído',
+ updated: 'Modelo atualizado',
+ created: 'Modelo criado',
+ applied: 'Modelo aplicado',
+ },
+ },
+ },
+
+ // Support/Debug
+ support: {
+ debugLoggingActive: 'Registro de depuração ativo',
+ manageLogs: 'Gerenciar',
+ collectItem7: 'Conectividade da impressora e versões de firmware',
+ collectItem8: 'Status de integração (Spoolman, MQTT, HA)',
+ collectItem9: 'Interfaces de rede (somente sub-redes)',
+ collectItem10: 'Versões de pacotes Python',
+ collectItem11: 'Verificações de integridade do banco de dados',
+ collectItem12: 'Detalhes do ambiente Docker',
+ },
+
+ // File manager
+ fileManager: {
+ title: 'Gerenciador de Arquivos',
+ subtitle: 'Organize e gerencie seus arquivos de impressão',
+ uploadFiles: 'Enviar Arquivos',
+ newFolder: 'Nova Pasta',
+ folderName: 'Nome da Pasta',
+ folderNamePlaceholder: 'ex.: Peças Funcionais',
+ renameFile: 'Renomear Arquivo',
+ renameFolder: 'Renomear Pasta',
+ moveFiles: 'Mover {{count}} Arquivo(s)',
+ rootNoFolder: 'Raiz (Sem Pasta)',
+ current: 'Atual',
+ linkFolder: 'Vincular Pasta',
+ linkFolderDescription: 'Vincular "{{name}}" a um projeto ou arquivo para acesso rápido.',
+ project: 'Projeto',
+ archive: 'Arquivo',
+ noProjectsFound: 'Nenhum projeto encontrado',
+ noArchivesFound: 'Nenhum arquivo encontrado',
+ unlink: 'Desvincular',
+ link: 'Vincular',
+ dragDropFiles: 'Arraste e solte os arquivos aqui',
+ dropFilesHere: 'Solte os arquivos aqui',
+ orClickToBrowse: 'ou clique para procurar',
+ allFileTypesSupported: 'Todos os tipos de arquivos são suportados. Arquivos ZIP serão extraídos.',
+ zipFilesDetected: 'Arquivos ZIP detectados',
+ zipExtractOptions: 'Arquivos ZIP serão extraídos. Escolha como lidar com a estrutura de pastas:',
+ preserveZipStructure: 'Preservar estrutura de pastas do ZIP',
+ createFolderFromZip: 'Criar pasta a partir do nome do arquivo ZIP',
+ stlThumbnailGeneration: 'Geração de miniaturas STL',
+ zipMayContainStl: 'Arquivos ZIP podem conter arquivos STL. Miniaturas podem ser geradas durante a extração.',
+ thumbnailsCanBeGenerated: 'Miniaturas podem ser geradas para arquivos STL. Modelos grandes podem levar mais tempo para processar.',
+ generateThumbnailsForStl: 'Gerar miniaturas para arquivos STL',
+ threemfDetected: 'Arquivos 3MF detectados',
+ threemfExtractionInfo: 'Modelo da impressora, material, cor e configurações de impressão serão extraídos automaticamente dos arquivos 3MF.',
+ willBeExtracted: 'Será extraído',
+ filesExtracted: '{{count}} arquivos extraídos',
+ uploadComplete: 'Upload concluído: {{succeeded}} bem-sucedidos',
+ uploadFailed: '{{count}} falhou',
+ uploading: 'Enviando...',
+ changeLink: 'Alterar link...',
+ linkTo: 'Vincular a...',
+ linkToProjectOrArchive: 'Vincular a projeto ou arquivo',
+ addToQueue: 'Adicionar à fila',
+ schedulePrint: 'Agendar impressão',
+ generateThumbnail: 'Gerar miniatura',
+ generateThumbnails: 'Gerar miniaturas',
+ generateThumbnailsForMissing: 'Gerar miniaturas para arquivos STL que não possuem',
+ gridView: 'Visualização em grade',
+ listView: 'Visualização em lista',
+ lowDiskSpaceWarning: 'Aviso de pouco espaço em disco',
+ lowDiskSpaceDetails: 'Apenas {{free}} livres de {{total}} no total. O limite está definido para {{threshold}} GB nas configurações.',
+ files: 'Arquivos',
+ folders: 'Pastas',
+ size: 'Tamanho',
+ free: 'Livre',
+ allFiles: 'Todos os arquivos',
+ wrap: 'Quebrar texto',
+ enableTextWrapping: 'Ativar quebra de texto',
+ disableTextWrapping: 'Desativar quebra de texto',
+ dragToResizeTooltip: 'Arraste para redimensionar, clique duas vezes para redefinir',
+ searchFiles: 'Pesquisar arquivos...',
+ allTypes: 'Todos os tipos',
+ prints: 'Impressões',
+ ascending: 'Crescente',
+ descending: 'Decrescente',
+ resultsCount: '{{showing}} de {{total}} arquivos',
+ selectAll: 'Selecionar tudo',
+ deselectAll: 'Desmarcar tudo',
+ selected: '{{count}} selecionado(s)',
+ adding: 'Adicionando...',
+ loadingFiles: 'Carregando arquivos...',
+ folderIsEmpty: 'A pasta está vazia',
+ noFilesYet: 'Nenhum arquivo ainda',
+ folderEmptyDescription: 'Envie arquivos ou mova arquivos para esta pasta para começar.',
+ noFilesDescription: 'Envie arquivos para começar a organizar seus arquivos relacionados à impressão.',
+ noMatchingFiles: 'Nenhum arquivo correspondente',
+ noMatchingFilesDescription: 'Nenhum arquivo corresponde aos seus critérios de pesquisa ou filtro.',
+ clearFilters: 'Limpar filtros',
+ printedCount: 'Impresso {{count}}x',
+ uploadedBy: 'Enviado por',
+ deleteFolder: 'Excluir pasta',
+ deleteFile: 'Excluir arquivo',
+ deleteFilesCount: 'Excluir {{count}} arquivos',
+ deleteFolderConfirm: 'Tem certeza de que deseja excluir esta pasta? Todos os arquivos dentro também serão excluídos.',
+ deleteFileConfirm: 'Tem certeza de que deseja excluir este arquivo?',
+ deleteFilesConfirm: 'Tem certeza de que deseja excluir {{count}} arquivos selecionados? Esta ação não pode ser desfeita.',
+ deleting: 'Excluindo...',
+ noPermissionRenameFolder: 'Você não tem permissão para renomear pastas',
+ noPermissionLinkFolder: 'Você não tem permissão para vincular pastas',
+ noPermissionDeleteFolder: 'Você não tem permissão para excluir pastas',
+ noPermissionPrint: 'Você não tem permissão para imprimir',
+ noPermissionAddToQueue: 'Você não tem permissão para adicionar à fila',
+ noPermissionDownload: 'Você não tem permissão para baixar arquivos',
+ noPermissionRenameFile: 'Você não tem permissão para renomear este arquivo',
+ noPermissionGenerateThumbnail: 'Você não tem permissão para gerar miniaturas',
+ noPermissionDeleteFile: 'Você não tem permissão para excluir este arquivo',
+ noPermissionCreateFolder: 'Você não tem permissão para criar pastas',
+ noPermissionUpload: 'Você não tem permissão para enviar arquivos',
+ noPermissionMoveFiles: 'Você não tem permissão para mover arquivos',
+ noPermissionDeleteFiles: 'Você não tem permissão para excluir arquivos',
+ toast: {
+ folderCreated: 'Pasta criada',
+ folderDeleted: 'Pasta excluída',
+ fileDeleted: 'Arquivo excluído',
+ filesDeleted: 'Excluídos {{count}} arquivos',
+ filesMoved: 'Arquivos movidos',
+ folderLinked: 'Pasta vinculada',
+ folderUnlinked: 'Pasta desvinculada',
+ addedToQueue: 'Adicionado {{count}} arquivo(s) à fila',
+ addedToQueuePartial: 'Adicionado {{added}} arquivo(s), {{failed}} falharam',
+ failedToAddToQueue: 'Falha ao adicionar arquivos: {{error}}',
+ fileRenamed: 'Arquivo renomeado',
+ folderRenamed: 'Pasta renomeada',
+ thumbnailsGenerated: 'Geradas {{count}} miniatura(s)',
+ thumbnailsGeneratedPartial: 'Geradas {{succeeded}} miniatura(s), {{failed}} falharam',
+ noStlMissingThumbnails: 'Nenhum arquivo STL sem miniatura',
+ failedToGenerateThumbnails: 'Falha ao gerar miniaturas: {{error}}',
+ thumbnailGenerated: 'Miniatura gerada',
+ failedToGenerateThumbnail: 'Falha ao gerar miniatura: {{error}}',
+ },
+ },
+
+ // Projects
+ projects: {
+ title: 'Projetos',
+ subtitle: 'Organize e acompanhe seus projetos de impressão 3D',
+ newProject: 'Novo Projeto',
+ editProject: 'Editar Projeto',
+ deleteProject: 'Excluir Projeto',
+ projectName: 'Nome do Projeto',
+ description: 'Descrição',
+ noProjects: 'Nenhum projeto ainda',
+ noProjectsFiltered: 'Nenhum projeto {{status}}',
+ noProjectsFilteredHelp: "Você não tem nenhum projeto {{status}}. Os projetos aparecerão aqui quando seu status mudar.",
+ createFirst: 'Crie seu primeiro projeto para começar a organizar impressões relacionadas, acompanhar o progresso e gerenciar suas construções.',
+ createFirstButton: 'Crie Seu Primeiro Projeto',
+ create: 'Criar',
+ files: 'Arquivos',
+ prints: 'Impressões',
+ plates: 'Placas',
+ parts: 'Peças',
+ lastModified: 'Última Modificação',
+ deleteConfirm: 'Tem certeza de que deseja excluir este projeto? Arquivos e itens da fila serão desvinculados, mas não excluídos.',
+ addFiles: 'Adicionar Arquivos',
+ removeFile: 'Remover Arquivo',
+ viewDetails: 'Ver Detalhes',
+ // Modal fields
+ namePlaceholder: 'ex., Voron 2.4 Build',
+ descriptionPlaceholder: 'Descrição opcional...',
+ color: 'Cor',
+ targetPlates: 'Placas Alvo',
+ targetPlatesPlaceholder: 'ex., 25',
+ targetPlatesHelp: 'Número de trabalhos de impressão',
+ targetParts: 'Peças Alvo',
+ targetPartsPlaceholder: 'ex., 150',
+ targetPartsHelp: 'Total de objetos necessários',
+ tagsLabel: 'Tags (separadas por vírgula)',
+ tagsPlaceholder: 'ex., voron, funcional, presente',
+ dueDate: 'Data de Vencimento',
+ priority: 'Prioridade',
+ priorityLow: 'Baixa',
+ priorityNormal: 'Normal',
+ priorityHigh: 'Alta',
+ priorityUrgent: 'Urgente',
+ // Status
+ statusActive: 'Ativo',
+ statusCompleted: 'Concluído',
+ statusArchived: 'Arquivado',
+ done: 'Concluído',
+ completed: 'Concluído',
+ failed: 'Falhou',
+ inQueue: 'Na fila',
+ noPrintsYet: 'Nenhuma impressão ainda',
+ // Footer stats
+ printJobs: 'Trabalhos de impressão (placas)',
+ partsPrinted: 'Peças impressas',
+ failedParts: 'Peças falhadas',
+ // Actions
+ import: 'Importar',
+ export: 'Exportar',
+ importProject: 'Importar projeto',
+ exportAll: 'Exportar todos os projetos',
+ loading: 'Carregando projetos...',
+ // Permissions
+ noEditPermission: 'Você não tem permissão para editar projetos',
+ noDeletePermission: 'Você não tem permissão para excluir projetos',
+ noCreatePermission: 'Você não tem permissão para criar projetos',
+ noImportPermission: 'Você não tem permissão para importar projetos',
+ noExportPermission: 'Você não tem permissão para exportar projetos',
+ // Toast
+ toast: {
+ created: 'Projeto criado',
+ updated: 'Projeto atualizado',
+ deleted: 'Projeto excluído',
+ imported: 'Projeto importado',
+ multipleImported: '{{count}} projetos importados',
+ importFailed: 'Falha na importação',
+ exported: 'Projetos exportados (apenas metadados)',
+ },
+ },
+
+ // Project detail page
+ projectDetail: {
+ notFound: 'Projeto não encontrado',
+ backToProjects: 'Voltar para Projetos',
+ export: 'Exportar',
+ exportProject: 'Exportar projeto',
+ noExportPermission: 'Você não tem permissão para exportar projetos',
+ noEditPermission: 'Você não tem permissão para editar projetos',
+ partOf: 'Parte de:',
+ priorityLabel: 'Prioridade:',
+ noPrints: 'Nenhuma impressão neste projeto ainda',
+ status: {
+ active: 'Ativo',
+ completed: 'Concluído',
+ archived: 'Arquivado',
+ },
+ priority: {
+ low: 'Baixa',
+ normal: 'Normal',
+ high: 'Alta',
+ urgent: 'Urgente',
+ },
+ dueDate: {
+ overdue: 'Atrasado',
+ today: 'Vence hoje',
+ daysLeft: '{{count}} dias restantes',
+ },
+ progress: {
+ platesProgress: 'Progresso das Placas',
+ partsProgress: 'Progresso das Peças',
+ printJobs: 'Trabalhos de Impressão',
+ parts: 'Peças',
+ percentComplete: '{{percent}}% concluído',
+ remaining: '{{count}} restantes',
+ },
+ stats: {
+ printJobs: 'Trabalhos de Impressão',
+ total: 'total',
+ failed: '{{count}} falhou',
+ partsPrinted: '{{count}} peças impressas',
+ printTime: 'Tempo de Impressão',
+ filamentUsed: 'Filamento Usado',
+ },
+ cost: {
+ title: 'Rastreamento de Custos',
+ filamentCost: 'Custo do Filamento',
+ energy: 'Energia',
+ budget: 'Orçamento',
+ remaining: 'Restante',
+ },
+ subProjects: {
+ title: 'Sub-projetos ({{count}})',
+ },
+ notes: {
+ title: 'Notas',
+ noEditPermission: 'Você não tem permissão para editar notas',
+ placeholder: 'Adicione notas sobre este projeto...',
+ empty: 'Nenhuma nota ainda. Clique em Editar para adicionar notas.',
+ },
+ files: {
+ title: 'Arquivos',
+ linkFolders: 'Vincular pastas do Gerenciador de Arquivos',
+ forQuickAccess: 'a este projeto para acesso rápido.',
+ fileCount: '{{count}} arquivo(s)',
+ empty: 'Nenhuma pasta vinculada. Vá para o Gerenciador de Arquivos e vincule uma pasta a este projeto.',
+ },
+ bom: {
+ title: 'Lista de Materiais',
+ acquired: '{{completed}}/{{total}} adquiridos',
+ showAll: 'Mostrar todos',
+ hideDone: 'Ocultar concluídos',
+ addPart: 'Adicionar Peça',
+ noAddPermission: 'Você não tem permissão para adicionar peças',
+ partNamePlaceholder: 'Nome da peça (ex.: parafusos M3x8)',
+ partName: 'Nome da peça',
+ qty: 'Quantidade',
+ price: 'Preço ({{currency}})',
+ sourcingUrlPlaceholder: 'URL de fornecimento (opcional)',
+ remarksPlaceholder: 'Observações (opcional)',
+ deletePart: 'Excluir Peça',
+ deleteConfirm: 'Tem certeza de que deseja excluir "{{name}}"?',
+ noUpdatePermission: 'Você não tem permissão para atualizar peças',
+ noEditPermission: 'Você não tem permissão para editar peças',
+ noDeletePermission: 'Você não tem permissão para excluir peças',
+ totalCost: 'Custo total:',
+ empty: 'Nenhuma peça na lista de materiais. Adicione hardware, eletrônicos ou outros componentes para rastrear o que precisa ser adquirido.',
+ },
+ timeline: {
+ title: 'Linha do Tempo de Atividades',
+ empty: 'Nenhuma atividade ainda.',
+ },
+ template: {
+ saveAsTemplate: 'Salvar como Modelo',
+ noCreatePermission: 'Você não tem permissão para criar modelos',
+ },
+ queue: {
+ title: 'Fila',
+ viewAll: 'Ver todos',
+ printing: '{{count}} imprimindo',
+ queued: '{{count}} na fila',
+ },
+ prints: {
+ title: 'Impressões ({{count}})',
+ },
+ toast: {
+ projectUpdated: 'Projeto atualizado',
+ partAdded: 'Peça adicionada',
+ partRemoved: 'Peça removida',
+ exportFailed: 'Falha na exportação',
+ projectExported: 'Projeto exportado',
+ templateCreated: 'Modelo criado',
+ },
+ },
+
+ // System info
+ system: {
+ title: 'Informações do Sistema',
+ version: 'Versão',
+ uptime: 'Tempo de Atividade',
+ cpuUsage: 'Uso da CPU',
+ memoryUsage: 'Uso da Memória',
+ diskUsage: 'Uso do Disco',
+ networkInfo: 'Informações de Rede',
+ logs: 'Logs',
+ debugMode: 'Modo de Depuração',
+ enableDebug: 'Ativar Registro de Depuração',
+ disableDebug: 'Desativar Registro de Depuração',
+ downloadLogs: 'Baixar Logs',
+ clearLogs: 'Limpar Logs',
+ dockerInfo: 'Informações do Docker',
+ containerName: 'Nome do Contêiner',
+ imageName: 'Nome da Imagem',
+ platform: 'Plataforma',
+ architecture: 'Arquitetura',
+ },
+
+ // Library (K Profiles)
+ library: {
+ title: 'Biblioteca de Filamentos',
+ addFilament: 'Adicionar Filamento',
+ editFilament: 'Editar Filamento',
+ deleteFilament: 'Excluir Filamento',
+ vendor: 'Fornecedor',
+ material: 'Material',
+ color: 'Cor',
+ kFactor: 'Fator K',
+ temperature: 'Temperatura',
+ noFilaments: 'Nenhum filamento na biblioteca',
+ deleteConfirm: 'Tem certeza de que deseja excluir este filamento?',
+ importFromPrinter: 'Importar da Impressora',
+ exportToFile: 'Exportar para Arquivo',
+ },
+
+ // Spoolman
+ spoolman: {
+ title: 'Integração com Spoolman',
+ enabled: 'Spoolman Ativado',
+ url: 'URL do Spoolman',
+ connected: 'Conectado',
+ disconnected: 'Não Conectado',
+ testConnection: 'Testar Conexão',
+ sync: 'Sincronizar',
+ syncing: 'Sincronizando...',
+ lastSync: 'Última Sincronização',
+ linkToSpoolman: 'Vincular ao Spoolman',
+ openInSpoolman: 'Abrir no Spoolman',
+ unlinkSpool: 'Desvincular Carretel',
+ selectSpool: 'Selecionar Carretel',
+ noUnlinkedSpools: 'Nenhum carretel desvinculado disponível',
+ linkSuccess: 'Carretel vinculado ao Spoolman com sucesso',
+ linkFailed: 'Falha ao vincular carretel',
+ spoolId: 'Carretel ID (Spool ID)',
+ fillSourceLabel: '(Spoolman)',
+ weight: 'Peso',
+ remaining: 'Restante',
+ disableWeightSync: 'Desativar Sincronização de Peso Estimado do AMS',
+ disableWeightSyncDesc: "Não atualize a capacidade restante a partir das estimativas do AMS. Use isso se preferir o rastreamento de uso do Spoolman em vez das estimativas baseadas em porcentagem do AMS. Novos carretéis ainda usarão a estimativa do AMS como seu peso inicial.",
+ reportPartialUsage: 'Relatar Uso Parcial para Impressões Falhadas',
+ reportPartialUsageDesc: 'Quando uma impressão falha ou é cancelada, relate o filamento estimado usado até aquele ponto com base no progresso das camadas.',
+ },
+
+ // Inventory
+ inventory: {
+ title: 'Inventário de Carretéis',
+ addSpool: 'Adicionar Carretel',
+ editSpool: 'Editar Carretel',
+ material: 'Material',
+ selectMaterial: 'Selecionar material...',
+ subtype: 'Subtipo',
+ brand: 'Marca',
+ searchBrand: 'Pesquisar marca...',
+ useCustomBrand: 'Usar "{{brand}}"',
+ colorName: 'Nome da Cor',
+ colorNamePlaceholder: 'Jade White, Fire Red...',
+ color: 'Cor',
+ hexColor: 'Cor Hexadecimal',
+ pickColor: 'Escolher cor personalizada',
+ labelWeight: 'Peso da Etiqueta',
+ coreWeight: 'Peso do Carretel Vazio',
+ searchSpoolWeight: 'Pesquisar peso do carretel...',
+ weightUsed: 'Usado',
+ currentWeight: 'Peso Restante',
+ measuredWeight: 'Peso Medido',
+ measuredWeightError: 'O peso medido deve estar entre {{min}}g e {{max}}g.',
+ slicerFilament: 'Filamento do Fatiador',
+ slicerFilamentName: 'Nome do Predefinido do Fatiador',
+ slicerPreset: 'Predefinido do Fatiador',
+ searchPresets: 'Pesquisar predefinições de filamento...',
+ selectedPreset: 'Selecionado',
+ noPresetsFound: 'Nenhuma predefinição encontrada',
+ tempOverrides: 'Substituições de Temperatura',
+ note: 'Nota',
+ notePlaceholder: 'Quaisquer notas adicionais sobre este spool...',
+ archive: 'Arquivar',
+ restore: 'Restaurar',
+ noSpools: 'Nenhum carretel ainda. Adicione seu primeiro carretel para começar.',
+ noManualSpools: 'Nenhum carretel adicionado manualmente disponível. Adicione um carretel ao seu inventário primeiro.',
+ kProfiles: 'K-Perfis',
+ addKProfile: 'Adicionar K-Perfil',
+ assignSpool: 'Atribuir Carretel',
+ unassignSpool: 'Desatribuir',
+ assignSuccess: 'Carretel atribuído e slot AMS configurado',
+ assignFailed: 'Falha ao atribuir carretel',
+ selectSpool: 'Selecione um carretel para atribuir a este slot',
+ assigned: 'Atribuído',
+ assigning: 'Atribuindo...',
+ searchSpools: 'Pesquisar carretéis...',
+ allMaterials: 'Todos os Materiais',
+ filterByBrand: 'Filtrar por marca...',
+ showArchived: 'Mostrar arquivados',
+ spoolCreated: 'Carretel criado',
+ spoolUpdated: 'Carretel atualizado',
+ spoolDeleted: 'Carretel excluído',
+ spoolArchived: 'Carretel arquivado',
+ spoolRestored: 'Carretel restaurado',
+ deleteConfirm: 'Tem certeza de que deseja excluir este carretel? Esta ação não pode ser desfeita.',
+ archiveConfirm: 'Tem certeza de que deseja arquivar este carretel?',
+ advancedSettings: 'Configurações Avançadas',
+ // Tabs
+ filamentInfoTab: 'Informações do Filamento',
+ paProfileTab: 'Perfil PA',
+ filamentInfo: 'Filamento',
+ additional: 'Adicional',
+ // Cloud
+ loadingPresets: 'Carregando predefinições da nuvem...',
+ cloudConnected: 'Nuvem conectada',
+ cloudNotConnected: 'Nuvem não conectada (usando padrões)',
+ // Colors
+ recentColors: 'Recentes',
+ searchColors: 'Pesquisar cores...',
+ searchResults: 'Resultados da pesquisa',
+ allColors: 'Todas as cores',
+ commonColors: 'Cores comuns',
+ showLess: 'Mostrar menos',
+ showAll: 'Mostrar tudo',
+ noColorsFound: 'Nenhuma cor corresponde à sua pesquisa',
+ noResults: 'Nenhum resultado encontrado',
+ // PA Profiles
+ selectMaterialFirst: 'Por favor, selecione um material primeiro na aba Informações do Filamento.',
+ noPrintersConfigured: 'Nenhuma impressora configurada. Adicione impressoras para usar perfis PA.',
+ matchingFilter: 'Correspondente',
+ anyBrand: 'Qualquer marca',
+ anyVariant: 'Qualquer variante',
+ autoSelect: 'Seleção automática',
+ matches: 'correspondências',
+ match: 'correspondência',
+ noMatches: 'Nenhuma correspondência',
+ connected: 'Conectado',
+ offline: 'Offline',
+ printerOffline: 'A impressora está offline. Conecte-se para visualizar os perfis de calibração.',
+ noKProfilesMatch: 'Nenhum K-perfil corresponde ao filamento selecionado.',
+ leftNozzle: 'Bico Esquerdo',
+ rightNozzle: 'Bico Direito',
+ profilesSelected: 'perfil(is) de calibração selecionado(s)',
+ // Stats & enhanced table
+ totalInventory: 'Inventário Total',
+ totalConsumed: 'Total Consumido',
+ byMaterial: 'Por Material',
+ inPrinter: 'Na Impressora',
+ lowStock: 'Estoque Baixo',
+ sinceTracking: 'Desde o início do rastreamento',
+ loadedInAms: 'Carregado no AMS/Ext',
+ remaining: 'Restante',
+ lowStockThreshold: '<20% restante',
+ search: 'Pesquisar carretéis...',
+ showing: 'Mostrando',
+ to: 'até',
+ of: 'de',
+ show: 'Mostrar',
+ spools: 'carretéis',
+ spool: 'carretel',
+ page: 'Página',
+ noSpoolsMatch: 'Nenhum resultado encontrado',
+ noSpoolsMatchDesc: 'Tente ajustar sua pesquisa ou filtros para encontrar o que você está procurando.',
+ active: 'Ativo',
+ archived: 'Arquivado',
+ all: 'Todos',
+ used: 'Usado',
+ new: 'Novo',
+ clearFilters: 'Limpar filtros',
+ table: 'Tabela',
+ cards: 'Cartões',
+ net: 'Líquido',
+ // Column config
+ columns: 'Colunas',
+ configureColumns: 'Configurar Colunas',
+ configureColumnsDesc: 'Arraste para reordenar as colunas ou use as setas. Alterne a visibilidade com o ícone de olho.',
+ visible: 'Visível',
+ reset: 'Redefinir',
+ cancel: 'Cancelar',
+ applyChanges: 'Aplicar Alterações',
+ moveUp: 'Mover para cima',
+ moveDown: 'Mover para baixo',
+ hideColumn: 'Ocultar coluna',
+ showColumn: 'Mostrar coluna',
+ // Tag linking
+ linkToSpool: 'Vincular ao Carretel',
+ tagLinked: 'Tag vinculada ao carretel',
+ tagLinkFailed: 'Falha ao vincular tag',
+ tagAlreadyLinked: 'Tag já vinculada a outro carretel',
+ unknownTag: 'Tag RFID desconhecida detectada',
+ // Usage history
+ usageHistory: 'Histórico de Uso',
+ noUsageHistory: 'Nenhum uso registrado ainda',
+ printName: 'Nome da Impressão',
+ weightConsumed: 'Peso Consumido',
+ clearHistory: 'Limpar',
+ historyCleared: 'Histórico de uso limpo',
+ fillSourceLabel: '(Inv)',
+ },
+
+ // Timelapse
+ timelapse: {
+ title: 'Timelapse',
+ create: 'Criar Timelapse',
+ download: 'Baixar',
+ delete: 'Excluir',
+ preview: 'Visualizar',
+ frameRate: 'Taxa de Quadros',
+ quality: 'Qualidade',
+ processing: 'Processando...',
+ noTimelapses: 'Nenhum timelapse disponível',
+ },
+
+ // AMS
+ ams: {
+ title: 'AMS',
+ slot: 'Slot',
+ empty: 'Vazio',
+ emptySlot: 'Slot vazio',
+ unknown: 'Desconhecido',
+ humidity: 'Umidade',
+ temperature: 'Temperatura',
+ filamentType: 'Tipo de Filamento',
+ filamentColor: 'Cor',
+ remaining: 'Restante',
+ history: 'Histórico do AMS',
+ noHistory: 'Nenhum histórico disponível',
+ configureSlot: 'Configurar Slot',
+ externalSpool: 'Carretel Externo',
+ profile: 'Perfil',
+ kFactor: 'Fator K',
+ fill: 'Preencher',
+ configure: 'Configurar',
+ },
+
+ // Print modal
+ printModal: {
+ title: 'Iniciar Impressão',
+ selectPrinter: 'Selecionar Impressora',
+ selectPlate: 'Selecionar Placa',
+ filamentMapping: 'Mapeamento de Filamento',
+ printSettings: 'Configurações de Impressão',
+ bedLeveling: 'Nivelamento da Mesa',
+ flowCalibration: 'Calibração de Fluxo',
+ vibrationCalibration: 'Calibração de Vibração',
+ layerInspection: 'Inspeção da Primeira Camada',
+ timelapse: 'Timelapse',
+ startPrint: 'Iniciar Impressão',
+ addToQueue: 'Adicionar à Fila',
+ cancel: 'Cancelar',
+ noPrintersAvailable: 'Nenhuma impressora disponível',
+ printerBusy: 'Impressora ocupada',
+ printerOffline: 'Impressora offline',
+ sameTypeDifferentColor: 'Mesmo tipo, cor diferente',
+ filamentTypeNotLoaded: 'Tipo de filamento não carregado',
+ openCalendar: 'Abrir calendário',
+ leftNozzle: 'L',
+ rightNozzle: 'R',
+ leftNozzleTooltip: 'Bico esquerdo',
+ rightNozzleTooltip: 'Bico direito',
+ },
+
+ // Backup
+ backup: {
+ title: 'Bakup e Restauração',
+ createBackup: 'Criar Backup',
+ restoreBackup: 'Restaurar Backup',
+ restoreDescription: 'Substituir todos os dados a partir de um arquivo de backup',
+ downloadBackup: 'Baixar Backup',
+ uploadBackup: 'Enviar Backup',
+ lastBackup: 'Último Backup',
+ autoBackup: 'Auto Backup',
+ backupNow: 'Fazer Backup Agora',
+ restoreWarning: 'Aviso: Restaurar um backup substituirá todos os dados atuais.',
+ includeArchives: 'Incluir Arquivos',
+ includeSettings: 'Incluir Configurações',
+ includeProfiles: 'Incluir Perfis',
+ backupSuccess: 'Backup criado com sucesso',
+ restoreSuccess: 'Backup restaurado com sucesso',
+ backupFailed: 'Falha ao criar backup',
+ restoreFailed: 'Falha ao restaurar backup',
+ restoreNote: 'A impressora virtual será parada durante a restauração',
+ },
+
+ // Tags
+ tags: {
+ title: 'Tags',
+ addTag: 'Adicionar Tag',
+ editTag: 'Editar Tag',
+ deleteTag: 'Excluir Tag',
+ tagName: 'Nome da Tag',
+ tagColor: 'Cor da Tag',
+ noTags: 'Nenhuma tag',
+ deleteConfirm: 'Tem certeza de que deseja excluir esta tag?',
+ manageTags: 'Gerenciar Tags',
+ },
+
+ // Upload modal (archives)
+ uploadModal: {
+ title: 'Upload Arquivos 3MF',
+ dragDrop: 'Arraste e solte arquivos .3mf aqui',
+ or: 'ou',
+ browseFiles: 'Procurar Arquivos',
+ extractionInfo: 'O modelo da impressora será extraído automaticamente dos metadados do arquivo 3MF.',
+ uploaded: 'enviado',
+ failed: 'falhou',
+ uploading: 'Enviando...',
+ upload: 'Enviar',
+ uploadFailed: 'Falha no envio',
+ },
+
+ // Edit archive modal
+ // Edit Archive Modal
+ editArchive: {
+ title: 'Editar Arquivo',
+ name: 'Nome',
+ namePlaceholder: 'Nome da impressão',
+ printer: 'Impressora',
+ noPrinter: 'Nenhuma impressora',
+ project: 'Projeto',
+ noProject: 'Nenhum projeto',
+ itemsPrinted: 'Itens Impressos',
+ itemsPrintedHelp: 'Número de itens produzidos neste trabalho de impressão',
+ notes: 'Notas',
+ notesPlaceholder: 'Adicione notas sobre esta impressão...',
+ externalLink: 'Link Externo',
+ externalLinkPlaceholder: 'https://printables.com/model/...',
+ externalLinkHelp: 'Link para Printables, Thingiverse ou outra fonte',
+ tags: 'Tags',
+ tagsPlaceholder: 'Adicionar tags...',
+ addMoreTags: 'Adicionar mais tags...',
+ matchingTags: 'Correspondendo "{{query}}"',
+ existingTags: 'Tags existentes',
+ clickToAdd: '(clique para adicionar)',
+ status: 'Status',
+ failureReason: 'Motivo da Falha',
+ selectReason: 'Selecione o motivo...',
+ photos: 'Fotos do Resultado da Impressão',
+ photosHelp: 'Clique em + para adicionar fotos do seu resultado impresso',
+ printResult: 'Resultado da Impressão',
+ saving: 'Salvando...',
+ // Failure reasons
+ failureReasons: {
+ adhesionFailure: 'Falha de adesão',
+ spaghettiDetached: 'Spaghetti / Destacado',
+ layerShift: 'Deslocamento de camada',
+ cloggedNozzle: 'Bico entupido',
+ filamentRunout: 'Fim do filamento',
+ warping: 'Warping',
+ stringing: 'Stringing',
+ underExtrusion: 'Under-extrusion',
+ powerFailure: 'Falha de energia',
+ userCancelled: 'Cancelado pelo usuário',
+ other: 'Outro',
+ },
+ // Archive statuses
+ statuses: {
+ completed: 'Concluído',
+ failed: 'Falhou',
+ aborted: 'Cancelado',
+ printing: 'Imprimindo',
+ },
+ },
+
+ // K-Profiles
+ kProfiles: {
+ title: 'K-Profiles',
+ noPrintersConfigured: 'Nenhuma impressora configurada',
+ addPrinterInSettings: 'Adicione uma impressora nas Configurações para gerenciar K-profiles',
+ noActivePrinters: 'Nenhuma impressora ativa',
+ enablePrinterConnection: 'Ative a conexão da impressora para visualizar seus K-profiles',
+ loadingProfiles: 'Carregando K-Profiles...',
+ printerOffline: 'Impressora Offline',
+ printerOfflineDesc: 'A impressora selecionada não está conectada. Ligue-a para visualizar os K-profiles.',
+ noMatchingProfiles: 'Nenhum Perfil Correspondente',
+ noMatchingProfilesDesc: 'Nenhum perfil corresponde aos seus critérios de pesquisa',
+ noKProfiles: 'Nenhum K-Profile',
+ noKProfilesDesc: 'Nenhum perfil de avanço de pressão encontrado para bico de {{diameter}}mm',
+ createFirstProfile: 'Criar Primeiro Perfil',
+ // Controls
+ printer: 'Impressora',
+ nozzle: 'Bico',
+ refresh: 'Atualizar',
+ addProfile: 'Adicionar Perfil',
+ export: 'Exportar',
+ import: 'Importar',
+ select: 'Selecionar',
+ selectAll: 'Selecionar Todos',
+ delete: 'Excluir',
+ // Filters
+ searchPlaceholder: 'Pesquisar por nome ou filamento...',
+ allExtruders: 'Todos os Extrusores',
+ leftOnly: 'Apenas Esquerdo',
+ rightOnly: 'Apenas Direito',
+ allFlow: 'Todo Fluxo',
+ hfOnly: 'Apenas HF',
+ sOnly: 'Apenas S',
+ sortName: 'Ordenar: Nome',
+ sortKValue: 'Ordenar: Valor K',
+ sortFilament: 'Ordenar: Filamento',
+ // Dual extruder labels
+ leftExtruder: 'Extrusor Esquerdo',
+ rightExtruder: 'Extrusor Direito',
+ // Modal
+ modal: {
+ addTitle: 'Adicionar K-Profile',
+ editTitle: 'Editar K-Profile',
+ profileName: 'Nome do Perfil',
+ profileNamePlaceholder: 'Meu Perfil PLA',
+ kValue: 'Valor K',
+ kValuePlaceholder: '0.020',
+ kValueHelp: 'Faixa típica: 0.01 - 0.06 para PLA, 0.02 - 0.10 para PETG',
+ filament: 'Filamento',
+ selectFilament: 'Selecionar filamento...',
+ noFilamentsHelp: 'Nenhum filamento encontrado. Crie um K-profile no Bambu Studio primeiro.',
+ flowType: 'Tipo de Fluxo',
+ highFlow: 'Alto Fluxo',
+ standard: 'Padrão',
+ nozzleSize: 'Tamanho do Bico',
+ extruder: 'Extrusor',
+ extruders: 'Extrusores',
+ left: 'Esquerdo',
+ right: 'Direito',
+ notes: 'Notas (armazenadas localmente)',
+ notesPlaceholder: 'Adicione notas sobre este perfil...',
+ notesHelp: 'As notas são salvas no Bambuddy, não na impressora',
+ syncing: 'Sincronizando com a impressora...',
+ savingExtruder: 'Salvando no extrusor {{current}}/{{total}}...',
+ pleaseWait: 'Por favor, aguarde',
+ },
+ // Delete confirmation
+ deleteConfirm: {
+ title: 'Excluir Perfil',
+ cannotUndo: 'Isso não pode ser desfeito',
+ message: 'Tem certeza de que deseja excluir "{{name}}" da impressora?',
+ },
+ // Bulk delete
+ bulkDelete: {
+ title: 'Excluir Perfis',
+ cannotUndo: 'Isso não pode ser desfeito',
+ message: 'Tem certeza de que deseja excluir {{count}} perfis selecionados da impressora?',
+ },
+ // Toast
+ toast: {
+ profileSaved: 'K-profile salvo',
+ profilesSaved: 'K-profile salvo em {{count}} extrusores',
+ selectAtLeastOneExtruder: 'Por favor, selecione pelo menos um extrusor',
+ profileDeleted: 'K-profile excluído',
+ profilesDeleted: '{{count}} perfis excluídos',
+ exportedProfiles: '{{count}} perfis exportados',
+ importedProfiles: '{{count}} de {{total}} perfis importados',
+ noProfilesToExport: 'Nenhum perfil para exportar',
+ invalidFileFormat: 'Formato de arquivo inválido',
+ failedToParseImport: 'Falha ao analisar o arquivo de importação',
+ failedToSaveBatch: 'Falha ao salvar K-profiles',
+ noteSaved: 'Nota salva',
+ failedToSaveNote: 'Falha ao salvar nota',
+ },
+ // Permissions
+ permission: {
+ noRead: 'Você não tem permissão para atualizar perfis',
+ noCreate: 'Você não tem permissão para adicionar perfis',
+ noUpdate: 'Você não tem permissão para atualizar K-profiles',
+ noDelete: 'Você não tem permissão para excluir K-profiles',
+ noExport: 'Você não tem permissão para exportar perfis',
+ noImport: 'Você não tem permissão para importar perfis',
+ },
+ },
+
+ // Virtual Printer
+ virtualPrinter: {
+ title: 'Impressora Virtual',
+ running: 'Em execução',
+ stopped: 'Parada',
+ description: {
+ default: 'Ative uma impressora virtual que aparece no Bambu Studio e no OrcaSlicer. Os arquivos enviados para esta impressora serão arquivados diretamente sem impressão.',
+ proxy: 'Ative um proxy que retransmite o tráfego do slicer para uma impressora real, permitindo impressão remota em qualquer rede.',
+ },
+ enable: {
+ title: 'Ativar Impressora Virtual',
+ visibleInSlicer: 'Visível como "Bambuddy" na descoberta do slicer',
+ proxyingTo: 'Proxy para {{name}}',
+ notActive: 'Não ativo',
+ },
+ model: {
+ title: 'Modelo da Impressora',
+ description: 'Selecione qual modelo de impressora emular.',
+ restartWarning: 'Alterar o modelo reiniciará a impressora virtual',
+ },
+ accessCode: {
+ title: 'Código de acesso',
+ isSet: 'O código de acesso está definido',
+ notSet: 'Nenhum código de acesso definido — necessário para ativar.',
+ placeholder: 'Digite um código de 8 caracteres',
+ placeholderChange: 'Digite um novo código para alterar',
+ hint: 'Deve ter exatamente 8 caracteres. Usado pelos slicers para autenticação.',
+ charCount: '({{count}}/8)',
+ },
+ targetPrinter: {
+ title: 'Impressora Alvo',
+ configured: 'Proxy alvo configurado',
+ notConfigured: 'Nenhuma impressora alvo selecionada - necessário para o modo proxy',
+ placeholder: 'Selecione uma impressora...',
+ hint: 'Selecione a impressora para a qual o tráfego do slicer será enviado. A impressora deve estar no modo LAN.',
+ noPrinters: 'Nenhuma impressora configurada. Adicione uma impressora primeiro para usar o modo proxy.',
+ },
+ remoteInterface: {
+ title: 'Substituição da Interface de Rede',
+ configured: 'Substituição da interface ativa',
+ optional: 'Opcional - use se o IP detectado automaticamente estiver errado (por exemplo, várias NICs, Docker, VPN)',
+ placeholder: 'Detecção automática (padrão)...',
+ hint: 'Substitua o endereço IP anunciado via SSDP e usado no certificado TLS. Útil quando o Bambuddy possui várias interfaces de rede.',
+ },
+ mode: {
+ title: 'Modo',
+ archive: 'Arquivar',
+ archiveDesc: 'Arquivar arquivos imediatamente',
+ review: 'Revisar',
+ reviewDesc: 'Revisar antes de arquivar',
+ queue: 'Fila',
+ queueDesc: 'Arquivar e adicionar à fila',
+ proxy: 'Proxy',
+ proxyDesc: 'Retransmitir para impressora real',
+ },
+ setupRequired: {
+ title: 'Configuração Necessária',
+ description: 'O recurso de impressora virtual requer configuração adicional do sistema antes de funcionar. Isso inclui encaminhamento de portas, regras de firewall e configurações específicas da plataforma.',
+ readGuide: 'Leia o guia de configuração antes de ativar',
+ },
+ howItWorks: {
+ title: 'Como funciona',
+ titleProxy: 'Como funciona (Modo Proxy)',
+ step1: 'Complete o guia de configuração para sua plataforma',
+ step2: 'Ative a impressora virtual e defina um código de acesso',
+ step3: 'No Bambu Studio ou OrcaSlicer, vá para "Adicionar Impressora"',
+ step4: 'A impressora "Bambuddy" deve aparecer na lista de descoberta',
+ step5: 'Conecte-se usando o código de acesso que você definiu',
+ step6: 'Quando você "imprimir" para o Bambuddy, o arquivo 3MF será arquivado em vez disso',
+ proxyStep1: 'Selecione a impressora alvo (deve estar no modo LAN)',
+ proxyStep2: 'Para rede cruzada: selecione a interface de rede do slicer',
+ proxyStep3: 'Ative o proxy - a impressora aparece na descoberta do slicer via SSDP',
+ proxyStep4: 'Conecte-se usando o código de acesso da impressora',
+ proxyStep5: 'Imprima normalmente - o tráfego é retransmitido pelo Bambuddy',
+ proxyStep6: 'O streaming da câmera requer NAT/encaminhamento de IP (veja a documentação)',
+ },
+ status: {
+ title: 'Detalhes do Status',
+ printerName: 'Nome da Impressora',
+ model: 'Modelo',
+ serialNumber: 'Número de Série',
+ mode: 'Modo',
+ pendingFiles: 'Arquivos Pendentes',
+ targetPrinter: 'Impressora Alvo',
+ ftpPort: 'Porta FTP',
+ mqttPort: 'Porta MQTT',
+ ftpConnections: 'Conexões FTP',
+ mqttConnections: 'Conexões MQTT',
+ },
+ toast: {
+ updated: 'Configurações da impressora virtual atualizadas',
+ failedToUpdate: 'Falha ao atualizar as configurações',
+ accessCodeRequired: 'Defina um código de acesso primeiro',
+ targetPrinterRequired: 'Selecione uma impressora alvo primeiro',
+ accessCodeEmpty: 'O código de acesso não pode estar vazio',
+ accessCodeLength: 'O código de acesso deve ter exatamente 8 caracteres',
+ },
+ },
+
+ // Model Viewer
+ modelViewer: {
+ openInSlicer: 'Abrir no Slicer',
+ tabs: {
+ model: 'Modelo 3D',
+ gcode: 'Pré-visualização G-code',
+ },
+ notAvailable: 'Não disponível',
+ notSliced: 'Não fatiado',
+ plates: 'Placas',
+ allPlates: 'Todas as Placas',
+ plateNumber: 'Placa {{number}}',
+ plateCount: '{{count}} placa',
+ plateCount_other: '{{count}} placas',
+ objectCount: '{{count}} objeto',
+ objectCount_other: '{{count}} objetos',
+ filamentCount: '{{count}} filamento',
+ filamentCount_other: '{{count}} filamentos',
+ eta: 'ETA {{minutes}} min',
+ noPreview: 'Pré-visualização não disponível para este arquivo',
+ pagination: {
+ pageOf: 'Página {{current}} de {{total}}',
+ prev: 'Anterior',
+ next: 'Próximo',
+ },
+ errors: {
+ failedToLoad: 'Falha ao carregar o arquivo',
+ noMeshes: 'Nenhuma malha encontrada no arquivo 3MF',
+ unsupportedFormat: 'Formato de arquivo não suportado',
+ },
+ },
+
+ // Maintenance type descriptions (built-in)
+ maintenanceDescriptions: {
+ lubricateCarbonRods: 'Aplique lubrificante nos eixos de carbono para um movimento suave',
+ lubricateRails: 'Aplique lubrificante nos trilhos lineares para um movimento suave',
+ cleanNozzle: 'Limpe o hotend e o bico para evitar entupimentos',
+ checkBelts: 'Verifique a tensão das correias para impressões precisas',
+ cleanBuildPlate: 'Limpe a placa de construção para melhor adesão',
+ checkExtruder: 'Verifique as engrenagens do extrusor quanto ao desgaste',
+ checkCooling: 'Verifique se os ventiladores de resfriamento estão funcionando corretamente',
+ generalInspection: 'Inspeção geral da impressora',
+ cleanCarbonRods: 'Limpe os eixos de carbono para reduzir o atrito',
+ cleanLinearRails: 'Limpe os trilhos lineares para remover poeira e detritos',
+ checkPtfeTube: 'Verifique o tubo PTFE quanto ao desgaste ou danos',
+ replaceHepaFilter: 'Substitua o filtro HEPA para qualidade do ar',
+ replaceCarbonFilter: 'Substitua o filtro de carbono ativado',
+ lubricateLeftNozzleRail: 'Lubrifique o trilho do bico esquerdo (série H2)',
+ },
+
+ // Smart Plugs
+ smartPlugs: {
+ offline: 'Offline',
+ admin: 'Admin',
+ openPlugAdminPage: 'Abrir o painel de administração da tomada inteligente',
+ deleteSmartPlug: 'Excluir Tomada Inteligente',
+ turnOnSmartPlug: 'Ligar Tomada Inteligente',
+ turnOffSmartPlug: 'Desligar Tomada Inteligente',
+ turnOn: 'Ligar',
+ turnOff: 'Desligar',
+ addSmartPlug: {
+ scanningNetwork: 'Procurando na rede...',
+ chooseEntity: 'Escolha uma entidade...',
+ connectionFailed: 'Falha na conexão',
+ searchEntities: 'Pesquisar entidades...',
+ searchPowerSensors: 'Pesquisar sensores de energia...',
+ searchEnergySensors: 'Pesquisar sensores de energia...',
+ placeholders: {
+ plugName: 'Tomada da Sala',
+ mqttStateOnValue: 'ON, true, 1',
+ mqttSameAsPower: 'Mesmo que o tópico de energia, ou diferente',
+ },
+ },
+ },
+
+ // Rich Text Editor
+ richTextEditor: {
+ bold: 'Negrito',
+ italic: 'Itálico',
+ underline: 'Sublinhado',
+ bulletList: 'Lista com marcadores',
+ numberedList: 'Lista numerada',
+ alignLeft: 'Alinhar à esquerda',
+ alignCenter: 'Centralizar',
+ alignRight: 'Alinhar à direita',
+ addLink: 'Adicionar link',
+ removeLink: 'Remover link',
+ },
+
+ // External Links
+ externalLinks: {
+ noLinksConfigured: 'Nenhum link externo configurado',
+ deleteLink: 'Excluir link',
+ removeCustomIcon: 'Remover ícone personalizado',
+ openInNewTab: 'Abrir em nova aba',
+ placeholders: {
+ linkName: 'Meu Link',
+ },
+ },
+
+ // Keyboard Shortcuts Modal
+ keyboardShortcuts: {
+ title: 'Atalhos de Teclado',
+ navigation: 'Navegação',
+ archivesSection: 'Arquivos',
+ kProfilesSection: 'K-Profiles',
+ generalSection: 'Geral',
+ shortcuts: {
+ goToPrinters: 'Ir para Impressoras',
+ goToArchives: 'Ir para Arquivos',
+ goToQueue: 'Ir para Fila',
+ goToStats: 'Ir para Estatísticas',
+ goToProfiles: 'Ir para Perfis na Nuvem',
+ goToSettings: 'Ir para Configurações',
+ focusSearch: 'Focar na pesquisa',
+ openUploadModal: 'Abrir modal de upload',
+ clearSelection: 'Limpar seleção / desfocar input',
+ contextMenu: 'Menu de contexto nos cartões',
+ refreshProfiles: 'Atualizar perfis',
+ newProfile: 'Novo perfil',
+ exitSelectionMode: 'Sair do modo de seleção',
+ showHelp: 'Mostrar esta ajuda',
+ },
+ footer: 'Pressione Esc ou clique fora para fechar',
+ },
+
+ // Notification Log
+ notificationLog: {
+ title: 'Registro de Notificações',
+ events: {
+ printStarted: 'Impressão Iniciada',
+ printComplete: 'Impressão Concluída',
+ printFailed: 'Impressão Falhou',
+ printStopped: 'Impressão Interrompida',
+ progress: 'Progresso',
+ printerOffline: 'Impressora Offline',
+ printerError: 'Erro na Impressora',
+ lowFilament: 'Filamento Baixo',
+ maintenanceDue: 'Manutenção Pendente',
+ test: 'Teste',
+ },
+ timeAgo: {
+ justNow: 'Agora mesmo',
+ minutesAgo: 'há {{minutes}} minutos',
+ hoursAgo: 'há {{hours}} horas',
+ },
+ },
+
+ // Restore/Backup Modal
+ restoreBackup: {
+ title: 'Restaurar Backup',
+ restoring: 'Restaurando...',
+ restoreComplete: 'Restauração Concluída',
+ restoreFailed: 'Falha na Restauração',
+ importSettings: 'Importar configurações de um arquivo de backup',
+ pleaseWait: 'Aguarde enquanto seus dados estão sendo restaurados',
+ clickToSelect: 'Clique para selecionar o arquivo de backup (.json ou .zip)',
+ howDuplicateHandling: 'Como funciona o tratamento de duplicatas:',
+ categories: {
+ printers: 'Impressoras',
+ smartPlugs: 'Tomadas Inteligentes',
+ notificationProviders: 'Provedores de Notificação',
+ filaments: 'Filamentos',
+ archives: 'Arquivos',
+ pendingUploads: 'Uploads Pendentes',
+ settingsTemplates: 'Configurações e Modelos',
+ },
+ matchingInfo: {
+ printers: 'correspondem pelo número de série',
+ smartPlugs: 'correspondem pelo endereço IP',
+ notificationProviders: 'correspondem pelo nome',
+ filaments: 'correspondem pelo nome + tipo + marca',
+ archives: 'correspondem pelo hash do conteúdo',
+ pendingUploads: 'correspondem pelo nome do arquivo',
+ settingsTemplates: 'sempre sobrescrito',
+ },
+ replaceExisting: 'Substituir dados existentes',
+ keepExisting: 'Manter dados existentes',
+ replaceDescription: 'Substituir itens que já existem com os dados do backup',
+ keepDescription: 'Restaurar apenas itens que não existem',
+ caution: 'Atenção:',
+ cautionText: 'Sobrescrever substituirá suas configurações atuais pelos dados do backup. Os códigos de acesso da impressora nunca são sobrescritos por segurança.',
+ itemsRestored: 'Itens Restaurados',
+ itemsSkipped: 'Itens Ignorados',
+ restored: 'Restaurado',
+ skipped: 'Ignorado (já existe)',
+ filesLabel: 'Arquivos (3MF, miniaturas, etc.)',
+ newApiKeysGenerated: 'Novas Chaves API Geradas',
+ newApiKeysWarning: 'Essas chaves são exibidas apenas uma vez. Copie-as agora!',
+ processingBackup: 'Processando arquivo de backup...',
+ noDataFound: 'Nenhum dado foi encontrado para restaurar no arquivo de backup.',
+ failedToRestore: 'Falha ao restaurar o backup. Verifique o formato do arquivo.',
+ },
+
+ // Backup Export Modal
+ backupExport: {
+ title: 'Exportar Backup',
+ selectData: 'Selecione os dados a incluir',
+ selectAll: 'Selecionar Todos',
+ selectNone: 'Selecionar Nenhum',
+ categoryDescriptions: {
+ settings: 'Idioma, tema, preferências de atualização',
+ notifications: 'ntfy, Pushover, Discord, etc.',
+ templates: 'Modelos de mensagens personalizadas',
+ smartPlugs: 'Configurações de tomadas Tasmota',
+ externalLinks: 'Links da barra lateral para serviços externos',
+ printers: 'Informações da impressora (códigos de acesso excluídos)',
+ plateDetection: 'Imagens de referência de placa vazia',
+ filaments: 'Tipos e custos de filamento',
+ maintenance: 'Cronogramas de manutenção personalizados',
+ archives: 'Todos os dados de impressão + arquivos (3MF, miniaturas, fotos)',
+ projects: 'Projetos, itens de BOM e anexos',
+ pendingUploads: 'Uploads de impressora virtual aguardando revisão',
+ apiKeys: 'Chaves API de webhook (novas chaves geradas na importação)',
+ },
+ requiresPrinters: 'Requer que as impressoras sejam selecionadas',
+ zipFileWarning: 'Um arquivo ZIP será criado.',
+ zipFileDescription: 'Inclui todos os arquivos 3MF, miniaturas, timelapses e fotos. Isso pode levar algum tempo e resultar em um arquivo grande.',
+ includeAccessCodes: 'Incluir Códigos de Acesso',
+ includeAccessCodesDescription: 'Para transferir para outra máquina',
+ includeAccessCodesWarning: 'Os códigos de acesso serão incluídos em texto simples. Mantenha este arquivo de backup seguro!',
+ categoriesSelected: '{{selectedCount}} categorias selecionadas',
+ },
+
+ // Pending Uploads Panel
+ pendingUploads: {
+ placeholders: {
+ notes: 'Adicione notas sobre esta impressão...',
+ },
+ discardUpload: 'Descartar Upload',
+ archiveAllUploads: 'Arquivar Todos os Uploads',
+ discardAllUploads: 'Descartar Todos os Uploads',
+ archive: 'Arquivar',
+ timeAgo: {
+ justNow: 'Agora mesmo',
+ minutesAgo: 'há {{minutes}} minutos',
+ hoursAgo: 'há {{hours}} horas',
+ daysAgo: 'há {{days}} dias',
+ },
+ },
+
+ // API Browser
+ apiBrowser: {
+ placeholders: {
+ requestBody: 'Corpo da requisição JSON...',
+ searchEndpoints: 'Pesquisar endpoints...',
+ },
+ },
+
+ // Configure AMS Slot Modal
+ configureAmsSlot: {
+ title: 'Configurar Slot AMS',
+ slotConfigured: 'Slot Configurado!',
+ configuringSlot: 'Configurando slot:',
+ slotLabel: '{{ams}} Slot {{slot}}',
+ searchPresets: 'Pesquisar predefinições...',
+ colorPlaceholder: 'Nome da cor ou hex (ex.: marrom, FF8800)',
+ clearCustomColor: 'Limpar cor personalizada',
+ noCloudPresets: 'Nenhuma predefinição na nuvem. Faça login no Bambu Cloud para sincronizar.',
+ noPresetsAvailable: 'Nenhuma predefinição disponível. Faça login no Bambu Cloud ou importe perfis locais.',
+ noMatchingPresets: 'Nenhuma predefinição correspondente encontrada.',
+ custom: 'Personalizado',
+ builtin: 'Integrado',
+ settingsSentToPrinter: 'Configurações enviadas para a impressora',
+ filamentProfile: 'Perfil de Filamento',
+ kProfileLabel: 'Perfil K (Avanço de Pressão)',
+ filteringFor: 'Filtrando por: {{material}}',
+ noKProfile: 'Nenhum perfil K (usar padrão 0.020)',
+ noMatchingKProfiles: 'Nenhum perfil K correspondente encontrado. O K padrão=0.020 será usado.',
+ selectFilamentFirst: 'Selecione um perfil de filamento primeiro',
+ kFromCalibration: 'K={{value}} da calibração da impressora',
+ customColorLabel: 'Cor Personalizada (opcional)',
+ presetColors: 'Cores de {{name}}:',
+ showLessColors: 'Mostrar menos cores',
+ showMoreColors: 'Mostrar mais cores',
+ clear: 'Limpar',
+ hexLabel: 'Hex: #{{hex}}',
+ resetting: 'Redefinindo...',
+ resetSlot: 'Redefinir Slot',
+ cancel: 'Cancelar',
+ configuring: 'Configurando...',
+ configureSlot: 'Configurar Slot',
+ },
+
+ // GitHub Backup Settings
+ githubBackup: {
+ title: 'Backup do GitHub',
+ history: 'Histórico',
+ downloadBackup: 'Baixar Backup',
+ restoreBackup: 'Restaurar Backup',
+ noBackupsYet: 'Nenhum backup ainda',
+ },
+
+ // Email Settings
+ emailSettings: {
+ placeholders: {
+ fromName: 'BamBuddy',
+ },
+ },
+
+ // Tag Management Modal
+ tagManagement: {
+ searchTags: 'Pesquisar tags...',
+ renameTag: 'Renomear tag',
+ deleteTag: 'Excluir tag',
+ },
+
+ // Notification Template Editor
+ notificationTemplates: {
+ placeholders: {
+ title: 'Título da notificação...',
+ body: 'Corpo da notificação...',
+ },
+ },
+
+ // Batch Tag Modal
+ batchTag: {
+ placeholders: {
+ newTag: 'Digite uma nova tag...',
+ },
+ },
+
+ // Photo Gallery Modal
+ photoGallery: {
+ deletePhoto: 'Excluir foto',
+ },
+
+ // Filament Hover Card
+ filamentHoverCard: {
+ copySpoolUuid: 'Copiar UUID do carretel',
+ },
+
+ // K Profiles View
+ kProfilesView: {
+ hasNote: 'Possui nota',
+ copyProfile: 'Copiar perfil',
+ },
+
+ // Layout/Navigation
+ layout: {
+ openMenu: 'Abrir menu',
+ noPermissionSystemInfo: 'Você não tem permissão para visualizar informações do sistema',
+ },
+
+ // Dashboard
+ dashboard: {
+ dragToReorder: 'Arrastar para reordenar',
+ hideWidget: 'Ocultar widget',
+ },
+
+ // Notification Provider Card
+ notificationProviderCard: {
+ deleteNotificationProvider: 'Excluir provedor de notificação',
+ },
+
+ // File Manager Modal
+ fileManagerModal: {
+ closeFileManager: 'Fechar gerenciador de arquivos',
+ sortFiles: 'Ordenar arquivos',
+ goToParentFolder: 'Ir para a pasta pai',
+ threeView: 'Visualização 3D',
+ },
+
+ // Embedded Camera Viewer
+ embeddedCameraViewer: {
+ refreshStream: 'Atualizar stream',
+ close: 'Fechar',
+ zoomOut: 'Reduzir zoom',
+ resetZoom: 'Redefinir zoom',
+ zoomIn: 'Aumentar zoom',
+ dragToResize: 'Arrastar para redimensionar',
+ },
+
+ // Timelapse Viewer
+ timelapseViewer: {
+ skipBack5s: 'Voltar 5s',
+ skipForward5s: 'Avançar 5s',
+ },
+
+ // Notification Providers
+ notificationProviders: {
+ descriptions: {
+ email: 'Notificações por email SMTP',
+ telegram: 'Notificações via bot do Telegram',
+ discord: 'Enviar para canal do Discord via webhook',
+ ntfy: 'Notificações push gratuitas e auto-hospedáveis',
+ pushover: 'Notificações push simples e confiáveis',
+ callmebot: 'Notificações gratuitas via WhatsApp pelo CallMeBot',
+ webhook: 'POST HTTP genérico para qualquer URL',
+ },
+ },
+
+ // Log Viewer
+ logViewer: {
+ searchPlaceholder: 'Pesquisar mensagem ou nome do logger...',
+ noLogEntries: 'Nenhuma entrada de log encontrada',
+ },
+
+ // Switchbar Popover
+ switchbarPopover: {
+ noSwitchesInSwitchbar: 'Nenhum switch na barra de switches',
+ },
+
+ // Project Page Modal
+ projectPageModal: {
+ placeholders: {
+ title: 'Título',
+ designer: 'Designer',
+ license: 'Licença',
+ description: 'Digite a descrição...',
+ profileTitle: 'Título do perfil',
+ profileDescription: 'Descrição do perfil...',
+ },
+ },
+
+ // Spoolman Settings
+ spoolmanSettings: {},
+};
diff --git a/frontend/src/pages/ArchivesPage.tsx b/frontend/src/pages/ArchivesPage.tsx
index 4256d07a1..4bf203765 100644
--- a/frontend/src/pages/ArchivesPage.tsx
+++ b/frontend/src/pages/ArchivesPage.tsx
@@ -100,6 +100,36 @@ function getArchiveFileType(filename: string | null | undefined): string | undef
// formatDate imported from '../utils/date' - handles UTC conversion
+/**
+ * Open an archive file in the slicer.
+ * Fetches a short-lived download token, then builds a token-authenticated URL
+ * that bypasses auth middleware (slicer protocol handlers can't send auth headers).
+ */
+async function openInSlicerWithToken(
+ archiveId: number,
+ filename: string,
+ resourceType: 'file' | 'source',
+ slicer: SlicerType,
+): Promise
{
+ try {
+ if (resourceType === 'source') {
+ const { token } = await api.createSourceSlicerToken(archiveId);
+ const path = api.getSourceSlicerDownloadUrl(archiveId, token, filename);
+ openInSlicer(`${window.location.origin}${path}`, slicer);
+ } else {
+ const { token } = await api.createArchiveSlicerToken(archiveId);
+ const path = api.getArchiveSlicerDownloadUrl(archiveId, token, filename);
+ openInSlicer(`${window.location.origin}${path}`, slicer);
+ }
+ } catch {
+ // Fallback to direct URL (works when auth is disabled)
+ const path = resourceType === 'source'
+ ? api.getSource3mfForSlicer(archiveId, filename)
+ : api.getArchiveForSlicer(archiveId, filename);
+ openInSlicer(`${window.location.origin}${path}`, slicer);
+ }
+}
+
function ArchiveCard({
archive,
printerName,
@@ -332,8 +362,7 @@ function ArchiveCard({
icon: ,
onClick: () => {
const filename = archive.print_name || archive.filename || 'model';
- const downloadUrl = `${window.location.origin}${api.getArchiveForSlicer(archive.id, filename)}`;
- openInSlicer(downloadUrl, preferredSlicer);
+ openInSlicerWithToken(archive.id, filename, 'file', preferredSlicer);
},
disabled: !archive.file_path,
title: !archive.file_path ? t('archives.card.noFileForReprint') : undefined,
@@ -344,8 +373,7 @@ function ArchiveCard({
icon: ,
onClick: () => {
const filename = archive.print_name || archive.filename || 'model';
- const downloadUrl = `${window.location.origin}${api.getArchiveForSlicer(archive.id, filename)}`;
- openInSlicer(downloadUrl, preferredSlicer);
+ openInSlicerWithToken(archive.id, filename, 'file', preferredSlicer);
},
},
]),
@@ -733,8 +761,7 @@ function ArchiveCard({
e.stopPropagation();
// Open source 3MF in Bambu Studio - use filename in URL for slicer compatibility
const sourceName = (archive.print_name || archive.filename || 'source').replace(/\.gcode\.3mf$/i, '') + '_source';
- const downloadUrl = `${window.location.origin}${api.getSource3mfForSlicer(archive.id, sourceName)}`;
- openInSlicer(downloadUrl, preferredSlicer);
+ openInSlicerWithToken(archive.id, sourceName, 'source', preferredSlicer);
}}
title={t('archives.card.openSource3mf')}
>
@@ -1003,8 +1030,7 @@ function ArchiveCard({
className="min-w-0 p-1 sm:p-1.5"
onClick={() => {
const filename = archive.print_name || archive.filename || 'model';
- const downloadUrl = `${window.location.origin}${api.getArchiveForSlicer(archive.id, filename)}`;
- openInSlicer(downloadUrl, preferredSlicer);
+ openInSlicerWithToken(archive.id, filename, 'file', preferredSlicer);
}}
title={t('archives.card.openInBambuStudio')}
>
@@ -1019,8 +1045,7 @@ function ArchiveCard({
className="flex-1 min-w-0"
onClick={() => {
const filename = archive.print_name || archive.filename || 'model';
- const downloadUrl = `${window.location.origin}${api.getArchiveForSlicer(archive.id, filename)}`;
- openInSlicer(downloadUrl, preferredSlicer);
+ openInSlicerWithToken(archive.id, filename, 'file', preferredSlicer);
}}
title={t('archives.card.openInBambuStudioToSlice')}
>
@@ -1541,8 +1566,7 @@ function ArchiveListRow({
icon: ,
onClick: () => {
const filename = archive.print_name || archive.filename || 'model';
- const downloadUrl = `${window.location.origin}${api.getArchiveForSlicer(archive.id, filename)}`;
- openInSlicer(downloadUrl, preferredSlicer);
+ openInSlicerWithToken(archive.id, filename, 'file', preferredSlicer);
},
disabled: !archive.file_path,
title: !archive.file_path ? t('archives.card.noFileForReprint') : undefined,
@@ -1553,8 +1577,7 @@ function ArchiveListRow({
icon: ,
onClick: () => {
const filename = archive.print_name || archive.filename || 'model';
- const downloadUrl = `${window.location.origin}${api.getArchiveForSlicer(archive.id, filename)}`;
- openInSlicer(downloadUrl, preferredSlicer);
+ openInSlicerWithToken(archive.id, filename, 'file', preferredSlicer);
},
},
]),
@@ -1895,8 +1918,7 @@ function ArchiveListRow({
size="sm"
onClick={() => {
const filename = archive.print_name || archive.filename || 'model';
- const downloadUrl = `${window.location.origin}${api.getArchiveForSlicer(archive.id, filename)}`;
- openInSlicer(downloadUrl, preferredSlicer);
+ openInSlicerWithToken(archive.id, filename, 'file', preferredSlicer);
}}
title={t('archives.card.openInBambuStudio')}
>
diff --git a/frontend/src/pages/PrintersPage.tsx b/frontend/src/pages/PrintersPage.tsx
index 0bb9cea71..859be1fa1 100644
--- a/frontend/src/pages/PrintersPage.tsx
+++ b/frontend/src/pages/PrintersPage.tsx
@@ -2907,6 +2907,7 @@ function PrinterCard({
material: assignment.spool.material,
brand: assignment.spool.brand,
color_name: assignment.spool.color_name,
+ remainingWeightGrams: Math.max(0, Math.round(assignment.spool.label_weight - assignment.spool.weight_used)),
} : null,
onAssignSpool: filamentData.vendor !== 'Bambu Lab' ? () => setAssignSpoolModal({
printerId: printer.id,
@@ -3144,6 +3145,7 @@ function PrinterCard({
material: assignment.spool.material,
brand: assignment.spool.brand,
color_name: assignment.spool.color_name,
+ remainingWeightGrams: Math.max(0, Math.round(assignment.spool.label_weight - assignment.spool.weight_used)),
} : null,
onAssignSpool: filamentData.vendor !== 'Bambu Lab' ? () => setAssignSpoolModal({
printerId: printer.id,
@@ -3330,6 +3332,7 @@ function PrinterCard({
material: assignment.spool.material,
brand: assignment.spool.brand,
color_name: assignment.spool.color_name,
+ remainingWeightGrams: Math.max(0, Math.round(assignment.spool.label_weight - assignment.spool.weight_used)),
} : null,
onAssignSpool: () => setAssignSpoolModal({
printerId: printer.id,
diff --git a/frontend/src/utils/slicer.ts b/frontend/src/utils/slicer.ts
index b6f376fcc..c2f21ebb5 100644
--- a/frontend/src/utils/slicer.ts
+++ b/frontend/src/utils/slicer.ts
@@ -1,12 +1,23 @@
/**
* Utility for opening files in slicer applications
*
- * Bambu Studio URL protocol is OS-specific:
- * - Windows: bambustudio://
- * - macOS/Linux: bambustudioopen://
+ * Protocol handler URL formats (from BambuStudio/OrcaSlicer source code):
*
- * OrcaSlicer uses the same protocol on all platforms:
- * - orcaslicer://open?file=
+ * Bambu Studio has TWO separate URL handlers:
+ * 1. post_init() [Windows/Linux CLI args]: bambustudio://open?file=
+ * - Checks: starts_with("bambustudio://open")
+ * - Calls url_decode(), then split_str(url, "file=")
+ * 2. MacOpenURL() [macOS Apple Events]: bambustudioopen://
+ * - Checks: starts_with("bambustudioopen://")
+ * - Strips prefix, then url_decode()
+ *
+ * OrcaSlicer Downloader accepts both formats via regex:
+ * - (orcaslicer|bambustudio|...)://open?file=
+ * - bambustudioopen://
+ *
+ * Key insight: Using ?file= query format, the browser's URL parser preserves
+ * http:// in the query string without any encoding. Only the macOS-specific
+ * bambustudioopen:// format needs encodeURIComponent (BS calls url_decode).
*/
export type SlicerType = 'bambu_studio' | 'orcaslicer';
@@ -34,8 +45,6 @@ export function detectPlatform(): Platform {
/**
* Open a URL in the specified slicer application.
- * Uses a temporary link element to trigger the protocol handler,
- * which avoids browser "unknown protocol" blocks on window.location.href.
* @param downloadUrl - The URL to the file to open
* @param slicer - Which slicer to use (defaults to bambu_studio)
*/
@@ -43,15 +52,26 @@ export function openInSlicer(downloadUrl: string, slicer: SlicerType = 'bambu_st
let url: string;
if (slicer === 'orcaslicer') {
+ // OrcaSlicer: ?file= query format — http:// preserved in query string
url = `orcaslicer://open?file=${downloadUrl}`;
} else {
const platform = detectPlatform();
- const protocol = platform === 'windows' ? 'bambustudio' : 'bambustudioopen';
- url = `${protocol}://${encodeURIComponent(downloadUrl)}`;
+ if (platform === 'macos') {
+ // macOS only: bambustudioopen scheme via MacOpenURL() callback.
+ // Must encode because bare http:// in authority gets mangled by browser.
+ // BS calls url_decode() after stripping "bambustudioopen://" prefix.
+ url = `bambustudioopen://${encodeURIComponent(downloadUrl)}`;
+ } else {
+ // Windows/Linux: bambustudio://open?file= via post_init() CLI args.
+ // The ?file= query format preserves http:// without encoding.
+ // IMPORTANT: On Linux, BS only handles "bambustudio://open" prefix —
+ // it does NOT process "bambustudioopen://" (that's macOS-only).
+ url = `bambustudio://open?file=${downloadUrl}`;
+ }
}
// Use a temporary element to trigger the protocol handler.
- // This works more reliably than window.location.href for custom protocols.
+ // This avoids navigating away from the page (unlike window.location.href).
const link = document.createElement('a');
link.href = url;
link.style.display = 'none';
diff --git a/static/assets/index-DmiEmqVo.js b/static/assets/index-BPE9r2Ne.js
similarity index 64%
rename from static/assets/index-DmiEmqVo.js
rename to static/assets/index-BPE9r2Ne.js
index 00a3b329d..669cc55f9 100644
--- a/static/assets/index-DmiEmqVo.js
+++ b/static/assets/index-BPE9r2Ne.js
@@ -1,18 +1,18 @@
-function Doe(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 s of i)if(s.type==="childList")for(const o of s.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&r(o)}).observe(document,{childList:!0,subtree:!0});function n(i){const s={};return i.integrity&&(s.integrity=i.integrity),i.referrerPolicy&&(s.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?s.credentials="include":i.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function r(i){if(i.ep)return;i.ep=!0;const s=n(i);fetch(i.href,s)}})();var l_=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function ec(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var zT={exports:{}},ry={};var GU;function Roe(){if(GU)return ry;GU=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function n(r,i,s){var o=null;if(s!==void 0&&(o=""+s),i.key!==void 0&&(o=""+i.key),"key"in i){s={};for(var l in i)l!=="key"&&(s[l]=i[l])}else s=i;return i=s.ref,{$$typeof:t,type:r,key:o,ref:i!==void 0?i:null,props:s}}return ry.Fragment=e,ry.jsx=n,ry.jsxs=n,ry}var qU;function Foe(){return qU||(qU=1,zT.exports=Roe()),zT.exports}var a=Foe(),UT={exports:{}},kn={};var WU;function Loe(){if(WU)return kn;WU=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"),s=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"),h=Symbol.for("react.activity"),f=Symbol.iterator;function p($){return $===null||typeof $!="object"?null:($=f&&$[f]||$["@@iterator"],typeof $=="function"?$:null)}var x={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},y=Object.assign,b={};function v($,X,ee){this.props=$,this.context=X,this.refs=b,this.updater=ee||x}v.prototype.isReactComponent={},v.prototype.setState=function($,X){if(typeof $!="object"&&typeof $!="function"&&$!=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,$,X,"setState")},v.prototype.forceUpdate=function($){this.updater.enqueueForceUpdate(this,$,"forceUpdate")};function S(){}S.prototype=v.prototype;function N($,X,ee){this.props=$,this.context=X,this.refs=b,this.updater=ee||x}var A=N.prototype=new S;A.constructor=N,y(A,v.prototype),A.isPureReactComponent=!0;var C=Array.isArray;function T(){}var k={H:null,A:null,T:null,S:null},j=Object.prototype.hasOwnProperty;function E($,X,ee){var J=ee.ref;return{$$typeof:t,type:$,key:X,ref:J!==void 0?J:null,props:ee}}function P($,X){return E($.type,X,$.props)}function U($){return typeof $=="object"&&$!==null&&$.$$typeof===t}function O($){var X={"=":"=0",":":"=2"};return"$"+$.replace(/[=:]/g,function(ee){return X[ee]})}var B=/\/+/g;function R($,X){return typeof $=="object"&&$!==null&&$.key!=null?O(""+$.key):X.toString(36)}function G($){switch($.status){case"fulfilled":return $.value;case"rejected":throw $.reason;default:switch(typeof $.status=="string"?$.then(T,T):($.status="pending",$.then(function(X){$.status==="pending"&&($.status="fulfilled",$.value=X)},function(X){$.status==="pending"&&($.status="rejected",$.reason=X)})),$.status){case"fulfilled":return $.value;case"rejected":throw $.reason}}throw $}function q($,X,ee,J,_e){var pe=typeof $;(pe==="undefined"||pe==="boolean")&&($=null);var me=!1;if($===null)me=!0;else switch(pe){case"bigint":case"string":case"number":me=!0;break;case"object":switch($.$$typeof){case t:case e:me=!0;break;case d:return me=$._init,q(me($._payload),X,ee,J,_e)}}if(me)return _e=_e($),me=J===""?"."+R($,0):J,C(_e)?(ee="",me!=null&&(ee=me.replace(B,"$&/")+"/"),q(_e,X,ee,"",function(ce){return ce})):_e!=null&&(U(_e)&&(_e=P(_e,ee+(_e.key==null||$&&$.key===_e.key?"":(""+_e.key).replace(B,"$&/")+"/")+me)),X.push(_e)),1;me=0;var je=J===""?".":J+":";if(C($))for(var de=0;de<$.length;de++)J=$[de],pe=je+R(J,de),me+=q(J,X,ee,pe,_e);else if(de=p($),typeof de=="function")for($=de.call($),de=0;!(J=$.next()).done;)J=J.value,pe=je+R(J,de++),me+=q(J,X,ee,pe,_e);else if(pe==="object"){if(typeof $.then=="function")return q(G($),X,ee,J,_e);throw X=String($),Error("Objects are not valid as a React child (found: "+(X==="[object Object]"?"object with keys {"+Object.keys($).join(", ")+"}":X)+"). If you meant to render a collection of children, use an array instead.")}return me}function z($,X,ee){if($==null)return $;var J=[],_e=0;return q($,J,"","",function(pe){return X.call(ee,pe,_e++)}),J}function K($){if($._status===-1){var X=$._result;X=X(),X.then(function(ee){($._status===0||$._status===-1)&&($._status=1,$._result=ee)},function(ee){($._status===0||$._status===-1)&&($._status=2,$._result=ee)}),$._status===-1&&($._status=0,$._result=X)}if($._status===1)return $._result.default;throw $._result}var Y=typeof reportError=="function"?reportError:function($){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var X=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof $=="object"&&$!==null&&typeof $.message=="string"?String($.message):String($),error:$});if(!window.dispatchEvent(X))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",$);return}console.error($)},oe={map:z,forEach:function($,X,ee){z($,function(){X.apply(this,arguments)},ee)},count:function($){var X=0;return z($,function(){X++}),X},toArray:function($){return z($,function(X){return X})||[]},only:function($){if(!U($))throw Error("React.Children.only expected to receive a single React element child.");return $}};return kn.Activity=h,kn.Children=oe,kn.Component=v,kn.Fragment=n,kn.Profiler=i,kn.PureComponent=N,kn.StrictMode=r,kn.Suspense=c,kn.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=k,kn.__COMPILER_RUNTIME={__proto__:null,c:function($){return k.H.useMemoCache($)}},kn.cache=function($){return function(){return $.apply(null,arguments)}},kn.cacheSignal=function(){return null},kn.cloneElement=function($,X,ee){if($==null)throw Error("The argument must be a React element, but you passed "+$+".");var J=y({},$.props),_e=$.key;if(X!=null)for(pe in X.key!==void 0&&(_e=""+X.key),X)!j.call(X,pe)||pe==="key"||pe==="__self"||pe==="__source"||pe==="ref"&&X.ref===void 0||(J[pe]=X[pe]);var pe=arguments.length-2;if(pe===1)J.children=ee;else if(1>>1,oe=q[Y];if(0>>1;Y<$;){var X=2*(Y+1)-1,ee=q[X],J=X+1,_e=q[J];if(0>i(ee,K))Ji(_e,ee)?(q[Y]=_e,q[J]=K,Y=J):(q[Y]=ee,q[X]=K,Y=X);else if(Ji(_e,K))q[Y]=_e,q[J]=K,Y=J;else break e}}return z}function i(q,z){var K=q.sortIndex-z.sortIndex;return K!==0?K:q.id-z.id}if(t.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var s=performance;t.unstable_now=function(){return s.now()}}else{var o=Date,l=o.now();t.unstable_now=function(){return o.now()-l}}var c=[],u=[],d=1,h=null,f=3,p=!1,x=!1,y=!1,b=!1,v=typeof setTimeout=="function"?setTimeout:null,S=typeof clearTimeout=="function"?clearTimeout:null,N=typeof setImmediate<"u"?setImmediate:null;function A(q){for(var z=n(u);z!==null;){if(z.callback===null)r(u);else if(z.startTime<=q)r(u),z.sortIndex=z.expirationTime,e(c,z);else break;z=n(u)}}function C(q){if(y=!1,A(q),!x)if(n(c)!==null)x=!0,T||(T=!0,O());else{var z=n(u);z!==null&&G(C,z.startTime-q)}}var T=!1,k=-1,j=5,E=-1;function P(){return b?!0:!(t.unstable_now()-Eq&&P());){var Y=h.callback;if(typeof Y=="function"){h.callback=null,f=h.priorityLevel;var oe=Y(h.expirationTime<=q);if(q=t.unstable_now(),typeof oe=="function"){h.callback=oe,A(q),z=!0;break t}h===n(c)&&r(c),A(q)}else r(c);h=n(c)}if(h!==null)z=!0;else{var $=n(u);$!==null&&G(C,$.startTime-q),z=!1}}break e}finally{h=null,f=K,p=!1}z=void 0}}finally{z?O():T=!1}}}var O;if(typeof N=="function")O=function(){N(U)};else if(typeof MessageChannel<"u"){var B=new MessageChannel,R=B.port2;B.port1.onmessage=U,O=function(){R.postMessage(null)}}else O=function(){v(U,0)};function G(q,z){k=v(function(){q(t.unstable_now())},z)}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(q){q.callback=null},t.unstable_forceFrameRate=function(q){0>q||125Y?(q.sortIndex=K,e(u,q),n(c)===null&&q===n(u)&&(y?(S(k),k=-1):y=!0,G(C,K-Y))):(q.sortIndex=oe,e(c,q),x||p||(x=!0,T||(T=!0,O()))),q},t.unstable_shouldYield=P,t.unstable_wrapCallback=function(q){var z=f;return function(){var K=f;f=z;try{return q.apply(this,arguments)}finally{f=K}}}})($T)),$T}var YU;function zoe(){return YU||(YU=1,HT.exports=Ioe()),HT.exports}var VT={exports:{}},ss={};var QU;function Uoe(){if(QU)return ss;QU=1;var t=Bb();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(),VT.exports=Uoe(),VT.exports}var JU;function Boe(){if(JU)return iy;JU=1;var t=zoe(),e=Bb(),n=bK();function r(m){var g="https://react.dev/errors/"+m;if(1oe||(m.current=Y[oe],Y[oe]=null,oe--)}function ee(m,g){oe++,Y[oe]=m.current,m.current=g}var J=$(null),_e=$(null),pe=$(null),me=$(null);function je(m,g){switch(ee(pe,g),ee(_e,m),ee(J,null),g.nodeType){case 9:case 11:m=(m=g.documentElement)&&(m=m.namespaceURI)?fU(m):0;break;default:if(m=g.tagName,g=g.namespaceURI)g=fU(g),m=pU(g,m);else switch(m){case"svg":m=1;break;case"math":m=2;break;default:m=0}}X(J),ee(J,m)}function de(){X(J),X(_e),X(pe)}function ce(m){m.memoizedState!==null&&ee(me,m);var g=J.current,_=pU(g,m.type);g!==_&&(ee(_e,m),ee(J,_))}function Se(m){_e.current===m&&(X(J),X(_e)),me.current===m&&(X(me),Jx._currentValue=K)}var ie,Fe;function V(m){if(ie===void 0)try{throw Error()}catch(_){var g=_.stack.trim().match(/\n( *(at )?)/);ie=g&&g[1]||"",Fe=-1<_.stack.indexOf(`
+function Roe(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 s of i)if(s.type==="childList")for(const o of s.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&r(o)}).observe(document,{childList:!0,subtree:!0});function n(i){const s={};return i.integrity&&(s.integrity=i.integrity),i.referrerPolicy&&(s.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?s.credentials="include":i.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function r(i){if(i.ep)return;i.ep=!0;const s=n(i);fetch(i.href,s)}})();var c_=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function ec(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var UT={exports:{}},iy={};var qU;function Foe(){if(qU)return iy;qU=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function n(r,i,s){var o=null;if(s!==void 0&&(o=""+s),i.key!==void 0&&(o=""+i.key),"key"in i){s={};for(var l in i)l!=="key"&&(s[l]=i[l])}else s=i;return i=s.ref,{$$typeof:t,type:r,key:o,ref:i!==void 0?i:null,props:s}}return iy.Fragment=e,iy.jsx=n,iy.jsxs=n,iy}var WU;function Loe(){return WU||(WU=1,UT.exports=Foe()),UT.exports}var a=Loe(),BT={exports:{}},kn={};var KU;function Ooe(){if(KU)return kn;KU=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"),s=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"),h=Symbol.for("react.activity"),f=Symbol.iterator;function p($){return $===null||typeof $!="object"?null:($=f&&$[f]||$["@@iterator"],typeof $=="function"?$:null)}var x={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},y=Object.assign,b={};function v($,X,ee){this.props=$,this.context=X,this.refs=b,this.updater=ee||x}v.prototype.isReactComponent={},v.prototype.setState=function($,X){if(typeof $!="object"&&typeof $!="function"&&$!=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,$,X,"setState")},v.prototype.forceUpdate=function($){this.updater.enqueueForceUpdate(this,$,"forceUpdate")};function S(){}S.prototype=v.prototype;function N($,X,ee){this.props=$,this.context=X,this.refs=b,this.updater=ee||x}var A=N.prototype=new S;A.constructor=N,y(A,v.prototype),A.isPureReactComponent=!0;var C=Array.isArray;function T(){}var k={H:null,A:null,T:null,S:null},j=Object.prototype.hasOwnProperty;function E($,X,ee){var J=ee.ref;return{$$typeof:t,type:$,key:X,ref:J!==void 0?J:null,props:ee}}function P($,X){return E($.type,X,$.props)}function U($){return typeof $=="object"&&$!==null&&$.$$typeof===t}function O($){var X={"=":"=0",":":"=2"};return"$"+$.replace(/[=:]/g,function(ee){return X[ee]})}var B=/\/+/g;function R($,X){return typeof $=="object"&&$!==null&&$.key!=null?O(""+$.key):X.toString(36)}function G($){switch($.status){case"fulfilled":return $.value;case"rejected":throw $.reason;default:switch(typeof $.status=="string"?$.then(T,T):($.status="pending",$.then(function(X){$.status==="pending"&&($.status="fulfilled",$.value=X)},function(X){$.status==="pending"&&($.status="rejected",$.reason=X)})),$.status){case"fulfilled":return $.value;case"rejected":throw $.reason}}throw $}function q($,X,ee,J,_e){var pe=typeof $;(pe==="undefined"||pe==="boolean")&&($=null);var he=!1;if($===null)he=!0;else switch(pe){case"bigint":case"string":case"number":he=!0;break;case"object":switch($.$$typeof){case t:case e:he=!0;break;case d:return he=$._init,q(he($._payload),X,ee,J,_e)}}if(he)return _e=_e($),he=J===""?"."+R($,0):J,C(_e)?(ee="",he!=null&&(ee=he.replace(B,"$&/")+"/"),q(_e,X,ee,"",function(ce){return ce})):_e!=null&&(U(_e)&&(_e=P(_e,ee+(_e.key==null||$&&$.key===_e.key?"":(""+_e.key).replace(B,"$&/")+"/")+he)),X.push(_e)),1;he=0;var je=J===""?".":J+":";if(C($))for(var de=0;de<$.length;de++)J=$[de],pe=je+R(J,de),he+=q(J,X,ee,pe,_e);else if(de=p($),typeof de=="function")for($=de.call($),de=0;!(J=$.next()).done;)J=J.value,pe=je+R(J,de++),he+=q(J,X,ee,pe,_e);else if(pe==="object"){if(typeof $.then=="function")return q(G($),X,ee,J,_e);throw X=String($),Error("Objects are not valid as a React child (found: "+(X==="[object Object]"?"object with keys {"+Object.keys($).join(", ")+"}":X)+"). If you meant to render a collection of children, use an array instead.")}return he}function z($,X,ee){if($==null)return $;var J=[],_e=0;return q($,J,"","",function(pe){return X.call(ee,pe,_e++)}),J}function K($){if($._status===-1){var X=$._result;X=X(),X.then(function(ee){($._status===0||$._status===-1)&&($._status=1,$._result=ee)},function(ee){($._status===0||$._status===-1)&&($._status=2,$._result=ee)}),$._status===-1&&($._status=0,$._result=X)}if($._status===1)return $._result.default;throw $._result}var Y=typeof reportError=="function"?reportError:function($){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var X=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof $=="object"&&$!==null&&typeof $.message=="string"?String($.message):String($),error:$});if(!window.dispatchEvent(X))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",$);return}console.error($)},oe={map:z,forEach:function($,X,ee){z($,function(){X.apply(this,arguments)},ee)},count:function($){var X=0;return z($,function(){X++}),X},toArray:function($){return z($,function(X){return X})||[]},only:function($){if(!U($))throw Error("React.Children.only expected to receive a single React element child.");return $}};return kn.Activity=h,kn.Children=oe,kn.Component=v,kn.Fragment=n,kn.Profiler=i,kn.PureComponent=N,kn.StrictMode=r,kn.Suspense=c,kn.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=k,kn.__COMPILER_RUNTIME={__proto__:null,c:function($){return k.H.useMemoCache($)}},kn.cache=function($){return function(){return $.apply(null,arguments)}},kn.cacheSignal=function(){return null},kn.cloneElement=function($,X,ee){if($==null)throw Error("The argument must be a React element, but you passed "+$+".");var J=y({},$.props),_e=$.key;if(X!=null)for(pe in X.key!==void 0&&(_e=""+X.key),X)!j.call(X,pe)||pe==="key"||pe==="__self"||pe==="__source"||pe==="ref"&&X.ref===void 0||(J[pe]=X[pe]);var pe=arguments.length-2;if(pe===1)J.children=ee;else if(1>>1,oe=q[Y];if(0>>1;Y<$;){var X=2*(Y+1)-1,ee=q[X],J=X+1,_e=q[J];if(0>i(ee,K))Ji(_e,ee)?(q[Y]=_e,q[J]=K,Y=J):(q[Y]=ee,q[X]=K,Y=X);else if(Ji(_e,K))q[Y]=_e,q[J]=K,Y=J;else break e}}return z}function i(q,z){var K=q.sortIndex-z.sortIndex;return K!==0?K:q.id-z.id}if(t.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var s=performance;t.unstable_now=function(){return s.now()}}else{var o=Date,l=o.now();t.unstable_now=function(){return o.now()-l}}var c=[],u=[],d=1,h=null,f=3,p=!1,x=!1,y=!1,b=!1,v=typeof setTimeout=="function"?setTimeout:null,S=typeof clearTimeout=="function"?clearTimeout:null,N=typeof setImmediate<"u"?setImmediate:null;function A(q){for(var z=n(u);z!==null;){if(z.callback===null)r(u);else if(z.startTime<=q)r(u),z.sortIndex=z.expirationTime,e(c,z);else break;z=n(u)}}function C(q){if(y=!1,A(q),!x)if(n(c)!==null)x=!0,T||(T=!0,O());else{var z=n(u);z!==null&&G(C,z.startTime-q)}}var T=!1,k=-1,j=5,E=-1;function P(){return b?!0:!(t.unstable_now()-Eq&&P());){var Y=h.callback;if(typeof Y=="function"){h.callback=null,f=h.priorityLevel;var oe=Y(h.expirationTime<=q);if(q=t.unstable_now(),typeof oe=="function"){h.callback=oe,A(q),z=!0;break t}h===n(c)&&r(c),A(q)}else r(c);h=n(c)}if(h!==null)z=!0;else{var $=n(u);$!==null&&G(C,$.startTime-q),z=!1}}break e}finally{h=null,f=K,p=!1}z=void 0}}finally{z?O():T=!1}}}var O;if(typeof N=="function")O=function(){N(U)};else if(typeof MessageChannel<"u"){var B=new MessageChannel,R=B.port2;B.port1.onmessage=U,O=function(){R.postMessage(null)}}else O=function(){v(U,0)};function G(q,z){k=v(function(){q(t.unstable_now())},z)}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(q){q.callback=null},t.unstable_forceFrameRate=function(q){0>q||125Y?(q.sortIndex=K,e(u,q),n(c)===null&&q===n(u)&&(y?(S(k),k=-1):y=!0,G(C,K-Y))):(q.sortIndex=oe,e(c,q),x||p||(x=!0,T||(T=!0,O()))),q},t.unstable_shouldYield=P,t.unstable_wrapCallback=function(q){var z=f;return function(){var K=f;f=z;try{return q.apply(this,arguments)}finally{f=K}}}})(VT)),VT}var QU;function Uoe(){return QU||(QU=1,$T.exports=zoe()),$T.exports}var GT={exports:{}},ss={};var ZU;function Boe(){if(ZU)return ss;ZU=1;var t=Hb();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(),GT.exports=Boe(),GT.exports}var eB;function Hoe(){if(eB)return ay;eB=1;var t=Uoe(),e=Hb(),n=xK();function r(m){var g="https://react.dev/errors/"+m;if(1oe||(m.current=Y[oe],Y[oe]=null,oe--)}function ee(m,g){oe++,Y[oe]=m.current,m.current=g}var J=$(null),_e=$(null),pe=$(null),he=$(null);function je(m,g){switch(ee(pe,g),ee(_e,m),ee(J,null),g.nodeType){case 9:case 11:m=(m=g.documentElement)&&(m=m.namespaceURI)?pU(m):0;break;default:if(m=g.tagName,g=g.namespaceURI)g=pU(g),m=gU(g,m);else switch(m){case"svg":m=1;break;case"math":m=2;break;default:m=0}}X(J),ee(J,m)}function de(){X(J),X(_e),X(pe)}function ce(m){m.memoizedState!==null&&ee(he,m);var g=J.current,_=gU(g,m.type);g!==_&&(ee(_e,m),ee(J,_))}function Se(m){_e.current===m&&(X(J),X(_e)),he.current===m&&(X(he),ey._currentValue=K)}var ie,Fe;function V(m){if(ie===void 0)try{throw Error()}catch(_){var g=_.stack.trim().match(/\n( *(at )?)/);ie=g&&g[1]||"",Fe=-1<_.stack.indexOf(`
at`)?" ()":-1<_.stack.indexOf("@")?"@unknown:0:0":""}return`
`+ie+m+Fe}var W=!1;function ue(m,g){if(!m||W)return"";W=!0;var _=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var M={DetermineComponentFrameRoot:function(){try{if(g){var Ct=function(){throw Error()};if(Object.defineProperty(Ct.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(Ct,[])}catch(gt){var mt=gt}Reflect.construct(m,[],Ct)}else{try{Ct.call()}catch(gt){mt=gt}m.call(Ct.prototype)}}else{try{throw Error()}catch(gt){mt=gt}(Ct=m())&&typeof Ct.catch=="function"&&Ct.catch(function(){})}}catch(gt){if(gt&&mt&&typeof gt.stack=="string")return[gt.stack,mt.stack]}return[null,null]}};M.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var F=Object.getOwnPropertyDescriptor(M.DetermineComponentFrameRoot,"name");F&&F.configurable&&Object.defineProperty(M.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var I=M.DetermineComponentFrameRoot(),te=I[0],ke=I[1];if(te&&ke){var Be=te.split(`
`),ht=ke.split(`
`);for(F=M=0;MF||Be[M]!==ht[F]){var yt=`
`+Be[M].replace(" at new "," at ");return m.displayName&&yt.includes("")&&(yt=yt.replace("",m.displayName)),yt}while(1<=M&&0<=F);break}}}finally{W=!1,Error.prepareStackTrace=_}return(_=m?m.displayName||m.name:"")?V(_):""}function Q(m,g){switch(m.tag){case 26:case 27:case 5:return V(m.type);case 16:return V("Lazy");case 13:return m.child!==g&&g!==null?V("Suspense Fallback"):V("Suspense");case 19:return V("SuspenseList");case 0:case 15:return ue(m.type,!1);case 11:return ue(m.type.render,!1);case 1:return ue(m.type,!0);case 31:return V("Activity");default:return""}}function se(m){try{var g="",_=null;do g+=Q(m,_),_=m,m=m.return;while(m);return g}catch(M){return`
Error generating stack: `+M.message+`
-`+M.stack}}var Z=Object.prototype.hasOwnProperty,Ne=t.unstable_scheduleCallback,ve=t.unstable_cancelCallback,fe=t.unstable_shouldYield,L=t.unstable_requestPaint,D=t.unstable_now,H=t.unstable_getCurrentPriorityLevel,he=t.unstable_ImmediatePriority,xe=t.unstable_UserBlockingPriority,be=t.unstable_NormalPriority,Ue=t.unstable_LowPriority,Oe=t.unstable_IdlePriority,Qe=t.log,rt=t.unstable_setDisableYieldValue,we=null,ye=null;function le(m){if(typeof Qe=="function"&&rt(m),ye&&typeof ye.setStrictMode=="function")try{ye.setStrictMode(we,m)}catch{}}var Le=Math.clz32?Math.clz32:Ae,Me=Math.log,$e=Math.LN2;function Ae(m){return m>>>=0,m===0?32:31-(Me(m)/$e|0)|0}var Je=256,Pe=262144,Xe=4194304;function De(m){var g=m&42;if(g!==0)return g;switch(m&-m){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 m&261888;case 262144:case 524288:case 1048576:case 2097152:return m&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return m&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return m}}function Re(m,g,_){var M=m.pendingLanes;if(M===0)return 0;var F=0,I=m.suspendedLanes,te=m.pingedLanes;m=m.warmLanes;var ke=M&134217727;return ke!==0?(M=ke&~I,M!==0?F=De(M):(te&=ke,te!==0?F=De(te):_||(_=ke&~m,_!==0&&(F=De(_))))):(ke=M&~I,ke!==0?F=De(ke):te!==0?F=De(te):_||(_=M&~m,_!==0&&(F=De(_)))),F===0?0:g!==0&&g!==F&&(g&I)===0&&(I=F&-F,_=g&-g,I>=_||I===32&&(_&4194048)!==0)?g:F}function qe(m,g){return(m.pendingLanes&~(m.suspendedLanes&~m.pingedLanes)&g)===0}function pt(m,g){switch(m){case 1:case 2:case 4:case 8:case 64:return g+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 g+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 At(){var m=Xe;return Xe<<=1,(Xe&62914560)===0&&(Xe=4194304),m}function Rt(m){for(var g=[],_=0;31>_;_++)g.push(m);return g}function Wt(m,g){m.pendingLanes|=g,g!==268435456&&(m.suspendedLanes=0,m.pingedLanes=0,m.warmLanes=0)}function Tt(m,g,_,M,F,I){var te=m.pendingLanes;m.pendingLanes=_,m.suspendedLanes=0,m.pingedLanes=0,m.warmLanes=0,m.expiredLanes&=_,m.entangledLanes&=_,m.errorRecoveryDisabledLanes&=_,m.shellSuspendCounter=0;var ke=m.entanglements,Be=m.expirationTimes,ht=m.hiddenUpdates;for(_=te&~_;0<_;){var yt=31-Le(_),Ct=1<"u")return null;try{return m.activeElement||m.body}catch{return m.body}}var Qr=/[\n"\\]/g;function gn(m){return m.replace(Qr,function(g){return"\\"+g.charCodeAt(0).toString(16)+" "})}function Zr(m,g,_,M,F,I,te,ke){m.name="",te!=null&&typeof te!="function"&&typeof te!="symbol"&&typeof te!="boolean"?m.type=te:m.removeAttribute("type"),g!=null?te==="number"?(g===0&&m.value===""||m.value!=g)&&(m.value=""+Mn(g)):m.value!==""+Mn(g)&&(m.value=""+Mn(g)):te!=="submit"&&te!=="reset"||m.removeAttribute("value"),g!=null?vi(m,te,Mn(g)):_!=null?vi(m,te,Mn(_)):M!=null&&m.removeAttribute("value"),F==null&&I!=null&&(m.defaultChecked=!!I),F!=null&&(m.checked=F&&typeof F!="function"&&typeof F!="symbol"),ke!=null&&typeof ke!="function"&&typeof ke!="symbol"&&typeof ke!="boolean"?m.name=""+Mn(ke):m.removeAttribute("name")}function Kn(m,g,_,M,F,I,te,ke){if(I!=null&&typeof I!="function"&&typeof I!="symbol"&&typeof I!="boolean"&&(m.type=I),g!=null||_!=null){if(!(I!=="submit"&&I!=="reset"||g!=null)){sn(m);return}_=_!=null?""+Mn(_):"",g=g!=null?""+Mn(g):_,ke||g===m.value||(m.value=g),m.defaultValue=g}M=M??F,M=typeof M!="function"&&typeof M!="symbol"&&!!M,m.checked=ke?m.checked:!!M,m.defaultChecked=!!M,te!=null&&typeof te!="function"&&typeof te!="symbol"&&typeof te!="boolean"&&(m.name=te),sn(m)}function vi(m,g,_){g==="number"&&Mr(m.ownerDocument)===m||m.defaultValue===""+_||(m.defaultValue=""+_)}function mi(m,g,_,M){if(m=m.options,g){g={};for(var F=0;F<_.length;F++)g["$"+_[F]]=!0;for(_=0;_"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),vr=!1;if(Ur)try{var Cl={};Object.defineProperty(Cl,"passive",{get:function(){vr=!0}}),window.addEventListener("test",Cl,Cl),window.removeEventListener("test",Cl,Cl)}catch{vr=!1}var Ma=null,oa=null,ic=null;function Br(){if(ic)return ic;var m,g=oa,_=g.length,M,F="value"in Ma?Ma.value:Ma.textContent,I=F.length;for(m=0;m<_&&g[m]===F[m];m++);var te=_-m;for(M=1;M<=te&&g[_-M]===F[I-M];M++);return ic=F.slice(m,1=vx),wI=" ",_I=!1;function SI(m,g){switch(m){case"keyup":return tse.indexOf(g.keyCode)!==-1;case"keydown":return g.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function NI(m){return m=m.detail,typeof m=="object"&&"data"in m?m.data:null}var fp=!1;function rse(m,g){switch(m){case"compositionend":return NI(g);case"keypress":return g.which!==32?null:(_I=!0,wI);case"textInput":return m=g.data,m===wI&&_I?null:m;default:return null}}function ise(m,g){if(fp)return m==="compositionend"||!GM&&SI(m,g)?(m=Br(),ic=oa=Ma=null,fp=!1,m):null;switch(m){case"paste":return null;case"keypress":if(!(g.ctrlKey||g.altKey||g.metaKey)||g.ctrlKey&&g.altKey){if(g.char&&1=g)return{node:_,offset:g-m};m=M}e:{for(;_;){if(_.nextSibling){_=_.nextSibling;break e}_=_.parentNode}_=void 0}_=jI(_)}}function RI(m,g){return m&&g?m===g?!0:m&&m.nodeType===3?!1:g&&g.nodeType===3?RI(m,g.parentNode):"contains"in m?m.contains(g):m.compareDocumentPosition?!!(m.compareDocumentPosition(g)&16):!1:!1}function FI(m){m=m!=null&&m.ownerDocument!=null&&m.ownerDocument.defaultView!=null?m.ownerDocument.defaultView:window;for(var g=Mr(m.document);g instanceof m.HTMLIFrameElement;){try{var _=typeof g.contentWindow.location.href=="string"}catch{_=!1}if(_)m=g.contentWindow;else break;g=Mr(m.document)}return g}function KM(m){var g=m&&m.nodeName&&m.nodeName.toLowerCase();return g&&(g==="input"&&(m.type==="text"||m.type==="search"||m.type==="tel"||m.type==="url"||m.type==="password")||g==="textarea"||m.contentEditable==="true")}var hse=Ur&&"documentMode"in document&&11>=document.documentMode,pp=null,XM=null,Nx=null,YM=!1;function LI(m,g,_){var M=_.window===_?_.document:_.nodeType===9?_:_.ownerDocument;YM||pp==null||pp!==Mr(M)||(M=pp,"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}),Nx&&Sx(Nx,M)||(Nx=M,M=Ww(XM,"onSelect"),0>=te,F-=te,ac=1<<32-Le(g)+F|_<Rn?(tr=en,en=null):tr=en.sibling;var dr=mt(nt,en,ut[Rn],St);if(dr===null){en===null&&(en=tr);break}m&&en&&dr.alternate===null&&g(nt,en),We=I(dr,We,Rn),ur===null?an=dr:ur.sibling=dr,ur=dr,en=tr}if(Rn===ut.length)return _(nt,en),rr&&Zc(nt,Rn),an;if(en===null){for(;RnRn?(tr=en,en=null):tr=en.sibling;var Vd=mt(nt,en,dr.value,St);if(Vd===null){en===null&&(en=tr);break}m&&en&&Vd.alternate===null&&g(nt,en),We=I(Vd,We,Rn),ur===null?an=Vd:ur.sibling=Vd,ur=Vd,en=tr}if(dr.done)return _(nt,en),rr&&Zc(nt,Rn),an;if(en===null){for(;!dr.done;Rn++,dr=ut.next())dr=Ct(nt,dr.value,St),dr!==null&&(We=I(dr,We,Rn),ur===null?an=dr:ur.sibling=dr,ur=dr);return rr&&Zc(nt,Rn),an}for(en=M(en);!dr.done;Rn++,dr=ut.next())dr=gt(en,nt,Rn,dr.value,St),dr!==null&&(m&&dr.alternate!==null&&en.delete(dr.key===null?Rn:dr.key),We=I(dr,We,Rn),ur===null?an=dr:ur.sibling=dr,ur=dr);return m&&en.forEach(function(joe){return g(nt,joe)}),rr&&Zc(nt,Rn),an}function Er(nt,We,ut,St){if(typeof ut=="object"&&ut!==null&&ut.type===y&&ut.key===null&&(ut=ut.props.children),typeof ut=="object"&&ut!==null){switch(ut.$$typeof){case p:e:{for(var an=ut.key;We!==null;){if(We.key===an){if(an=ut.type,an===y){if(We.tag===7){_(nt,We.sibling),St=F(We,ut.props.children),St.return=nt,nt=St;break e}}else if(We.elementType===an||typeof an=="object"&&an!==null&&an.$$typeof===j&&Nm(an)===We.type){_(nt,We.sibling),St=F(We,ut.props),Ex(St,ut),St.return=nt,nt=St;break e}_(nt,We);break}else g(nt,We);We=We.sibling}ut.type===y?(St=ym(ut.props.children,nt.mode,St,ut.key),St.return=nt,nt=St):(St=uw(ut.type,ut.key,ut.props,null,nt.mode,St),Ex(St,ut),St.return=nt,nt=St)}return te(nt);case x:e:{for(an=ut.key;We!==null;){if(We.key===an)if(We.tag===4&&We.stateNode.containerInfo===ut.containerInfo&&We.stateNode.implementation===ut.implementation){_(nt,We.sibling),St=F(We,ut.children||[]),St.return=nt,nt=St;break e}else{_(nt,We);break}else g(nt,We);We=We.sibling}St=rA(ut,nt.mode,St),St.return=nt,nt=St}return te(nt);case j:return ut=Nm(ut),Er(nt,We,ut,St)}if(G(ut))return Qt(nt,We,ut,St);if(O(ut)){if(an=O(ut),typeof an!="function")throw Error(r(150));return ut=an.call(ut),mn(nt,We,ut,St)}if(typeof ut.then=="function")return Er(nt,We,bw(ut),St);if(ut.$$typeof===N)return Er(nt,We,mw(nt,ut),St);xw(nt,ut)}return typeof ut=="string"&&ut!==""||typeof ut=="number"||typeof ut=="bigint"?(ut=""+ut,We!==null&&We.tag===6?(_(nt,We.sibling),St=F(We,ut),St.return=nt,nt=St):(_(nt,We),St=nA(ut,nt.mode,St),St.return=nt,nt=St),te(nt)):_(nt,We)}return function(nt,We,ut,St){try{Tx=0;var an=Er(nt,We,ut,St);return Cp=null,an}catch(en){if(en===kp||en===pw)throw en;var ur=fo(29,en,null,nt.mode);return ur.lanes=St,ur.return=nt,ur}}}var Cm=a5(!0),s5=a5(!1),Md=!1;function pA(m){m.updateQueue={baseState:m.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function gA(m,g){m=m.updateQueue,g.updateQueue===m&&(g.updateQueue={baseState:m.baseState,firstBaseUpdate:m.firstBaseUpdate,lastBaseUpdate:m.lastBaseUpdate,shared:m.shared,callbacks:null})}function Ad(m){return{lane:m,tag:0,payload:null,callback:null,next:null}}function Td(m,g,_){var M=m.updateQueue;if(M===null)return null;if(M=M.shared,(pr&2)!==0){var F=M.pending;return F===null?g.next=g:(g.next=F.next,F.next=g),M.pending=g,g=cw(m),$I(m,null,_),g}return lw(m,M,g,_),cw(m)}function Px(m,g,_){if(g=g.updateQueue,g!==null&&(g=g.shared,(_&4194048)!==0)){var M=g.lanes;M&=m.pendingLanes,_|=M,g.lanes=_,xt(m,_)}}function bA(m,g){var _=m.updateQueue,M=m.alternate;if(M!==null&&(M=M.updateQueue,_===M)){var F=null,I=null;if(_=_.firstBaseUpdate,_!==null){do{var te={lane:_.lane,tag:_.tag,payload:_.payload,callback:null,next:null};I===null?F=I=te:I=I.next=te,_=_.next}while(_!==null);I===null?F=I=g:I=I.next=g}else F=I=g;_={baseState:M.baseState,firstBaseUpdate:F,lastBaseUpdate:I,shared:M.shared,callbacks:M.callbacks},m.updateQueue=_;return}m=_.lastBaseUpdate,m===null?_.firstBaseUpdate=g:m.next=g,_.lastBaseUpdate=g}var xA=!1;function jx(){if(xA){var m=Np;if(m!==null)throw m}}function Dx(m,g,_,M){xA=!1;var F=m.updateQueue;Md=!1;var I=F.firstBaseUpdate,te=F.lastBaseUpdate,ke=F.shared.pending;if(ke!==null){F.shared.pending=null;var Be=ke,ht=Be.next;Be.next=null,te===null?I=ht:te.next=ht,te=Be;var yt=m.alternate;yt!==null&&(yt=yt.updateQueue,ke=yt.lastBaseUpdate,ke!==te&&(ke===null?yt.firstBaseUpdate=ht:ke.next=ht,yt.lastBaseUpdate=Be))}if(I!==null){var Ct=F.baseState;te=0,yt=ht=Be=null,ke=I;do{var mt=ke.lane&-536870913,gt=mt!==ke.lane;if(gt?(er&mt)===mt:(M&mt)===mt){mt!==0&&mt===Sp&&(xA=!0),yt!==null&&(yt=yt.next={lane:0,tag:ke.tag,payload:ke.payload,callback:null,next:null});e:{var Qt=m,mn=ke;mt=g;var Er=_;switch(mn.tag){case 1:if(Qt=mn.payload,typeof Qt=="function"){Ct=Qt.call(Er,Ct,mt);break e}Ct=Qt;break e;case 3:Qt.flags=Qt.flags&-65537|128;case 0:if(Qt=mn.payload,mt=typeof Qt=="function"?Qt.call(Er,Ct,mt):Qt,mt==null)break e;Ct=h({},Ct,mt);break e;case 2:Md=!0}}mt=ke.callback,mt!==null&&(m.flags|=64,gt&&(m.flags|=8192),gt=F.callbacks,gt===null?F.callbacks=[mt]:gt.push(mt))}else gt={lane:mt,tag:ke.tag,payload:ke.payload,callback:ke.callback,next:null},yt===null?(ht=yt=gt,Be=Ct):yt=yt.next=gt,te|=mt;if(ke=ke.next,ke===null){if(ke=F.shared.pending,ke===null)break;gt=ke,ke=gt.next,gt.next=null,F.lastBaseUpdate=gt,F.shared.pending=null}}while(!0);yt===null&&(Be=Ct),F.baseState=Be,F.firstBaseUpdate=ht,F.lastBaseUpdate=yt,I===null&&(F.shared.lanes=0),Rd|=te,m.lanes=te,m.memoizedState=Ct}}function o5(m,g){if(typeof m!="function")throw Error(r(191,m));m.call(g)}function l5(m,g){var _=m.callbacks;if(_!==null)for(m.callbacks=null,m=0;m<_.length;m++)o5(_[m],g)}var Mp=$(null),yw=$(0);function c5(m,g){m=cu,ee(yw,m),ee(Mp,g),cu=m|g.baseLanes}function yA(){ee(yw,cu),ee(Mp,Mp.current)}function vA(){cu=yw.current,X(Mp),X(yw)}var po=$(null),Vo=null;function Ed(m){var g=m.alternate;ee(Ii,Ii.current&1),ee(po,m),Vo===null&&(g===null||Mp.current!==null||g.memoizedState!==null)&&(Vo=m)}function wA(m){ee(Ii,Ii.current),ee(po,m),Vo===null&&(Vo=m)}function u5(m){m.tag===22?(ee(Ii,Ii.current),ee(po,m),Vo===null&&(Vo=m)):Pd()}function Pd(){ee(Ii,Ii.current),ee(po,po.current)}function go(m){X(po),Vo===m&&(Vo=null),X(Ii)}var Ii=$(0);function vw(m){for(var g=m;g!==null;){if(g.tag===13){var _=g.memoizedState;if(_!==null&&(_=_.dehydrated,_===null||MT(_)||AT(_)))return g}else if(g.tag===19&&(g.memoizedProps.revealOrder==="forwards"||g.memoizedProps.revealOrder==="backwards"||g.memoizedProps.revealOrder==="unstable_legacy-backwards"||g.memoizedProps.revealOrder==="together")){if((g.flags&128)!==0)return g}else if(g.child!==null){g.child.return=g,g=g.child;continue}if(g===m)break;for(;g.sibling===null;){if(g.return===null||g.return===m)return null;g=g.return}g.sibling.return=g.return,g=g.sibling}return null}var tu=0,En=null,Ar=null,Wi=null,ww=!1,Ap=!1,Mm=!1,_w=0,Rx=0,Tp=null,_se=0;function Ni(){throw Error(r(321))}function _A(m,g){if(g===null)return!1;for(var _=0;_I?I:8;var te=q.T,ke={};q.T=ke,OA(m,!1,g,_);try{var Be=F(),ht=q.S;if(ht!==null&&ht(ke,Be),Be!==null&&typeof Be=="object"&&typeof Be.then=="function"){var yt=wse(Be,M);Lx(m,g,yt,yo(m))}else Lx(m,g,M,yo(m))}catch(Ct){Lx(m,g,{then:function(){},status:"rejected",reason:Ct},yo())}finally{z.p=I,te!==null&&ke.types!==null&&(te.types=ke.types),q.T=te}}function Mse(){}function FA(m,g,_,M){if(m.tag!==5)throw Error(r(476));var F=U5(m).queue;z5(m,F,g,K,_===null?Mse:function(){return B5(m),_(M)})}function U5(m){var g=m.memoizedState;if(g!==null)return g;g={memoizedState:K,baseState:K,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:nu,lastRenderedState:K},next:null};var _={};return g.next={memoizedState:_,baseState:_,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:nu,lastRenderedState:_},next:null},m.memoizedState=g,m=m.alternate,m!==null&&(m.memoizedState=g),g}function B5(m){var g=U5(m);g.next===null&&(g=m.alternate.memoizedState),Lx(m,g.next.queue,{},yo())}function LA(){return $a(Jx)}function H5(){return zi().memoizedState}function $5(){return zi().memoizedState}function Ase(m){for(var g=m.return;g!==null;){switch(g.tag){case 24:case 3:var _=yo();m=Ad(_);var M=Td(g,m,_);M!==null&&(qs(M,g,_),Px(M,g,_)),g={cache:dA()},m.payload=g;return}g=g.return}}function Tse(m,g,_){var M=yo();_={lane:M,revertLane:0,gesture:null,action:_,hasEagerState:!1,eagerState:null,next:null},Aw(m)?G5(g,_):(_=eA(m,g,_,M),_!==null&&(qs(_,m,M),q5(_,g,M)))}function V5(m,g,_){var M=yo();Lx(m,g,_,M)}function Lx(m,g,_,M){var F={lane:M,revertLane:0,gesture:null,action:_,hasEagerState:!1,eagerState:null,next:null};if(Aw(m))G5(g,F);else{var I=m.alternate;if(m.lanes===0&&(I===null||I.lanes===0)&&(I=g.lastRenderedReducer,I!==null))try{var te=g.lastRenderedState,ke=I(te,_);if(F.hasEagerState=!0,F.eagerState=ke,mo(ke,te))return lw(m,g,F,0),Hr===null&&ow(),!1}catch{}if(_=eA(m,g,F,M),_!==null)return qs(_,m,M),q5(_,g,M),!0}return!1}function OA(m,g,_,M){if(M={lane:2,revertLane:pT(),gesture:null,action:M,hasEagerState:!1,eagerState:null,next:null},Aw(m)){if(g)throw Error(r(479))}else g=eA(m,_,M,2),g!==null&&qs(g,m,2)}function Aw(m){var g=m.alternate;return m===En||g!==null&&g===En}function G5(m,g){Ap=ww=!0;var _=m.pending;_===null?g.next=g:(g.next=_.next,_.next=g),m.pending=g}function q5(m,g,_){if((_&4194048)!==0){var M=g.lanes;M&=m.pendingLanes,_|=M,g.lanes=_,xt(m,_)}}var Ox={readContext:$a,use:Nw,useCallback:Ni,useContext:Ni,useEffect:Ni,useImperativeHandle:Ni,useLayoutEffect:Ni,useInsertionEffect:Ni,useMemo:Ni,useReducer:Ni,useRef:Ni,useState:Ni,useDebugValue:Ni,useDeferredValue:Ni,useTransition:Ni,useSyncExternalStore:Ni,useId:Ni,useHostTransitionStatus:Ni,useFormState:Ni,useActionState:Ni,useOptimistic:Ni,useMemoCache:Ni,useCacheRefresh:Ni};Ox.useEffectEvent=Ni;var W5={readContext:$a,use:Nw,useCallback:function(m,g){return Cs().memoizedState=[m,g===void 0?null:g],m},useContext:$a,useEffect:E5,useImperativeHandle:function(m,g,_){_=_!=null?_.concat([m]):null,Cw(4194308,4,R5.bind(null,g,m),_)},useLayoutEffect:function(m,g){return Cw(4194308,4,m,g)},useInsertionEffect:function(m,g){Cw(4,2,m,g)},useMemo:function(m,g){var _=Cs();g=g===void 0?null:g;var M=m();if(Mm){le(!0);try{m()}finally{le(!1)}}return _.memoizedState=[M,g],M},useReducer:function(m,g,_){var M=Cs();if(_!==void 0){var F=_(g);if(Mm){le(!0);try{_(g)}finally{le(!1)}}}else F=g;return M.memoizedState=M.baseState=F,m={pending:null,lanes:0,dispatch:null,lastRenderedReducer:m,lastRenderedState:F},M.queue=m,m=m.dispatch=Tse.bind(null,En,m),[M.memoizedState,m]},useRef:function(m){var g=Cs();return m={current:m},g.memoizedState=m},useState:function(m){m=EA(m);var g=m.queue,_=V5.bind(null,En,g);return g.dispatch=_,[m.memoizedState,_]},useDebugValue:DA,useDeferredValue:function(m,g){var _=Cs();return RA(_,m,g)},useTransition:function(){var m=EA(!1);return m=z5.bind(null,En,m.queue,!0,!1),Cs().memoizedState=m,[!1,m]},useSyncExternalStore:function(m,g,_){var M=En,F=Cs();if(rr){if(_===void 0)throw Error(r(407));_=_()}else{if(_=g(),Hr===null)throw Error(r(349));(er&127)!==0||f5(M,g,_)}F.memoizedState=_;var I={value:_,getSnapshot:g};return F.queue=I,E5(g5.bind(null,M,I,m),[m]),M.flags|=2048,Ep(9,{destroy:void 0},p5.bind(null,M,I,_,g),null),_},useId:function(){var m=Cs(),g=Hr.identifierPrefix;if(rr){var _=sc,M=ac;_=(M&~(1<<32-Le(M)-1)).toString(32)+_,g="_"+g+"R_"+_,_=_w++,0<_&&(g+="H"+_.toString(32)),g+="_"}else _=_se++,g="_"+g+"r_"+_.toString(32)+"_";return m.memoizedState=g},useHostTransitionStatus:LA,useFormState:k5,useActionState:k5,useOptimistic:function(m){var g=Cs();g.memoizedState=g.baseState=m;var _={pending:null,lanes:0,dispatch:null,lastRenderedReducer:null,lastRenderedState:null};return g.queue=_,g=OA.bind(null,En,!0,_),_.dispatch=g,[m,g]},useMemoCache:MA,useCacheRefresh:function(){return Cs().memoizedState=Ase.bind(null,En)},useEffectEvent:function(m){var g=Cs(),_={impl:m};return g.memoizedState=_,function(){if((pr&2)!==0)throw Error(r(440));return _.impl.apply(void 0,arguments)}}},IA={readContext:$a,use:Nw,useCallback:L5,useContext:$a,useEffect:jA,useImperativeHandle:F5,useInsertionEffect:j5,useLayoutEffect:D5,useMemo:O5,useReducer:kw,useRef:T5,useState:function(){return kw(nu)},useDebugValue:DA,useDeferredValue:function(m,g){var _=zi();return I5(_,Ar.memoizedState,m,g)},useTransition:function(){var m=kw(nu)[0],g=zi().memoizedState;return[typeof m=="boolean"?m:Fx(m),g]},useSyncExternalStore:m5,useId:H5,useHostTransitionStatus:LA,useFormState:C5,useActionState:C5,useOptimistic:function(m,g){var _=zi();return y5(_,Ar,m,g)},useMemoCache:MA,useCacheRefresh:$5};IA.useEffectEvent=P5;var K5={readContext:$a,use:Nw,useCallback:L5,useContext:$a,useEffect:jA,useImperativeHandle:F5,useInsertionEffect:j5,useLayoutEffect:D5,useMemo:O5,useReducer:TA,useRef:T5,useState:function(){return TA(nu)},useDebugValue:DA,useDeferredValue:function(m,g){var _=zi();return Ar===null?RA(_,m,g):I5(_,Ar.memoizedState,m,g)},useTransition:function(){var m=TA(nu)[0],g=zi().memoizedState;return[typeof m=="boolean"?m:Fx(m),g]},useSyncExternalStore:m5,useId:H5,useHostTransitionStatus:LA,useFormState:A5,useActionState:A5,useOptimistic:function(m,g){var _=zi();return Ar!==null?y5(_,Ar,m,g):(_.baseState=m,[m,_.queue.dispatch])},useMemoCache:MA,useCacheRefresh:$5};K5.useEffectEvent=P5;function zA(m,g,_,M){g=m.memoizedState,_=_(M,g),_=_==null?g:h({},g,_),m.memoizedState=_,m.lanes===0&&(m.updateQueue.baseState=_)}var UA={enqueueSetState:function(m,g,_){m=m._reactInternals;var M=yo(),F=Ad(M);F.payload=g,_!=null&&(F.callback=_),g=Td(m,F,M),g!==null&&(qs(g,m,M),Px(g,m,M))},enqueueReplaceState:function(m,g,_){m=m._reactInternals;var M=yo(),F=Ad(M);F.tag=1,F.payload=g,_!=null&&(F.callback=_),g=Td(m,F,M),g!==null&&(qs(g,m,M),Px(g,m,M))},enqueueForceUpdate:function(m,g){m=m._reactInternals;var _=yo(),M=Ad(_);M.tag=2,g!=null&&(M.callback=g),g=Td(m,M,_),g!==null&&(qs(g,m,_),Px(g,m,_))}};function X5(m,g,_,M,F,I,te){return m=m.stateNode,typeof m.shouldComponentUpdate=="function"?m.shouldComponentUpdate(M,I,te):g.prototype&&g.prototype.isPureReactComponent?!Sx(_,M)||!Sx(F,I):!0}function Y5(m,g,_,M){m=g.state,typeof g.componentWillReceiveProps=="function"&&g.componentWillReceiveProps(_,M),typeof g.UNSAFE_componentWillReceiveProps=="function"&&g.UNSAFE_componentWillReceiveProps(_,M),g.state!==m&&UA.enqueueReplaceState(g,g.state,null)}function Am(m,g){var _=g;if("ref"in g){_={};for(var M in g)M!=="ref"&&(_[M]=g[M])}if(m=m.defaultProps){_===g&&(_=h({},_));for(var F in m)_[F]===void 0&&(_[F]=m[F])}return _}function Q5(m){sw(m)}function Z5(m){console.error(m)}function J5(m){sw(m)}function Tw(m,g){try{var _=m.onUncaughtError;_(g.value,{componentStack:g.stack})}catch(M){setTimeout(function(){throw M})}}function ez(m,g,_){try{var M=m.onCaughtError;M(_.value,{componentStack:_.stack,errorBoundary:g.tag===1?g.stateNode:null})}catch(F){setTimeout(function(){throw F})}}function BA(m,g,_){return _=Ad(_),_.tag=3,_.payload={element:null},_.callback=function(){Tw(m,g)},_}function tz(m){return m=Ad(m),m.tag=3,m}function nz(m,g,_,M){var F=_.type.getDerivedStateFromError;if(typeof F=="function"){var I=M.value;m.payload=function(){return F(I)},m.callback=function(){ez(g,_,M)}}var te=_.stateNode;te!==null&&typeof te.componentDidCatch=="function"&&(m.callback=function(){ez(g,_,M),typeof F!="function"&&(Fd===null?Fd=new Set([this]):Fd.add(this));var ke=M.stack;this.componentDidCatch(M.value,{componentStack:ke!==null?ke:""})})}function Ese(m,g,_,M,F){if(_.flags|=32768,M!==null&&typeof M=="object"&&typeof M.then=="function"){if(g=_.alternate,g!==null&&_p(g,_,F,!0),_=po.current,_!==null){switch(_.tag){case 31:case 13:return Vo===null?Bw():_.alternate===null&&ki===0&&(ki=3),_.flags&=-257,_.flags|=65536,_.lanes=F,M===gw?_.flags|=16384:(g=_.updateQueue,g===null?_.updateQueue=new Set([M]):g.add(M),hT(m,M,F)),!1;case 22:return _.flags|=65536,M===gw?_.flags|=16384:(g=_.updateQueue,g===null?(g={transitions:null,markerInstances:null,retryQueue:new Set([M])},_.updateQueue=g):(_=g.retryQueue,_===null?g.retryQueue=new Set([M]):_.add(M)),hT(m,M,F)),!1}throw Error(r(435,_.tag))}return hT(m,M,F),Bw(),!1}if(rr)return g=po.current,g!==null?((g.flags&65536)===0&&(g.flags|=256),g.flags|=65536,g.lanes=F,M!==sA&&(m=Error(r(422),{cause:M}),Cx(Uo(m,_)))):(M!==sA&&(g=Error(r(423),{cause:M}),Cx(Uo(g,_))),m=m.current.alternate,m.flags|=65536,F&=-F,m.lanes|=F,M=Uo(M,_),F=BA(m.stateNode,M,F),bA(m,F),ki!==4&&(ki=2)),!1;var I=Error(r(520),{cause:M});if(I=Uo(I,_),Gx===null?Gx=[I]:Gx.push(I),ki!==4&&(ki=2),g===null)return!0;M=Uo(M,_),_=g;do{switch(_.tag){case 3:return _.flags|=65536,m=F&-F,_.lanes|=m,m=BA(_.stateNode,M,m),bA(_,m),!1;case 1:if(g=_.type,I=_.stateNode,(_.flags&128)===0&&(typeof g.getDerivedStateFromError=="function"||I!==null&&typeof I.componentDidCatch=="function"&&(Fd===null||!Fd.has(I))))return _.flags|=65536,F&=-F,_.lanes|=F,F=tz(F),nz(F,m,_,M),bA(_,F),!1}_=_.return}while(_!==null);return!1}var HA=Error(r(461)),Ki=!1;function Va(m,g,_,M){g.child=m===null?s5(g,null,_,M):Cm(g,m.child,_,M)}function rz(m,g,_,M,F){_=_.render;var I=g.ref;if("ref"in M){var te={};for(var ke in M)ke!=="ref"&&(te[ke]=M[ke])}else te=M;return _m(g),M=SA(m,g,_,te,I,F),ke=NA(),m!==null&&!Ki?(kA(m,g,F),ru(m,g,F)):(rr&&ke&&iA(g),g.flags|=1,Va(m,g,M,F),g.child)}function iz(m,g,_,M,F){if(m===null){var I=_.type;return typeof I=="function"&&!tA(I)&&I.defaultProps===void 0&&_.compare===null?(g.tag=15,g.type=I,az(m,g,I,M,F)):(m=uw(_.type,null,M,g,g.mode,F),m.ref=g.ref,m.return=g,g.child=m)}if(I=m.child,!YA(m,F)){var te=I.memoizedProps;if(_=_.compare,_=_!==null?_:Sx,_(te,M)&&m.ref===g.ref)return ru(m,g,F)}return g.flags|=1,m=Qc(I,M),m.ref=g.ref,m.return=g,g.child=m}function az(m,g,_,M,F){if(m!==null){var I=m.memoizedProps;if(Sx(I,M)&&m.ref===g.ref)if(Ki=!1,g.pendingProps=M=I,YA(m,F))(m.flags&131072)!==0&&(Ki=!0);else return g.lanes=m.lanes,ru(m,g,F)}return $A(m,g,_,M,F)}function sz(m,g,_,M){var F=M.children,I=m!==null?m.memoizedState:null;if(m===null&&g.stateNode===null&&(g.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),M.mode==="hidden"){if((g.flags&128)!==0){if(I=I!==null?I.baseLanes|_:_,m!==null){for(M=g.child=m.child,F=0;M!==null;)F=F|M.lanes|M.childLanes,M=M.sibling;M=F&~I}else M=0,g.child=null;return oz(m,g,I,_,M)}if((_&536870912)!==0)g.memoizedState={baseLanes:0,cachePool:null},m!==null&&fw(g,I!==null?I.cachePool:null),I!==null?c5(g,I):yA(),u5(g);else return M=g.lanes=536870912,oz(m,g,I!==null?I.baseLanes|_:_,_,M)}else I!==null?(fw(g,I.cachePool),c5(g,I),Pd(),g.memoizedState=null):(m!==null&&fw(g,null),yA(),Pd());return Va(m,g,F,_),g.child}function Ix(m,g){return m!==null&&m.tag===22||g.stateNode!==null||(g.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),g.sibling}function oz(m,g,_,M,F){var I=mA();return I=I===null?null:{parent:qi._currentValue,pool:I},g.memoizedState={baseLanes:_,cachePool:I},m!==null&&fw(g,null),yA(),u5(g),m!==null&&_p(m,g,M,!0),g.childLanes=F,null}function Ew(m,g){return g=jw({mode:g.mode,children:g.children},m.mode),g.ref=m.ref,m.child=g,g.return=m,g}function lz(m,g,_){return Cm(g,m.child,null,_),m=Ew(g,g.pendingProps),m.flags|=2,go(g),g.memoizedState=null,m}function Pse(m,g,_){var M=g.pendingProps,F=(g.flags&128)!==0;if(g.flags&=-129,m===null){if(rr){if(M.mode==="hidden")return m=Ew(g,M),g.lanes=536870912,Ix(null,m);if(wA(g),(m=Jr)?(m=vU(m,$o),m=m!==null&&m.data==="&"?m:null,m!==null&&(g.memoizedState={dehydrated:m,treeContext:Sd!==null?{id:ac,overflow:sc}:null,retryLane:536870912,hydrationErrors:null},_=GI(m),_.return=g,g.child=_,Ha=g,Jr=null)):m=null,m===null)throw kd(g);return g.lanes=536870912,null}return Ew(g,M)}var I=m.memoizedState;if(I!==null){var te=I.dehydrated;if(wA(g),F)if(g.flags&256)g.flags&=-257,g=lz(m,g,_);else if(g.memoizedState!==null)g.child=m.child,g.flags|=128,g=null;else throw Error(r(558));else if(Ki||_p(m,g,_,!1),F=(_&m.childLanes)!==0,Ki||F){if(M=Hr,M!==null&&(te=wt(M,_),te!==0&&te!==I.retryLane))throw I.retryLane=te,xm(m,te),qs(M,m,te),HA;Bw(),g=lz(m,g,_)}else m=I.treeContext,Jr=Go(te.nextSibling),Ha=g,rr=!0,Nd=null,$o=!1,m!==null&&KI(g,m),g=Ew(g,M),g.flags|=4096;return g}return m=Qc(m.child,{mode:M.mode,children:M.children}),m.ref=g.ref,g.child=m,m.return=g,m}function Pw(m,g){var _=g.ref;if(_===null)m!==null&&m.ref!==null&&(g.flags|=4194816);else{if(typeof _!="function"&&typeof _!="object")throw Error(r(284));(m===null||m.ref!==_)&&(g.flags|=4194816)}}function $A(m,g,_,M,F){return _m(g),_=SA(m,g,_,M,void 0,F),M=NA(),m!==null&&!Ki?(kA(m,g,F),ru(m,g,F)):(rr&&M&&iA(g),g.flags|=1,Va(m,g,_,F),g.child)}function cz(m,g,_,M,F,I){return _m(g),g.updateQueue=null,_=h5(g,M,_,F),d5(m),M=NA(),m!==null&&!Ki?(kA(m,g,I),ru(m,g,I)):(rr&&M&&iA(g),g.flags|=1,Va(m,g,_,I),g.child)}function uz(m,g,_,M,F){if(_m(g),g.stateNode===null){var I=xp,te=_.contextType;typeof te=="object"&&te!==null&&(I=$a(te)),I=new _(M,I),g.memoizedState=I.state!==null&&I.state!==void 0?I.state:null,I.updater=UA,g.stateNode=I,I._reactInternals=g,I=g.stateNode,I.props=M,I.state=g.memoizedState,I.refs={},pA(g),te=_.contextType,I.context=typeof te=="object"&&te!==null?$a(te):xp,I.state=g.memoizedState,te=_.getDerivedStateFromProps,typeof te=="function"&&(zA(g,_,te,M),I.state=g.memoizedState),typeof _.getDerivedStateFromProps=="function"||typeof I.getSnapshotBeforeUpdate=="function"||typeof I.UNSAFE_componentWillMount!="function"&&typeof I.componentWillMount!="function"||(te=I.state,typeof I.componentWillMount=="function"&&I.componentWillMount(),typeof I.UNSAFE_componentWillMount=="function"&&I.UNSAFE_componentWillMount(),te!==I.state&&UA.enqueueReplaceState(I,I.state,null),Dx(g,M,I,F),jx(),I.state=g.memoizedState),typeof I.componentDidMount=="function"&&(g.flags|=4194308),M=!0}else if(m===null){I=g.stateNode;var ke=g.memoizedProps,Be=Am(_,ke);I.props=Be;var ht=I.context,yt=_.contextType;te=xp,typeof yt=="object"&&yt!==null&&(te=$a(yt));var Ct=_.getDerivedStateFromProps;yt=typeof Ct=="function"||typeof I.getSnapshotBeforeUpdate=="function",ke=g.pendingProps!==ke,yt||typeof I.UNSAFE_componentWillReceiveProps!="function"&&typeof I.componentWillReceiveProps!="function"||(ke||ht!==te)&&Y5(g,I,M,te),Md=!1;var mt=g.memoizedState;I.state=mt,Dx(g,M,I,F),jx(),ht=g.memoizedState,ke||mt!==ht||Md?(typeof Ct=="function"&&(zA(g,_,Ct,M),ht=g.memoizedState),(Be=Md||X5(g,_,Be,M,mt,ht,te))?(yt||typeof I.UNSAFE_componentWillMount!="function"&&typeof I.componentWillMount!="function"||(typeof I.componentWillMount=="function"&&I.componentWillMount(),typeof I.UNSAFE_componentWillMount=="function"&&I.UNSAFE_componentWillMount()),typeof I.componentDidMount=="function"&&(g.flags|=4194308)):(typeof I.componentDidMount=="function"&&(g.flags|=4194308),g.memoizedProps=M,g.memoizedState=ht),I.props=M,I.state=ht,I.context=te,M=Be):(typeof I.componentDidMount=="function"&&(g.flags|=4194308),M=!1)}else{I=g.stateNode,gA(m,g),te=g.memoizedProps,yt=Am(_,te),I.props=yt,Ct=g.pendingProps,mt=I.context,ht=_.contextType,Be=xp,typeof ht=="object"&&ht!==null&&(Be=$a(ht)),ke=_.getDerivedStateFromProps,(ht=typeof ke=="function"||typeof I.getSnapshotBeforeUpdate=="function")||typeof I.UNSAFE_componentWillReceiveProps!="function"&&typeof I.componentWillReceiveProps!="function"||(te!==Ct||mt!==Be)&&Y5(g,I,M,Be),Md=!1,mt=g.memoizedState,I.state=mt,Dx(g,M,I,F),jx();var gt=g.memoizedState;te!==Ct||mt!==gt||Md||m!==null&&m.dependencies!==null&&hw(m.dependencies)?(typeof ke=="function"&&(zA(g,_,ke,M),gt=g.memoizedState),(yt=Md||X5(g,_,yt,M,mt,gt,Be)||m!==null&&m.dependencies!==null&&hw(m.dependencies))?(ht||typeof I.UNSAFE_componentWillUpdate!="function"&&typeof I.componentWillUpdate!="function"||(typeof I.componentWillUpdate=="function"&&I.componentWillUpdate(M,gt,Be),typeof I.UNSAFE_componentWillUpdate=="function"&&I.UNSAFE_componentWillUpdate(M,gt,Be)),typeof I.componentDidUpdate=="function"&&(g.flags|=4),typeof I.getSnapshotBeforeUpdate=="function"&&(g.flags|=1024)):(typeof I.componentDidUpdate!="function"||te===m.memoizedProps&&mt===m.memoizedState||(g.flags|=4),typeof I.getSnapshotBeforeUpdate!="function"||te===m.memoizedProps&&mt===m.memoizedState||(g.flags|=1024),g.memoizedProps=M,g.memoizedState=gt),I.props=M,I.state=gt,I.context=Be,M=yt):(typeof I.componentDidUpdate!="function"||te===m.memoizedProps&&mt===m.memoizedState||(g.flags|=4),typeof I.getSnapshotBeforeUpdate!="function"||te===m.memoizedProps&&mt===m.memoizedState||(g.flags|=1024),M=!1)}return I=M,Pw(m,g),M=(g.flags&128)!==0,I||M?(I=g.stateNode,_=M&&typeof _.getDerivedStateFromError!="function"?null:I.render(),g.flags|=1,m!==null&&M?(g.child=Cm(g,m.child,null,F),g.child=Cm(g,null,_,F)):Va(m,g,_,F),g.memoizedState=I.state,m=g.child):m=ru(m,g,F),m}function dz(m,g,_,M){return vm(),g.flags|=256,Va(m,g,_,M),g.child}var VA={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function GA(m){return{baseLanes:m,cachePool:e5()}}function qA(m,g,_){return m=m!==null?m.childLanes&~_:0,g&&(m|=xo),m}function hz(m,g,_){var M=g.pendingProps,F=!1,I=(g.flags&128)!==0,te;if((te=I)||(te=m!==null&&m.memoizedState===null?!1:(Ii.current&2)!==0),te&&(F=!0,g.flags&=-129),te=(g.flags&32)!==0,g.flags&=-33,m===null){if(rr){if(F?Ed(g):Pd(),(m=Jr)?(m=vU(m,$o),m=m!==null&&m.data!=="&"?m:null,m!==null&&(g.memoizedState={dehydrated:m,treeContext:Sd!==null?{id:ac,overflow:sc}:null,retryLane:536870912,hydrationErrors:null},_=GI(m),_.return=g,g.child=_,Ha=g,Jr=null)):m=null,m===null)throw kd(g);return AT(m)?g.lanes=32:g.lanes=536870912,null}var ke=M.children;return M=M.fallback,F?(Pd(),F=g.mode,ke=jw({mode:"hidden",children:ke},F),M=ym(M,F,_,null),ke.return=g,M.return=g,ke.sibling=M,g.child=ke,M=g.child,M.memoizedState=GA(_),M.childLanes=qA(m,te,_),g.memoizedState=VA,Ix(null,M)):(Ed(g),WA(g,ke))}var Be=m.memoizedState;if(Be!==null&&(ke=Be.dehydrated,ke!==null)){if(I)g.flags&256?(Ed(g),g.flags&=-257,g=KA(m,g,_)):g.memoizedState!==null?(Pd(),g.child=m.child,g.flags|=128,g=null):(Pd(),ke=M.fallback,F=g.mode,M=jw({mode:"visible",children:M.children},F),ke=ym(ke,F,_,null),ke.flags|=2,M.return=g,ke.return=g,M.sibling=ke,g.child=M,Cm(g,m.child,null,_),M=g.child,M.memoizedState=GA(_),M.childLanes=qA(m,te,_),g.memoizedState=VA,g=Ix(null,M));else if(Ed(g),AT(ke)){if(te=ke.nextSibling&&ke.nextSibling.dataset,te)var ht=te.dgst;te=ht,M=Error(r(419)),M.stack="",M.digest=te,Cx({value:M,source:null,stack:null}),g=KA(m,g,_)}else if(Ki||_p(m,g,_,!1),te=(_&m.childLanes)!==0,Ki||te){if(te=Hr,te!==null&&(M=wt(te,_),M!==0&&M!==Be.retryLane))throw Be.retryLane=M,xm(m,M),qs(te,m,M),HA;MT(ke)||Bw(),g=KA(m,g,_)}else MT(ke)?(g.flags|=192,g.child=m.child,g=null):(m=Be.treeContext,Jr=Go(ke.nextSibling),Ha=g,rr=!0,Nd=null,$o=!1,m!==null&&KI(g,m),g=WA(g,M.children),g.flags|=4096);return g}return F?(Pd(),ke=M.fallback,F=g.mode,Be=m.child,ht=Be.sibling,M=Qc(Be,{mode:"hidden",children:M.children}),M.subtreeFlags=Be.subtreeFlags&65011712,ht!==null?ke=Qc(ht,ke):(ke=ym(ke,F,_,null),ke.flags|=2),ke.return=g,M.return=g,M.sibling=ke,g.child=M,Ix(null,M),M=g.child,ke=m.child.memoizedState,ke===null?ke=GA(_):(F=ke.cachePool,F!==null?(Be=qi._currentValue,F=F.parent!==Be?{parent:Be,pool:Be}:F):F=e5(),ke={baseLanes:ke.baseLanes|_,cachePool:F}),M.memoizedState=ke,M.childLanes=qA(m,te,_),g.memoizedState=VA,Ix(m.child,M)):(Ed(g),_=m.child,m=_.sibling,_=Qc(_,{mode:"visible",children:M.children}),_.return=g,_.sibling=null,m!==null&&(te=g.deletions,te===null?(g.deletions=[m],g.flags|=16):te.push(m)),g.child=_,g.memoizedState=null,_)}function WA(m,g){return g=jw({mode:"visible",children:g},m.mode),g.return=m,m.child=g}function jw(m,g){return m=fo(22,m,null,g),m.lanes=0,m}function KA(m,g,_){return Cm(g,m.child,null,_),m=WA(g,g.pendingProps.children),m.flags|=2,g.memoizedState=null,m}function mz(m,g,_){m.lanes|=g;var M=m.alternate;M!==null&&(M.lanes|=g),cA(m.return,g,_)}function XA(m,g,_,M,F,I){var te=m.memoizedState;te===null?m.memoizedState={isBackwards:g,rendering:null,renderingStartTime:0,last:M,tail:_,tailMode:F,treeForkCount:I}:(te.isBackwards=g,te.rendering=null,te.renderingStartTime=0,te.last=M,te.tail=_,te.tailMode=F,te.treeForkCount=I)}function fz(m,g,_){var M=g.pendingProps,F=M.revealOrder,I=M.tail;M=M.children;var te=Ii.current,ke=(te&2)!==0;if(ke?(te=te&1|2,g.flags|=128):te&=1,ee(Ii,te),Va(m,g,M,_),M=rr?kx:0,!ke&&m!==null&&(m.flags&128)!==0)e:for(m=g.child;m!==null;){if(m.tag===13)m.memoizedState!==null&&mz(m,_,g);else if(m.tag===19)mz(m,_,g);else if(m.child!==null){m.child.return=m,m=m.child;continue}if(m===g)break e;for(;m.sibling===null;){if(m.return===null||m.return===g)break e;m=m.return}m.sibling.return=m.return,m=m.sibling}switch(F){case"forwards":for(_=g.child,F=null;_!==null;)m=_.alternate,m!==null&&vw(m)===null&&(F=_),_=_.sibling;_=F,_===null?(F=g.child,g.child=null):(F=_.sibling,_.sibling=null),XA(g,!1,F,_,I,M);break;case"backwards":case"unstable_legacy-backwards":for(_=null,F=g.child,g.child=null;F!==null;){if(m=F.alternate,m!==null&&vw(m)===null){g.child=F;break}m=F.sibling,F.sibling=_,_=F,F=m}XA(g,!0,_,null,I,M);break;case"together":XA(g,!1,null,null,void 0,M);break;default:g.memoizedState=null}return g.child}function ru(m,g,_){if(m!==null&&(g.dependencies=m.dependencies),Rd|=g.lanes,(_&g.childLanes)===0)if(m!==null){if(_p(m,g,_,!1),(_&g.childLanes)===0)return null}else return null;if(m!==null&&g.child!==m.child)throw Error(r(153));if(g.child!==null){for(m=g.child,_=Qc(m,m.pendingProps),g.child=_,_.return=g;m.sibling!==null;)m=m.sibling,_=_.sibling=Qc(m,m.pendingProps),_.return=g;_.sibling=null}return g.child}function YA(m,g){return(m.lanes&g)!==0?!0:(m=m.dependencies,!!(m!==null&&hw(m)))}function jse(m,g,_){switch(g.tag){case 3:je(g,g.stateNode.containerInfo),Cd(g,qi,m.memoizedState.cache),vm();break;case 27:case 5:ce(g);break;case 4:je(g,g.stateNode.containerInfo);break;case 10:Cd(g,g.type,g.memoizedProps.value);break;case 31:if(g.memoizedState!==null)return g.flags|=128,wA(g),null;break;case 13:var M=g.memoizedState;if(M!==null)return M.dehydrated!==null?(Ed(g),g.flags|=128,null):(_&g.child.childLanes)!==0?hz(m,g,_):(Ed(g),m=ru(m,g,_),m!==null?m.sibling:null);Ed(g);break;case 19:var F=(m.flags&128)!==0;if(M=(_&g.childLanes)!==0,M||(_p(m,g,_,!1),M=(_&g.childLanes)!==0),F){if(M)return fz(m,g,_);g.flags|=128}if(F=g.memoizedState,F!==null&&(F.rendering=null,F.tail=null,F.lastEffect=null),ee(Ii,Ii.current),M)break;return null;case 22:return g.lanes=0,sz(m,g,_,g.pendingProps);case 24:Cd(g,qi,m.memoizedState.cache)}return ru(m,g,_)}function pz(m,g,_){if(m!==null)if(m.memoizedProps!==g.pendingProps)Ki=!0;else{if(!YA(m,_)&&(g.flags&128)===0)return Ki=!1,jse(m,g,_);Ki=(m.flags&131072)!==0}else Ki=!1,rr&&(g.flags&1048576)!==0&&WI(g,kx,g.index);switch(g.lanes=0,g.tag){case 16:e:{var M=g.pendingProps;if(m=Nm(g.elementType),g.type=m,typeof m=="function")tA(m)?(M=Am(m,M),g.tag=1,g=uz(null,g,m,M,_)):(g.tag=0,g=$A(null,g,m,M,_));else{if(m!=null){var F=m.$$typeof;if(F===A){g.tag=11,g=rz(null,g,m,M,_);break e}else if(F===k){g.tag=14,g=iz(null,g,m,M,_);break e}}throw g=R(m)||m,Error(r(306,g,""))}}return g;case 0:return $A(m,g,g.type,g.pendingProps,_);case 1:return M=g.type,F=Am(M,g.pendingProps),uz(m,g,M,F,_);case 3:e:{if(je(g,g.stateNode.containerInfo),m===null)throw Error(r(387));M=g.pendingProps;var I=g.memoizedState;F=I.element,gA(m,g),Dx(g,M,null,_);var te=g.memoizedState;if(M=te.cache,Cd(g,qi,M),M!==I.cache&&uA(g,[qi],_,!0),jx(),M=te.element,I.isDehydrated)if(I={element:M,isDehydrated:!1,cache:te.cache},g.updateQueue.baseState=I,g.memoizedState=I,g.flags&256){g=dz(m,g,M,_);break e}else if(M!==F){F=Uo(Error(r(424)),g),Cx(F),g=dz(m,g,M,_);break e}else for(m=g.stateNode.containerInfo,m.nodeType===9?m=m.body:m=m.nodeName==="HTML"?m.ownerDocument.body:m,Jr=Go(m.firstChild),Ha=g,rr=!0,Nd=null,$o=!0,_=s5(g,null,M,_),g.child=_;_;)_.flags=_.flags&-3|4096,_=_.sibling;else{if(vm(),M===F){g=ru(m,g,_);break e}Va(m,g,M,_)}g=g.child}return g;case 26:return Pw(m,g),m===null?(_=CU(g.type,null,g.pendingProps,null))?g.memoizedState=_:rr||(_=g.type,m=g.pendingProps,M=Kw(pe.current).createElement(_),M[Pn]=g,M[cr]=m,Ga(M,_,m),Ve(M),g.stateNode=M):g.memoizedState=CU(g.type,m.memoizedProps,g.pendingProps,m.memoizedState),null;case 27:return ce(g),m===null&&rr&&(M=g.stateNode=SU(g.type,g.pendingProps,pe.current),Ha=g,$o=!0,F=Jr,zd(g.type)?(TT=F,Jr=Go(M.firstChild)):Jr=F),Va(m,g,g.pendingProps.children,_),Pw(m,g),m===null&&(g.flags|=4194304),g.child;case 5:return m===null&&rr&&((F=M=Jr)&&(M=loe(M,g.type,g.pendingProps,$o),M!==null?(g.stateNode=M,Ha=g,Jr=Go(M.firstChild),$o=!1,F=!0):F=!1),F||kd(g)),ce(g),F=g.type,I=g.pendingProps,te=m!==null?m.memoizedProps:null,M=I.children,NT(F,I)?M=null:te!==null&&NT(F,te)&&(g.flags|=32),g.memoizedState!==null&&(F=SA(m,g,Sse,null,null,_),Jx._currentValue=F),Pw(m,g),Va(m,g,M,_),g.child;case 6:return m===null&&rr&&((m=_=Jr)&&(_=coe(_,g.pendingProps,$o),_!==null?(g.stateNode=_,Ha=g,Jr=null,m=!0):m=!1),m||kd(g)),null;case 13:return hz(m,g,_);case 4:return je(g,g.stateNode.containerInfo),M=g.pendingProps,m===null?g.child=Cm(g,null,M,_):Va(m,g,M,_),g.child;case 11:return rz(m,g,g.type,g.pendingProps,_);case 7:return Va(m,g,g.pendingProps,_),g.child;case 8:return Va(m,g,g.pendingProps.children,_),g.child;case 12:return Va(m,g,g.pendingProps.children,_),g.child;case 10:return M=g.pendingProps,Cd(g,g.type,M.value),Va(m,g,M.children,_),g.child;case 9:return F=g.type._context,M=g.pendingProps.children,_m(g),F=$a(F),M=M(F),g.flags|=1,Va(m,g,M,_),g.child;case 14:return iz(m,g,g.type,g.pendingProps,_);case 15:return az(m,g,g.type,g.pendingProps,_);case 19:return fz(m,g,_);case 31:return Pse(m,g,_);case 22:return sz(m,g,_,g.pendingProps);case 24:return _m(g),M=$a(qi),m===null?(F=mA(),F===null&&(F=Hr,I=dA(),F.pooledCache=I,I.refCount++,I!==null&&(F.pooledCacheLanes|=_),F=I),g.memoizedState={parent:M,cache:F},pA(g),Cd(g,qi,F)):((m.lanes&_)!==0&&(gA(m,g),Dx(g,null,null,_),jx()),F=m.memoizedState,I=g.memoizedState,F.parent!==M?(F={parent:M,cache:M},g.memoizedState=F,g.lanes===0&&(g.memoizedState=g.updateQueue.baseState=F),Cd(g,qi,M)):(M=I.cache,Cd(g,qi,M),M!==F.cache&&uA(g,[qi],_,!0))),Va(m,g,g.pendingProps.children,_),g.child;case 29:throw g.pendingProps}throw Error(r(156,g.tag))}function iu(m){m.flags|=4}function QA(m,g,_,M,F){if((g=(m.mode&32)!==0)&&(g=!1),g){if(m.flags|=16777216,(F&335544128)===F)if(m.stateNode.complete)m.flags|=8192;else if(Hz())m.flags|=8192;else throw km=gw,fA}else m.flags&=-16777217}function gz(m,g){if(g.type!=="stylesheet"||(g.state.loading&4)!==0)m.flags&=-16777217;else if(m.flags|=16777216,!PU(g))if(Hz())m.flags|=8192;else throw km=gw,fA}function Dw(m,g){g!==null&&(m.flags|=4),m.flags&16384&&(g=m.tag!==22?At():536870912,m.lanes|=g,Rp|=g)}function zx(m,g){if(!rr)switch(m.tailMode){case"hidden":g=m.tail;for(var _=null;g!==null;)g.alternate!==null&&(_=g),g=g.sibling;_===null?m.tail=null:_.sibling=null;break;case"collapsed":_=m.tail;for(var M=null;_!==null;)_.alternate!==null&&(M=_),_=_.sibling;M===null?g||m.tail===null?m.tail=null:m.tail.sibling=null:M.sibling=null}}function ei(m){var g=m.alternate!==null&&m.alternate.child===m.child,_=0,M=0;if(g)for(var F=m.child;F!==null;)_|=F.lanes|F.childLanes,M|=F.subtreeFlags&65011712,M|=F.flags&65011712,F.return=m,F=F.sibling;else for(F=m.child;F!==null;)_|=F.lanes|F.childLanes,M|=F.subtreeFlags,M|=F.flags,F.return=m,F=F.sibling;return m.subtreeFlags|=M,m.childLanes=_,g}function Dse(m,g,_){var M=g.pendingProps;switch(aA(g),g.tag){case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return ei(g),null;case 1:return ei(g),null;case 3:return _=g.stateNode,M=null,m!==null&&(M=m.memoizedState.cache),g.memoizedState.cache!==M&&(g.flags|=2048),eu(qi),de(),_.pendingContext&&(_.context=_.pendingContext,_.pendingContext=null),(m===null||m.child===null)&&(wp(g)?iu(g):m===null||m.memoizedState.isDehydrated&&(g.flags&256)===0||(g.flags|=1024,oA())),ei(g),null;case 26:var F=g.type,I=g.memoizedState;return m===null?(iu(g),I!==null?(ei(g),gz(g,I)):(ei(g),QA(g,F,null,M,_))):I?I!==m.memoizedState?(iu(g),ei(g),gz(g,I)):(ei(g),g.flags&=-16777217):(m=m.memoizedProps,m!==M&&iu(g),ei(g),QA(g,F,m,M,_)),null;case 27:if(Se(g),_=pe.current,F=g.type,m!==null&&g.stateNode!=null)m.memoizedProps!==M&&iu(g);else{if(!M){if(g.stateNode===null)throw Error(r(166));return ei(g),null}m=J.current,wp(g)?XI(g):(m=SU(F,M,_),g.stateNode=m,iu(g))}return ei(g),null;case 5:if(Se(g),F=g.type,m!==null&&g.stateNode!=null)m.memoizedProps!==M&&iu(g);else{if(!M){if(g.stateNode===null)throw Error(r(166));return ei(g),null}if(I=J.current,wp(g))XI(g);else{var te=Kw(pe.current);switch(I){case 1:I=te.createElementNS("http://www.w3.org/2000/svg",F);break;case 2:I=te.createElementNS("http://www.w3.org/1998/Math/MathML",F);break;default:switch(F){case"svg":I=te.createElementNS("http://www.w3.org/2000/svg",F);break;case"math":I=te.createElementNS("http://www.w3.org/1998/Math/MathML",F);break;case"script":I=te.createElement("div"),I.innerHTML="
-
+
+