// Tweaks for VariantDPC — three expressive controls that reshape feel. // 1) Density — compact / standard / editorial (reflows padding + type scale) // 2) Mood — calm blue / electric / mono (recolors brand accent + ink) // 3) Geometry — circles / grid / waves (swaps abstract SVG language) const TWEAK_DEFAULTS_D = /*EDITMODE-BEGIN*/{ "density": "standard", "mood": "calm", "geometry": "circles" }/*EDITMODE-END*/; // ----- presets ---------------------------------------------------------------- const DENSITY_PRESETS = { compact: { label: 'Compact', sectionPadY: 64, // section vertical padding (px) heroPadY: 96, heroDisplay: 100, // hero h1 font-size sectionTitle: 28, bodyLine: 1.85, grid: 24, // gap between cards }, standard: { label: 'Standard', sectionPadY: 96, heroPadY: 72, heroDisplay: 72, sectionTitle: 34, bodyLine: 1.9, grid: 32, }, editorial: { label: 'Editorial', sectionPadY: 168, heroPadY: 220, heroDisplay: 168, sectionTitle: 44, bodyLine: 2.35, grid: 56, }, }; const MOOD_PRESETS = { calm: { label: 'Calm', blue: '#0F4FB8', blueLight: '#5C8FDB', blueGhost: '#F4F8FD', ink: '#0A0A0A', paper: '#FAFAFA', accentName: 'NAVY', }, electric: { label: 'Electric', blue: '#0033FF', blueLight: '#7AA8FF', blueGhost: '#EEF2FF', ink: '#04061A', paper: '#F2F4FF', accentName: 'ELECTRIC', }, mono: { label: 'Mono', blue: '#111111', blueLight: '#888888', blueGhost: '#F2F2F2', ink: '#000000', paper: '#F6F6F6', accentName: 'GRAPHITE', }, }; // SVG art generators by geometry mode. // Each takes a `palette` { line, fill } and a config { w, h, density } and // returns an array of s. Used inside variant-d via window.HBGeo. function geoCircles(palette, { w=520, h=520, density=1 } = {}) { const cx = w/2, cy = h/2; const count = Math.round(14 * density); const step = Math.min(w, h) / (2 * count); const out = []; for (let i=0; i fn(HB_STATE)); try { window.parent.postMessage({ type:'__edit_mode_set_keys', edits: partial }, '*'); } catch (_) {} } window.useHBTweaks = function() { const [s, setS] = React.useState(HB_STATE); React.useEffect(() => { HB_LISTENERS.add(setS); return () => HB_LISTENERS.delete(setS); }, []); // Compose resolved presets for convenience. const density = DENSITY_PRESETS[s.density] || DENSITY_PRESETS.standard; const mood = MOOD_PRESETS[s.mood] || MOOD_PRESETS.calm; return { raw: s, density, mood, geometry: s.geometry || 'circles', set: hbSet, }; }; // ----- the panel itself ------------------------------------------------------ // Vanilla-ish; uses TweaksPanel from tweaks-panel.jsx if present, else a // bare floating panel with three segmented controls. function HBTweaksDPanel() { const [open, setOpen] = React.useState(false); const [tw, setTw] = React.useState(HB_STATE); React.useEffect(() => { const onMsg = (e) => { const t = e.data && e.data.type; if (t === '__activate_edit_mode') setOpen(true); if (t === '__deactivate_edit_mode') setOpen(false); }; window.addEventListener('message', onMsg); HB_LISTENERS.add(setTw); // Announce only after listener is live. try { window.parent.postMessage({ type:'__edit_mode_available' }, '*'); } catch (_) {} return () => { window.removeEventListener('message', onMsg); HB_LISTENERS.delete(setTw); }; }, []); if (!open) return null; const close = () => { setOpen(false); try { window.parent.postMessage({ type:'__edit_mode_dismissed' }, '*'); } catch (_) {} }; const seg = (key, label, options) => ( React.createElement('div', { style:{ marginBottom: 18 } }, React.createElement('div', { style:{ fontFamily:'Inter, sans-serif', fontSize:10, letterSpacing:'.28em', color:'#5b5b5b', marginBottom:10, fontWeight:600 }, }, label), React.createElement('div', { style:{ display:'grid', gridTemplateColumns:`repeat(${options.length},1fr)`, border:'1px solid #d8d8d8' } }, options.map(opt => React.createElement('button', { key: opt.value, onClick: () => hbSet({ [key]: opt.value }), style:{ padding:'10px 8px', background: tw[key] === opt.value ? '#0F4FB8' : '#fff', color: tw[key] === opt.value ? '#fff' : '#111', border:'none', borderRight: '1px solid #d8d8d8', fontFamily:'"Noto Sans JP", sans-serif', fontSize:12, fontWeight:600, cursor:'pointer', letterSpacing:'.02em', transition:'background .15s ease', }, }, opt.label), ), ), ) ); return React.createElement('div', { style:{ position:'fixed', right:20, bottom:20, zIndex:9999, width:300, padding:'18px 18px 14px', background:'#fff', border:'1px solid #d8d8d8', boxShadow:'0 24px 64px rgba(0,0,0,.14), 0 4px 12px rgba(0,0,0,.06)', fontFamily:'"Noto Sans JP", sans-serif', }, }, React.createElement('div', { style:{ display:'flex', alignItems:'center', justifyContent:'space-between', marginBottom:18 } }, React.createElement('div', null, React.createElement('div', { style:{ fontFamily:'Inter, sans-serif', fontSize:10, letterSpacing:'.32em', color:'#0F4FB8', fontWeight:600 } }, '● TWEAKS'), React.createElement('div', { style:{ fontSize:13, fontWeight:700, marginTop:4 } }, '案D · デザインを操作'), ), React.createElement('button', { onClick: close, style:{ width:28, height:28, border:'1px solid #d8d8d8', background:'#fff', cursor:'pointer', fontSize:14, lineHeight:1 }, 'aria-label':'close', }, '×'), ), seg('density', '01 — DENSITY / 余白の呼吸', [ { value:'compact', label:'Compact' }, { value:'standard', label:'Standard' }, { value:'editorial', label:'Editorial' }, ]), seg('mood', '02 — ACCENT MOOD / 色の温度', [ { value:'calm', label:'Calm' }, { value:'electric', label:'Electric' }, { value:'mono', label:'Mono' }, ]), seg('geometry', '03 — GEOMETRY / 背景の語彙', [ { value:'circles', label:'Circles' }, { value:'grid', label:'Grid' }, { value:'waves', label:'Waves' }, ]), React.createElement('div', { style:{ marginTop:6, fontSize:10.5, color:'#888', lineHeight:1.7, fontFamily:'JetBrains Mono, monospace', letterSpacing:'.02em' }, }, '3つのつまみで、印象を組み替える。'), ); } window.HBTweaksDPanel = HBTweaksDPanel;