feat: multi-material overlays, presentation guides, and release prebuilds

Align control/presentation with presentation screen rect and darkness z-order; sync window titles and session window cleanup. Pack Win/Mac/Linux with npmRebuild disabled and release-native-prep for classic-level and sharp.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-08-03 11:28:18 +08:00
parent 61446dacfc
commit 2979d06f1c
41 changed files with 1060 additions and 252 deletions
+27 -11
View File
@@ -1096,6 +1096,7 @@ export function EditorApp() {
previewAssetId={sc?.previewAssetId ?? null}
previewAssetType={sc?.previewAssetType ?? null}
previewVideoAutostart={sc?.previewVideoAutostart ?? false}
previewVideoLoop={sc?.settings.loopVideo ?? false}
previewRotationDeg={sc?.previewRotationDeg ?? 0}
darkenScene={sc?.darkenScene ?? false}
previewBusy={previewBusy}
@@ -1108,6 +1109,9 @@ export function EditorApp() {
onPreviewVideoAutostartChange={(next) =>
void actions.updateScene(sid, { previewVideoAutostart: next })
}
onPreviewVideoLoopChange={(next) =>
void actions.updateScene(sid, { settings: { loopVideo: next } })
}
onDarkenSceneChange={(next) => void actions.updateScene(sid, { darkenScene: next })}
onTitleChange={(title) => void actions.updateScene(sid, { title })}
onDescriptionChange={(description) =>
@@ -2322,6 +2326,7 @@ type SceneInspectorProps = {
previewAssetId: AssetId | null;
previewAssetType: 'image' | 'video' | null;
previewVideoAutostart: boolean;
previewVideoLoop: boolean;
previewRotationDeg: 0 | 90 | 180 | 270;
darkenScene: boolean;
previewBusy: boolean;
@@ -2330,6 +2335,7 @@ type SceneInspectorProps = {
audioRefs: SceneAudioRef[];
onAudioRefsChange: (next: SceneAudioRef[]) => void;
onPreviewVideoAutostartChange: (next: boolean) => void;
onPreviewVideoLoopChange: (next: boolean) => void;
onDarkenSceneChange: (next: boolean) => void;
onTitleChange: (v: string) => void;
onDescriptionChange: (v: string) => void;
@@ -2456,6 +2462,7 @@ function SceneInspector({
previewAssetId,
previewAssetType,
previewVideoAutostart,
previewVideoLoop,
previewRotationDeg,
darkenScene,
previewBusy,
@@ -2464,6 +2471,7 @@ function SceneInspector({
audioRefs,
onAudioRefsChange,
onPreviewVideoAutostartChange,
onPreviewVideoLoopChange,
onDarkenSceneChange,
onTitleChange,
onDescriptionChange,
@@ -2578,7 +2586,7 @@ function SceneInspector({
muted
playsInline
autoPlay={previewVideoAutostart}
loop
loop={previewVideoLoop}
preload="metadata"
className={styles.videoCover}
/>
@@ -2595,12 +2603,14 @@ function SceneInspector({
</div>
) : null}
</div>
<div className={styles.actionsRow}>
<div className={styles.actionsRowHalf}>
<Button variant="primary" disabled={previewBusy} onClick={onImportPreview}>
{previewAssetId ? t('scene.change') : t('campaign.upload')}
</Button>
{previewAssetId ? <Button onClick={onClearPreview}>{t('scene.clear')}</Button> : null}
{previewAssetId && previewAssetType === 'video' ? (
{previewAssetId ? <Button onClick={onClearPreview}>{t('scene.clear')}</Button> : <span aria-hidden />}
</div>
{previewAssetId && previewAssetType === 'video' ? (
<div className={styles.actionsRowVideoChecks}>
<label className={styles.checkboxLabel}>
<input
type="checkbox"
@@ -2609,8 +2619,19 @@ function SceneInspector({
/>
<span className={styles.spanSm}>{t('scene.autostart')}</span>
</label>
) : null}
{previewAssetId && previewAssetType === 'image' ? (
<label className={styles.checkboxLabel}>
<input
type="checkbox"
checked={previewVideoLoop}
onChange={(e) => onPreviewVideoLoopChange(e.target.checked)}
/>
<span className={styles.spanSm}>{t('campaign.loop')}</span>
</label>
</div>
) : null}
{previewAssetId && previewAssetType === 'image' ? (
<>
<div className={styles.spacer6} />
<Button
onClick={() => {
const next = ((previewRotationDeg + 90) % 360) as 0 | 90 | 180 | 270;
@@ -2619,10 +2640,6 @@ function SceneInspector({
>
{t('scene.rotate')}
</Button>
) : null}
</div>
{previewAssetId && previewAssetType === 'image' ? (
<>
<div className={styles.spacer6} />
<label className={styles.checkboxLabel}>
<input
@@ -2879,7 +2896,6 @@ function SceneListCard({
</div>
<div className={styles.sceneCardBody}>
<div className={styles.sceneCardHeader}>
{scene.active ? <div className={styles.badgeCurrent}>{t('sceneCard.current')}</div> : null}
<div
ref={titleRef}
className={styles.sceneCardTitle}