Voltar aos projetos
2026 · Aplicação web

Crop & Quest

Editor local-first de retratos para RPGs, com vários presets de jogos, formatos personalizados reutilizáveis e exportação com dimensões exatas.

  • Página inicial do Crop & Quest com o título do projeto, uma chamada de uma linha e três passos numerados: escolher um preset, carregar a arte e recortar.
  • Etapa de recorte com controles de zoom e rotação sobre o retrato de um personagem, ao lado da tela de revisão com as variações pequena, média e grande prontas para download.

O Crop & Quest prepara retratos personalizados para RPGs que exigem dimensões, formatos, convenções de nomes e locais de instalação específicos para os retratos. Seus 20 presets definem 42 variantes de retratos para jogos da Infinity Engine, RPGs da Owlcat, Pillars of Eternity, Shadowrun, Wasteland, Neverwinter Nights e outros títulos.

O usuário seleciona uma imagem local, recorta cada variante necessária, revisa os arquivos gerados e faz o download individual ou em ZIP. A imagem original e os retratos gerados permanecem no navegador; a aplicação não exige conta, banco de dados, armazenamento em nuvem nem processamento de imagens no servidor.


🧩 Decisões técnicas

1. Um único editor guiado por presets tipados

Desafio: Cada jogo exige uma combinação diferente de dimensões, proporções, formatos, padrões de nomes, estruturas de pastas e instruções de instalação. Inserir essas regras diretamente nos componentes de página deixaria a interface acoplada a cada título.

Implementação: Um modelo tipado de Preset define variantes, comportamento da exportação, limites de nomes e instruções de instalação. O fluxo no App Router resolve um dos presets integrados ou um preset personalizado armazenado localmente. O Valibot valida os formulários de presets personalizados e os nomes dos retratos antes que esses valores cheguem ao pipeline de exportação.

Resultado:

  • As mesmas rotas de seleção, recorte, revisão e exportação atendem às 42 variantes integradas.
  • Novos jogos podem ser adicionados como dados, sem outra implementação do editor.
  • O usuário pode criar, editar, reutilizar e excluir presets personalizados sem uma conta.

2. Arquivos exatos gerados no navegador, inclusive em formatos antigos

Desafio: O recorte visual precisa se transformar em um arquivo com dimensões, rotação, formato e nome exatos. RPGs mais antigos também exigem BMP ou TGA, formatos que não estão disponíveis nos encoders padrão da Canvas API.

Implementação: O react-easy-crop fornece a área selecionada em pixels. O pipeline primeiro desenha a rotação em um canvas intermediário e depois renderiza o recorte em outro canvas com a largura e a altura exatas do preset. A codificação nativa do canvas cuida de PNG, JPEG e WebP; encoders binários próprios geram BMP de 24 bits e TGA de 32 bits. Templates de nomes definem cada caminho de download, e o JSZip monta o conjunto completo.

Resultado:

  • Os presets incluídos podem gerar PNG, BMP e TGA para jogos atuais e antigos.
  • Downloads individuais e arquivos ZIP seguem as dimensões, os nomes e a estrutura de pastas definidos pelo preset escolhido.
  • A recodificação pelo canvas evita levar os metadados da imagem original para os retratos exportados.

3. Separação explícita entre estado temporário e persistente

Desafio: O editor passa por upload, várias rotas de recorte, revisão e exportação, mas as imagens de origem não devem ser persistidas. Ao mesmo tempo, as definições de presets personalizados precisam sobreviver ao reinício do navegador.

Implementação: O Zustand mantém a imagem ativa, a object URL e os recortes em uma sessão temporária. Trocar ou limpar a imagem revoga as URLs anteriores, enquanto as rotas redirecionam sessões incompletas para a seleção de imagem. Um aviso antes de fechar a página protege o trabalho em andamento. Os metadados dos presets personalizados usam outra store com persistência, permitindo reutilizá-los sem armazenar as imagens do usuário.

Resultado:

  • Imagens ausentes e rotas de presets inválidos são tratadas com redirecionamentos explícitos.
  • A limpeza da sessão libera recursos do navegador em vez de manter blobs antigos.
  • As imagens do usuário permanecem temporárias, enquanto as configurações reutilizáveis continuam disponíveis localmente.

🏗️ Fluxo de imagem e exportação

preset tipado -> proporção, dimensões, formato, nome e instruções de instalação
imagem local -> object URL -> estado do recorte -> canvas -> encoder -> Blob
                                                               |
                                                   download individual ou ZIP

O upload aceita arquivos locais em PNG, JPEG e WebP com até 10 MB. A etapa de seleção rejeita arquivos vazios, imagens HEIC ou HEIF, tipos MIME incompatíveis e links externos arrastados antes de criar a object URL local.

✅ Entrega e verificação

  • Testes unitários e de componentes para schemas, formulários, resolução de presets, encoders, exportação, rotas, cálculos e stores do Zustand.
  • Fluxos no Playwright cobrem geração de retratos e download do ZIP, ciclo de vida de presets personalizados e controles do editor.
  • Cada fluxo do Playwright é executado no Chromium, Firefox, WebKit e em um perfil mobile do Chrome.
  • O GitHub Actions separa as verificações de formatação, lint e tipos dos jobs de testes unitários e end-to-end. Execuções de navegador não canceladas mantêm o relatório HTML por 30 dias.
  • A aplicação publicada oferece o fluxo completo sem cadastro.

Escopo e limitações

A velocidade e o uso de memória dependem do navegador e do dispositivo; o projeto não publica benchmarks de processamento. A sessão ativa de retratos não sobrevive ao recarregamento da página por decisão de privacidade, enquanto os presets personalizados permanecem salvos. O editor processa uma imagem de origem por sessão e ainda não oferece criação de retratos em lote.


🛠️ Tech stack

  • Aplicação: Next.js App Router, React, TypeScript
  • Pipeline de imagem: Canvas API, react-easy-crop, encoders próprios de BMP e TGA, JSZip
  • Estado e formulários: Zustand, TanStack Form, Valibot
  • Interface: Tailwind CSS, Radix UI
  • Qualidade: Vitest, Testing Library, Playwright, Oxlint
  • Distribuição: GitHub Actions, Vercel
  • Next.js
  • React
  • TypeScript
  • Canvas API
  • Vitest
  • Playwright