shots: full-bleed layout (shots.layout bleed) with corner captions, per-size frames, and landscape orientation, on top of the landscape sizes
This commit is contained in:
parent
9ed1890de8
commit
d4cf1ad3a1
11 changed files with 320 additions and 11 deletions
|
|
@ -13,6 +13,9 @@
|
|||
// paths) are served from where they are at /fonts/local/... (local-fonts.ts); a file changed since `shots fonts` fails.
|
||||
// Style: shotsStyle() = shots.style, else app for the app / subscription-app profiles (template-app.html,
|
||||
// feature-app.html: calm gradient, device frame, headline + subline), else game (template.html, feature.html).
|
||||
// Layout: shots.layout 'bleed' renders template-bleed.html / feature-bleed.html instead (the frame of that size fills
|
||||
// the image, the title is a corner caption with a text shadow, no device or background); shots.orientation 'landscape'
|
||||
// swaps every other portrait screenshot size (renderDims in lib.ts).
|
||||
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { SHOTS_PAGES } from '../lib/embedded';
|
||||
import { fontCodepoints } from './cmap';
|
||||
|
|
@ -91,6 +94,7 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
const { phone, tablet } = frameDims(config);
|
||||
const rtl = new Map(config.languages.map(l => [l.id, !!l.rtl] as const));
|
||||
const style = shotsStyle(config);
|
||||
const bleedLayout = shots.layout === 'bleed';
|
||||
const rf = resolveShotFonts(shots, style, resolve, root);
|
||||
const sf = { title: rf.title.family, body: rf.body.family };
|
||||
const fonts = { title: sf.title, body: sf.body };
|
||||
|
|
@ -156,10 +160,10 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
const st = stacks(lang, copy);
|
||||
const ls = shots.landscape?.size;
|
||||
const landscape = { w: ls?.w ?? 1440, h: ls?.h ?? 900, bar: 0 };
|
||||
const base = { style, phone, tablet, landscape, side: shots.landscape?.side ?? 'left', rtl: rtl.get(lang), logo: !!shots.logo, appName: config.app.name,
|
||||
const base = { style, layout: shots.layout ?? 'device', phone, tablet, landscape, side: shots.landscape?.side ?? 'left', rtl: rtl.get(lang), logo: !!shots.logo, appName: config.app.name,
|
||||
fonts: { ...fonts, script: st.script, titleStack: st.title, bodyStack: st.body } };
|
||||
if (url.searchParams.has('feature'))
|
||||
return Response.json({ ...base, frame: shots.feature?.frame ?? scenes[0]!.frames[0]!.src,
|
||||
return Response.json({ ...base, frame: shots.feature?.frame ?? scenes[0]!.frames[0]!.src, featureTitle: shots.feature?.title,
|
||||
device: featureLayout(style, phone, { fit: shots.feature?.fit, scale: shots.feature?.scale }),
|
||||
palette: look(palette(shots.feature?.palette ?? scenes[0]!.palette, style), style === 'app' ? 135 : 160, style === 'app' ? 55 : 50, shots.feature?.background) });
|
||||
const sid = url.searchParams.get('s') ?? '';
|
||||
|
|
@ -173,7 +177,7 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
else if (p.startsWith('/frames/')) file = join(framesDir, p.slice(8));
|
||||
else if (p.startsWith('/fonts/')) file = fontsRoute(p.slice(7), fontsDir, root, manifest) ?? '';
|
||||
else if (p.startsWith('/out/')) file = join(outDir, p.slice(5));
|
||||
else if (/^\/(template|feature)(-app)?\.html$/.test(p)) return new Response(SHOTS_PAGES[p.slice(1)]!, { headers: { 'content-type': TYPES.html! } });
|
||||
else if (/^\/(template|feature)(-app|-bleed)?\.html$/.test(p)) return new Response(SHOTS_PAGES[p.slice(1)]!, { headers: { 'content-type': TYPES.html! } });
|
||||
if (!file || file.includes('..')) return new Response('no', { status: 404 });
|
||||
return rt.fileResponse(file, { 'content-type': TYPES[file.split('.').pop()!.toLowerCase()] ?? 'application/octet-stream' }) ?? new Response('missing ' + p, { status: 404 });
|
||||
},
|
||||
|
|
@ -254,7 +258,7 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
const s = SIZES[size]!, d = renderDims(size, shots);
|
||||
if (s.feature) {
|
||||
const f = join(dir, 'feature', '1024x500.png');
|
||||
items.push({ url: `${base}/${style === 'app' ? 'feature-app' : 'feature'}.html?lang=${encodeURIComponent(lang)}${vq}`, w: s.w, h: s.h, out: f });
|
||||
items.push({ url: `${base}/${bleedLayout ? 'feature-bleed' : style === 'app' ? 'feature-app' : 'feature'}.html?lang=${encodeURIComponent(lang)}${vq}`, w: s.w, h: s.h, out: f });
|
||||
rows.push({ size, files: [f] });
|
||||
continue;
|
||||
}
|
||||
|
|
@ -262,7 +266,7 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
for (const [i, id] of sceneIds.entries()) {
|
||||
const f = join(dir, size, `${String(i + 1).padStart(2, '0')}.png`);
|
||||
const dev = d.landscape ? `&device=landscape&frame=${d.frame}` : s.device ? '&device=tablet' : '';
|
||||
items.push({ url: `${base}/${style === 'app' ? 'template-app' : 'template'}.html?s=${encodeURIComponent(id)}&w=${d.w}&h=${d.h}&lang=${encodeURIComponent(lang)}${dev}${size === 'tab7' || size === 'tab10' ? '&style=android' : ''}${vq}`, w: d.w, h: d.h, out: f });
|
||||
items.push({ url: `${base}/${bleedLayout ? 'template-bleed' : style === 'app' ? 'template-app' : 'template'}.html?s=${encodeURIComponent(id)}&w=${d.w}&h=${d.h}&lang=${encodeURIComponent(lang)}&size=${encodeURIComponent(size)}${bleedLayout ? '' : dev}${size === 'tab7' || size === 'tab10' ? '&style=android' : ''}${vq}`, w: d.w, h: d.h, out: f });
|
||||
files.push(f);
|
||||
}
|
||||
rows.push({ size, files, dims: { w: d.w, h: d.h } });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue