"use client";

import { memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
import {
  AlignEndVertical,
  ArrowLeft,
  Box,
  BriefcaseBusiness,
  CalendarHeart,
  ChartColumn,
  Clapperboard,
  Rotate3d,
  Scissors,
  Flower2,
  Frame,
  Layers,
  PartyPopper,
  Shapes,
  Smile,
  Sparkles,
  Store,
  Tag,
  AlignHorizontalJustifyCenter,
  AlignStartVertical,
  AlignVerticalJustifyCenter,
  AlignVerticalJustifyEnd,
  AlignVerticalJustifyStart,
  ArrowDownToLine,
  ArrowLeftRight,
  ArrowUpToLine,
  ArrowRight,
  Blend,
  ChevronDown,
  ChevronUp,
  CircleDot,
  Crop,
  Droplet,
  FlipHorizontal2,
  FlipVertical2,
  Highlighter,
  History,
  Loader2,
  ImagePlus,
  Maximize2,
  Move,
  MousePointer2,
  Palette,
  Pipette,
  RotateCcw,
  Search,
  SlidersHorizontal,
  Type,
  Upload,
  WandSparkles,
} from "lucide-react";
import {
  type ContagemDaPagina,
  FONTES,
  TIPOS_DE_FONTE,
  garantirFontes,
  fontesDaMarca,
  caixaDaPeca,
  conteudoDaPeca,
  cssDaPintura,
  degrade,
  solida,
  type DocumentoDoBanner,
  type EstiloDeTextura,
  type Peca,
  type PecaDeForma,
  type PecaDeIcone,
  type PecaDeImagem,
  type PecaDeTexto,
  type PecaDeTextura,
  type PecaDeTraco,
  type PecaDeQr,
  type PecaDeTabela,
  type PecaDeGrafico,
  type Pintura,
  type PontosDoDegrade,
} from "@/lib/banner-editor/documento";
import { MODELOS_DO_EDITOR, RAMOS_DOS_MODELOS, modeloAparece, type PedidoDoModelo } from "@/lib/banner-editor/modelos";
import {
  GRAFICOS_PRONTOS,
  MOLDURAS_PRONTAS,
  TRACOS_PRONTOS,
  qrCode,
  tabela,
  CHAMADAS_PRONTAS,
  COMBINACOES_PRONTAS,
  FORMAS_A_MAIS,
  FORMAS_PRONTAS,
  FUNDOS_PRONTOS,
  ICONES_PRONTOS,
  TEXTOS_PRONTOS,
  contadorDePaginas,
  TEXTURAS_PRONTAS,
  imagem,
  type Medida,
  type PecaPronta,
} from "@/lib/banner-editor/pecas-novas";
import type { ResumoDaArte } from "@/lib/banner-editor/artes";
import { TIPOS_DE_MODELO, enderecoDaMiniatura, miniaturaDoModelo, pedidoNeutro } from "@/lib/banner-editor/miniaturas";
import { VideoQueToca } from "@/components/artes/video-que-toca";
import { formatarTrecho, useTrechoEscolhido } from "./texto-rico";
import { arrastavel } from "./arrastar-do-painel";
import { PainelDeGlifos } from "./painel-de-glifos";
import { BibliotecaDeImagens } from "./biblioteca-de-imagens";
import { GRUPOS_DOS_ICONES_3D, GRUPOS_DOS_ORNAMENTOS, GRUPOS_DOS_RECORTES } from "@/lib/banner-editor/biblioteca";
import { FONTES_COM_GLIFOS, ehFonteComGlifos } from "@/lib/banner-editor/fontes-com-glifos";
import { exibicaoGuardada, htmlDoFundo } from "./desenho-guardado";
import { EfeitosDoTraco, EstiloDaLetra, PainelDaTabela, PainelDoGrafico, PainelDoQr } from "./paineis-novos";
import { AjustesCompletos, SecaoDeDistorcer, SecaoDeMesclagem } from "./paineis-pro";
import { PainelDaReticula, PainelDasCoresDaFoto } from "./paineis-da-foto";
import type { MeioTom, TratamentoDaFoto, TrocaDeCor } from "@/lib/banner-editor/tratamento-da-foto";
import { ATALHOS_DO_BANCO, BancoDeImagens, BibliotecaDeFigurinhas, BuscaDeIcones, IconesEscolhidos, type AtalhoDoBanco } from "./bibliotecas-extras";
import { DATAS_ESPECIAIS, ELEMENTOS_3D } from "@/lib/banner-editor/pacotes";
import { DEGRADES_METALICOS } from "@/lib/banner-editor/metalicos";
import { CUPONS_PRONTOS, SELOS_DE_VENDA } from "@/lib/banner-editor/cupons-e-selos";
import { DESTAQUES_DE_VIDEO, LEGENDAS_DE_VIDEO, PARTICULAS_DE_VIDEO, TELAS_DE_VIDEO } from "@/lib/banner-editor/elementos-de-video";
import { MiniCena } from "./cena-no-palco";
import type { ProdutoDoEditor } from "../campos";
import { BotaoContaGotas, BotaoDoPainel, BotoesDeIcone, CampoDePintura, ContextoDasCores, Dica, Numero, Opcoes, PALETA_VIVA, Regua, Secao, lembrarCor, useCoresRecentes } from "./campos";
import { PainelDaCurva, PainelDaForma, PainelDeContorno, PainelDeSombra, PainelDoEspacamento, PainelDoFundoDoTexto, PainelDoMarcaTexto, PainelDoRecorte, SecaoDoDuotone } from "./paineis-da-peca";
import { IconeDaReticulaViva, PainelDaReticulaViva } from "./painel-da-reticula-viva";
import { IconeDaCurva, IconeDaSombra, IconeDoContorno, IconeDoEspacamento, IconeDoFundoDoTexto } from "./icones-do-editor";
import type { ContornoQueSepara, ModoDeSeparar } from "@/lib/banner-editor/separar-contorno";

/*
  Os painéis da esquerda do editor de banners, um por vez (como no Canva):
  a biblioteca (modelos, elementos, texto, imagens, camadas) e os ajustes que a
  barra flutuante abre para a peça escolhida (fonte, cor, efeitos, posição,
  ajustes da foto, forma, textura). Toda prévia é desenhada com o mesmo HTML
  do banner.
*/

export type AbaDaBiblioteca = "modelos" | "elementos" | "texto" | "imagens" | "camadas" | "artes" | "marca";
/*
 * O que a barra da peça abre no painel. Cada coisa no seu lugar: contorno e
 * sombra valem para qualquer peça; espaçamento, curva, fundo, marca-texto e
 * estilo são do texto; forma, da forma; recorte, da foto. "efeitos" ficou
 * para o painel próprio do traço, do QR, da tabela e do gráfico; "camada"
 * (os efeitos da camada) abre o Contorno e a Sombra juntos.
 */
export type AjusteDaPeca =
  | "fonte"
  | "glifos"
  | "vetor"
  | "cor"
  | "cor-do-fundo"
  | "efeitos"
  | "posicao"
  | "ajustes"
  | "forma"
  | "textura"
  | "transparencia"
  | "camada"
  | "cores-da-foto"
  | "reticula"
  | "contorno"
  | "sombra"
  | "espacamento"
  | "curva"
  | "fundo-do-texto"
  | "marca-texto"
  | "estilo"
  | "recorte"
  | "reticula-viva";

/* ============================================================ peças */

/* Memo: a miniatura só refaz quando a página (ou o número dela) muda. */
export const Miniatura = memo(
  function Miniatura({ doc, largura, contagem = null }: { doc: DocumentoDoBanner; largura: number; contagem?: ContagemDaPagina | null }) {
  const escala = largura / doc.largura;
  /* A fatia do carrossel contínuo mostra o pedaço dela da arte larga (as fixas ficam no lugar delas). */
  const vista = doc.vista;
  const desenhar = (lista: typeof doc.pecas) =>
    lista.map((p) => {
      const ex = p.oculta ? null : exibicaoGuardada(p, doc.pecas, contagem);
      return ex ? <div key={p.id} style={{ position: "absolute", ...ex.estilo }} dangerouslySetInnerHTML={ex.html} /> : null;
    });
  return (
    <div className="relative overflow-hidden rounded-[10px]" style={{ width: largura, height: doc.altura * escala }}>
      <div className="absolute left-0 top-0 overflow-hidden" style={{ width: doc.largura, height: doc.altura, transform: `scale(${escala})`, transformOrigin: "0 0" }}>
        <div className="absolute top-0" style={{ left: -(vista?.x ?? 0), width: vista ? vista.larguraTotal : doc.largura, height: doc.altura }}>
          <div className="absolute inset-0" dangerouslySetInnerHTML={htmlDoFundo(doc.fundo, vista ? vista.larguraTotal : doc.largura, doc.altura)} />
          {desenhar(vista ? doc.pecas.filter((p) => !p.fixa) : doc.pecas)}
        </div>
        {vista ? desenhar(doc.pecas.filter((p) => p.fixa)) : null}
      </div>
    </div>
  );
  },
  (a, b) => a.doc === b.doc && a.largura === b.largura && (a.contagem?.pagina ?? 0) === (b.contagem?.pagina ?? 0) && (a.contagem?.total ?? 0) === (b.contagem?.total ?? 0),
);

/* O desenho da amostra guardado pela peça: a amostra só refaz (textura, filtro) quando a própria peça muda. */
const desenhoDaAmostra = new WeakMap<Peca, { caixa: React.CSSProperties; html: { __html: string } }>();
function desenhoGuardado(peca: Peca) {
  let d = desenhoDaAmostra.get(peca);
  if (!d) {
    const p = { ...peca, x: 0, y: 0 };
    d = { caixa: caixaDaPeca(p), html: { __html: conteudoDaPeca(p) } };
    desenhoDaAmostra.set(peca, d);
  }
  return d;
}

export const Amostra = memo(function Amostra({ peca, largura, altura, fundo }: { peca: Peca; largura: number; altura: number; fundo: string }) {
  const folga = Math.max(peca.w, peca.h) * 0.16;
  const escala = Math.min((largura - 10) / (peca.w + folga), (altura - 10) / (peca.h + folga));
  const desenho = desenhoGuardado(peca);
  return (
    <div className="relative overflow-hidden rounded-[10px]" style={{ width: largura, height: altura, background: fundo }}>
      <div className="absolute" style={{ left: (largura - peca.w * escala) / 2, top: (altura - peca.h * escala) / 2, width: peca.w, height: peca.h, transform: `scale(${escala})`, transformOrigin: "0 0" }}>
        <div style={{ position: "absolute", ...desenho.caixa }} dangerouslySetInnerHTML={desenho.html} />
      </div>
    </div>
  );
});

/* Várias peças juntas (combinação de texto), encolhidas para caber. */
function AmostraDeVarias({ pecas, largura, altura, fundo }: { pecas: Peca[]; largura: number; altura: number; fundo?: string }) {
  const x = Math.min(...pecas.map((p) => p.x));
  const y = Math.min(...pecas.map((p) => p.y));
  const w = Math.max(...pecas.map((p) => p.x + p.w)) - x;
  const h = Math.max(...pecas.map((p) => p.y + p.h)) - y;
  const escala = Math.min((largura - 12) / w, (altura - 12) / h);
  const claro = pecas.every((p) => p.tipo === "texto" && p.pintura.cor.toUpperCase() !== "#FFFFFF" && luminanciaSimples(p.pintura.cor) < 0.6);
  return (
    <div className="relative overflow-hidden rounded-[10px]" style={{ width: largura, height: altura, background: fundo ?? (claro ? "#F5EFE6" : "linear-gradient(135deg,#6D28D9,#1E1B4B)") }}>
      <div className="absolute" style={{ left: (largura - w * escala) / 2, top: (altura - h * escala) / 2, width: w, height: h, transform: `scale(${escala})`, transformOrigin: "0 0" }}>
        {pecas.map((p) => (
          <div key={p.id} style={{ position: "absolute", ...caixaDaPeca({ ...p, x: p.x - x, y: p.y - y }) }} dangerouslySetInnerHTML={{ __html: conteudoDaPeca(p) }} />
        ))}
      </div>
    </div>
  );
}

/* A foto da arte guardada (a mesma da galeria); sem foto ainda, a arte é montada aqui mesmo. */
function FotoDaArteGuardada({ arte, largura }: { arte: ResumoDaArte; largura: number }) {
  const [semFoto, setSemFoto] = useState(false);
  if (semFoto || !arte.updated_at) return arte.documento?.pecas ? <Miniatura doc={arte.documento} largura={largura} /> : <ImagePlus size={18} className="text-[#B4AEC6]" />;
  /* eslint-disable-next-line @next/next/no-img-element */
  return <img src={enderecoDaMiniatura(arte)} alt={arte.nome} loading="lazy" decoding="async" onError={() => setSemFoto(true)} className="block h-full w-auto object-contain" style={{ width: largura }} />;
}

function luminanciaSimples(hex: string) {
  const h = hex.replace("#", "");
  const [r, g, b] = [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16) / 255);
  return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}

