From 8ad99a971d5e4fcdd2b33fd79ac159c159bc9705 Mon Sep 17 00:00:00 2001
From: Lucas Rayan
Date: Sun, 23 Aug 2026 12:19:21 -0300
Subject: [PATCH] chore: remove unused assets and polyfills, add puppeteer for
CMYK export automation
---
.gitignore | 1 +
app/api/export-cmyk/route.js | 134 +++++++
package-lock.json | 329 ++++++++++++++++++
package.json | 4 +-
public/{apostilas => formacoes}/1/.next/trace | 0
.../1/esp32-s3-blocos.png | Bin
public/{apostilas => formacoes}/1/index.html | 141 +++++++-
public/{apostilas => formacoes}/1/pratica.fzz | Bin
public/{apostilas => formacoes}/1/pratica.png | Bin
.../assets/apostila.css | 235 ++++++++++++-
.../assets/capa-curvas.svg | 0
.../assets/laser-escuro.svg | 0
.../{apostilas => formacoes}/assets/laser.svg | 0
.../assets/paged.polyfill.js | 0
scripts/exportar-cmyk.mjs | 80 +++++
15 files changed, 918 insertions(+), 6 deletions(-)
create mode 100644 app/api/export-cmyk/route.js
rename public/{apostilas => formacoes}/1/.next/trace (100%)
rename public/{apostilas => formacoes}/1/esp32-s3-blocos.png (100%)
rename public/{apostilas => formacoes}/1/index.html (94%)
rename public/{apostilas => formacoes}/1/pratica.fzz (100%)
rename public/{apostilas => formacoes}/1/pratica.png (100%)
rename public/{apostilas => formacoes}/assets/apostila.css (76%)
rename public/{apostilas => formacoes}/assets/capa-curvas.svg (100%)
rename public/{apostilas => formacoes}/assets/laser-escuro.svg (100%)
rename public/{apostilas => formacoes}/assets/laser.svg (100%)
rename public/{apostilas => formacoes}/assets/paged.polyfill.js (100%)
create mode 100644 scripts/exportar-cmyk.mjs
diff --git a/.gitignore b/.gitignore
index 5ef6a52..c5e8b4f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,7 @@
# production
/build
+/dist
# misc
.DS_Store
diff --git a/app/api/export-cmyk/route.js b/app/api/export-cmyk/route.js
new file mode 100644
index 0000000..6666926
--- /dev/null
+++ b/app/api/export-cmyk/route.js
@@ -0,0 +1,134 @@
+import { execSync } from "node:child_process";
+import fs from "node:fs";
+import os from "node:os";
+import path from "node:path";
+import { NextResponse } from "next/server";
+import puppeteer from "puppeteer";
+
+export const runtime = "nodejs";
+export const dynamic = "force-dynamic";
+
+export async function GET(request) {
+ const { searchParams } = new URL(request.url);
+ const modulo = searchParams.get("modulo") || "1";
+
+ const publicDir = path.resolve(process.cwd(), "public/formacoes", modulo);
+ const htmlFile = path.join(publicDir, "index.html");
+
+ if (!fs.existsSync(htmlFile)) {
+ return NextResponse.json(
+ { error: `Módulo ${modulo} não encontrado.` },
+ { status: 404 }
+ );
+ }
+
+ const tmpDir = os.tmpdir();
+ const tmpRgbPdf = path.join(tmpDir, `espdocs-mod-${modulo}-${Date.now()}-rgb.pdf`);
+ const tmpCmykPdf = path.join(tmpDir, `espdocs-mod-${modulo}-${Date.now()}-cmyk.pdf`);
+
+ let browser = null;
+ try {
+ browser = await puppeteer.launch({
+ headless: "new",
+ args: [
+ "--no-sandbox",
+ "--disable-setuid-sandbox",
+ "--disable-web-security",
+ "--allow-file-access-from-files"
+ ]
+ });
+
+ const page = await browser.newPage();
+ await page.setViewport({ width: 1200, height: 1600, deviceScaleFactor: 2 });
+ await page.goto(`file://${htmlFile}`, { waitUntil: "networkidle0", timeout: 60000 });
+
+ // Aguarda o Paged.js concluir a montagem
+ await page.waitForFunction(() => {
+ const pages = document.querySelectorAll(".pagedjs_page");
+ return pages.length > 0 && !document.querySelector(".aviso-carregando");
+ }, { timeout: 30000 });
+
+ await new Promise((r) => setTimeout(r, 1000));
+
+ await page.pdf({
+ path: tmpRgbPdf,
+ format: "A4",
+ printBackground: true,
+ preferCSSPageSize: true,
+ margin: { top: 0, right: 0, bottom: 0, left: 0 }
+ });
+
+ await browser.close();
+ browser = null;
+
+ // Converte para CMYK (DeviceCMYK / ISO Coated)
+ const gsCmd = `gs -dSAFER -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sColorConversionStrategy=CMYK -dProcessColorModel=/DeviceCMYK -dCompatibilityLevel=1.4 -sOutputFile="${tmpCmykPdf}" "${tmpRgbPdf}"`;
+ execSync(gsCmd, { stdio: "pipe" });
+
+ const pdfBuffer = fs.readFileSync(tmpCmykPdf);
+
+ return new NextResponse(pdfBuffer, {
+ status: 200,
+ headers: {
+ "Content-Type": "application/pdf",
+ "Content-Disposition": `attachment; filename="LASER-ESP32-Modulo-${modulo}-CMYK.pdf"`,
+ "Content-Length": pdfBuffer.length.toString()
+ }
+ });
+ } catch (error) {
+ console.error("Erro na conversão CMYK:", error);
+ return NextResponse.json(
+ { error: "Falha ao gerar PDF CMYK", details: error.message },
+ { status: 500 }
+ );
+ } finally {
+ if (browser) await browser.close();
+ if (fs.existsSync(tmpRgbPdf)) fs.unlinkSync(tmpRgbPdf);
+ if (fs.existsSync(tmpCmykPdf)) fs.unlinkSync(tmpCmykPdf);
+ }
+}
+
+export async function POST(request) {
+ try {
+ const formData = await request.formData();
+ const file = formData.get("pdf");
+
+ if (!file) {
+ return NextResponse.json({ error: "Nenhum arquivo PDF enviado." }, { status: 400 });
+ }
+
+ const bytes = await file.arrayBuffer();
+ const buffer = Buffer.from(bytes);
+
+ const tmpDir = os.tmpdir();
+ const inputPath = path.join(tmpDir, `input-${Date.now()}.pdf`);
+ const outputPath = path.join(tmpDir, `cmyk-${Date.now()}.pdf`);
+
+ fs.writeFileSync(inputPath, buffer);
+
+ // Converte com Ghostscript
+ const gsCmd = `gs -dSAFER -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sColorConversionStrategy=CMYK -dProcessColorModel=/DeviceCMYK -dCompatibilityLevel=1.4 -sOutputFile="${outputPath}" "${inputPath}"`;
+ execSync(gsCmd, { stdio: "pipe" });
+
+ const outputBuffer = fs.readFileSync(outputPath);
+
+ // Limpa temporários
+ if (fs.existsSync(inputPath)) fs.unlinkSync(inputPath);
+ if (fs.existsSync(outputPath)) fs.unlinkSync(outputPath);
+
+ return new NextResponse(outputBuffer, {
+ status: 200,
+ headers: {
+ "Content-Type": "application/pdf",
+ "Content-Disposition": `attachment; filename="documento-CMYK.pdf"`,
+ "Content-Length": outputBuffer.length.toString()
+ }
+ });
+ } catch (error) {
+ console.error("Erro ao converter PDF POST:", error);
+ return NextResponse.json(
+ { error: "Erro ao converter PDF para CMYK", details: error.message },
+ { status: 500 }
+ );
+ }
+}
diff --git a/package-lock.json b/package-lock.json
index fb7b9fe..5800c8d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -25,6 +25,7 @@
"@tailwindcss/postcss": "^4",
"eslint": "^9",
"eslint-config-next": "15.5.6",
+ "puppeteer": "^25.8.0",
"tailwindcss": "^4"
}
},
@@ -1542,6 +1543,35 @@
"url": "https://opencollective.com/popperjs"
}
},
+ "node_modules/@puppeteer/browsers": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-3.2.1.tgz",
+ "integrity": "sha512-KDz+3qDRdBAlRlMjmKyj6dEs33YHTk/xRHEENSXq6TNnhgoU15ruSHtEBeVF6OZ9tBDY55Se4P0nFMNsipzU9A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "modern-tar": "^0.8.0",
+ "yargs": "^18.0.0"
+ },
+ "bin": {
+ "browsers": "lib/main-cli.js"
+ },
+ "engines": {
+ "node": ">=22.12.0"
+ },
+ "peerDependencies": {
+ "proxy-agent": ">=8.0.1",
+ "yauzl": "^2.10.0 || ^3.4.0"
+ },
+ "peerDependenciesMeta": {
+ "proxy-agent": {
+ "optional": true
+ },
+ "yauzl": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@rtsao/scc": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -2904,12 +2934,100 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
+ "node_modules/chromium-bidi": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-17.0.2.tgz",
+ "integrity": "sha512-5v9GQFhTktFvotn/OFNJBmKLKRAb6n9r0bVCwf7sHgWc3/JryK0bj1nn93L3pHFrfgcsu6Be6EWsDi+1XHTGDg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "mitt": "^3.0.1",
+ "zod": "^3.24.1"
+ },
+ "engines": {
+ "node": ">=20.19.0 <22.0.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "devtools-protocol": "*"
+ }
+ },
"node_modules/client-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
"license": "MIT"
},
+ "node_modules/cliui": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
+ "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^7.2.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/cliui/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/cliui/node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cliui/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cliui/node_modules/wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
@@ -3126,6 +3244,13 @@
"node": ">=8"
}
},
+ "node_modules/devtools-protocol": {
+ "version": "0.0.1666840",
+ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1666840.tgz",
+ "integrity": "sha512-gCcO42XCHKEs7Ag0S7aGYsnJ7hlgrO3qderYqeiY0Eqk+0GFfuvT13IA0hHreJTa2KCdDVyGMeOhdMNmrrTjVg==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
"node_modules/doctrine": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
@@ -3381,6 +3506,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -4128,6 +4263,29 @@
"node": ">= 0.4"
}
},
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-east-asian-width": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz",
+ "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
@@ -5436,6 +5594,19 @@
"url": "https://opencollective.com/parcel"
}
},
+ "node_modules/lilconfig": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
+ "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@@ -5567,6 +5738,23 @@
"node": ">=16 || 14 >=14.17"
}
},
+ "node_modules/mitt": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
+ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/modern-tar": {
+ "version": "0.8.4",
+ "resolved": "https://registry.npmjs.org/modern-tar/-/modern-tar-0.8.4.tgz",
+ "integrity": "sha512-gN54ddmyzEg10orwZ2u4OOv+bjpMWdIl5jIkodK97bMq8QBSL5c0D7YX0lT1Ooz+99S7+PvFbnxzdjgHo1r41g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
"node_modules/motion-dom": {
"version": "12.40.0",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.40.0.tgz",
@@ -6127,6 +6315,46 @@
"node": ">=6"
}
},
+ "node_modules/puppeteer": {
+ "version": "25.8.0",
+ "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-25.8.0.tgz",
+ "integrity": "sha512-3gcUJ+Jfodb5zNa/lWLZukBUwYiRIwAc8WRICoqfi+ZYmNWqpsPFyanTU3Gw/lhgII9aotVbAmhT6/NKHWgyUA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@puppeteer/browsers": "3.2.1",
+ "chromium-bidi": "17.0.2",
+ "devtools-protocol": "0.0.1666840",
+ "lilconfig": "^3.1.3",
+ "puppeteer-core": "25.8.0",
+ "typed-query-selector": "^2.12.2"
+ },
+ "bin": {
+ "puppeteer": "lib/puppeteer/node/cli.js"
+ },
+ "engines": {
+ "node": ">=22.12.0"
+ }
+ },
+ "node_modules/puppeteer-core": {
+ "version": "25.8.0",
+ "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-25.8.0.tgz",
+ "integrity": "sha512-LDOrawV8vfCVk+yLj2ozvajNP4Sv3OV9y3Tpiyy2g2Z+aQlbcozP6KJfI4iSBq7YQER+86ihEtPa5ioiZyWxMQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@puppeteer/browsers": "3.2.1",
+ "chromium-bidi": "17.0.2",
+ "devtools-protocol": "0.0.1666840",
+ "typed-query-selector": "^2.12.2",
+ "webdriver-bidi-protocol": "0.4.2",
+ "ws": "^8.21.1"
+ },
+ "engines": {
+ "node": ">=22.12.0"
+ }
+ },
"node_modules/qs": {
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
@@ -7162,6 +7390,13 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/typed-query-selector": {
+ "version": "2.12.2",
+ "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.2.tgz",
+ "integrity": "sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
@@ -7256,6 +7491,13 @@
"node": ">= 8"
}
},
+ "node_modules/webdriver-bidi-protocol": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.2.tgz",
+ "integrity": "sha512-VSV+fzfChirL3e7jay2yUC7B4HQCGtEWEg/MSSQbK+qWbqeGlRLlXTzPpYr3XGUvbpDHumWZBJxgesg4N7dbtA==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -7458,6 +7700,38 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
+ "node_modules/ws": {
+ "version": "8.21.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz",
+ "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/yaml": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
@@ -7467,6 +7741,51 @@
"node": ">= 6"
}
},
+ "node_modules/yargs": {
+ "version": "18.1.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.1.0.tgz",
+ "integrity": "sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^9.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "string-width": "^8.2.1",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^22.0.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "22.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
+ "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
+ }
+ },
+ "node_modules/yargs/node_modules/string-width": {
+ "version": "8.2.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz",
+ "integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-east-asian-width": "^1.5.0",
+ "strip-ansi": "^7.1.2"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
@@ -7479,6 +7798,16 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/zod": {
+ "version": "3.25.76",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
+ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
}
}
}
diff --git a/package.json b/package.json
index 730f112..656034d 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,8 @@
"build": "next build --turbopack",
"start": "next start",
"lint": "eslint",
- "auditar-temas": "node scripts/auditar-temas.mjs"
+ "auditar-temas": "node scripts/auditar-temas.mjs",
+ "exportar-cmyk": "node scripts/exportar-cmyk.mjs"
},
"dependencies": {
"@emotion/react": "^11.14.0",
@@ -27,6 +28,7 @@
"@tailwindcss/postcss": "^4",
"eslint": "^9",
"eslint-config-next": "15.5.6",
+ "puppeteer": "^25.8.0",
"tailwindcss": "^4"
}
}
diff --git a/public/apostilas/1/.next/trace b/public/formacoes/1/.next/trace
similarity index 100%
rename from public/apostilas/1/.next/trace
rename to public/formacoes/1/.next/trace
diff --git a/public/apostilas/1/esp32-s3-blocos.png b/public/formacoes/1/esp32-s3-blocos.png
similarity index 100%
rename from public/apostilas/1/esp32-s3-blocos.png
rename to public/formacoes/1/esp32-s3-blocos.png
diff --git a/public/apostilas/1/index.html b/public/formacoes/1/index.html
similarity index 94%
rename from public/apostilas/1/index.html
rename to public/formacoes/1/index.html
index c906df2..65af4d9 100644
--- a/public/apostilas/1/index.html
+++ b/public/formacoes/1/index.html
@@ -1960,12 +1960,147 @@ Temperatura: 26.1 C | Umidade: 62.0 %
+
+
+
+
+ Apostila:
+
+
+
+
+
+
+