feat: add portrait sharepic and update generation script
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 2.8 MiB |
@@ -0,0 +1,318 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Kapteins Daagbok — Sharepic (Portrait)</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
width: 1080px;
|
||||||
|
height: 1920px;
|
||||||
|
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||||
|
color: #e2e8f0;
|
||||||
|
background: #0f172a;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 100px 80px;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 50% 10%, rgba(56, 189, 248, 0.18) 0%, transparent 45%),
|
||||||
|
radial-gradient(circle at 50% 90%, rgba(134, 59, 255, 0.22) 0%, transparent 45%),
|
||||||
|
linear-gradient(180deg, #090d16 0%, #111827 50%, #090d16 100%);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subtle background grid pattern */
|
||||||
|
body::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background-image: linear-gradient(rgba(148, 163, 184, 0.03) 1px, transparent 1px),
|
||||||
|
linear-gradient(90deg, rgba(148, 163, 184, 0.03) 1px, transparent 1px);
|
||||||
|
background-size: 40px 40px;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Outer border */
|
||||||
|
.outer-border {
|
||||||
|
position: absolute;
|
||||||
|
inset: 40px;
|
||||||
|
border: 1px solid rgba(148, 163, 184, 0.1);
|
||||||
|
border-radius: 30px;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 30px;
|
||||||
|
z-index: 2;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 140px;
|
||||||
|
height: 140px;
|
||||||
|
object-fit: contain;
|
||||||
|
filter: drop-shadow(0 8px 24px rgba(56, 189, 248, 0.3));
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-group h1 {
|
||||||
|
font-size: 64px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: -0.03em;
|
||||||
|
color: #ffffff;
|
||||||
|
line-height: 1.1;
|
||||||
|
background: linear-gradient(135deg, #ffffff 60%, #94a3b8 100%);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-group p {
|
||||||
|
font-size: 26px;
|
||||||
|
color: #38bdf8;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-top: 8px;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 50px;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.intro-text {
|
||||||
|
font-size: 26px;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: #cbd5e1;
|
||||||
|
font-weight: 400;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.intro-text strong {
|
||||||
|
color: #ffffff;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.features-card {
|
||||||
|
background: rgba(30, 41, 59, 0.45);
|
||||||
|
backdrop-filter: blur(16px);
|
||||||
|
-webkit-backdrop-filter: blur(16px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
border-radius: 24px;
|
||||||
|
padding: 50px 60px;
|
||||||
|
box-shadow: 0 30px 60px rgba(0, 0, 0, 0.4);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.features-card::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
border-radius: 24px;
|
||||||
|
padding: 1px;
|
||||||
|
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.02));
|
||||||
|
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||||
|
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||||
|
-webkit-mask-composite: xor;
|
||||||
|
mask-composite: exclude;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
color: #94a3b8;
|
||||||
|
margin-bottom: 35px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title::after {
|
||||||
|
content: "";
|
||||||
|
flex: 1;
|
||||||
|
height: 1px;
|
||||||
|
background: rgba(148, 163, 184, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-premium {
|
||||||
|
background: linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%);
|
||||||
|
color: #1e293b;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 800;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
padding: 6px 14px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.features-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 30px;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 20px;
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 1.4;
|
||||||
|
color: #cbd5e1;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-icon {
|
||||||
|
color: #38bdf8;
|
||||||
|
font-size: 26px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 3px;
|
||||||
|
text-shadow: 0 0 10px rgba(56, 189, 248, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-section {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 50px;
|
||||||
|
z-index: 2;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-badge {
|
||||||
|
background: linear-gradient(135deg, #38bdf8 0%, #0284c7 100%);
|
||||||
|
color: #0f172a;
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 800;
|
||||||
|
padding: 20px 45px;
|
||||||
|
border-radius: 16px;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
box-shadow: 0 10px 30px rgba(56, 189, 248, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-code {
|
||||||
|
width: 160px;
|
||||||
|
height: 160px;
|
||||||
|
background: #ffffff;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-code img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #64748b;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer strong {
|
||||||
|
color: #94a3b8;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="outer-border"></div>
|
||||||
|
|
||||||
|
<div class="brand">
|
||||||
|
<img class="logo" src="../../client/public/logo.png" alt="Kapteins Daagbok" />
|
||||||
|
<div class="title-group">
|
||||||
|
<h1>Kapteins Daagbok</h1>
|
||||||
|
<p>Digitales Yacht-Logbuch</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="main-content">
|
||||||
|
<p class="intro-text">
|
||||||
|
Führe dein Bordlogbuch modern & digital: Reisetage, GPS-Tracks, Crew- und Schiffsdaten —
|
||||||
|
<strong>Ende-zu-Ende-verschlüsselt</strong>, als App installierbar und <strong>auch offline</strong> auf See nutzbar.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="features-card">
|
||||||
|
<div class="card-title">Top Features <span class="badge-premium">Kostenlos & Werbefrei</span></div>
|
||||||
|
<ul class="features-list">
|
||||||
|
<li class="feature-item">
|
||||||
|
<span class="feature-icon">✦</span>
|
||||||
|
<span>Nautisches Logbuch-Format & Streckenstatistik</span>
|
||||||
|
</li>
|
||||||
|
<li class="feature-item">
|
||||||
|
<span class="feature-icon">✦</span>
|
||||||
|
<span>Offline-first PWA — läuft auf allen Smartphones & Tablets</span>
|
||||||
|
</li>
|
||||||
|
<li class="feature-item">
|
||||||
|
<span class="feature-icon">✦</span>
|
||||||
|
<span>Ende-zu-Ende Verschlüsselung (Zero-Knowledge)</span>
|
||||||
|
</li>
|
||||||
|
<li class="feature-item">
|
||||||
|
<span class="feature-icon">✦</span>
|
||||||
|
<span>Einfache passwortlose Passkey-Anmeldung</span>
|
||||||
|
</li>
|
||||||
|
<li class="feature-item">
|
||||||
|
<span class="feature-icon">✦</span>
|
||||||
|
<span>GPS-Track-Upload & automatische NMEA-Erfassung</span>
|
||||||
|
</li>
|
||||||
|
<li class="feature-item">
|
||||||
|
<span class="feature-icon">✦</span>
|
||||||
|
<span>Crew-Einladung zur gemeinsamen Logbuch-Arbeit</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bottom-section">
|
||||||
|
<div class="cta-container">
|
||||||
|
<div class="cta-badge">
|
||||||
|
kapteins-daagbok.eu
|
||||||
|
</div>
|
||||||
|
<div class="qr-code">
|
||||||
|
<img src="assets/qr-kapteins-daagbok.eu.png" alt="QR Code" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<strong>Kapteins Daagbok</strong> ist ein werbefreies, privates Hobbyprojekt.
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
/**
|
/**
|
||||||
* Generates the sharepic PNG from docs/marketing/sharepic.html
|
* Generates the sharepic PNGs (landscape & portrait) from HTML files
|
||||||
*
|
*
|
||||||
* Usage:
|
* Usage:
|
||||||
* node scripts/generate-sharepic.mjs
|
* node scripts/generate-sharepic.mjs
|
||||||
@@ -15,8 +15,6 @@ const __dirname = dirname(fileURLToPath(import.meta.url))
|
|||||||
const repoRoot = resolve(__dirname, '..')
|
const repoRoot = resolve(__dirname, '..')
|
||||||
const clientDir = resolve(repoRoot, 'client')
|
const clientDir = resolve(repoRoot, 'client')
|
||||||
const marketingDir = resolve(repoRoot, 'docs/marketing')
|
const marketingDir = resolve(repoRoot, 'docs/marketing')
|
||||||
const htmlPath = resolve(marketingDir, 'sharepic.html')
|
|
||||||
const pngPath = resolve(marketingDir, 'kapteins-daagbok-sharepic.png')
|
|
||||||
|
|
||||||
const require = createRequire(resolve(clientDir, 'package.json'))
|
const require = createRequire(resolve(clientDir, 'package.json'))
|
||||||
|
|
||||||
@@ -50,35 +48,48 @@ function loadPlaywright() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function renderSharepic(browser, htmlName, pngName, width, height) {
|
||||||
const playwright = loadPlaywright()
|
const htmlPath = resolve(marketingDir, htmlName)
|
||||||
await ensurePlaywrightChromium(playwright)
|
const pngPath = resolve(marketingDir, pngName)
|
||||||
|
|
||||||
console.log('Generating sharepic from HTML...')
|
console.log(`Generating sharepic (${width}x${height}) from ${htmlName}...`)
|
||||||
const browser = await playwright.chromium.launch({ headless: true })
|
|
||||||
|
const context = await browser.newContext({
|
||||||
|
viewport: { width, height },
|
||||||
|
deviceScaleFactor: 2 // High-DPI for crisp text
|
||||||
|
})
|
||||||
|
const page = await context.newPage()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const context = await browser.newContext({
|
|
||||||
viewport: { width: 1200, height: 630 },
|
|
||||||
deviceScaleFactor: 2 // High-DPI for crisp text
|
|
||||||
})
|
|
||||||
const page = await context.newPage()
|
|
||||||
|
|
||||||
// Wait for fonts/images to load fully
|
|
||||||
await page.goto(pathToFileURL(htmlPath).href, { waitUntil: 'networkidle' })
|
await page.goto(pathToFileURL(htmlPath).href, { waitUntil: 'networkidle' })
|
||||||
|
|
||||||
// Take a screenshot of the viewport
|
|
||||||
await page.screenshot({
|
await page.screenshot({
|
||||||
path: pngPath,
|
path: pngPath,
|
||||||
type: 'png'
|
type: 'png'
|
||||||
})
|
})
|
||||||
console.log('Sharepic PNG written successfully to:', pngPath)
|
console.log('Successfully wrote:', pngPath)
|
||||||
|
} finally {
|
||||||
|
await page.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const playwright = loadPlaywright()
|
||||||
|
await ensurePlaywrightChromium(playwright)
|
||||||
|
|
||||||
|
const browser = await playwright.chromium.launch({ headless: true })
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Landscape 1200x630
|
||||||
|
await renderSharepic(browser, 'sharepic.html', 'kapteins-daagbok-sharepic.png', 1200, 630)
|
||||||
|
|
||||||
|
// Portrait 1080x1920
|
||||||
|
await renderSharepic(browser, 'sharepic-portrait.html', 'kapteins-daagbok-sharepic-portrait.png', 1080, 1920)
|
||||||
} finally {
|
} finally {
|
||||||
await browser.close()
|
await browser.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main().catch((err) => {
|
main().catch((err) => {
|
||||||
console.error('Error generating sharepic:', err)
|
console.error('Error generating sharepics:', err)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user