/* Branco não aparece em fundo claro: a amostra ganha fundo escuro. */
export function fundoDaAmostra(p: Peca) {
  const cor =
    p.tipo === "texto" ? (p.fundo ? null : p.pintura.cor) : p.tipo === "forma" ? (p.pintura.tipo === "nenhuma" ? p.borda?.cor : p.pintura.cor) : p.tipo === "textura" ? p.cor : p.tipo === "icone" ? p.pintura.cor : null;
  return cor && cor.toUpperCase() === "#FFFFFF" ? "linear-gradient(135deg,#6D28D9,#3B0764)" : "#F3F1F9";
}

/* Memo: a arte mexendo (arrastar, digitar) não refaz as amostras da biblioteca. */
const Grade = memo(function Grade<T extends Peca>({ pecas, medida, aoPor, colunas = 2, altura = 84, filtro = "" }: { pecas: PecaPronta<T>[]; medida: Medida; aoPor: (p: Peca) => void; colunas?: 2 | 3; altura?: number; filtro?: string }) {
  const amostras = useMemo(() => pecas.map((p) => ({ pronta: p, peca: p.criar(medida) })), [pecas, medida]);
  const visiveis = amostras.filter(({ pronta }) => pronta.nome.toLowerCase().includes(filtro.trim().toLowerCase()));
  if (!visiveis.length) return null;
  const largura = colunas === 3 ? 92 : 142;
  return (
    <div className={`grid gap-2 ${colunas === 3 ? "grid-cols-3" : "grid-cols-2"}`}>
      {visiveis.map(({ pronta, peca }) => (
        <button key={pronta.chave} type="button" onClick={() => aoPor(pronta.criar(medida))} {...arrastavel(() => pronta.criar(medida))} className="group rounded-[12px] p-1 text-left transition hover:bg-[#F3F0FF]" title={`${pronta.nome} · clique ou arraste para a arte`}>
          <div className="overflow-hidden rounded-[10px] ring-1 ring-black/5 transition group-hover:ring-2 group-hover:ring-[#8B5CF6]">
            <Amostra peca={peca} largura={largura} altura={altura} fundo={fundoDaAmostra(peca)} />
          </div>
          <div className="mt-1 truncate px-0.5 text-[11px] font-medium text-[#4B4760]">{pronta.nome}</div>
        </button>
      ))}
    </div>
  );
});

/*
 * A amostra de um elemento para vídeo: parada, como as outras; com o mouse em
 * cima, toca a animação dele (recortada em volta das peças, com folga para o
 * que se mexe).
 */
function AmostraQueToca({ pecas, medida, largura, altura, fundo }: { pecas: Peca[]; medida: { largura: number; altura: number }; largura: number; altura: number; fundo: string }) {
  const [tocando, setTocando] = useState(false);
  const pagina = useMemo(() => {
    const x = Math.min(...pecas.map((p) => p.x));
    const y = Math.min(...pecas.map((p) => p.y));
    const w = Math.max(...pecas.map((p) => p.x + p.w)) - x;
    const h = Math.max(...pecas.map((p) => p.y + p.h)) - y;
    const fim = Math.max(1.6, ...pecas.map((p) => (p.animacao ? (p.animacao.fim ?? p.animacao.inicio + p.animacao.duracao) + 0.8 : 0)));
    const doc: DocumentoDoBanner = { versao: 3, largura: medida.largura, altura: medida.altura, fundo: { pintura: solida(fundo), textura: { tipo: "nenhuma", cor: "#FFFFFF", opacidade: 0, quantidade: 1, tamanho: 0, centroX: 50, centroY: 50, giro: 0 } }, pecas, animacao: { duracao: Math.min(6, fim), fundo: "parado" } };
    return { doc, recorte: { x: x - w * 0.12, y: y - h * 0.12, w: w * 1.24, h: h * 1.24 } };
  }, [pecas, medida, fundo]);
  return (
    <div className="relative overflow-hidden rounded-[10px]" style={{ width: largura, height: altura, background: fundo }} onMouseEnter={() => setTocando(true)} onMouseLeave={() => setTocando(false)}>
      {tocando ? <MiniCena paginas={[pagina.doc]} lado={largura} alto={altura} recorte={pagina.recorte} tocar /> : <AmostraDeVarias pecas={pecas} largura={largura} altura={altura} fundo={fundo} />}
    </div>
  );
}

/* A letra do atalho de teclado, ao lado do nome. */
function Tecla({ children }: { children: React.ReactNode }) {
  return <kbd className="inline-flex h-5 min-w-5 items-center justify-center rounded-md bg-white px-1 font-sans text-[10.5px] font-bold text-[#6B6584] ring-1 ring-black/10">{children}</kbd>;
}

function Busca({ valor, aoMudar, placeholder }: { valor: string; aoMudar: (v: string) => void; placeholder: string }) {
  return (
    <label className="mb-5 flex h-10 items-center gap-2 rounded-[10px] bg-[#F1EFF6] px-3 text-[#77708F] focus-within:bg-white focus-within:ring-2 focus-within:ring-[#8B5CF6]">
      <Search size={16} />
      <input value={valor} onChange={(e) => aoMudar(e.target.value)} placeholder={placeholder} className="h-full min-w-0 flex-1 bg-transparent text-[13px] text-[#1F1B2E] outline-none placeholder:text-[#9A94AE]" />
    </label>
  );
}

/* ======================================================== biblioteca */

/* A lista fica a mesma entre um desenho e outro (a Grade guarda as amostras por ela). */
const TODAS_AS_FORMAS = [...FORMAS_PRONTAS, ...FORMAS_A_MAIS];
const semAcento = (s: string) => s.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase();

/* ======================================================= categorias de Elementos */

type PacoteDoPainel = { chave: string; nome: string; busca?: string; criar: (m: Medida) => Peca[] };

/* Os pacotes de "Datas especiais" que são da vida (aniversário, casamento...) e da empresa; o resto é data do comércio. */
const DATAS_DA_VIDA = ["aniversario", "parabens", "cha-de-bebe", "casamento", "formatura", "dia-das-criancas"];
const DATAS_DA_EMPRESA = ["inauguracao", "obrigado"];

/* Ícones do Lucide escolhidos por assunto (o nome em inglês é o do Lucide). */
const ICONES_DE_PROFISSAO = ["stethoscope", "syringe", "pill", "briefcase-medical", "heart-pulse", "scissors", "spray-can", "chef-hat", "cooking-pot", "utensils", "graduation-cap", "school", "book-open", "scale", "gavel", "wrench", "hammer", "drill", "hard-hat", "construction", "paintbrush", "palette", "camera", "car", "truck", "tractor", "dumbbell", "microscope", "flask-conical", "laptop", "code", "printer", "pen-tool", "ruler", "shirt", "paw-print", "plane", "shield", "music", "mic"];
const ICONES_DE_EMPRESA = ["store", "building", "factory", "landmark", "handshake", "briefcase-business", "chart-line", "trending-up", "chart-column", "badge-dollar-sign", "banknote", "wallet", "credit-card", "receipt", "calculator", "megaphone", "phone", "smartphone", "mail", "map-pin", "clock", "calendar", "package", "shopping-bag", "shopping-cart", "tag", "percent", "award", "trophy", "users", "id-card", "lightbulb", "wifi", "printer"];
const ICONES_DE_FESTA = ["cake", "cake-slice", "party-popper", "gift", "candy", "baby", "sparkles", "crown", "star", "heart", "music", "wine", "beer", "ice-cream-cone", "camera", "gem", "smile", "sticker"];

/* As fotos de cada assunto (o nome em português; a busca no acervo é em inglês). */
const FOTOS_DE_FESTA: AtalhoDoBanco[] = [
  { nome: "Aniversário", q: "birthday party" },
  { nome: "Bolo", q: "birthday cake" },
  { nome: "Balões", q: "balloons" },
  { nome: "Confete", q: "confetti" },
  { nome: "Festa infantil", q: "kids party" },
  { nome: "Casamento", q: "wedding" },
  { nome: "Bebê", q: "baby" },
  { nome: "Brinde", q: "toast champagne" },
];
const FOTOS_DE_PROFISSOES: AtalhoDoBanco[] = [
  { nome: "Médica", q: "doctor" },
  { nome: "Enfermagem", q: "nurse" },
  { nome: "Dentista", q: "dentist" },
  { nome: "Cabeleireira", q: "hairdresser" },
  { nome: "Barbearia", q: "barber" },
  { nome: "Manicure", q: "manicure nails" },
  { nome: "Cozinheira", q: "chef cooking" },
  { nome: "Confeitaria", q: "bakery pastry" },
  { nome: "Mecânico", q: "mechanic" },
  { nome: "Advocacia", q: "lawyer" },
  { nome: "Professora", q: "teacher classroom" },
  { nome: "Costureira", q: "sewing" },
  { nome: "Construção", q: "construction worker" },
  { nome: "Agricultura", q: "farmer" },
  { nome: "Fotógrafo", q: "photographer" },
  { nome: "Academia", q: "fitness gym" },
];
const FOTOS_DE_EMPRESA: AtalhoDoBanco[] = [
  { nome: "Escritório", q: "office" },
  { nome: "Reunião", q: "business meeting" },
  { nome: "Atendimento", q: "customer service" },
  { nome: "Loja", q: "shop store" },
  { nome: "Fábrica", q: "factory" },
  { nome: "Entrega", q: "delivery boxes" },
  { nome: "Computador", q: "laptop desk" },
  { nome: "Dinheiro", q: "money" },
];

/* As categorias do topo de Elementos (como as do Canva): cada uma abre só as seções dela. */
const CATEGORIAS_DOS_ELEMENTOS: Array<{ chave: string; nome: string; cor: string; Icone: typeof Shapes; secoes: string[] }> = [
  { chave: "formas", nome: "Formas", cor: "#06B6D4", Icone: Shapes, secoes: ["formas", "linhas"] },
  { chave: "figurinhas", nome: "Figurinhas", cor: "#F59E0B", Icone: Smile, secoes: ["figurinhas"] },
  { chave: "recortes", nome: "Recortes sem fundo", cor: "#0369A1", Icone: Scissors, secoes: ["recortes"] },
  { chave: "icones-3d", nome: "Ícones 3D", cor: "#7E22CE", Icone: Rotate3d, secoes: ["icones-3d"] },
  { chave: "ornamentos", nome: "Ornamentos e florais", cor: "#B45309", Icone: Flower2, secoes: ["ornamentos"] },
  { chave: "festas", nome: "Aniversário e festas", cor: "#EC4899", Icone: PartyPopper, secoes: ["festas-pacotes", "festas-figurinhas", "festas-recortes", "festas-icones", "festas-fotos"] },
  { chave: "profissoes", nome: "Profissões", cor: "#2563EB", Icone: BriefcaseBusiness, secoes: ["profissoes-figurinhas", "profissoes-icones", "profissoes-fotos"] },
  { chave: "empresas", nome: "Empresas", cor: "#0D9488", Icone: Store, secoes: ["empresas-pacotes", "empresas-figurinhas", "empresas-recortes", "empresas-3d", "empresas-icones", "empresas-fotos"] },
  { chave: "datas", nome: "Datas comemorativas", cor: "#EF4444", Icone: CalendarHeart, secoes: ["datas", "natal-3d", "halloween-3d"] },
  { chave: "icones", nome: "Ícones", cor: "#8B5CF6", Icone: Sparkles, secoes: ["icones"] },
  { chave: "vendas", nome: "Selos e ofertas", cor: "#F97316", Icone: Tag, secoes: ["selos", "cupons", "chamadas"] },
  { chave: "molduras", nome: "Molduras", cor: "#22C55E", Icone: Frame, secoes: ["molduras", "molduras-svg"] },
  { chave: "3d", nome: "Elementos 3D", cor: "#4F46E5", Icone: Box, secoes: ["3d"] },
  { chave: "texturas", nome: "Texturas e fundos", cor: "#CA8A04", Icone: Layers, secoes: ["texturas", "fundos"] },
  { chave: "graficos", nome: "Gráficos e QR", cor: "#65A30D", Icone: ChartColumn, secoes: ["graficos"] },
  { chave: "video", nome: "Para vídeo", cor: "#C026D3", Icone: Clapperboard, secoes: ["video-telas", "video-destaques", "video-legendas", "video-particulas"] },
];

