dragonflight/services/web-ui/public/screens-editor.jsx

130 lines
5.7 KiB
JavaScript

// screens-editor.jsx — Editor (timeline)
function Editor() {
const { ASSETS } = window.ZAMPP_DATA;
const [playing, setPlaying] = React.useState(false);
const [currentMs, setCurrentMs] = React.useState(0);
React.useEffect(() => {
if (!playing) return;
const i = setInterval(() => setCurrentMs(t => t + 100), 100);
return () => clearInterval(i);
}, [playing]);
return (
<div className="editor-shell">
<div className="editor-topbar">
<span style={{ fontWeight: 600, fontSize: 13 }}>New sequence</span>
<div style={{ flex: 1 }} />
<button className="btn ghost sm"><Icon name="download" />Export</button>
<button className="btn primary sm">Publish</button>
</div>
<div className="editor-body">
<aside className="editor-bins">
<div style={{ padding: '12px 12px 8px', display: 'flex', alignItems: 'center', gap: 6 }}>
<span style={{ fontWeight: 600, fontSize: 12 }}>Project bin</span>
<span style={{ flex: 1 }} />
<button className="icon-btn"><Icon name="search" size={12} /></button>
</div>
<div style={{ padding: '0 8px 12px', display: 'flex', flexDirection: 'column', gap: 4 }}>
{ASSETS.length === 0 ? (
<div style={{ padding: '16px 4px', color: 'var(--text-3)', fontSize: 12 }}>No assets in library.</div>
) : ASSETS.slice(0, 12).map(a => (
<div key={a.id} className="editor-bin-item">
<div className="editor-bin-thumb"><AssetThumb asset={a} /></div>
<div style={{ flex: 1, minWidth: 0 }}>
<div style={{ fontSize: 11.5, fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{a.name}</div>
<div className="mono" style={{ fontSize: 10, color: 'var(--text-3)' }}>{a.duration}</div>
</div>
</div>
))}
</div>
</aside>
<div className="editor-viewer">
<div className="editor-canvas">
<FauxFrame />
{!playing && (
<button className="player-play-overlay" onClick={() => setPlaying(true)}>
<Icon name="play" size={28} />
</button>
)}
<div className="player-tc"><span className="mono">{msToTimecode ? msToTimecode(currentMs) : '00:00:00:00'}</span></div>
</div>
<div className="editor-transport">
<button className="icon-btn" onClick={() => setCurrentMs(0)}><Icon name="arrowLeft" size={14} /></button>
<button className="icon-btn" onClick={() => setPlaying(p => !p)}>
<Icon name={playing ? 'pause' : 'play'} size={14} />
</button>
<button className="icon-btn"><Icon name="arrowRight" size={14} /></button>
<span style={{ flex: 1 }} />
<button className="btn ghost sm">Mark in</button>
<button className="btn ghost sm">Mark out</button>
<button className="btn subtle sm">Add to timeline</button>
</div>
</div>
<aside className="editor-insp">
<div style={{ padding: '12px', fontSize: 12, fontWeight: 600, borderBottom: '1px solid var(--border)' }}>Inspector</div>
<div style={{ padding: 12, display: 'flex', flexDirection: 'column', gap: 12 }}>
<InspGroup title="Transform">
<InspRow label="Position" value="0, 0" />
<InspRow label="Scale" value="100%" />
<InspRow label="Rotation" value="0°" />
</InspGroup>
<InspGroup title="Color">
<InspRow label="Exposure" value="0.0" />
<InspRow label="Contrast" value="0.0" />
<InspRow label="Saturation" value="0.0" />
</InspGroup>
<InspGroup title="Audio">
<InspRow label="Level" value="0.0 dB" />
<InspRow label="Pan" value="C" />
</InspGroup>
</div>
</aside>
</div>
<EditorTimeline currentMs={currentMs} total={60} clips={[]} />
</div>
);
}
function InspGroup({ title, children }) {
return (
<div>
<div className="muted" style={{ fontSize: 10, textTransform: 'uppercase', letterSpacing: '0.06em', fontWeight: 600, marginBottom: 6 }}>{title}</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>{children}</div>
</div>
);
}
function InspRow({ label, value }) {
return (
<div style={{ display: 'grid', gridTemplateColumns: '70px 1fr', alignItems: 'center', fontSize: 11.5 }}>
<span style={{ color: 'var(--text-3)' }}>{label}</span>
<span className="mono" style={{ background: 'var(--bg-2)', padding: '3px 6px', borderRadius: 4, border: '1px solid var(--border)' }}>{value}</span>
</div>
);
}
function EditorTimeline({ currentMs, total = 60, clips = [] }) {
const playheadPct = total > 0 ? ((currentMs / 1000) / total) * 100 : 0;
return (
<div className="editor-timeline">
<div className="editor-timeline-head">
<span className="mono" style={{ fontSize: 11, color: 'var(--text-3)' }}>Timeline</span>
<span style={{ flex: 1 }} />
</div>
<div className="timeline-ruler">
{Array.from({ length: 13 }).map((_, i) => (
<div key={i} className="ruler-tick">
{i % 2 === 0 && <span className="mono">{`00:${String(i * 5).padStart(2, '0')}`}</span>}
</div>
))}
</div>
{clips.length === 0 && (
<div style={{ padding: '20px 40px', color: 'var(--text-3)', fontSize: 12 }}>Drop assets from the bin to build a sequence.</div>
)}
<div className="timeline-playhead" style={{ left: `calc(40px + (100% - 40px) * ${playheadPct / 100})` }} />
</div>
);
}
window.Editor = Editor;