feat(marca): atualiza logo, ícones, manifest e Open Graph para a identidade atual (#3)
- Logo em duas cores (teal + cobre), inline no cabeçalho e rodapé para seguir o tema do site; SVGs estáticos com prefers-color-scheme. - scripts/generate-favicons.mjs passa a gerar favicons, ícones de app (maskable), site.webmanifest e a imagem Open Graph 1200x630. - Manifest com fundo/tema obsidiana e lang pt-BR. Claude-Session: https://claude.ai/code/session_016W1xmizFcmXLrmabSvfSuc Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
No files matched your search
|
Before Width: | Height: | Size: 185 KiB After Width: | Height: | Size: 199 KiB |
@@ -1,4 +1,5 @@
|
||||
import Link from "next/link";
|
||||
import Logo from "@/components/Logo";
|
||||
import { ArrowUpRight } from "lucide-react";
|
||||
import { SITE } from "@/lib/seo";
|
||||
|
||||
@@ -41,8 +42,7 @@ export default function Footer() {
|
||||
<div className="grid gap-12 lg:grid-cols-[1.1fr_2fr]">
|
||||
<div>
|
||||
<Link href="/" className="inline-flex min-h-11 items-center gap-2.5" aria-label="ESPDocs, página inicial">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src="/marca/espdocs-marca.svg" alt="" aria-hidden="true" width={28} height={28} className="h-7 w-7" />
|
||||
<Logo className="h-7 w-7" />
|
||||
<span className="font-display text-2xl font-semibold tracking-[-0.045em] text-ink">ESPDocs</span>
|
||||
</Link>
|
||||
<p className="mt-5 max-w-sm text-sm leading-relaxed text-muted">
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import Link from "next/link";
|
||||
import Logo from "@/components/Logo";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useTheme } from "next-themes";
|
||||
import {
|
||||
@@ -121,15 +122,7 @@ export default function Header() {
|
||||
<div className="shell">
|
||||
<div className="nav-pill flex h-16 items-center justify-between gap-3 rounded-full px-3 sm:px-4">
|
||||
<Link href="/" className="flex min-h-11 shrink-0 items-center gap-2.5 rounded-full pl-1 pr-2" aria-label="ESPDocs, página inicial">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src="/marca/espdocs-marca.svg"
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
width={30}
|
||||
height={30}
|
||||
className="h-[30px] w-[30px]"
|
||||
/>
|
||||
<Logo className="h-[30px] w-[30px]" />
|
||||
<span className="font-display text-[1.3rem] font-semibold leading-none tracking-[-0.045em] text-ink">ESPDocs</span>
|
||||
</Link>
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Marca do ESPDocs: o encapsulamento do chip com pinos à esquerda; os pinos e
|
||||
* o corpo viram linhas de texto, a documentação. Inline para seguir o tema do
|
||||
* site (--accent e --copper trocam entre claro e escuro, ao contrário de uma
|
||||
* imagem externa, que só enxerga a preferência do sistema).
|
||||
*/
|
||||
export default function Logo({ className = "h-8 w-8", title }) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 64 64"
|
||||
className={className}
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
role={title ? "img" : undefined}
|
||||
aria-label={title}
|
||||
aria-hidden={title ? undefined : "true"}
|
||||
>
|
||||
{title && <title>{title}</title>}
|
||||
<g stroke="var(--accent)" strokeWidth="3.4">
|
||||
<path d="M2 21h7" />
|
||||
<path d="M2 32h7" />
|
||||
<path d="M2 43h7" />
|
||||
</g>
|
||||
<rect x="9.25" y="9.25" width="45.5" height="45.5" rx="13" stroke="var(--accent)" strokeWidth="4.5" />
|
||||
<g stroke="var(--copper)" strokeWidth="4.5">
|
||||
<path d="M19 21h26" />
|
||||
<path d="M19 32h21" />
|
||||
<path d="M19 43h15" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 686 B After Width: | Height: | Size: 554 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 975 B |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 8.4 KiB |
@@ -1,29 +1,17 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="ESPDocs">
|
||||
<title>ESPDocs</title>
|
||||
<defs>
|
||||
<linearGradient id="g" x1="6" y1="58" x2="58" y2="6" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#3b82f6"/>
|
||||
<stop offset="0.55" stop-color="#8b5cf6"/>
|
||||
<stop offset="1" stop-color="#ec4899"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.p{stroke:#0a5852}.l{stroke:#a8452f}.t{fill:#0b0d10}
|
||||
@media (prefers-color-scheme: dark){.p{stroke:#2dd4bf}.l{stroke:#ffb39b}.t{fill:#f4f6f8}}
|
||||
</style>
|
||||
<!-- Pinos entrando pela esquerda: o sinal que chega do silício. -->
|
||||
<g stroke="url(#g)" stroke-width="3.4" stroke-linecap="round">
|
||||
<path d="M2 21h7"/>
|
||||
<path d="M2 32h7"/>
|
||||
<path d="M2 43h7"/>
|
||||
<g class="p" fill="none" stroke-width="3.4" stroke-linecap="round">
|
||||
<path d="M2 21h7"/><path d="M2 32h7"/><path d="M2 43h7"/>
|
||||
</g>
|
||||
|
||||
<!-- Corpo do encapsulamento. -->
|
||||
<rect x="9.25" y="9.25" width="45.5" height="45.5" rx="13"
|
||||
fill="none" stroke="url(#g)" stroke-width="4.5"/>
|
||||
|
||||
<!-- Os mesmos três níveis viram linhas de texto: a documentação.
|
||||
Comprimento decrescente para ler como parágrafo. -->
|
||||
<g stroke="url(#g)" stroke-width="4.5" stroke-linecap="round">
|
||||
<path d="M19 21h26"/>
|
||||
<path d="M19 32h21"/>
|
||||
<path d="M19 43h15"/>
|
||||
<rect class="p" x="9.25" y="9.25" width="45.5" height="45.5" rx="13" fill="none" stroke-width="4.5"/>
|
||||
<!-- Os mesmos três níveis viram linhas de texto: a documentação. -->
|
||||
<g class="l" fill="none" stroke-width="4.5" stroke-linecap="round">
|
||||
<path d="M19 21h26"/><path d="M19 32h21"/><path d="M19 43h15"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 869 B |
@@ -1,19 +1,18 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 260 64" role="img" aria-label="ESPDocs">
|
||||
<title>ESPDocs</title>
|
||||
<defs>
|
||||
<linearGradient id="gl" x1="6" y1="58" x2="250" y2="6" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#24645f"/>
|
||||
<stop offset="0.55" stop-color="#8a3410"/>
|
||||
<stop offset="1" stop-color="#c4744f"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g stroke="url(#gl)" stroke-width="3.4" stroke-linecap="round">
|
||||
<style>
|
||||
.p{stroke:#0a5852}.l{stroke:#823626}.t{fill:#0b0d10}
|
||||
@media (prefers-color-scheme: dark){.p{stroke:#2dd4bf}.l{stroke:#ffb39b}.t{fill:#f4f6f8}}
|
||||
</style>
|
||||
<!-- Pinos entrando pela esquerda: o sinal que chega do silício. -->
|
||||
<g class="p" fill="none" stroke-width="3.4" stroke-linecap="round">
|
||||
<path d="M2 21h7"/><path d="M2 32h7"/><path d="M2 43h7"/>
|
||||
</g>
|
||||
<rect x="9.25" y="9.25" width="45.5" height="45.5" rx="13" fill="none" stroke="url(#gl)" stroke-width="4.5"/>
|
||||
<g stroke="url(#gl)" stroke-width="4.5" stroke-linecap="round">
|
||||
<!-- Corpo do encapsulamento. -->
|
||||
<rect class="p" x="9.25" y="9.25" width="45.5" height="45.5" rx="13" fill="none" stroke-width="4.5"/>
|
||||
<!-- Os mesmos três níveis viram linhas de texto: a documentação. -->
|
||||
<g class="l" fill="none" stroke-width="4.5" stroke-linecap="round">
|
||||
<path d="M19 21h26"/><path d="M19 32h21"/><path d="M19 43h15"/>
|
||||
</g>
|
||||
<text x="72" y="43" font-family="Outfit, Inter, system-ui, sans-serif" font-size="34"
|
||||
font-weight="800" letter-spacing="-0.5" fill="url(#gl)">ESPDocs</text>
|
||||
<text class="t" x="72" y="43" font-family="'Space Grotesk', Inter, system-ui, sans-serif" font-size="34" font-weight="600" letter-spacing="-1.5">ESPDocs</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 954 B After Width: | Height: | Size: 1.0 KiB |
@@ -1,29 +1,17 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="ESPDocs">
|
||||
<title>ESPDocs</title>
|
||||
<defs>
|
||||
<linearGradient id="g" x1="6" y1="58" x2="58" y2="6" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#24645f"/>
|
||||
<stop offset="0.55" stop-color="#8a3410"/>
|
||||
<stop offset="1" stop-color="#c4744f"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.p{stroke:#0a5852}.l{stroke:#823626}.t{fill:#0b0d10}
|
||||
@media (prefers-color-scheme: dark){.p{stroke:#2dd4bf}.l{stroke:#ffb39b}.t{fill:#f4f6f8}}
|
||||
</style>
|
||||
<!-- Pinos entrando pela esquerda: o sinal que chega do silício. -->
|
||||
<g stroke="url(#g)" stroke-width="3.4" stroke-linecap="round">
|
||||
<path d="M2 21h7"/>
|
||||
<path d="M2 32h7"/>
|
||||
<path d="M2 43h7"/>
|
||||
<g class="p" fill="none" stroke-width="3.4" stroke-linecap="round">
|
||||
<path d="M2 21h7"/><path d="M2 32h7"/><path d="M2 43h7"/>
|
||||
</g>
|
||||
|
||||
<!-- Corpo do encapsulamento. -->
|
||||
<rect x="9.25" y="9.25" width="45.5" height="45.5" rx="13"
|
||||
fill="none" stroke="url(#g)" stroke-width="4.5"/>
|
||||
|
||||
<!-- Os mesmos três níveis viram linhas de texto: a documentação.
|
||||
Comprimento decrescente para ler como parágrafo. -->
|
||||
<g stroke="url(#g)" stroke-width="4.5" stroke-linecap="round">
|
||||
<path d="M19 21h26"/>
|
||||
<path d="M19 32h21"/>
|
||||
<path d="M19 43h15"/>
|
||||
<rect class="p" x="9.25" y="9.25" width="45.5" height="45.5" rx="13" fill="none" stroke-width="4.5"/>
|
||||
<!-- Os mesmos três níveis viram linhas de texto: a documentação. -->
|
||||
<g class="l" fill="none" stroke-width="4.5" stroke-linecap="round">
|
||||
<path d="M19 21h26"/><path d="M19 32h21"/><path d="M19 43h15"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 869 B |
@@ -2,10 +2,11 @@
|
||||
"name": "ESPDocs",
|
||||
"short_name": "ESPDocs",
|
||||
"description": "Documentação do ecossistema ESP32 em português brasileiro",
|
||||
"lang": "pt-BR",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#020617",
|
||||
"theme_color": "#8b5cf6",
|
||||
"background_color": "#07090d",
|
||||
"theme_color": "#07090d",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/favicon-48x48.png",
|
||||
@@ -15,12 +16,14 @@
|
||||
{
|
||||
"src": "/icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "/icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,31 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
import puppeteer from 'puppeteer';
|
||||
|
||||
const svgPath = fs.existsSync(path.resolve('public/icon.svg'))
|
||||
? path.resolve('public/icon.svg')
|
||||
: fs.existsSync(path.resolve('app/icon.svg'))
|
||||
? path.resolve('app/icon.svg')
|
||||
: path.resolve('public/marca/espdocs-marca.svg');
|
||||
const svgContent = fs.readFileSync(svgPath, 'utf8');
|
||||
/**
|
||||
* Gera todos os ativos de marca a partir da geometria do logo:
|
||||
* favicons (transparentes), ícones de app (tile obsidiana), site.webmanifest
|
||||
* e a imagem de compartilhamento (Open Graph, 1200x630).
|
||||
*
|
||||
* Cores = tokens de app/globals.css. Uso: npm run favicons
|
||||
* (usa o Chrome do Puppeteer; defina CHROME_PATH para usar outro binário)
|
||||
*/
|
||||
|
||||
const COR = {
|
||||
obsidiana: "#07090d",
|
||||
tealEscuro: "#2dd4bf",
|
||||
cobreEscuro: "#ffb39b",
|
||||
tealMedio: "#1a8f83", // legível em aba clara e escura
|
||||
cobreMedio: "#c8694e",
|
||||
texto: "#f4f6f8",
|
||||
suave: "#a6afbd",
|
||||
};
|
||||
|
||||
const marca = (teal, cobre) => `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="none" stroke-linecap="round">
|
||||
<g stroke="${teal}" stroke-width="3.4"><path d="M2 21h7"/><path d="M2 32h7"/><path d="M2 43h7"/></g>
|
||||
<rect x="9.25" y="9.25" width="45.5" height="45.5" rx="13" stroke="${teal}" stroke-width="4.5"/>
|
||||
<g stroke="${cobre}" stroke-width="4.5"><path d="M19 21h26"/><path d="M19 32h21"/><path d="M19 43h15"/></g>
|
||||
</svg>`;
|
||||
|
||||
function createIco(pngBuffers) {
|
||||
// pngBuffers: array of { width, height, buffer }
|
||||
@@ -39,92 +58,97 @@ function createIco(pngBuffers) {
|
||||
return Buffer.concat([header, ...entries, ...pngBuffers.map(b => b.buffer)]);
|
||||
}
|
||||
|
||||
|
||||
const pagina = (corpo, css = "") => `<!DOCTYPE html><html><head><meta charset="utf-8">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;500&family=Space+Grotesk:wght@600&display=block" rel="stylesheet">
|
||||
<style>*{margin:0;padding:0;box-sizing:border-box}html,body{width:100%;height:100%;overflow:hidden;background:transparent}${css}</style>
|
||||
</head><body>${corpo}</body></html>`;
|
||||
|
||||
async function run() {
|
||||
const browser = await puppeteer.launch({
|
||||
executablePath: process.env.CHROME_PATH || undefined,
|
||||
args: ['--no-sandbox', '--disable-setuid-sandbox']
|
||||
});
|
||||
const page = await browser.newPage();
|
||||
|
||||
const html = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
html, body { width: 100%; height: 100%; overflow: hidden; background: transparent; }
|
||||
svg { width: 100%; height: 100%; display: block; }
|
||||
</style>
|
||||
</head>
|
||||
<body>${svgContent}</body>
|
||||
</html>`;
|
||||
|
||||
await page.setContent(html);
|
||||
|
||||
const sizes = [16, 32, 48, 96, 180, 192, 512];
|
||||
// 1. Favicons: marca sozinha, fundo transparente. O logo ocupa x 2..55; centraliza.
|
||||
const favicon = pagina(`<div style="width:100%;height:100%;display:grid;place-items:center">${marca(COR.tealMedio, COR.cobreMedio).replace('<svg ', '<svg style="width:100%;height:100%;display:block" ')}</div>`);
|
||||
await page.setContent(favicon);
|
||||
const rendered = {};
|
||||
|
||||
for (const size of sizes) {
|
||||
for (const size of [16, 32, 48]) {
|
||||
await page.setViewport({ width: size, height: size, deviceScaleFactor: 1 });
|
||||
const buffer = await page.screenshot({
|
||||
type: 'png',
|
||||
omitBackground: true,
|
||||
clip: { x: 0, y: 0, width: size, height: size }
|
||||
});
|
||||
rendered[size] = buffer;
|
||||
console.log(`Rendered ${size}x${size}`);
|
||||
rendered[size] = await page.screenshot({ type: 'png', omitBackground: true, clip: { x: 0, y: 0, width: size, height: size } });
|
||||
}
|
||||
|
||||
// 2. Ícones de app: tile obsidiana, marca com margem de segurança (maskable).
|
||||
const tile = pagina(`<div style="width:100%;height:100%;background:${COR.obsidiana};display:grid;place-items:center">
|
||||
<div style="width:58%;height:58%;transform:translateX(5%)">${marca(COR.tealEscuro, COR.cobreEscuro).replace('<svg ', '<svg style="width:100%;height:100%;display:block" ')}</div></div>`);
|
||||
await page.setContent(tile);
|
||||
for (const size of [180, 192, 512]) {
|
||||
await page.setViewport({ width: size, height: size, deviceScaleFactor: 1 });
|
||||
rendered[size] = await page.screenshot({ type: 'png', clip: { x: 0, y: 0, width: size, height: size } });
|
||||
}
|
||||
|
||||
// 3. Open Graph / Twitter: 1200x630.
|
||||
const og = pagina(`
|
||||
<main>
|
||||
<div class="topo">${marca(COR.tealEscuro, COR.cobreEscuro).replace('<svg ', '<svg class="logo" ')}<span class="nome">ESPDocs</span></div>
|
||||
<h1>Documentação do ESP32<br>em português</h1>
|
||||
<p>Especificações, pinagem, comparação e seleção das 12 séries do ESP32.</p>
|
||||
<div class="chips"><span>Séries</span><span>Seletor</span><span>Comparador</span><span>Diagnóstico</span><span>Livro</span></div>
|
||||
</main>
|
||||
<div class="barra"></div>`, `
|
||||
body{background:${COR.obsidiana};font-family:'Manrope',system-ui,sans-serif;color:${COR.texto};position:relative}
|
||||
body::before{content:"";position:absolute;inset:0;background:radial-gradient(52rem 30rem at 50% -14%,rgba(45,212,191,.16),transparent 70%),radial-gradient(40rem 26rem at 100% 30%,rgba(56,140,248,.09),transparent 70%)}
|
||||
main{position:relative;height:100%;padding:76px 88px 0}
|
||||
.topo{display:flex;align-items:center;gap:20px}
|
||||
.logo{width:68px;height:68px}
|
||||
.nome{font:600 44px 'Space Grotesk',sans-serif;letter-spacing:-0.045em}
|
||||
h1{margin-top:64px;font:600 86px/1.03 'Space Grotesk',sans-serif;letter-spacing:-0.04em}
|
||||
p{margin-top:30px;max-width:900px;font-size:30px;line-height:1.45;color:${COR.suave}}
|
||||
.chips{position:absolute;left:88px;bottom:62px;display:flex;gap:12px}
|
||||
.chips span{border:1.5px solid #2e3846;border-radius:999px;padding:9px 22px;font-size:22px;font-weight:500;color:${COR.texto}}
|
||||
.barra{position:absolute;left:0;right:0;bottom:0;height:10px;background:${COR.tealEscuro}}`);
|
||||
await page.setViewport({ width: 1200, height: 630, deviceScaleFactor: 1 });
|
||||
await page.setContent(og, { waitUntil: 'networkidle0' });
|
||||
await page.evaluate(() => document.fonts.ready);
|
||||
const ogPng = await page.screenshot({ type: 'png', clip: { x: 0, y: 0, width: 1200, height: 630 } });
|
||||
|
||||
await browser.close();
|
||||
|
||||
// Create ICO from 16, 32, 48
|
||||
const icoBuffer = createIco([
|
||||
{ width: 16, height: 16, buffer: rendered[16] },
|
||||
{ width: 32, height: 32, buffer: rendered[32] },
|
||||
{ width: 48, height: 48, buffer: rendered[48] }
|
||||
]);
|
||||
|
||||
// Write favicon.ico to public
|
||||
fs.writeFileSync('public/favicon.ico', icoBuffer);
|
||||
console.log('Saved favicon.ico to public/');
|
||||
|
||||
// Write PNG favicons to public
|
||||
fs.writeFileSync('public/favicon-48x48.png', rendered[48]);
|
||||
fs.writeFileSync('public/favicon-32x32.png', rendered[32]);
|
||||
fs.writeFileSync('public/favicon-16x16.png', rendered[16]);
|
||||
fs.writeFileSync('public/apple-touch-icon.png', rendered[180]);
|
||||
fs.writeFileSync('public/icon-192.png', rendered[192]);
|
||||
fs.writeFileSync('public/icon-512.png', rendered[512]);
|
||||
console.log('Saved PNG icons to public/');
|
||||
fs.writeFileSync('app/opengraph-image.png', ogPng);
|
||||
console.log('Favicons, ícones de app e imagem Open Graph gerados.');
|
||||
|
||||
// Create site.webmanifest
|
||||
const manifest = {
|
||||
name: "ESPDocs",
|
||||
short_name: "ESPDocs",
|
||||
description: "Documentação do ecossistema ESP32 em português brasileiro",
|
||||
lang: "pt-BR",
|
||||
start_url: "/",
|
||||
display: "standalone",
|
||||
background_color: "#020617",
|
||||
theme_color: "#8b5cf6",
|
||||
background_color: COR.obsidiana,
|
||||
theme_color: COR.obsidiana,
|
||||
icons: [
|
||||
{
|
||||
src: "/favicon-48x48.png",
|
||||
sizes: "48x48",
|
||||
type: "image/png"
|
||||
},
|
||||
{
|
||||
src: "/icon-192.png",
|
||||
sizes: "192x192",
|
||||
type: "image/png"
|
||||
},
|
||||
{
|
||||
src: "/icon-512.png",
|
||||
sizes: "512x512",
|
||||
type: "image/png"
|
||||
}
|
||||
{ src: "/favicon-48x48.png", sizes: "48x48", type: "image/png" },
|
||||
{ src: "/icon-192.png", sizes: "192x192", type: "image/png", purpose: "any maskable" },
|
||||
{ src: "/icon-512.png", sizes: "512x512", type: "image/png", purpose: "any maskable" }
|
||||
]
|
||||
};
|
||||
|
||||
fs.writeFileSync('public/site.webmanifest', JSON.stringify(manifest, null, 2));
|
||||
console.log('Saved site.webmanifest to public/');
|
||||
fs.writeFileSync('public/site.webmanifest', JSON.stringify(manifest, null, 2) + '\n');
|
||||
console.log('site.webmanifest gerado.');
|
||||
}
|
||||
|
||||
run().catch(err => {
|
||||
|
||||