← back to Dw Slideshow Gen
lib/design.js
72 lines
// lib/design.js — read a project DESIGN.md into brand tokens, with DW-luxe defaults.
// Tolerant parser: every field has a default, and `found:false` is returned when no
// DESIGN.md is present so callers can warn. One source of truth for fonts/colors/logo
// across every slideshow.
import fs from 'node:fs'
import { exists } from './util.js'
// Designer Wallcoverings house defaults — warm-charcoal ground, cream type, brass accent,
// Didot display serif (fashion-magazine luxe) over Avenir body.
export const DW_DEFAULTS = {
brand: 'Designer Wallcoverings',
kicker: 'DESIGNER WALLCOVERINGS',
url: 'designerwallcoverings.com',
// fonts: family alias -> font file we register at render time
fonts: {
display: { family: 'Didot', path: '/System/Library/Fonts/Supplemental/Didot.ttc' },
body: { family: 'Avenir', path: '/System/Library/Fonts/Avenir.ttc' },
},
colors: {
bgTop: '#1C1B19', // warm charcoal
bgBot: '#2A2723',
ink: '#F5F0E6', // warm cream
accent:'#C2A45A', // muted brass/gold
muted: '#B9B2A4', // soft taupe for subtitles
},
cta: { label: 'Shop the collection', sub: 'designerwallcoverings.com' },
voice: 'editorial, calm, design-forward — speaks to interior designers and discerning homeowners',
hashtags: ['#designerwallcoverings', '#wallcovering', '#interiordesign', '#wallpaper', '#homedecor'],
}
// extremely tolerant key:value-ish extractor from a DESIGN.md
function pull(md, keys) {
for (const k of keys) {
const re = new RegExp(`(?:^|\\n)\\s*(?:[-*]\\s*)?\\*{0,2}${k}\\*{0,2}\\s*[:=]\\s*(.+)`, 'i')
const m = md.match(re)
if (m) return m[1]
.replace(/<!--.*?-->/g, '') // strip trailing HTML comments
.replace(/\*/g, '') // strip markdown bold markers
.trim()
.replace(/^["'`]|["'`]$/g, '')
}
return null
}
export function loadDesign(designPath) {
const d = structuredClone(DW_DEFAULTS)
if (!designPath || !exists(designPath)) return { ...d, found: false, source: null }
const md = fs.readFileSync(designPath, 'utf8')
const brand = pull(md, ['brand', 'brand name', 'name'])
const url = pull(md, ['url', 'website', 'domain'])
const accent = pull(md, ['accent', 'accent color', 'primary'])
const ink = pull(md, ['ink', 'text color', 'foreground'])
const bgTop = pull(md, ['bg', 'background', 'bg top', 'background top'])
const bgBot = pull(md, ['bg bottom', 'background bottom'])
const cta = pull(md, ['cta', 'call to action', 'cta label'])
const voice = pull(md, ['voice', 'tone'])
const tags = pull(md, ['hashtags', 'tags'])
if (brand) { d.brand = brand; d.kicker = brand.toUpperCase() }
if (url) { d.url = url; d.cta.sub = url }
if (accent) d.colors.accent = accent
if (ink) d.colors.ink = ink
if (bgTop) d.colors.bgTop = bgTop
if (bgBot) d.colors.bgBot = bgBot || bgTop
if (cta) d.cta.label = cta
if (voice) d.voice = voice
if (tags) d.hashtags = tags.split(/[,\s]+/).filter(Boolean).map(t => t.startsWith('#') ? t : '#' + t).slice(0, 5)
return { ...d, found: true, source: designPath }
}