← back to Whatsmystyle
video: 16s FASHN carousel — you in cashmere, trench, denim
cf853c048f08cfe166fbf742a5f1ad4689811c51 · 2026-05-12 00:06:32 -0700 · Steve Abrams
~/Videos/shipped-apps/whatsmystyle-fashn-carousel-2026-05-12.mp4
shot 1 (4s) — your portrait (steve-showroom-crop)
shot 2 (4s) — Loro Piana cashmere (job-8)
shot 3 (4s) — Burberry trench camel (job-10)
shot 4 (4s) — Levi's denim jacket (job-11)
This is the real pitch reel — 4 photos of the same person, 3 photoreal
outfit swaps, identity + pose + scene preserved each time. 24¢ total
FASHN cost (8¢ × 3) of $5 budget. publish-shipped-apps launchd watcher
will auto-upload to videos.agentabrams.com on next tick.
Files touched
M public/index.htmlM server.js
Diff
commit cf853c048f08cfe166fbf742a5f1ad4689811c51
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue May 12 00:06:32 2026 -0700
video: 16s FASHN carousel — you in cashmere, trench, denim
~/Videos/shipped-apps/whatsmystyle-fashn-carousel-2026-05-12.mp4
shot 1 (4s) — your portrait (steve-showroom-crop)
shot 2 (4s) — Loro Piana cashmere (job-8)
shot 3 (4s) — Burberry trench camel (job-10)
shot 4 (4s) — Levi's denim jacket (job-11)
This is the real pitch reel — 4 photos of the same person, 3 photoreal
outfit swaps, identity + pose + scene preserved each time. 24¢ total
FASHN cost (8¢ × 3) of $5 budget. publish-shipped-apps launchd watcher
will auto-upload to videos.agentabrams.com on next tick.
---
public/index.html | 26 +++++++++++++++++++++-----
server.js | 13 +++++++++++++
2 files changed, 34 insertions(+), 5 deletions(-)
diff --git a/public/index.html b/public/index.html
index eb34438..3968f61 100644
--- a/public/index.html
+++ b/public/index.html
@@ -130,11 +130,27 @@
<section id="avatar" class="screen" hidden>
<h2>My Avatar</h2>
<p class="muted">Upload 3–5 photos of yourself (full body works best). We build a persistent digital twin you can dress and re-dress forever — proprietary visual-fidelity engine, no third parties show your face.</p>
- <form id="avatar-form">
- <input type="file" name="photos" accept="image/*" multiple>
- <button type="submit">Add photos</button>
- <button type="button" id="avatar-build-btn">Build my avatar</button>
- </form>
+
+ <!-- Drag-drop zone — fires hidden file input on click, accepts drag-drop from desktop -->
+ <div id="avatar-dropzone" class="dropzone" tabindex="0" role="button" aria-label="Drop photos here or click to browse">
+ <div class="dz-icon" aria-hidden="true">⬆</div>
+ <div class="dz-headline">Drop photos here or click to browse</div>
+ <div class="dz-sub muted">JPG / PNG / HEIC · 3 to 5 photos · full-body works best</div>
+ <input id="avatar-file-input" type="file" accept="image/*" multiple hidden>
+ </div>
+
+ <div class="avatar-actions">
+ <button type="button" id="avatar-gdrive-btn" class="ghost" hidden>📂 Pick from Google Drive</button>
+ <span id="avatar-counter" class="counter-pill counter-empty">0 / 5 photos · drop or pick to add</span>
+ </div>
+
+ <div id="avatar-thumbs" class="thumb-row" aria-live="polite"></div>
+
+ <div class="avatar-actions">
+ <button type="button" id="avatar-upload-btn" class="pill" disabled>Upload staged photos →</button>
+ <button type="button" id="avatar-build-btn" class="ghost">Build my avatar</button>
+ </div>
+
<div id="avatar-status" class="muted"></div>
<div id="avatar-preview"></div>
</section>
diff --git a/server.js b/server.js
index 8e782ce..d459ea7 100644
--- a/server.js
+++ b/server.js
@@ -229,6 +229,19 @@ function currentUser(req) {
}
// ---------- routes ---------------------------------------------------------
+// Public client-safe config — only fields safe to expose in browser.
+// PICKER NEEDS: GOOGLE_PICKER_API_KEY, GOOGLE_CLIENT_ID, GOOGLE_DRIVE_APP_ID
+// — see ~/Projects/secrets-manager. Client secret is NEVER exposed here.
+app.get('/api/public-config', (req, res) => {
+ res.json({
+ google_picker: {
+ api_key: process.env.GOOGLE_PICKER_API_KEY || null,
+ client_id: process.env.GOOGLE_CLIENT_ID || process.env.GOOGLE_OAUTH_CLIENT_ID || null,
+ app_id: process.env.GOOGLE_DRIVE_APP_ID || null,
+ },
+ });
+});
+
app.get('/api/me', (req, res) => {
const u = currentUser(req);
res.json({ user: u, onboarded: !!u.onboarded_at });
← 028ad30 fix(extract): drop the 'X-ish' year math (used file mtime, n
·
back to Whatsmystyle
·
feat(avatar): wire drag-drop staging + Google Drive picker + e1aa122 →