[object Object]

← back to Interiordesignershowroom

refine: real Gemini guide heroes (replace picsum placeholders, 1200x630 jpg ~90kb) + unpublish junk test rooms from index

4a02b54c2e4647bb7fbe8cb1f98c8d425a4220a1 · 2026-08-01 22:35:47 -0700 · Steve Abrams

Files touched

Diff

commit 4a02b54c2e4647bb7fbe8cb1f98c8d425a4220a1
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sat Aug 1 22:35:47 2026 -0700

    refine: real Gemini guide heroes (replace picsum placeholders, 1200x630 jpg ~90kb) + unpublish junk test rooms from index
---
 public/img/guides/best-sectionals-small-spaces.jpg | Bin 0 -> 65494 bytes
 public/img/guides/best-velvet-sofas.jpg            | Bin 0 -> 97130 bytes
 public/img/guides/coastal-living-room-budget.jpg   | Bin 0 -> 123717 bytes
 public/img/guides/how-to-layer-lighting.jpg        | Bin 0 -> 74278 bytes
 .../guides/shop-the-look-mid-century-bedroom.jpg   | Bin 0 -> 85734 bytes
 scripts/gen-guide-heroes.js                        |  71 +++++++++++++++++++++
 6 files changed, 71 insertions(+)

diff --git a/public/img/guides/best-sectionals-small-spaces.jpg b/public/img/guides/best-sectionals-small-spaces.jpg
new file mode 100644
index 0000000..6cce71f
Binary files /dev/null and b/public/img/guides/best-sectionals-small-spaces.jpg differ
diff --git a/public/img/guides/best-velvet-sofas.jpg b/public/img/guides/best-velvet-sofas.jpg
new file mode 100644
index 0000000..8875370
Binary files /dev/null and b/public/img/guides/best-velvet-sofas.jpg differ
diff --git a/public/img/guides/coastal-living-room-budget.jpg b/public/img/guides/coastal-living-room-budget.jpg
new file mode 100644
index 0000000..741e1ee
Binary files /dev/null and b/public/img/guides/coastal-living-room-budget.jpg differ
diff --git a/public/img/guides/how-to-layer-lighting.jpg b/public/img/guides/how-to-layer-lighting.jpg
new file mode 100644
index 0000000..580a795
Binary files /dev/null and b/public/img/guides/how-to-layer-lighting.jpg differ
diff --git a/public/img/guides/shop-the-look-mid-century-bedroom.jpg b/public/img/guides/shop-the-look-mid-century-bedroom.jpg
new file mode 100644
index 0000000..036c3b3
Binary files /dev/null and b/public/img/guides/shop-the-look-mid-century-bedroom.jpg differ
diff --git a/scripts/gen-guide-heroes.js b/scripts/gen-guide-heroes.js
new file mode 100644
index 0000000..8de8cc7
--- /dev/null
+++ b/scripts/gen-guide-heroes.js
@@ -0,0 +1,71 @@
+// Generate a REAL editorial hero image per published guide with Gemini 2.5 Flash
+// Image ("nano-banana") — replacing the picsum.photos placeholders (Steve's rule:
+// no placeholders, only real images). Landscape 16:9 so it doubles as the OG card.
+// Idempotent: only touches guides whose hero is still a picsum placeholder unless
+// --force. Paid: ~$0.039/image. Run: node scripts/gen-guide-heroes.js [--force]
+require('dotenv').config();
+const fs = require('fs');
+const path = require('path');
+const db = require('../lib/db');
+
+const MODEL = 'gemini-2.5-flash-image';
+const COST = 0.039;
+const OUT_DIR = path.join(__dirname, '..', 'public', 'img', 'guides');
+const FORCE = process.argv.includes('--force');
+
+// Per-guide subject; falls back to the title if a slug isn't mapped.
+const SUBJECTS = {
+  'best-velvet-sofas':
+    'a designer living room anchored by a plush emerald-green velvet sofa, brass accents, a marble coffee table and a large abstract artwork',
+  'shop-the-look-mid-century-bedroom':
+    'a warm mid-century-modern bedroom: walnut furniture, a low platform bed with mustard and cream linens, a globe pendant, and a leafy plant',
+  'best-sectionals-small-spaces':
+    'a compact modern living room making smart use of a low-profile bouclé sectional, floating shelves, and a round jute rug',
+  'how-to-layer-lighting':
+    'an elegant living room at dusk demonstrating layered lighting — a warm floor lamp, table lamps, and recessed accent lights washing the walls',
+  'coastal-living-room-budget':
+    'an elevated coastal living room: pale oak floors, a linen slipcovered sofa, rattan accents, blue-and-white textiles, and soft ocean light',
+};
+
+async function genImage(prompt) {
+  const key = process.env.GEMINI_API_KEY;
+  if (!key) throw new Error('GEMINI_API_KEY not set');
+  const instruction = [
+    `A high-end interior-design editorial photograph of ${prompt}.`,
+    'Shot on a full-frame camera with a 35mm lens, natural window light, shallow depth of field.',
+    'Styled as one cohesive, believably-designed space — like a full-page flagship photograph pulled straight from Architectural Digest.',
+    'Wide 16:9 landscape composition. No people, no text, no watermarks, no logos.',
+  ].join(' ');
+  const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${MODEL}:generateContent?key=${key}`, {
+    method: 'POST', headers: { 'Content-Type': 'application/json' },
+    body: JSON.stringify({ contents: [{ parts: [{ text: instruction }] }], generationConfig: { responseModalities: ['IMAGE'] } }),
+  });
+  const j = await res.json();
+  if (j.error) throw new Error(j.error.message || 'gemini error');
+  const out = (j.candidates && j.candidates[0] && j.candidates[0].content.parts || []).find((p) => p.inlineData);
+  if (!out) throw new Error('no image returned');
+  return Buffer.from(out.inlineData.data, 'base64');
+}
+
+(async () => {
+  fs.mkdirSync(OUT_DIR, { recursive: true });
+  const { rows } = await db.query('SELECT slug, title, hero_image FROM guides WHERE published ORDER BY created_at');
+  const targets = rows.filter((g) => FORCE || !g.hero_image || /picsum\.photos/.test(g.hero_image));
+  console.log(`[heroes] ${targets.length}/${rows.length} guides need a real hero. Est cost: $${(targets.length * COST).toFixed(3)} (${MODEL} @ $${COST}/img)`);
+  let spent = 0, done = 0;
+  for (const g of targets) {
+    const prompt = SUBJECTS[g.slug] || `a beautifully designed interior that illustrates "${g.title}"`;
+    try {
+      const buf = await genImage(prompt);
+      const rel = `/img/guides/${g.slug}.png`;
+      fs.writeFileSync(path.join(OUT_DIR, `${g.slug}.png`), buf);
+      await db.query('UPDATE guides SET hero_image=$1, updated_at=now() WHERE slug=$2', [rel, g.slug]);
+      spent += COST; done += 1;
+      console.log(`  ✓ ${g.slug} -> ${rel} (${(buf.length / 1024).toFixed(0)}kb)  running total: $${spent.toFixed(3)}`);
+    } catch (e) {
+      console.error(`  ✗ ${g.slug}: ${e.message}`);
+    }
+  }
+  console.log(`[heroes] done. ${done}/${targets.length} generated. Actual spend: $${spent.toFixed(3)} (local DB updated).`);
+  process.exit(0);
+})().catch((e) => { console.error(e); process.exit(1); });

← 082c438 refine: soften freshness chip to 'Price checked' + SEO struc  ·  back to Interiordesignershowroom  ·  auto-save: 2026-08-01T22:40:01 (3 files) — lib/rooms.js lib/ 8df6b7b →