/* Os grupos da biblioteca que a busca de cada categoria procura (as outras procuram em tudo). */
function gruposDaCategoria(chave: string): string[] | undefined {
  if (chave === "icones-3d") return GRUPOS_DOS_ICONES_3D;
  if (chave === "recortes") return GRUPOS_DOS_RECORTES;
  if (chave === "ornamentos") return GRUPOS_DOS_ORNAMENTOS;
  if (chave === "molduras") return ["molduras-svg", "cantos", "divisores"];
  if (chave === "datas") return ["3d-natal", "3d-halloween"];
  if (chave === "empresas") return ["negocios", "3d-geral"];
  return undefined;
}

/* A lista inteira (sem categoria aberta): o que serve a qualquer arte primeiro; o que é só de vídeo no fim. */
const ORDEM_DOS_ELEMENTOS = ["formas", "figurinhas", "recortes-amostra", "icones-3d-amostra", "ornamentos-amostra", "festas-pacotes", "datas", "empresas-pacotes", "icones", "selos", "cupons", "chamadas", "molduras", "linhas", "3d", "texturas", "fundos", "graficos", "video-telas", "video-destaques", "video-legendas", "video-particulas"];

export function PainelDaBiblioteca({
  aba,
  doc,
  pedidoDoModelo,
  logo,
  proporcaoDoLogo,
  produtos,
  fotoDosModelos,
  enviando,
  artes,
  arteAberta,
  galeriaIndisponivel,
  imagensDaPessoa,
  aoTrocarDocumento,
  aoPor,
  aoPorVarias,
  aoEnviarImagem,
  aoEscolherFotoDosModelos,
  aoAbrirArte,
}: {
  aba: AbaDaBiblioteca;
  doc: DocumentoDoBanner;
  pedidoDoModelo: PedidoDoModelo;
  logo: string | null;
  proporcaoDoLogo: number | null;
  produtos: ProdutoDoEditor[];
  fotoDosModelos: string | null;
  enviando: boolean;
  /* As artes guardadas (null = carregando). */
  artes: ResumoDaArte[] | null;
  arteAberta: string | null;
  galeriaIndisponivel: boolean;
  /* Imagens que a pessoa já enviou (nesta arte e nas guardadas). */
  imagensDaPessoa: string[];
  aoTrocarDocumento: (d: DocumentoDoBanner) => void;
  aoPor: (p: Peca) => void;
  aoPorVarias: (p: Peca[]) => void;
  aoEnviarImagem: (arquivos: FileList | File[] | null) => void;
  aoEscolherFotoDosModelos: (url: string | null) => void;
  aoAbrirArte: (arte: ResumoDaArte) => void;
}) {
  const medida = useMemo(() => ({ largura: doc.largura, altura: doc.altura }), [doc.largura, doc.altura]);
  /* Os feitos para este tamanho primeiro (reels no story, post e carrossel no post). */
  /*
   * Na lista vai a FOTO pronta de cada modelo (a mesma da galeria), sem montar
   * as peças; o clique monta o modelo com a cor, o logo e a foto do produto
   * escolhida aqui.
   */
  const amostras = useMemo(
    () =>
      MODELOS_DO_EDITOR.filter((m) => modeloAparece(m, pedidoDoModelo.largura, pedidoDoModelo.altura, Boolean(doc.impressao))).map((m) => {
        const tipo = TIPOS_DE_MODELO.find((t) => t.filtro(m)) ?? null;
        const neutro = tipo ? m.montar(pedidoNeutro(tipo)) : null;
        return { modelo: m, foto: tipo && neutro ? miniaturaDoModelo(tipo.chave, m.chave, neutro) : null, neutro };
      }),
    // eslint-disable-next-line react-hooks/exhaustive-deps -- a impressão só muda com o tamanho
    [pedidoDoModelo.largura, pedidoDoModelo.altura, Boolean(doc.impressao)],
  );
  const [filtro, setFiltro] = useState("");
  /* A categoria aberta em Elementos (como as do Canva); null = a lista com todas. */
  const [categoria, setCategoria] = useState<string | null>(null);
  const arquivo = useRef<HTMLInputElement | null>(null);
  const comFoto = produtos.filter((p) => p.image_url && p.name.toLowerCase().includes(filtro.trim().toLowerCase()));

  if (aba === "modelos") {
    return (
      <div>
        {produtos.some((p) => p.image_url) ? (
          <Secao titulo="Foto do produto nos modelos">
            <div className="-mx-1 flex gap-1.5 overflow-x-auto px-1 pb-1">
              <button type="button" onClick={() => aoEscolherFotoDosModelos(null)} className={`flex h-14 w-14 shrink-0 items-center justify-center rounded-[10px] text-[11px] font-semibold ring-1 ${!fotoDosModelos ? "text-[#6D28D9] ring-2 ring-[#8B5CF6]" : "text-[#77708F] ring-black/10"}`}>
                Sem foto
              </button>
              {produtos
                .filter((p) => p.image_url)
                .slice(0, 30)
                .map((p) => (
                  <button key={p.id} type="button" title={p.name} onClick={() => aoEscolherFotoDosModelos(p.image_url || null)} className={`h-14 w-14 shrink-0 overflow-hidden rounded-[10px] ring-1 ${fotoDosModelos === p.image_url ? "ring-2 ring-[#8B5CF6]" : "ring-black/10"}`}>
                    {/* eslint-disable-next-line @next/next/no-img-element */}
                    <img src={p.image_url || ""} alt="" className="h-full w-full object-cover" />
                  </button>
                ))}
            </div>
          </Secao>
        ) : null}
        <Secao titulo="Modelos prontos">
          <div className="space-y-3">
            {amostras.map(({ modelo, foto, neutro }) => (
              <button key={modelo.chave} type="button" onClick={() => aoTrocarDocumento(modelo.montar(pedidoDoModelo))} className="group block w-full text-left" data-modelo-do-painel={modelo.chave}>
                <div className="overflow-hidden rounded-[12px] ring-1 ring-black/5 transition group-hover:ring-2 group-hover:ring-[#8B5CF6]">
                  {modelo.chave === "branco" ? (
                    <div className="flex items-center justify-center bg-[#F3F1F9] text-[12px] font-bold text-[#7C3AED]" style={{ aspectRatio: `${Math.max(0.5, Math.min(3.6, pedidoDoModelo.largura / pedidoDoModelo.altura))}` }} data-foto-do-painel>
                      Só o fundo
                    </div>
                  ) : foto?.video && foto.video.marca === foto.marca ? (
                    <span className="pointer-events-none block" data-foto-do-painel>
                      <VideoQueToca src={foto.video.src} capa={foto.src} largura={foto.largura} altura={foto.altura} nome={modelo.nome} duracao={foto.video.duracao} />
                    </span>
                  ) : foto ? (
                    /* eslint-disable-next-line @next/next/no-img-element */
                    <img src={foto.src} alt={modelo.nome} width={foto.largura} height={foto.altura} loading="lazy" decoding="async" draggable={false} className="block h-auto w-full select-none" data-foto-do-painel />
                  ) : (
                    <Miniatura doc={neutro ?? modelo.montar(pedidoDoModelo)} largura={292} />
                  )}
                </div>
                <div className="mt-1.5 flex items-baseline justify-between gap-2 px-0.5">
                  <span className="text-[12.5px] font-semibold text-[#1F1B2E]">{modelo.nome}</span>
                  <span className="truncate text-[11px] text-[#8A849E]">
                    {modelo.feitoPara === "carrossel" ? `Carrossel · ${modelo.paginas ?? 2} páginas` : modelo.feitoPara === "story" ? "Reels" : modelo.feitoPara === "caneca" ? "Caneca" : modelo.ramo ? RAMOS_DOS_MODELOS[modelo.ramo] : modelo.descricao}
                  </span>
                </div>
              </button>
            ))}
          </div>
        </Secao>
      </div>
    );
  }

  if (aba === "elementos") {
    const termo = semAcento(filtro.trim());
    const filtrados = <T extends { nome: string; busca?: string }>(lista: T[], extra = "") => (termo ? lista.filter((c) => semAcento(`${c.nome} ${c.busca ?? ""} ${extra}`).includes(termo)) : lista);
    /* Cartões de elementos prontos (várias peças): os de vídeo tocam a animação com o mouse em cima. */
    const cartoes = (titulo: string, lista: PacoteDoPainel[], marca: string, jeito: "video" | "selo" | "data" = "selo", extra = "") => {
      const vistos = filtrados(lista, extra);
      if (!vistos.length) return null;
      return (
        <Secao key={marca} titulo={titulo}>
          <div className="grid grid-cols-2 gap-2" {...{ [marca]: "" }}>
            {vistos.map((c) => (
              <button
                key={c.chave}
                type="button"
                onClick={() => aoPorVarias(c.criar(medida))}
                {...arrastavel(() => c.criar(medida))}
                className="group rounded-[12px] p-1 text-left transition hover:bg-[#F3F0FF]"
                title={jeito === "video" ? `${c.nome} (já animado: passe o mouse para ver)` : c.nome}
                {...(jeito === "data" ? { "data-pacote": c.chave } : { "data-elemento-pronto": c.chave })}
              >
                <div className={`overflow-hidden rounded-[10px] ring-1 ring-black/5 transition group-hover:ring-2 group-hover:ring-[#8B5CF6] ${jeito === "video" ? "" : "bg-[#F3F1F9]"}`}>
                  {jeito === "video" ? (
                    <AmostraQueToca pecas={c.criar(medida)} medida={medida} largura={142} altura={96} fundo={/cursor|mao|contagem|botao|v-p-/.test(c.chave) ? "#2A2540" : "#ECE8F6"} />
                  ) : (
                    <AmostraDeVarias pecas={c.criar(medida)} largura={142} altura={96} fundo={jeito === "selo" ? "#F1EEF8" : undefined} />
                  )}
                </div>
                <div className="mt-1 truncate px-0.5 text-[11px] font-medium text-[#4B4760]">{c.nome}</div>
              </button>
            ))}
          </div>
        </Secao>
      );
    };
    /* Figurinhas, ícones e fotos só aparecem sem a busca de cima (eles têm a busca própria). */
    const semBusca = (no: React.ReactNode) => (termo ? null : no);
    const SECOES: Record<string, () => React.ReactNode> = {
      formas: () => (
        <Secao key="formas" titulo="Formas" acao={<span className="flex items-center gap-1 text-[10.5px] text-[#8A849E]" title="Atalhos de teclado">
            <Tecla>R</Tecla> retângulo <Tecla>C</Tecla> círculo
          </span>}>
          <Grade pecas={TODAS_AS_FORMAS} medida={medida} aoPor={aoPor} colunas={3} altura={72} filtro={filtro} />
        </Secao>
      ),
      figurinhas: () => semBusca(
        <Secao key="figurinhas" titulo="Figurinhas">
          <BibliotecaDeFigurinhas medida={medida} aoPor={aoPor} />
        </Secao>,
      ),
      "festas-pacotes": () => cartoes("Aniversário e festas", DATAS_ESPECIAIS.filter((c) => DATAS_DA_VIDA.includes(c.chave)), "data-festas", "data", "aniversario festa"),
      "festas-figurinhas": () => semBusca(
        <Secao key="festas-figurinhas" titulo="Figurinhas de festa">
          <BibliotecaDeFigurinhas medida={medida} aoPor={aoPor} grupos={["festas", "amor"]} />
        </Secao>,
      ),
      "festas-recortes": () => semBusca(<BibliotecaDeImagens key="festas-recortes" titulo="Corações, confetes e efeitos" medida={medida} aoPor={aoPor} grupos={["clipart", "efeitos"]} />),
      "empresas-recortes": () => semBusca(<BibliotecaDeImagens key="empresas-recortes" titulo="Pessoas trabalhando (recortes)" medida={medida} aoPor={aoPor} grupos={["negocios"]} limite={12} aoVerTodos={() => setCategoria("recortes")} />),
      "empresas-3d": () => semBusca(<BibliotecaDeImagens key="empresas-3d" titulo="Ícones 3D" medida={medida} aoPor={aoPor} grupos={["3d-geral"]} limite={12} aoVerTodos={() => setCategoria("icones-3d")} />),
      "natal-3d": () => semBusca(<BibliotecaDeImagens key="natal-3d" titulo="Natal em 3D" medida={medida} aoPor={aoPor} grupos={["3d-natal"]} />),
      "halloween-3d": () => semBusca(<BibliotecaDeImagens key="halloween-3d" titulo="Halloween em 3D" medida={medida} aoPor={aoPor} grupos={["3d-halloween"]} />),
      recortes: () => semBusca(<BibliotecaDeImagens key="recortes" titulo="Recortes sem fundo (PNG)" medida={medida} aoPor={aoPor} grupos={GRUPOS_DOS_RECORTES} />),
      "icones-3d": () => semBusca(<BibliotecaDeImagens key="icones-3d" titulo="Ícones 3D" medida={medida} aoPor={aoPor} grupos={GRUPOS_DOS_ICONES_3D} />),
      "recortes-amostra": () => semBusca(<BibliotecaDeImagens key="recortes-amostra" titulo="Recortes sem fundo (PNG)" medida={medida} aoPor={aoPor} grupos={GRUPOS_DOS_RECORTES} limite={9} aoVerTodos={() => setCategoria("recortes")} />),
      /* Os ornamentos em SVG: a cor de cada parte troca nas bolinhas da barra. */
      ornamentos: () => semBusca(<BibliotecaDeImagens key="ornamentos" titulo="Ornamentos e florais (cor trocável)" medida={medida} aoPor={aoPor} grupos={GRUPOS_DOS_ORNAMENTOS} />),
      "ornamentos-amostra": () => semBusca(<BibliotecaDeImagens key="ornamentos-amostra" titulo="Ornamentos e florais (cor trocável)" medida={medida} aoPor={aoPor} grupos={GRUPOS_DOS_ORNAMENTOS} limite={9} aoVerTodos={() => setCategoria("ornamentos")} />),
      "molduras-svg": () => semBusca(<BibliotecaDeImagens key="molduras-svg" titulo="Molduras, cantos e bordas decorativas" medida={medida} aoPor={aoPor} grupos={["molduras-svg", "cantos", "divisores"]} />),
      "icones-3d-amostra": () => semBusca(<BibliotecaDeImagens key="icones-3d-amostra" titulo="Ícones 3D" medida={medida} aoPor={aoPor} grupos={GRUPOS_DOS_ICONES_3D} limite={9} aoVerTodos={() => setCategoria("icones-3d")} />),
      "festas-icones": () => semBusca(
        <Secao key="festas-icones" titulo="Ícones de festa">
          <IconesEscolhidos medida={medida} aoPor={aoPor} nomes={ICONES_DE_FESTA} />
        </Secao>,
      ),
      "festas-fotos": () => semBusca(
        <Secao key="festas-fotos" titulo="Fotos de festa">
          <BancoDeImagens aoEnviarImagem={(lista) => aoEnviarImagem(lista)} atalhos={FOTOS_DE_FESTA} inicial={FOTOS_DE_FESTA[0]} />
        </Secao>,
      ),
      "profissoes-figurinhas": () => semBusca(
        <Secao key="profissoes-figurinhas" titulo="Figurinhas de profissões">
          <BibliotecaDeFigurinhas medida={medida} aoPor={aoPor} grupos={["profissoes", "objetos"]} />
        </Secao>,
      ),
      "profissoes-icones": () => semBusca(
        <Secao key="profissoes-icones" titulo="Ícones de profissões">
          <IconesEscolhidos medida={medida} aoPor={aoPor} nomes={ICONES_DE_PROFISSAO} />
        </Secao>,
      ),
      "profissoes-fotos": () => semBusca(
        <Secao key="profissoes-fotos" titulo="Fotos de profissões">
          <BancoDeImagens aoEnviarImagem={(lista) => aoEnviarImagem(lista)} atalhos={FOTOS_DE_PROFISSOES} inicial={FOTOS_DE_PROFISSOES[0]} />
        </Secao>,
      ),
      "empresas-pacotes": () => cartoes("Para a empresa", DATAS_ESPECIAIS.filter((c) => DATAS_DA_EMPRESA.includes(c.chave)), "data-empresa", "data", "empresa negocio loja"),
      "empresas-figurinhas": () => semBusca(
        <Secao key="empresas-figurinhas" titulo="Figurinhas de empresa">
          <BibliotecaDeFigurinhas medida={medida} aoPor={aoPor} grupos={["negocios"]} />
        </Secao>,
      ),
      "empresas-icones": () => semBusca(
        <Secao key="empresas-icones" titulo="Ícones de empresa">
          <IconesEscolhidos medida={medida} aoPor={aoPor} nomes={ICONES_DE_EMPRESA} />
        </Secao>,
      ),
      "empresas-fotos": () => semBusca(
        <Secao key="empresas-fotos" titulo="Fotos de empresa">
          <BancoDeImagens aoEnviarImagem={(lista) => aoEnviarImagem(lista)} atalhos={FOTOS_DE_EMPRESA} inicial={FOTOS_DE_EMPRESA[0]} />
        </Secao>,
      ),
      datas: () => cartoes("Datas comemorativas", DATAS_ESPECIAIS.filter((c) => !DATAS_DA_VIDA.includes(c.chave) && !DATAS_DA_EMPRESA.includes(c.chave)), "data-datas-especiais", "data"),
      icones: () => (
        <Secao key="icones" titulo="Ícones">
          <Grade pecas={ICONES_PRONTOS} medida={medida} aoPor={aoPor} colunas={3} altura={60} filtro={filtro} />
          <div className="mt-3">
            <BuscaDeIcones medida={medida} aoPor={aoPor} />
          </div>
        </Secao>
      ),
      selos: () => cartoes("Selos de venda", SELOS_DE_VENDA, "data-selos-de-venda"),
      cupons: () => cartoes("Cupons e vales", CUPONS_PRONTOS, "data-cupons"),
      chamadas: () => (
        <Secao key="chamadas" titulo="Selos, etiquetas e faixas">
          <Grade pecas={CHAMADAS_PRONTAS} medida={medida} aoPor={aoPor} filtro={filtro} />
        </Secao>
      ),
      molduras: () => (
        <Secao key="molduras" titulo="Molduras de foto">
          <p className="mb-2 text-[11.5px] leading-snug text-[#8A849E]">Ponha a moldura e solte uma foto em cima dela.</p>
          <Grade pecas={MOLDURAS_PRONTAS} medida={medida} aoPor={aoPor} colunas={3} altura={72} filtro={filtro} />
        </Secao>
      ),
      linhas: () => (
        <Secao key="linhas" titulo="Linhas e setas">
          <p className="mb-2 text-[11.5px] leading-snug text-[#8A849E]">Para desenhar à mão ou com a caneta, use as ferramentas no lado direito da arte.</p>
          <Grade pecas={TRACOS_PRONTOS} medida={medida} aoPor={aoPor} colunas={2} altura={56} filtro={filtro} />
        </Secao>
      ),
      "3d": () => {
        const vistos = filtrados(ELEMENTOS_3D, "3d");
        if (!vistos.length) return null;
        return (
          <Secao key="3d" titulo="Elementos 3D">
            <div className="grid grid-cols-4 gap-1.5" data-elementos-3d>
              {vistos.map((c) => (
                <button key={c.chave} type="button" onClick={() => aoPorVarias(c.criar(medida))} {...arrastavel(() => c.criar(medida))} className="group rounded-[10px] p-0.5 text-center transition hover:bg-[#F3F0FF]" title={c.nome} data-pacote={c.chave}>
                  <div className="overflow-hidden rounded-[9px] bg-[#F3F1F9] ring-1 ring-black/5 transition group-hover:ring-2 group-hover:ring-[#8B5CF6]">
                    <AmostraDeVarias pecas={c.criar(medida)} largura={66} altura={62} />
                  </div>
                  <div className="mt-0.5 truncate text-[10.5px] font-medium text-[#4B4760]">{c.nome}</div>
                </button>
              ))}
            </div>
          </Secao>
        );
      },
      texturas: () => (
        <Secao key="texturas" titulo="Texturas">
          <Grade pecas={TEXTURAS_PRONTAS} medida={medida} aoPor={aoPor} colunas={3} altura={60} filtro={filtro} />
        </Secao>
      ),
      fundos: () => (
        <Secao key="fundos" titulo="Fundos">
          <Grade pecas={FUNDOS_PRONTOS} medida={medida} aoPor={aoPor} colunas={3} altura={52} filtro={filtro} />
        </Secao>
      ),
      graficos: () => (
        <Secao key="graficos" titulo="QR code, tabela e gráficos">
          <div className="mb-2 grid grid-cols-2 gap-2">
            <button type="button" onClick={() => aoPor(qrCode(medida))} {...arrastavel(() => qrCode(medida))} className="flex h-10 items-center justify-center rounded-[10px] bg-[#F1EFF6] text-[12.5px] font-bold text-[#1F1B2E] transition hover:bg-[#E9E4F8]">
              QR code
            </button>
            <button type="button" onClick={() => aoPor(tabela(medida))} {...arrastavel(() => tabela(medida))} className="flex h-10 items-center justify-center rounded-[10px] bg-[#F1EFF6] text-[12.5px] font-bold text-[#1F1B2E] transition hover:bg-[#E9E4F8]">
              Tabela
            </button>
          </div>
          <Grade pecas={GRAFICOS_PRONTOS} medida={medida} aoPor={aoPor} colunas={3} altura={64} filtro={filtro} />
        </Secao>
      ),
      "video-telas": () => cartoes("Para vídeo: telas e interface", TELAS_DE_VIDEO, "data-telas-de-video", "video", "video animado"),
      "video-destaques": () => cartoes("Para vídeo: destaques animados", DESTAQUES_DE_VIDEO, "data-destaques-de-video", "video", "video animado"),
      "video-legendas": () => cartoes("Para vídeo: títulos e legendas", LEGENDAS_DE_VIDEO, "data-legendas-de-video", "video", "video animado"),
      "video-particulas": () => cartoes("Para vídeo: partículas", PARTICULAS_DE_VIDEO, "data-particulas-de-video", "video", "video animado"),
    };
    const aberta = CATEGORIAS_DOS_ELEMENTOS.find((c) => c.chave === categoria) ?? null;
    const lista = aberta ? aberta.secoes : ORDEM_DOS_ELEMENTOS;
    return (
      <div>
        <Busca valor={filtro} aoMudar={setFiltro} placeholder={aberta ? `Procurar em ${aberta.nome.toLowerCase()}…` : "Procurar formas, selos, figurinhas…"} />
        {aberta ? (
          <div className="mb-4 flex items-center gap-2" data-categoria-aberta={aberta.chave}>
            <button type="button" onClick={() => setCategoria(null)} className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[#F3F1F9] text-[#4B4760] transition hover:bg-[#EDE7FF] hover:text-[#6D28D9]" aria-label="Voltar para todas as categorias" title="Todas as categorias" data-voltar-categorias>
              <ArrowLeft size={16} />
            </button>
            <h3 className="text-[15px] font-bold text-[#1F1B2E]">{aberta.nome}</h3>
          </div>
        ) : termo ? null : (
          <Secao titulo="Explorar categorias">
            <div className="grid grid-cols-3 gap-x-1.5 gap-y-3" data-categorias-dos-elementos>
              {CATEGORIAS_DOS_ELEMENTOS.map((c) => (
                <button key={c.chave} type="button" onClick={() => setCategoria(c.chave)} className="group flex min-w-0 flex-col items-center gap-1.5 rounded-[12px] px-0.5 pb-1 pt-2 text-center transition hover:bg-[#F6F3FF]" data-categoria={c.chave}>
                  <span className="relative block h-[54px] w-[54px]">
                    <span className="absolute inset-0 translate-x-[5px] -translate-y-[4px] rotate-[8deg] rounded-[15px] opacity-30" style={{ background: c.cor }} />
                    <span className="absolute inset-0 flex items-center justify-center rounded-[15px] text-white shadow-[inset_0_-3px_0_rgba(0,0,0,0.14)]" style={{ background: c.cor }}>
                      <c.Icone size={25} strokeWidth={2.1} />
                    </span>
                  </span>
                  <span className="line-clamp-2 text-[11.5px] font-semibold leading-tight text-[#1F1B2E]">{c.nome}</span>
                </button>
              ))}
            </div>
          </Secao>
        )}
        {/* Com a busca: o que a biblioteca (recortes e ícones 3D) tem com essa palavra vem primeiro. */}
        {termo ? <BibliotecaDeImagens titulo="Recortes, ícones 3D e ornamentos" medida={medida} aoPor={aoPor} termo={filtro} grupos={aberta ? gruposDaCategoria(aberta.chave) : undefined} /> : null}
        {lista.map((chave) => SECOES[chave]?.() ?? null)}
      </div>
    );
  }

  if (aba === "texto") {
    const botaoGrande = "block w-full rounded-[10px] bg-[#F1EFF6] px-4 text-left text-[#1F1B2E] transition hover:bg-[#E9E4F8]";
    return (
      <div>
        <div className="mb-6 space-y-2">
          <button type="button" className={`${botaoGrande} py-3 text-[22px] font-extrabold`} onClick={() => aoPor(TEXTOS_PRONTOS[0].criar(medida))} {...arrastavel(() => TEXTOS_PRONTOS[0].criar(medida))}>
            Adicionar um título
          </button>
          <button type="button" className={`${botaoGrande} py-2.5 text-[16px] font-bold`} onClick={() => aoPor(TEXTOS_PRONTOS[1].criar(medida))} {...arrastavel(() => TEXTOS_PRONTOS[1].criar(medida))}>
            Adicionar um subtítulo
          </button>
          <button type="button" className={`${botaoGrande} flex items-center justify-between py-2 text-[13px]`} onClick={() => aoPor(TEXTOS_PRONTOS[2].criar(medida))} {...arrastavel(() => TEXTOS_PRONTOS[2].criar(medida))} title="Atalho: tecla T">
            Adicionar um texto <Tecla>T</Tecla>
          </button>
          <button type="button" className={`${botaoGrande} flex items-center justify-between py-2 text-[13px]`} onClick={() => aoPor(contadorDePaginas(medida))} title="O número da página e o total (01/05), fixo em todas as páginas">
            Contador de páginas <span className="rounded-md bg-[#EDE7FF] px-1.5 py-0.5 text-[11px] font-bold tabular-nums text-[#5B21B6]">01/05</span>
          </button>
        </div>
        <Secao titulo="Combinações de texto">
          <div className="grid grid-cols-2 gap-2">
            {COMBINACOES_PRONTAS.map((c) => (
              <button key={c.chave} type="button" onClick={() => aoPorVarias(c.criar(medida))} {...arrastavel(() => c.criar(medida))} className="group rounded-[12px] p-1 text-left transition hover:bg-[#F3F0FF]" title={c.nome}>
                <div className="overflow-hidden rounded-[10px] ring-1 ring-black/5 transition group-hover:ring-2 group-hover:ring-[#8B5CF6]">
                  <AmostraDeVarias pecas={c.criar(medida)} largura={142} altura={96} />
                </div>
                <div className="mt-1 truncate px-0.5 text-[11px] font-medium text-[#4B4760]">{c.nome}</div>
              </button>
            ))}
          </div>
        </Secao>
        <Secao titulo="Estilos de texto">
          <Grade pecas={TEXTOS_PRONTOS.slice(3)} medida={medida} aoPor={aoPor} />
        </Secao>
        <Secao titulo="Chamadas de venda">
          <Grade pecas={CHAMADAS_PRONTAS} medida={medida} aoPor={aoPor} />
        </Secao>
      </div>
    );
  }

  if (aba === "imagens") {
    return (
      <div>
        <input ref={arquivo} type="file" accept="image/png,image/jpeg,image/webp" multiple className="hidden" onChange={(e) => {
          aoEnviarImagem(e.target.files);
          /* Limpa a escolha: mandar as mesmas fotos de novo também funciona. */
          e.target.value = "";
        }} />
        <button
          type="button"
          disabled={enviando}
          onClick={() => arquivo.current?.click()}
          className="mb-2 flex w-full flex-col items-center justify-center gap-1.5 rounded-[14px] border-2 border-dashed border-[#C4B5FD] bg-[#F7F4FF] px-4 py-5 text-[#5B21B6] transition hover:border-[#8B5CF6] hover:bg-[#EFE9FF] disabled:opacity-60"
        >
          {enviando ? <Loader2 size={22} className="animate-spin" /> : <Upload size={22} />}
          <span className="text-[13.5px] font-bold">{enviando ? "Enviando…" : "Enviar imagem do computador"}</span>
          <span className="text-[11.5px] font-medium text-[#7C6BA8]">PNG, JPG ou WebP · também dá para arrastar para a arte ou colar (Ctrl+V)</span>
        </button>
        <div className="mb-6" />
        {imagensDaPessoa.length ? (
          <Secao titulo="Suas imagens">
            <div className="grid grid-cols-3 gap-1.5">
              {imagensDaPessoa.slice(0, 60).map((src) => (
                <button key={src} type="button" onClick={() => aoPor(imagem(medida, src, "foto"))} {...arrastavel(() => imagem(medida, src, "foto"))} className="aspect-square overflow-hidden rounded-[10px] bg-[#F3F1F9] ring-1 ring-black/5 transition hover:ring-2 hover:ring-[#8B5CF6]">
                  {/* eslint-disable-next-line @next/next/no-img-element */}
                  <img src={src} alt="" className="h-full w-full object-cover" />
                </button>
              ))}
            </div>
          </Secao>
        ) : null}
        {logo ? (
          <Secao titulo="Logo da loja">
            <button type="button" onClick={() => aoPor(imagem(medida, logo, "logo", proporcaoDoLogo || 3))} {...arrastavel(() => imagem(medida, logo, "logo", proporcaoDoLogo || 3))} className="flex h-20 w-full items-center justify-center rounded-[12px] bg-[#F3F1F9] p-3 ring-1 ring-black/5 transition hover:ring-2 hover:ring-[#8B5CF6]">
              {/* eslint-disable-next-line @next/next/no-img-element */}
              <img src={logo} alt="Logo da loja" className="max-h-full max-w-full object-contain" />
            </button>
          </Secao>
        ) : null}
        <BibliotecaDeImagens titulo="Recortes sem fundo e ícones 3D" medida={medida} aoPor={aoPor} />
        <Secao titulo="Banco de imagens grátis">
          <BancoDeImagens aoEnviarImagem={(lista) => aoEnviarImagem(lista)} inicial={ATALHOS_DO_BANCO[0]} />
        </Secao>
        <Secao titulo="Fotos dos produtos">
          <Busca valor={filtro} aoMudar={setFiltro} placeholder="Procurar produto" />
          {comFoto.length ? (
            <div className="grid grid-cols-3 gap-1.5">
              {comFoto.slice(0, 90).map((p) => (
                <button key={p.id} type="button" title={p.name} onClick={() => p.image_url && aoPor(imagem(medida, p.image_url, "foto"))} {...arrastavel(() => imagem(medida, p.image_url || "", "foto"))} className="aspect-square overflow-hidden rounded-[10px] bg-[#F3F1F9] ring-1 ring-black/5 transition hover:ring-2 hover:ring-[#8B5CF6]">
                  {/* eslint-disable-next-line @next/next/no-img-element */}
                  <img src={p.image_url || ""} alt={p.name} className="h-full w-full object-cover" />
                </button>
              ))}
            </div>
          ) : (
            <p className="text-[12.5px] text-[#8A849E]">Nenhum produto com foto{filtro ? " com esse nome" : ""}.</p>
          )}
        </Secao>
      </div>
    );
  }

  /* As camadas moram em camadas.tsx (o editor abre o painel delas direto). */
  if (aba === "artes") {
    if (galeriaIndisponivel) {
      return <p className="rounded-[12px] bg-[#FFF7E6] p-3 text-[12.5px] leading-snug text-[#8A5300]">A galeria de artes ainda não foi ligada no banco. Você pode criar e baixar normalmente; as artes passam a ficar guardadas assim que ela for ligada.</p>;
    }
    if (!artes) {
      return (
        <div className="flex items-center gap-2 text-[12.5px] text-[#8A849E]">
          <Loader2 size={15} className="animate-spin" /> Abrindo as suas artes…
        </div>
      );
    }
    if (!artes.length) return <p className="text-[12.5px] leading-snug text-[#8A849E]">Nenhuma arte guardada ainda. Esta aqui já fica guardada sozinha a cada mudança.</p>;
    return (
      <div>
        <p className="mb-3 text-[12px] leading-snug text-[#8A849E]">As artes ficam guardadas sozinhas. Abrir uma de outro tamanho adapta ela a este.</p>
        <div className="grid grid-cols-2 gap-2.5">
          {artes.map((arte) => {
            const escala = 142 / arte.largura;
            const alto = Math.min(150, arte.altura * escala);
            return (
              <button key={arte.id} type="button" onClick={() => aoAbrirArte(arte)} className={`group rounded-[12px] p-1 text-left transition hover:bg-[#F3F0FF] ${arte.id === arteAberta ? "bg-[#F1ECFF] ring-2 ring-[#8B5CF6]" : ""}`}>
                <div className="flex items-center justify-center overflow-hidden rounded-[10px] bg-[#F3F1F9] ring-1 ring-black/5" style={{ height: alto }}>
                  <FotoDaArteGuardada arte={arte} largura={Math.min(142, (alto / arte.altura) * arte.largura)} />
                </div>
                <div className="mt-1 truncate px-0.5 text-[11.5px] font-semibold text-[#1F1B2E]">{arte.nome}</div>
                <div className="px-0.5 text-[10.5px] text-[#8A849E]">
                  {arte.largura} × {arte.altura}
                </div>
              </button>
            );
          })}
        </div>
      </div>
    );
  }

  return null;
}

type PropsDaBiblioteca = Parameters<typeof PainelDaBiblioteca>[0];

/* Da arte, o painel só usa o tamanho e se é de impressão; as funções são chamadas pela versão mais nova (abaixo). */
const BibliotecaQueNaoRedesenha = memo(PainelDaBiblioteca, (antes, agora) =>
  (Object.keys(agora) as Array<keyof PropsDaBiblioteca>).every((chave) => {
    if (chave === "doc") return antes.doc.largura === agora.doc.largura && antes.doc.altura === agora.doc.altura && Boolean(antes.doc.impressao) === Boolean(agora.doc.impressao);
    const [a, b] = [antes[chave], agora[chave]];
    if (typeof b === "function") return typeof a === "function";
    if (Array.isArray(a) && Array.isArray(b)) return a.length === b.length && a.every((v, i) => v === b[i]);
    return a === b;
  }),
);

/**
 * O painel da biblioteca (modelos, elementos, imagens...) sem redesenhar a
 * cada movimento da arte: arrastar uma peça muda a arte a cada quadro, e as
 * centenas de miniaturas do painel eram refeitas junto (pesava o arrasto).
 * Ele só redesenha quando muda o que mostra.
 */
export function PainelDaBibliotecaParado(props: PropsDaBiblioteca) {
  const ultimas = useRef(props);
  useLayoutEffect(() => {
    ultimas.current = props;
  });
  /* As funções ficam as mesmas a cada desenho e chamam a versão mais nova das do editor. */
  const aoTrocarDocumento = useCallback((d: DocumentoDoBanner) => ultimas.current.aoTrocarDocumento(d), []);
  const aoPor = useCallback((p: Peca) => ultimas.current.aoPor(p), []);
  const aoPorVarias = useCallback((p: Peca[]) => ultimas.current.aoPorVarias(p), []);
  const aoEnviarImagem = useCallback((arquivos: FileList | File[] | null) => ultimas.current.aoEnviarImagem(arquivos), []);
  const aoEscolherFotoDosModelos = useCallback((url: string | null) => ultimas.current.aoEscolherFotoDosModelos(url), []);
  const aoAbrirArte = useCallback((arte: ResumoDaArte) => ultimas.current.aoAbrirArte(arte), []);
  return <BibliotecaQueNaoRedesenha {...props} aoTrocarDocumento={aoTrocarDocumento} aoPor={aoPor} aoPorVarias={aoPorVarias} aoEnviarImagem={aoEnviarImagem} aoEscolherFotoDosModelos={aoEscolherFotoDosModelos} aoAbrirArte={aoAbrirArte} />;
}

/* ===================================================== ajustes da peça */

const DEGRADES_PRONTOS: Pintura[] = [
  degrade("#FFD400", "#FF5A00", 90),
  degrade("#FF3D00", "#E11D48", 120),
  degrade("#F472B6", "#7C3AED", 120),
  degrade("#7C3AED", "#2563EB", 120),
  degrade("#06B6D4", "#10B981", 120),
  degrade("#84CC16", "#047857", 120),
  degrade("#1E1B4B", "#0B0618", 160),
  degrade("#FFFFFF", "#E4E4E7", 180),
];

/*
 * As cores de um elemento em SVG (como o Canva): as bolinhas de todas as
 * cores dele em cima (a escolhida com o anel), a paleta para trocar a
 * escolhida e o "voltar às cores originais".
 */
function CoresDoVetor({ p, mudar, escolhida, aoEscolher }: { p: PecaDeImagem; mudar: (c: Partial<PecaDeImagem>) => void; escolhida: string; aoEscolher?: (original: string) => void }) {
  const vetor = p.vetor!;
  const atual = (c: string) => vetor.trocas?.[c] ?? c;
  const trocar = (original: string, cor: string) => mudar({ vetor: { ...vetor, trocas: { ...(vetor.trocas || {}), [original]: cor.toUpperCase() } } });
  const trocadas = Object.entries(vetor.trocas || {}).filter(([de, para]) => de !== para).length;
  return (
    <div data-cores-do-vetor>
      <Secao titulo="Cores deste elemento" icone={Palette}>
        <div className="flex flex-wrap gap-2">
          {vetor.cores.map((c) => (
            <button key={c} type="button" title={c === atual(c) ? c : `${c} → ${atual(c)}`} onClick={() => aoEscolher?.(c)} className={`h-9 w-9 rounded-full ring-1 ring-black/15 transition hover:scale-110 ${c === escolhida ? "ring-2 ring-[#8B5CF6] ring-offset-2" : ""}`} style={{ background: atual(c) }} data-cor-do-vetor={c} />
          ))}
        </div>
        {trocadas ? (
          <div className="mt-3">
            <BotaoDoPainel icone={RotateCcw} aoClicar={() => mudar({ vetor: { cores: vetor.cores } })} titulo="Voltar às cores de antes">
              Voltar às cores originais
            </BotaoDoPainel>
          </div>
        ) : null}
      </Secao>
      <PainelDeCor valor={solida(atual(escolhida))} aoMudar={(pintura) => trocar(escolhida, pintura.cor)} rotulo="Cor escolhida" soCor />
    </div>
  );
}

/** O painel grande de cor (como o do Canva): paleta da loja, usadas no banner, paleta viva, degradês prontos e o ajuste fino. */
function PainelDeCor({
  valor,
  aoMudar,
  rotulo,
  soCor = false,
  permiteSem = false,
  comAlcas = false,
}: {
  valor: Pintura;
  aoMudar: (p: Pintura) => void;
  rotulo: string;
  soCor?: boolean;
  permiteSem?: boolean;
  /* A peça mostra as alças do degradê no palco (forma, texto, ícone). */
  comAlcas?: boolean;
}) {
  const { daLoja, usadas } = useContext(ContextoDasCores);
  const recentes = useCoresRecentes();
  /* Trocar a cor mantém a transparência que já havia. */
  const porCor = (cor: string) => {
    lembrarCor(cor);
    aoMudar({ ...solida(cor), ...(valor.tipo === "solida" && valor.alfa !== undefined ? { alfa: valor.alfa } : {}) });
  };
  const amostra = (cor: string, chave: string) => (
    <button
      key={chave}
      type="button"
      title={cor}
      onClick={() => porCor(cor)}
      className={`h-9 w-9 rounded-[10px] ring-1 ring-black/10 transition hover:scale-110 ${valor.tipo === "solida" && valor.cor === cor ? "ring-2 ring-[#8B5CF6] ring-offset-2" : ""}`}
      style={{ background: cor }}
    />
  );
  return (
    <div>
      <div className="mb-1 flex items-center gap-2.5">
        <BotaoContaGotas aoPegar={porCor} grande />
        <Dica icone={Pipette}>Conta-gotas: pegue uma cor de qualquer ponto da arte.</Dica>
      </div>
      {recentes.length ? (
        <Secao titulo="Recentes" icone={History}>
          <div className="flex flex-wrap gap-2">{recentes.map((c) => amostra(c, `r${c}`))}</div>
        </Secao>
      ) : null}
      {daLoja.length ? (
        <Secao titulo="Cores da loja" icone={Store}>
          <div className="flex flex-wrap gap-2">{daLoja.map((c) => amostra(c, `l${c}`))}</div>
        </Secao>
      ) : null}
      {usadas.length ? (
        <Secao titulo="Nesta arte" icone={Palette}>
          <div className="flex flex-wrap gap-2">{usadas.slice(0, 16).map((c) => amostra(c, `u${c}`))}</div>
        </Secao>
      ) : null}
      <Secao titulo="Cores" icone={Palette}>
        <div className="grid grid-cols-7 gap-2">{PALETA_VIVA.map((c) => amostra(c, `p${c}`))}</div>
      </Secao>
      {soCor ? null : (
        <Secao titulo="Degradês" icone={Blend}>
          <div className="grid grid-cols-7 gap-2">
            {DEGRADES_PRONTOS.map((d, i) => (
              <button key={i} type="button" onClick={() => aoMudar(d)} className="h-9 w-9 rounded-[10px] ring-1 ring-black/10 transition hover:scale-110" style={{ background: `linear-gradient(${d.angulo}deg, ${d.cor}, ${d.cor2})` }} />
            ))}
          </div>
        </Secao>
      )}
      {soCor ? null : (
        <Secao titulo="Metálicos" icone={Sparkles}>
          <div className="grid grid-cols-5 gap-x-2 gap-y-2.5" data-metalicos>
            {DEGRADES_METALICOS.map((m) => {
              const escolhido = valor.tipo === "linear" && valor.cor === m.pintura.cor && valor.cor2 === m.pintura.cor2;
              return (
                <button key={m.chave} type="button" title={m.nome} data-metalico={m.chave} onClick={() => aoMudar({ ...m.pintura, paradas: m.pintura.paradas?.map((q) => ({ ...q })), ...(valor.tipo === "linear" && valor.pontos ? { pontos: valor.pontos } : {}) })} className="group flex min-w-0 flex-col items-center gap-1">
                  <span className={`block h-9 w-full rounded-[10px] ring-1 ring-black/10 transition group-hover:scale-105 ${escolhido ? "ring-2 ring-[#8B5CF6] ring-offset-2" : ""}`} style={{ background: cssDaPintura(m.pintura) }} />
                  <span className="max-w-full truncate text-[10.5px] font-medium text-[#4B4760]">{m.nome}</span>
                </button>
              );
            })}
          </div>
        </Secao>
      )}
      {comAlcas && valor.tipo === "linear" ? <DicaDasAlcas texto="Arraste as alças na peça para mudar a direção e onde cada cor começa. Clique numa alça para ajustar quanto ela cobre." /> : null}
      <Secao titulo={`Ajuste fino · ${rotulo}`} icone={SlidersHorizontal}>
        {soCor ? (
          <CampoDePintura valor={{ ...valor, tipo: "solida" }} aoMudar={(p) => aoMudar({ ...p, tipo: "solida" })} rotulo={rotulo} />
        ) : (
          <CampoDePintura valor={valor} aoMudar={aoMudar} rotulo={rotulo} permiteSem={permiteSem} />
        )}
      </Secao>
    </div>
  );
}

function PainelDaFonte({ p, mudar }: { p: PecaDeTexto; mudar: (c: Partial<PecaDeTexto>) => void }) {
  const [filtro, setFiltro] = useState("");
  const comTrecho = useTrechoEscolhido();
  const grupos = TIPOS_DE_FONTE;
  /* O painel mostra cada fonte no desenho dela: a biblioteca inteira carrega quando ele abre. */
  useEffect(() => garantirFontes(FONTES.map((f) => f.chave)), []);
  return (
    <div>
      {comTrecho ? (
        <div className="mb-3">
          <Dica icone={Type}>A fonte vale só para o trecho escolhido.</Dica>
        </div>
      ) : null}
      <Busca valor={filtro} aoMudar={setFiltro} placeholder="Procurar fonte" />
      {[
        { titulo: "Com glifos (letras com floreio)", lista: FONTES_COM_GLIFOS, selo: "glifos", marca: "data-fontes-com-glifos" },
        { titulo: "Da marca", lista: fontesDaMarca().filter((f) => !ehFonteComGlifos(f.chave)), selo: "sua", marca: "data-fontes-da-marca" },
      ].map(({ titulo, lista, selo, marca }) =>
        lista.filter((f) => f.nome.toLowerCase().includes(filtro.trim().toLowerCase())).length ? (
        <Secao key={titulo} titulo={titulo}>
          {selo === "glifos" ? <p className="-mt-1 mb-2 text-[11.5px] leading-snug text-[#8A849E]">Escreva o nome e abra os Glifos (Ω) para trocar as letras pelas de perninha.</p> : null}
          <div className="space-y-1" {...{ [marca]: "" }}>
            {lista
              .filter((f) => f.nome.toLowerCase().includes(filtro.trim().toLowerCase()))
              .map((f) => (
                <button
                  key={f.chave}
                  type="button"
                  onMouseDown={(e) => e.preventDefault()}
                  onClick={() => {
                    if (!(comTrecho && formatarTrecho("fonte", f.chave))) mudar({ fonte: f.chave });
                  }}
                  className={`flex w-full items-center justify-between rounded-[10px] px-3 py-2.5 text-left transition ${p.fonte === f.chave ? "bg-[#F1ECFF] ring-2 ring-[#8B5CF6]" : "hover:bg-[#F6F4FB]"}`}
                >
                  <span className="truncate text-[19px] leading-none text-[#1F1B2E]" style={{ fontFamily: `'${f.chave}', sans-serif` }} data-fonte={f.chave}>
                    {f.nome.replace(" (com glifos)", "")}
                  </span>
                  <span className="text-[10.5px] text-[#9A94AE]">{selo}</span>
                </button>
              ))}
          </div>
        </Secao>
        ) : null,
      )}
      {grupos.map(([tipo, titulo]) => {
        const lista = FONTES.filter((f) => f.tipo === tipo && f.familia.toLowerCase().includes(filtro.trim().toLowerCase()));
        if (!lista.length) return null;
        return (
          <Secao key={tipo} titulo={titulo}>
            <div className="space-y-1">
              {lista.map((f) => (
                <button
                  key={f.chave}
                  type="button"
                  onMouseDown={(e) => e.preventDefault()}
                  onClick={() => {
                    if (!(comTrecho && formatarTrecho("fonte", f.familia))) mudar({ fonte: f.chave });
                  }}
                  className={`flex w-full items-center justify-between rounded-[10px] px-3 py-2.5 text-left transition ${p.fonte === f.chave ? "bg-[#F1ECFF] ring-2 ring-[#8B5CF6]" : "hover:bg-[#F6F4FB]"}`}
                >
                  <span className="text-[19px] leading-none text-[#1F1B2E]" style={{ fontFamily: `'${f.familia}'`, fontWeight: f.pesos[f.pesos.length - 1] }}>
                    {f.familia}
                  </span>
                  <span className="text-[10.5px] text-[#9A94AE]">{f.pesos.length > 1 ? `${f.pesos.length} pesos` : ""}</span>
                </button>
              ))}
            </div>
          </Secao>
        );
      })}
    </div>
  );
}

function DicaDasAlcas({ texto }: { texto: string }) {
  return (
    <div className="mb-3">
      <Dica icone={MousePointer2}>{texto}</Dica>
    </div>
  );
}

/* As direções prontas do sumiço (de onde aparece para onde some). */
const DIRECOES_DO_SUMICO: Array<{ nome: string; pontos: PontosDoDegrade }> = [
  { nome: "Some embaixo", pontos: { x1: 0.5, y1: 0.45, x2: 0.5, y2: 1 } },
  { nome: "Some em cima", pontos: { x1: 0.5, y1: 0.55, x2: 0.5, y2: 0 } },
  { nome: "Some à direita", pontos: { x1: 0.45, y1: 0.5, x2: 1, y2: 0.5 } },
  { nome: "Some à esquerda", pontos: { x1: 0.55, y1: 0.5, x2: 0, y2: 0.5 } },
  { nome: "Na diagonal", pontos: { x1: 0.3, y1: 0.3, x2: 1, y2: 1 } },
];

/** As direções prontas, o inverter e o quanto aparece em cada ponta (as alças fazem o resto, na peça). */
function SumicoComAlcas({ t, aoMudar }: { t: NonNullable<Peca["transparencia"]>; aoMudar: (t: NonNullable<Peca["transparencia"]>) => void }) {
  return (
    <div className="space-y-3.5">
      <DicaDasAlcas texto="Arraste as alças na peça: a primeira é onde ela aparece, a da seta é onde some. Vale qualquer ângulo; Shift trava de 15 em 15 graus." />
      <div className="grid grid-cols-2 gap-1.5">
        {DIRECOES_DO_SUMICO.map((d) => {
          const ativa = Math.abs(t.x1 - d.pontos.x1) < 0.01 && Math.abs(t.y1 - d.pontos.y1) < 0.01 && Math.abs(t.x2 - d.pontos.x2) < 0.01 && Math.abs(t.y2 - d.pontos.y2) < 0.01;
          return (
            <button
              key={d.nome}
              type="button"
              onClick={() => aoMudar({ ...t, ...d.pontos })}
              className={`flex h-9 items-center gap-2 rounded-[9px] border px-2.5 text-[12px] font-medium transition ${ativa ? "border-[#7C3AED] text-[#5B21B6]" : "border-[#EEEBF5] text-[#4B4760] hover:border-[#C4B5FD]"}`}
            >
              <span className="h-5 w-5 shrink-0 rounded-[5px] ring-1 ring-black/10" style={{ background: `linear-gradient(${Math.round((Math.atan2(d.pontos.x2 - d.pontos.x1, -(d.pontos.y2 - d.pontos.y1)) * 180) / Math.PI)}deg, #1F1B2E, rgba(31,27,46,0.06))` }} />
              {d.nome}
            </button>
          );
        })}
        <button type="button" onClick={() => aoMudar({ ...t, x1: t.x2, y1: t.y2, x2: t.x1, y2: t.y1 })} className="flex h-9 items-center gap-2 rounded-[9px] border border-[#EEEBF5] px-2.5 text-[12px] font-medium text-[#4B4760] transition hover:border-[#C4B5FD]">
          <ArrowLeftRight size={15} className="shrink-0" /> Inverter
        </button>
      </div>
      <Regua rotulo="Aparece no começo" valor={Math.round(t.alfa * 100)} min={0} max={100} aoMudar={(v) => aoMudar({ ...t, alfa: v / 100 })} formato={(v) => `${Math.round(v)}%`} />
      <Regua rotulo="Aparece no fim" valor={Math.round(t.alfa2 * 100)} min={0} max={100} aoMudar={(v) => aoMudar({ ...t, alfa2: v / 100 })} formato={(v) => `${Math.round(v)}%`} />
    </div>
  );
}

/** Transparência da peça: por igual ou em degradê, com as alças no palco (como a do Corel). */
function PainelDaTransparencia({ peca, mudar }: { peca: Peca; mudar: (c: Partial<Peca>) => void }) {
  const t = peca.transparencia;
  return (
    <div>
      <Secao titulo="A peça inteira" icone={Droplet}>
        <Regua rotulo="Aparece" valor={Math.round(peca.opacidade * 100)} min={5} max={100} aoMudar={(v) => mudar({ opacidade: v / 100 })} formato={(v) => `${Math.round(v)}%`} padrao={100} />
      </Secao>
      <Secao
        titulo={peca.tipo === "imagem" ? "Sumir aos poucos (em degradê)" : "Em degradê"}
        icone={Blend}
        ligado={Boolean(t)}
        aoLigar={(v) => mudar({ transparencia: v ? { ...DIRECOES_DO_SUMICO[0].pontos, alfa: 1, alfa2: 0 } : null, ...(peca.tipo === "imagem" ? { desvanecer: null } : {}) } as Partial<Peca>)}
        dica="A peça some aos poucos de um ponto a outro (forma, foto, texto ou ícone). Ligue e arraste as alças na própria peça."
      >
        {t ? (
          <>
            <div className="mb-3">
              <Opcoes
                valor={t.radial ? "radial" : "linear"}
                opcoes={[
                  { valor: "linear", rotulo: "De um lado ao outro", icone: ArrowRight },
                  { valor: "radial", rotulo: "Do centro para fora", icone: CircleDot },
                ]}
                aoMudar={(v) => mudar({ transparencia: v === "radial" ? { ...t, radial: true, x1: 0.5, y1: 0.5, x2: 1, y2: 0.5 } : { ...t, radial: undefined, ...DIRECOES_DO_SUMICO[0].pontos } })}
              />
            </div>
            {t.radial ? (
              <div className="space-y-3">
                <Dica icone={MousePointer2}>Arraste as alças na peça: a do começo é o centro, a do fim é a borda do círculo.</Dica>
                <div className="grid grid-cols-2 gap-2">
                  <Regua rotulo="No centro" valor={Math.round(t.alfa * 100)} min={0} max={100} aoMudar={(v) => mudar({ transparencia: { ...t, alfa: v / 100 } })} formato={(v) => `${Math.round(v)}%`} />
                  <Regua rotulo="Na borda" valor={Math.round(t.alfa2 * 100)} min={0} max={100} aoMudar={(v) => mudar({ transparencia: { ...t, alfa2: v / 100 } })} formato={(v) => `${Math.round(v)}%`} />
                </div>
              </div>
            ) : (
              <SumicoComAlcas t={t} aoMudar={(transparencia) => mudar({ transparencia })} />
            )}
          </>
        ) : null}
      </Secao>
      <SecaoDeMesclagem peca={peca} mudar={mudar} />
    </div>
  );
}

const ESTILOS: Array<{ valor: EstiloDeTextura; nome: string }> = [
  { valor: "raios", nome: "Raios" },
  { valor: "pontos", nome: "Bolinhas" },
  { valor: "listras", nome: "Listras" },
  { valor: "xadrez", nome: "Xadrez" },
  { valor: "grade", nome: "Quadriculado" },
  { valor: "ondas", nome: "Ondas" },
  { valor: "grao", nome: "Grão" },
  { valor: "meio-tom", nome: "Retícula" },
  { valor: "topografia", nome: "Curvas de nível" },
];
/* As que se mexem no vídeo (particulas.ts). */
const PARTICULAS: Array<{ valor: EstiloDeTextura; nome: string }> = [
  { valor: "confete", nome: "Confete" },
  { valor: "neve", nome: "Neve" },
  { valor: "brilhos", nome: "Brilhos" },
  { valor: "bolhas", nome: "Bolhas" },
  { valor: "coracoes", nome: "Corações" },
  { valor: "faiscas", nome: "Faíscas" },
  { valor: "fogos", nome: "Fogos" },
];

function AjustesDaTextura({ p, mudar }: { p: PecaDeTextura; mudar: (c: Partial<PecaDeTextura>) => void }) {
  const particula = PARTICULAS.some((e) => e.valor === p.estilo);
  return (
    <div>
      <Secao titulo="Partículas (se mexem no vídeo)">
        <div className="grid grid-cols-3 gap-2" data-particulas>
          {PARTICULAS.map((e) => {
            const amostra: PecaDeTextura = { ...p, id: `amostra-${e.valor}`, x: 0, y: 0, w: 96, h: 56, rot: 0, opacidade: 1, estilo: e.valor, tamanho: 5, quantidade: 26, cor: "#FFFFFF", centroX: 50, centroY: 90 };
            return (
              <button key={e.valor} type="button" onClick={() => mudar({ estilo: e.valor, quantidade: particula ? p.quantidade : 60, tamanho: particula ? p.tamanho : 0, opacidade: particula ? p.opacidade : 1, centroX: e.valor === "faiscas" ? 50 : p.centroX, centroY: e.valor === "faiscas" ? 80 : p.centroY })} className="group text-center" data-particula={e.valor}>
                <span className={`relative block h-[56px] overflow-hidden rounded-[10px] bg-[linear-gradient(135deg,#1E1B4B,#4C1D95)] transition ${p.estilo === e.valor ? "ring-2 ring-[#8B5CF6] ring-offset-1" : "ring-1 ring-black/5"}`}>
                  <span className="absolute inset-0" dangerouslySetInnerHTML={{ __html: conteudoDaPeca(amostra) }} />
                </span>
                <span className="mt-1 block text-[11px] font-medium text-[#4B4760]">{e.nome}</span>
              </button>
            );
          })}
        </div>
      </Secao>
      <Secao titulo="Desenho">
        <div className="grid grid-cols-3 gap-2">
          {ESTILOS.map((e) => {
            const amostra: PecaDeTextura = { ...p, id: `amostra-${e.valor}`, x: 0, y: 0, w: 96, h: 56, rot: 0, opacidade: 1, estilo: e.valor, tamanho: 12, cor: "#FFFFFF" };
            return (
              <button key={e.valor} type="button" onClick={() => mudar({ estilo: e.valor })} className="group text-center">
                <span className={`relative block h-[56px] overflow-hidden rounded-[10px] bg-[linear-gradient(135deg,#7C3AED,#4C1D95)] transition ${p.estilo === e.valor ? "ring-2 ring-[#8B5CF6] ring-offset-1" : "ring-1 ring-black/5"}`}>
                  <span className="absolute inset-0 opacity-50" dangerouslySetInnerHTML={{ __html: conteudoDaPeca(amostra) }} />
                </span>
                <span className="mt-1 block text-[11px] font-medium text-[#4B4760]">{e.nome}</span>
              </button>
            );
          })}
        </div>
      </Secao>
      <Secao titulo="Ajustes">
        <div className="space-y-3.5">
          <Regua rotulo="Força" valor={Math.round(p.opacidade * 100)} min={2} max={100} aoMudar={(v) => mudar({ opacidade: v / 100 })} formato={(v) => `${Math.round(v)}%`} />
          {particula ? (
            <>
              <Regua rotulo="Quantas" valor={p.quantidade} min={4} max={300} aoMudar={(quantidade) => mudar({ quantidade })} />
              <Regua rotulo="Velocidade" valor={Math.round((p.velocidade ?? 1) * 100)} min={20} max={300} aoMudar={(v) => mudar({ velocidade: v / 100 })} formato={(v) => `${(v / 100).toFixed(1).replace(".", ",")}×`} />
              {p.estilo === "faiscas" ? <Regua rotulo="De onde saem (altura)" valor={p.centroY} min={0} max={100} aoMudar={(centroY) => mudar({ centroY })} formato={(v) => `${Math.round(v)}%`} /> : null}
            </>
          ) : null}
          {p.estilo === "raios" || p.estilo === "topografia" ? (
            <>
              <Regua rotulo={p.estilo === "raios" ? "Quantidade de raios" : "Quantidade de curvas"} valor={p.quantidade} min={4} max={p.estilo === "raios" ? 60 : 40} aoMudar={(quantidade) => mudar({ quantidade })} />
              <Regua rotulo="Centro na largura" valor={p.centroX} min={-20} max={120} aoMudar={(centroX) => mudar({ centroX })} formato={(v) => `${Math.round(v)}%`} />
              <Regua rotulo="Centro na altura" valor={p.centroY} min={-20} max={120} aoMudar={(centroY) => mudar({ centroY })} formato={(v) => `${Math.round(v)}%`} />
            </>
          ) : null}
          {p.estilo !== "raios" && p.estilo !== "grao" ? <Regua rotulo="Tamanho do desenho" valor={p.tamanho || Math.round(Math.min(p.w, p.h) * 0.06)} min={6} max={240} aoMudar={(tamanho) => mudar({ tamanho })} /> : null}
          {p.estilo !== "grao" && !particula ? <Regua rotulo="Giro do desenho" valor={p.giro} min={0} max={360} aoMudar={(giro) => mudar({ giro })} formato={(v) => `${Math.round(v)}°`} /> : null}
        </div>
      </Secao>
    </div>
  );
}

function PainelDePosicao({ peca, medida, mudar, aoOrdem, aoDistorcer, distorcendo }: { peca: Peca; medida: Medida; mudar: (c: Partial<Peca>) => void; aoOrdem: (para: "frente" | "tras" | "subir" | "descer") => void; aoDistorcer?: () => void; distorcendo?: boolean }) {
  return (
    <div>
      <Secao titulo="Tamanho e posição" icone={Move}>
        <div className="grid grid-cols-2 gap-2">
          <Numero rotulo="Largura" valor={peca.w} aoMudar={(w) => mudar({ w: Math.max(4, w) })} />
          <Numero rotulo="Altura" valor={peca.h} aoMudar={(h) => mudar({ h: Math.max(4, h) })} />
          <Numero rotulo="X" valor={peca.x} aoMudar={(x) => mudar({ x })} negativo />
          <Numero rotulo="Y" valor={peca.y} aoMudar={(y) => mudar({ y })} negativo />
        </div>
        <div className="mt-3 flex items-end gap-2">
          <div className="min-w-0 flex-1">
            <Regua rotulo="Giro" valor={peca.rot} min={-180} max={180} aoMudar={(rot) => mudar({ rot })} formato={(v) => `${Math.round(v)}°`} padrao={0} />
          </div>
          <BotaoDoPainel icone={RotateCcw} titulo="Sem giro" aoClicar={() => mudar({ rot: 0 })} />
        </div>
        <div className="mt-3 grid grid-cols-2 gap-2">
          <BotaoDoPainel icone={FlipHorizontal2} ativo={Boolean(peca.espelharX)} aoClicar={() => mudar({ espelharX: !peca.espelharX })} titulo="Espelhar na horizontal">
            Espelhar
          </BotaoDoPainel>
          <BotaoDoPainel icone={FlipVertical2} ativo={Boolean(peca.espelharY)} aoClicar={() => mudar({ espelharY: !peca.espelharY })} titulo="Virar de cabeça para baixo">
            Inverter
          </BotaoDoPainel>
        </div>
      </Secao>
      <Secao titulo="Alinhar na arte" icone={AlignHorizontalJustifyCenter}>
        <BotoesDeIcone
          botoes={[
            { icone: AlignStartVertical, titulo: "Encostar à esquerda", aoClicar: () => mudar({ x: 0 }) },
            { icone: AlignHorizontalJustifyCenter, titulo: "Centralizar na horizontal", aoClicar: () => mudar({ x: (medida.largura - peca.w) / 2 }) },
            { icone: AlignEndVertical, titulo: "Encostar à direita", aoClicar: () => mudar({ x: medida.largura - peca.w }) },
            { icone: AlignVerticalJustifyStart, titulo: "Encostar no topo", aoClicar: () => mudar({ y: 0 }) },
            { icone: AlignVerticalJustifyCenter, titulo: "Centralizar na vertical", aoClicar: () => mudar({ y: (medida.altura - peca.h) / 2 }) },
            { icone: AlignVerticalJustifyEnd, titulo: "Encostar na base", aoClicar: () => mudar({ y: medida.altura - peca.h }) },
          ]}
        />
        <div className="mt-2">
          <BotaoDoPainel icone={Maximize2} largo aoClicar={() => mudar({ x: 0, y: 0, w: medida.largura, h: medida.altura, rot: 0 })} titulo="A peça cobre a arte inteira">
            Ocupar a arte inteira
          </BotaoDoPainel>
        </div>
      </Secao>
      <Secao titulo="Camada" icone={Layers}>
        <div className="grid grid-cols-2 gap-2">
          <BotaoDoPainel icone={ArrowUpToLine} aoClicar={() => aoOrdem("frente")} titulo="Por cima de todas">
            Para a frente
          </BotaoDoPainel>
          <BotaoDoPainel icone={ArrowDownToLine} aoClicar={() => aoOrdem("tras")} titulo="Por baixo de todas">
            Para trás
          </BotaoDoPainel>
          <BotaoDoPainel icone={ChevronUp} aoClicar={() => aoOrdem("subir")} titulo="Uma camada acima">
            Subir uma
          </BotaoDoPainel>
          <BotaoDoPainel icone={ChevronDown} aoClicar={() => aoOrdem("descer")} titulo="Uma camada abaixo">
            Descer uma
          </BotaoDoPainel>
        </div>
      </Secao>
      <SecaoDeDistorcer peca={peca} mudar={mudar} aoDistorcer={aoDistorcer} distorcendo={distorcendo} />
    </div>
  );
}

/** O ajuste que a barra flutuante abriu, para a peça escolhida. */
export function PainelDoAjuste({
  ajuste,
  peca,
  medida,
  mudar,
  aoOrdem,
  aoEnquadrar,
  aoDistorcer,
  distorcendo,
  corDoVetor = null,
  aoCorDoVetor,
  foto = null,
  aoSepararContorno,
  pontoVivo = null,
  aoEscolherPontoVivo,
}: {
  ajuste: AjusteDaPeca;
  peca: Peca;
  /* Elemento em SVG: qual cor original está sendo trocada. */
  corDoVetor?: string | null;
  aoCorDoVetor?: (original: string) => void;
  medida: Medida;
  mudar: (campos: Partial<Peca>) => void;
  aoOrdem: (para: "frente" | "tras" | "subir" | "descer") => void;
  /* Foto: entra no enquadre arrastando na arte. */
  aoEnquadrar?: () => void;
  /* Distorcer pelos 4 cantos na arte (liga e desliga as alças dos cantos). */
  aoDistorcer?: () => void;
  distorcendo?: boolean;
  /* Foto: o tratamento de agora (o que está sendo mexido vence o guardado), a foto de antes e quem aplica (o editor). */
  foto?: {
    tratamento: TratamentoDaFoto | null;
    origem: string;
    aoTratar: (campos: { trocas?: TrocaDeCor[]; meioTom?: MeioTom | null }) => void;
    aplicando: boolean;
    corInicial: string | null;
    dpi: number | null;
  } | null;
  /* "Separar contorno": o contorno vira uma peça própria (só a borda ou cheio). */
  aoSepararContorno?: (qual: ContornoQueSepara, modo: ModoDeSeparar) => void;
  /* Retícula Viva: o ponto de cor escolhido (o mesmo na arte e no painel). */
  pontoVivo?: number | null;
  aoEscolherPontoVivo?: (i: number | null) => void;
}) {
  const comTrecho = useTrechoEscolhido();
  /* Retícula Viva vale para qualquer peça (texto, forma, foto, ícone, desenho). */
  if (ajuste === "reticula-viva") return <PainelDaReticulaViva peca={peca} mudar={mudar} ponto={pontoVivo} aoEscolherPonto={aoEscolherPontoVivo ?? (() => undefined)} />;
  if (peca.tipo === "imagem" && foto && ajuste === "cores-da-foto") return <PainelDasCoresDaFoto key={foto.corInicial ?? ""} origem={foto.origem} tratamento={foto.tratamento} aoTratar={foto.aoTratar} aplicando={foto.aplicando} corInicial={foto.corInicial} />;
  if (peca.tipo === "imagem" && foto && ajuste === "reticula") return <PainelDaReticula foto={peca} tratamento={foto.tratamento} aoTratar={foto.aoTratar} aplicando={foto.aplicando} dpi={foto.dpi} />;
  if (ajuste === "transparencia") return <PainelDaTransparencia peca={peca} mudar={mudar} />;
  if (ajuste === "vetor" && peca.tipo === "imagem" && peca.vetor) return <CoresDoVetor p={peca} mudar={mudar as (c: Partial<PecaDeImagem>) => void} escolhida={corDoVetor ?? peca.vetor.cores[0]} aoEscolher={aoCorDoVetor} />;
  /* Contorno e sombra valem para qualquer peça (os efeitos da camada de antes abrem os dois juntos). */
  if (ajuste === "contorno") return <PainelDeContorno peca={peca} mudar={mudar} aoSeparar={aoSepararContorno} />;
  if (ajuste === "sombra") return <PainelDeSombra peca={peca} mudar={mudar} />;
  if (ajuste === "camada") {
    return (
      <>
        <PainelDeContorno peca={peca} mudar={mudar} aoSeparar={aoSepararContorno} />
        <PainelDeSombra peca={peca} mudar={mudar} />
      </>
    );
  }
  if (ajuste !== "posicao") {
    if (peca.tipo === "traco") {
      const m = mudar as (c: Partial<PecaDeTraco>) => void;
      /* Desenho fechado: a cor é a de dentro (o contorno fica nos ajustes dele). */
      if (ajuste === "cor" && peca.fechado) return <PainelDeCor valor={peca.preenchimento} aoMudar={(preenchimento) => m({ preenchimento })} rotulo="Cor de dentro" permiteSem comAlcas />;
      if (ajuste === "cor") return <PainelDeCor valor={solida(peca.cor)} aoMudar={(pintura) => m({ cor: pintura.cor })} rotulo="Cor da linha" soCor />;
      return <EfeitosDoTraco p={peca} mudar={m} />;
    }
    if (peca.tipo === "qr") return <PainelDoQr p={peca} mudar={mudar as (c: Partial<PecaDeQr>) => void} />;
    if (peca.tipo === "tabela") return <PainelDaTabela p={peca} mudar={mudar as (c: Partial<PecaDeTabela>) => void} />;
    if (peca.tipo === "grafico") return <PainelDoGrafico p={peca} mudar={mudar as (c: Partial<PecaDeGrafico>) => void} />;
  }
  if (peca.tipo === "icone" && ajuste !== "posicao") {
    const m = mudar as (c: Partial<PecaDeIcone>) => void;
    if (ajuste === "cor") return <PainelDeCor valor={peca.pintura} aoMudar={(pintura) => m({ pintura })} rotulo="Cor do ícone" comAlcas />;
    /* O traço do ícone mora no Contorno. */
    return <PainelDeContorno peca={peca} mudar={mudar} aoSeparar={aoSepararContorno} />;
  }
  if (ajuste === "posicao") return <PainelDePosicao peca={peca} medida={medida} mudar={mudar} aoOrdem={aoOrdem} aoDistorcer={aoDistorcer} distorcendo={distorcendo} />;
  if (peca.tipo === "texto") {
    const m = mudar as (c: Partial<PecaDeTexto>) => void;
    if (ajuste === "fonte") return <PainelDaFonte p={peca} mudar={m} />;
    if (ajuste === "glifos") return <PainelDeGlifos p={peca} mudar={m} />;
    if (ajuste === "cor") {
      return (
        <PainelDeCor
          valor={peca.pintura}
          aoMudar={(pintura) => {
            /* Com um trecho escolhido, a cor (sólida) vai só para ele. */
            if (!(comTrecho && pintura.tipo === "solida" && formatarTrecho("cor", pintura.cor))) m({ pintura });
          }}
          rotulo={comTrecho ? "Cor do trecho" : "Cor da letra"}
          permiteSem={!comTrecho}
          soCor={comTrecho}
          comAlcas={!comTrecho}
        />
      );
    }
    if (ajuste === "cor-do-fundo" && peca.fundo) return <PainelDeCor valor={peca.fundo.pintura} aoMudar={(pintura) => m({ fundo: { ...peca.fundo!, pintura } })} rotulo="Fundo do texto" permiteSem />;
    if (ajuste === "espacamento") return <PainelDoEspacamento p={peca} mudar={m} />;
    if (ajuste === "curva") return <PainelDaCurva p={peca} mudar={m} />;
    if (ajuste === "fundo-do-texto") return <PainelDoFundoDoTexto p={peca} mudar={m} />;
    if (ajuste === "marca-texto") return <PainelDoMarcaTexto p={peca} mudar={m} />;
    /* "estilo" (e o "efeitos" de antes): os estilos prontos da letra. */
    return <EstiloDaLetra p={peca} mudar={m} />;
  }
  if (peca.tipo === "forma") {
    const m = mudar as (c: Partial<PecaDeForma>) => void;
    if (ajuste === "cor") return <PainelDeCor valor={peca.pintura} aoMudar={(pintura) => m({ pintura })} rotulo={peca.nome === "Fundo" ? "Cor do fundo" : "Preenchimento"} permiteSem comAlcas />;
    return <PainelDaForma p={peca} mudar={m} />;
  }
  if (peca.tipo === "textura") {
    const m = mudar as (c: Partial<PecaDeTextura>) => void;
    if (ajuste === "cor") return <PainelDeCor valor={solida(peca.cor)} aoMudar={(p) => m({ cor: p.cor })} rotulo="Cor da textura" soCor />;
    return <AjustesDaTextura p={peca} mudar={m} />;
  }
  if (peca.tipo !== "imagem") return null;
  const m = mudar as (c: Partial<PecaDeImagem>) => void;
  if (ajuste === "ajustes") {
    return (
      <>
        <AjustesCompletos p={peca} mudar={m} />
        <SecaoDoDuotone p={peca} mudar={m} />
      </>
    );
  }
  /* "recorte" (e o "efeitos" de antes): inteira ou preenchendo, cantos, a foto dentro de uma forma e o enquadre. */
  return <PainelDoRecorte p={peca} mudar={m} aoEnquadrar={aoEnquadrar} />;
}

export const TITULOS_DOS_AJUSTES: Record<AjusteDaPeca, string> = {
  fonte: "Fonte",
  glifos: "Glifos",
  vetor: "Cores do elemento",
  cor: "Cor",
  "cor-do-fundo": "Cor do fundo do texto",
  efeitos: "Ajustes",
  posicao: "Posição",
  ajustes: "Ajustes da foto",
  forma: "Forma",
  textura: "Textura",
  transparencia: "Transparência e mesclagem",
  camada: "Contorno e sombra",
  "cores-da-foto": "Trocar cores",
  reticula: "Retícula (halftone)",
  contorno: "Contorno",
  sombra: "Sombra e brilho",
  espacamento: "Espaçamento e parágrafo",
  curva: "Curva",
  "fundo-do-texto": "Fundo do texto",
  "marca-texto": "Marca-texto",
  estilo: "Estilo da letra",
  recorte: "Recorte e moldura",
  "reticula-viva": "Retícula Viva",
};

/** O ícone de cada ajuste (o mesmo na barra da peça e no título do painel). */
export const ICONES_DOS_AJUSTES: Record<AjusteDaPeca, React.ComponentType<{ size?: number; className?: string }>> = {
  fonte: Type,
  glifos: Type,
  vetor: Palette,
  cor: Palette,
  "cor-do-fundo": Palette,
  efeitos: SlidersHorizontal,
  posicao: Move,
  ajustes: SlidersHorizontal,
  forma: Shapes,
  textura: Sparkles,
  transparencia: Blend,
  camada: WandSparkles,
  "cores-da-foto": Palette,
  reticula: Sparkles,
  contorno: IconeDoContorno,
  sombra: IconeDaSombra,
  espacamento: IconeDoEspacamento,
  curva: IconeDaCurva,
  "fundo-do-texto": IconeDoFundoDoTexto,
  "marca-texto": Highlighter,
  estilo: Sparkles,
  recorte: Crop,
  "reticula-viva": IconeDaReticulaViva,
};
