Some checks are pending
E2E / Playwright e2e (push) Waiting to run
- Introduced turbopack alias for canvas in next.config.mjs. - Updated package.json scripts for development and branding tasks. - Added new dependencies for Tiptap extensions. - Implemented new demo layouts for agenda, contacts, drive, and mail applications. - Enhanced globals.css for improved theming and splash screen animations. - Added OAuth callback handling for drive mounts. - Updated layout components to integrate new demo shells and improve structure.
105 lines
4.1 KiB
JavaScript
105 lines
4.1 KiB
JavaScript
/**
|
||
* Assets brand Ultimail générés depuis le mark vectoriel plat 4 couleurs :
|
||
* - source : public/ultimail-mark.svg
|
||
*
|
||
* Sorties :
|
||
* - public/brand/ultimail-header-icon.png (288, transparent)
|
||
* - public/brand/ultimail-mark.{png,jpg} (256)
|
||
* - public/brand/ultimail-wordmark-stacked.{png,jpg} + -dark.png (800×800)
|
||
* - public/brand/ultimail-wordmark-horizontal.{png,jpg} (1600×460)
|
||
* - app/icon.png (32) + app/apple-icon.png (180, fond blanc)
|
||
*
|
||
* pnpm run brand:ultimail
|
||
*/
|
||
import { mkdirSync, readFileSync } from "node:fs"
|
||
import { dirname, join } from "node:path"
|
||
import { fileURLToPath } from "node:url"
|
||
import sharp from "sharp"
|
||
|
||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||
const root = join(__dirname, "..")
|
||
const markPath = join(root, "public/ultimail-mark.svg")
|
||
const brandDir = join(root, "public/brand")
|
||
const appDir = join(root, "app")
|
||
|
||
const FONT = "Helvetica Neue, Helvetica, Arial, sans-serif"
|
||
|
||
/** Contenu interne du mark (sans la balise <svg>), pour l'inliner dans les lockups. */
|
||
function markInnerSvg() {
|
||
const raw = readFileSync(markPath, "utf8")
|
||
const open = raw.indexOf(">", raw.indexOf("<svg"))
|
||
const close = raw.lastIndexOf("</svg>")
|
||
return raw.slice(open + 1, close)
|
||
}
|
||
|
||
/** Wordmark empilé 800×800 : mark + « Ultimail » dessous. */
|
||
function stackedSvg(textColor) {
|
||
const inner = markInnerSvg()
|
||
return `<svg xmlns="http://www.w3.org/2000/svg" width="800" height="800" viewBox="0 0 800 800">
|
||
<g transform="translate(248 160) scale(7.6)">${inner}</g>
|
||
<text x="400" y="612" text-anchor="middle" font-family="${FONT}" font-size="104" font-weight="700" letter-spacing="-2" fill="${textColor}">Ultimail</text>
|
||
</svg>`
|
||
}
|
||
|
||
/** Wordmark horizontal 1600×460 : mark à gauche, « Ultimail » à droite. */
|
||
function horizontalSvg(textColor) {
|
||
const inner = markInnerSvg()
|
||
return `<svg xmlns="http://www.w3.org/2000/svg" width="1600" height="460" viewBox="0 0 1600 460">
|
||
<g transform="translate(80 70) scale(8)">${inner}</g>
|
||
<text x="460" y="298" font-family="${FONT}" font-size="190" font-weight="700" letter-spacing="-5" fill="${textColor}">Ultimail</text>
|
||
</svg>`
|
||
}
|
||
|
||
async function writeSvgPng(svg, outPath, { background } = {}) {
|
||
let pipe = sharp(Buffer.from(svg), { density: 144 })
|
||
if (background) pipe = pipe.flatten({ background })
|
||
await pipe.png({ compressionLevel: 9 }).toFile(outPath)
|
||
console.log("Wrote", outPath.replace(`${root}/`, ""))
|
||
}
|
||
|
||
async function writeSvgJpg(svg, outPath) {
|
||
await sharp(Buffer.from(svg), { density: 144 })
|
||
.flatten({ background: "#ffffff" })
|
||
.jpeg({ quality: 92, mozjpeg: true })
|
||
.toFile(outPath)
|
||
console.log("Wrote", outPath.replace(`${root}/`, ""))
|
||
}
|
||
|
||
async function writeMarkPng(outPath, size, { background } = {}) {
|
||
let pipe = sharp(markPath, { density: 300 }).resize(size, size, {
|
||
fit: "contain",
|
||
background: { r: 0, g: 0, b: 0, alpha: 0 },
|
||
})
|
||
if (background) pipe = pipe.flatten({ background })
|
||
await pipe.png({ compressionLevel: 9 }).toFile(outPath)
|
||
console.log("Wrote", outPath.replace(`${root}/`, ""))
|
||
}
|
||
|
||
async function main() {
|
||
mkdirSync(brandDir, { recursive: true })
|
||
|
||
// Picto seul
|
||
await writeMarkPng(join(brandDir, "ultimail-header-icon.png"), 288)
|
||
await writeMarkPng(join(brandDir, "ultimail-mark.png"), 256)
|
||
await sharp(join(brandDir, "ultimail-mark.png"))
|
||
.flatten({ background: "#ffffff" })
|
||
.jpeg({ quality: 92, mozjpeg: true })
|
||
.toFile(join(brandDir, "ultimail-mark.jpg"))
|
||
|
||
// Wordmarks
|
||
await writeSvgPng(stackedSvg("#202124"), join(brandDir, "ultimail-wordmark-stacked.png"))
|
||
await writeSvgJpg(stackedSvg("#202124"), join(brandDir, "ultimail-wordmark-stacked.jpg"))
|
||
await writeSvgPng(stackedSvg("#e8eaed"), join(brandDir, "ultimail-wordmark-stacked-dark.png"))
|
||
await writeSvgPng(horizontalSvg("#202124"), join(brandDir, "ultimail-wordmark-horizontal.png"))
|
||
await writeSvgJpg(horizontalSvg("#202124"), join(brandDir, "ultimail-wordmark-horizontal.jpg"))
|
||
|
||
// Favicons Next.js (conventions app/)
|
||
await writeMarkPng(join(appDir, "icon.png"), 32)
|
||
await writeMarkPng(join(appDir, "apple-icon.png"), 180, { background: "#ffffff" })
|
||
}
|
||
|
||
main().catch((e) => {
|
||
console.error(e)
|
||
process.exit(1)
|
||
})
|