← back to Dw Photo Capture
scripts/probooth-figma-build.js
180 lines
// use_figma build script — "DW Photo — Pro Booth (iPad/Phone)" (TK-12162).
// Run in ONE use_figma call after create_new_file. Builds 6 frames on one page:
// iPad landscape 1024×768 (Live / Result / Saved) + Phone 390×844 (Live / Result / Saved).
(async () => {
await figma.loadFontAsync({ family: 'Inter', style: 'Regular' });
await figma.loadFontAsync({ family: 'Inter', style: 'Semi Bold' });
await figma.loadFontAsync({ family: 'Inter', style: 'Bold' });
const C = {
bg: { r: 0.067, g: 0.063, b: 0.031 }, // #111008
panel: { r: 0.106, g: 0.098, b: 0.071 }, // #1b1912
line: { r: 0.173, g: 0.161, b: 0.125 }, // #2c2920
ink: { r: 0.949, g: 0.929, b: 0.882 }, // #f2ede1
dim: { r: 0.604, g: 0.569, b: 0.490 }, // #9a917d
gold: { r: 0.784, g: 0.635, b: 0.290 }, // #c8a24a
gold2: { r: 0.886, g: 0.741, b: 0.400 }, // #e2bd66
black: { r: 0, g: 0, b: 0 },
darkBtn: { r: 0.090, g: 0.075, b: 0.031 }, // #171308
stage: { r: 0.16, g: 0.15, b: 0.13 },
};
const fill = c => [{ type: 'SOLID', color: c }];
function txt(parent, s, size, color, style, ls) {
const t = figma.createText();
t.fontName = { family: 'Inter', style: style || 'Regular' };
t.characters = s; t.fontSize = size; t.fills = fill(color);
if (ls) t.letterSpacing = { value: ls, unit: 'PERCENT' };
parent.appendChild(t); return t;
}
function rect(parent, w, h, c, r) {
const n = figma.createRectangle(); n.resize(w, h); n.fills = fill(c);
if (r) n.cornerRadius = r; parent.appendChild(n); return n;
}
function frameNode(name, w, h, x, y, c) {
const f = figma.createFrame(); f.name = name; f.resize(w, h); f.x = x; f.y = y;
f.fills = fill(c || C.bg); figma.currentPage.appendChild(f); return f;
}
// slider group: label row (label + value) over a track with a gold round thumb
function slider(parent, x, y, w, label, val, pct) {
const g = figma.createFrame(); g.name = 'Slider · ' + label;
g.resize(w, 56); g.x = x; g.y = y; g.fills = [];
parent.appendChild(g);
const l = txt(g, label.toUpperCase(), 11, C.dim, 'Semi Bold', 18); l.x = 0; l.y = 0;
const v = txt(g, String(val), 14, C.gold2, 'Regular'); v.x = w - 34; v.y = -2; v.textAlignHorizontal = 'RIGHT';
const track = rect(g, w, 4, C.line, 2); track.x = 0; track.y = 34;
const notch = rect(g, 2, 8, C.gold, 1); notch.x = w / 2 - 1; notch.y = 32;
const th = figma.createEllipse(); th.resize(30, 30);
th.fills = fill(C.gold); th.strokes = fill(C.darkBtn); th.strokeWeight = 2;
th.x = Math.round((w - 30) * pct); th.y = 21; th.effects = [{ type: 'DROP_SHADOW', color: { r: 0, g: 0, b: 0, a: 0.55 }, offset: { x: 0, y: 2 }, radius: 8, visible: true, blendMode: 'NORMAL' }];
g.appendChild(th);
return g;
}
function shutter(parent, cx, cy, d) {
d = d || 78;
const o = figma.createEllipse(); o.resize(d, d); o.x = cx - d / 2; o.y = cy - d / 2;
o.fills = []; o.strokes = fill(C.ink); o.strokeWeight = 4; parent.appendChild(o);
const i = figma.createEllipse(); i.resize(d - 16, d - 16); i.x = cx - (d - 16) / 2; i.y = cy - (d - 16) / 2;
i.fills = fill(C.ink); parent.appendChild(i);
}
function chip(parent, x, y, label, goldBorder) {
const g = figma.createFrame(); g.name = 'Chip'; g.layoutMode = 'HORIZONTAL';
g.paddingLeft = 14; g.paddingRight = 14; g.paddingTop = 7; g.paddingBottom = 7;
g.primaryAxisSizingMode = 'AUTO'; g.counterAxisSizingMode = 'AUTO';
g.cornerRadius = 999; g.fills = [{ type: 'SOLID', color: C.black, opacity: 0.35 }];
g.strokes = fill(goldBorder ? C.gold : C.line); g.strokeWeight = 1;
g.x = x; g.y = y; parent.appendChild(g);
txt(g, label, 12.5, C.dim, 'Regular');
return g;
}
function brand(parent, x, y) {
const t = txt(parent, 'DW PRO BOOTH', 12, C.gold, 'Semi Bold', 34); t.x = x; t.y = y; return t;
}
function field(parent, x, y, w, label, value, placeholder) {
const l = txt(parent, label.toUpperCase(), 10.5, C.dim, 'Semi Bold', 20); l.x = x; l.y = y;
const box = rect(parent, w, 46, C.panel, 10); box.x = x; box.y = y + 18;
box.strokes = fill(C.line); box.strokeWeight = 1;
const v = txt(parent, value || placeholder || '', 16, value ? C.ink : C.dim, 'Regular');
v.x = x + 12; v.y = y + 31;
}
function button(parent, x, y, w, label, gold) {
const b = rect(parent, w, 54, gold ? C.gold : C.bg, 12); b.x = x; b.y = y;
if (!gold) { b.strokes = fill(C.line); b.strokeWeight = 1; b.fills = []; }
const t = txt(parent, label, 16, gold ? C.darkBtn : C.dim, 'Semi Bold');
t.resize(w, 20); t.textAlignHorizontal = 'CENTER'; t.x = x; t.y = y + 17;
}
figma.currentPage.name = 'Pro Booth · iPad + Phone';
/* ═══ iPad · LIVE (1024×768) ═══ */
let f = frameNode('iPad · 1 · Live', 1024, 768, 0, 0, C.black);
// stage
const stage = rect(f, 788, 768, C.stage); stage.x = 0; stage.y = 0;
const sampleHint = txt(f, 'live viewfinder — WYSIWYG preview (same bake as capture)', 12, C.dim, 'Regular');
sampleHint.resize(788, 16); sampleHint.textAlignHorizontal = 'CENTER'; sampleHint.x = 0; sampleHint.y = 730;
brand(f, 16, 14); chip(f, 806 - 196, 10, '↺ Reset'); chip(f, 806 - 106, 10, '✕');
// right rail
const rail = rect(f, 236, 768, C.panel); rail.x = 788; rail.y = 0;
const railLine = rect(f, 1, 768, C.line); railLine.x = 788; railLine.y = 0;
slider(f, 808, 170, 196, 'Exposure', '+12', 0.56);
slider(f, 808, 258, 196, 'Warmth', '-8', 0.46);
slider(f, 808, 346, 196, 'Saturation', '+6', 0.53);
shutter(f, 906, 520);
const sess = txt(f, '3 ✓', 12, C.dim, 'Regular'); sess.x = 960; sess.y = 514;
/* ═══ iPad · RESULT ═══ */
f = frameNode('iPad · 2 · Result', 1024, 768, 1104, 0);
brand(f, 16, 14);
const bkchip = chip(f, 850, 10, 'baked · full res'); bkchip.strokes = [];
const ph = rect(f, 520, 620, C.stage, 12); ph.x = 22; ph.y = 78; ph.strokes = fill(C.line); ph.strokeWeight = 1;
const phLbl = txt(f, 'captured JPEG — already baked, no re-processing', 12, C.dim, 'Regular');
phLbl.resize(520, 16); phLbl.textAlignHorizontal = 'CENTER'; phLbl.x = 22; phLbl.y = 380;
const fx = 586, fw = 196;
const st = txt(f, '✓ Label read — check the fields, then Save.', 12.5, C.gold2, 'Regular'); st.x = fx; st.y = 150;
field(f, fx, 186, fw, 'Vendor', 'Schumacher');
field(f, fx + fw + 14, 186, fw, 'Mfr # / SKU', '5015301');
field(f, fx, 268, fw, 'Pattern name', 'Torbay Handprint');
field(f, fx + fw + 14, 268, fw, 'Color', 'Aloe');
button(f, fx, 372, fw, '↩ Retake', false);
button(f, fx + fw + 14, 372, fw, 'Save draft', true);
const tm = txt(f, 'bake 60ms @ 2000×1125 · upload started +61ms after shutter · extract 808ms', 11, { r: 0.435, g: 0.408, b: 0.310 }, 'Regular');
tm.resize(fw * 2 + 14, 14); tm.textAlignHorizontal = 'CENTER'; tm.x = fx; tm.y = 446;
/* ═══ iPad · SAVED ═══ */
f = frameNode('iPad · 3 · Saved', 1024, 768, 2208, 0);
const ck = txt(f, '✓', 52, C.ink, 'Regular'); ck.resize(1024, 60); ck.textAlignHorizontal = 'CENTER'; ck.x = 0; ck.y = 250;
const sku = txt(f, 'DWSH-99001', 26, C.gold2, 'Bold', 4); sku.resize(1024, 34); sku.textAlignHorizontal = 'CENTER'; sku.x = 0; sku.y = 330;
const sub = txt(f, 'Saved as a Shopify draft — never auto-published.\nAdjustments and camera stay warm for the next sample.', 13.5, C.dim, 'Regular');
sub.resize(1024, 40); sub.textAlignHorizontal = 'CENTER'; sub.x = 0; sub.y = 380;
button(f, 392, 452, 240, 'Next sample', true);
const s2 = txt(f, '3 saved this session', 12, C.dim, 'Regular'); s2.resize(1024, 16); s2.textAlignHorizontal = 'CENTER'; s2.x = 0; s2.y = 522;
/* ═══ Phone · LIVE (390×844) ═══ */
f = frameNode('Phone · 1 · Live', 390, 844, 0, 880, C.black);
const pstage = rect(f, 390, 470, C.stage); pstage.x = 0; pstage.y = 0;
brand(f, 16, 14); chip(f, 250, 10, '↺'); chip(f, 316, 10, '✕');
const ph2 = txt(f, 'Frame the sample · adjust · shoot', 12, C.dim, 'Regular');
ph2.resize(390, 16); ph2.textAlignHorizontal = 'CENTER'; ph2.x = 0; ph2.y = 440;
const ctl = rect(f, 390, 374, { r: 0.082, g: 0.075, b: 0.047 }); ctl.x = 0; ctl.y = 470;
// phone sliders: label 96 + track + value, horizontal rows
function phoneSlider(y, label, val, pct) {
const l = txt(f, label.toUpperCase(), 11, C.dim, 'Semi Bold', 18); l.x = 16; l.y = y + 6;
const v = txt(f, String(val), 14, C.gold2, 'Regular'); v.x = 344; v.y = y + 4;
const track = rect(f, 210, 4, C.line, 2); track.x = 118; track.y = y + 12;
const notch = rect(f, 2, 8, C.gold, 1); notch.x = 222; notch.y = y + 10;
const th = figma.createEllipse(); th.resize(30, 30); th.fills = fill(C.gold);
th.strokes = fill(C.darkBtn); th.strokeWeight = 2;
th.x = 118 + Math.round(180 * pct); th.y = y - 1; f.appendChild(th);
}
phoneSlider(500, 'Exposure', '+12', 0.56);
phoneSlider(558, 'Warmth', '-8', 0.46);
phoneSlider(616, 'Saturation', '+6', 0.53);
shutter(f, 195, 730);
const psess = txt(f, '3 ✓', 12, C.dim, 'Regular'); psess.x = 285, psess.y = 724;
/* ═══ Phone · RESULT ═══ */
f = frameNode('Phone · 2 · Result', 390, 844, 470, 880);
brand(f, 16, 14);
const pph = rect(f, 358, 300, C.stage, 12); pph.x = 16; pph.y = 60; pph.strokes = fill(C.line); pph.strokeWeight = 1;
const pst = txt(f, '✓ Label read — check the fields, then Save.', 12.5, C.gold2, 'Regular'); pst.x = 16; pst.y = 376;
const pfw = 172;
field(f, 16, 402, pfw, 'Vendor', 'Schumacher');
field(f, 202, 402, pfw, 'Mfr # / SKU', '5015301');
field(f, 16, 484, pfw, 'Pattern name', 'Torbay Handprint');
field(f, 202, 484, pfw, 'Color', 'Aloe');
button(f, 16, 588, pfw, '↩ Retake', false);
button(f, 202, 588, pfw, 'Save draft', true);
const ptm = txt(f, 'bake 60ms · upload +61ms after shutter · extract 808ms', 11, { r: 0.435, g: 0.408, b: 0.310 }, 'Regular');
ptm.resize(358, 14); ptm.textAlignHorizontal = 'CENTER'; ptm.x = 16; ptm.y = 660;
/* ═══ Phone · SAVED ═══ */
f = frameNode('Phone · 3 · Saved', 390, 844, 940, 880);
const pck = txt(f, '✓', 52, C.ink, 'Regular'); pck.resize(390, 60); pck.textAlignHorizontal = 'CENTER'; pck.x = 0; pck.y = 280;
const psku = txt(f, 'DWSH-99001', 26, C.gold2, 'Bold', 4); psku.resize(390, 34); psku.textAlignHorizontal = 'CENTER'; psku.x = 0; psku.y = 360;
const psub = txt(f, 'Saved as a Shopify draft — never auto-published.\nAdjustments and camera stay warm.', 13.5, C.dim, 'Regular');
psub.resize(390, 40); psub.textAlignHorizontal = 'CENTER'; psub.x = 0; psub.y = 410;
button(f, 75, 486, 240, 'Next sample', true);
figma.viewport.scrollAndZoomIntoView(figma.currentPage.children);
return 'built ' + figma.currentPage.children.length + ' frames';
})()