// @ts-nocheck
// ============================================================================
// MOTION GRAPHICS · OBSIDIAN ORBIT — Scene 08 / CREATE EPISODE (13s, 390f)
//
// "Keynote object-worship". ONE hero artifact: a DASHBOARD slab. A thick glass
// slab whose face is a project dashboard — a glowing "+ Create Episode" button
// gets clicked (a ripple fires), a name field TYPES a title, then a back-end
// directory SKELETON self-assembles row by row on the face. Instant scaffolding,
// keynote calm. The WORLD orbits (never the camera) in slow hold->ease arcs;
// spec Callouts billboard on drawn leader lines at waypoints ("+ CREATE EPISODE",
// "AUTO-SCAFFOLD"); the angle-tracked sheen rakes the turn; a gentle settle on
// the payoff as the tree finishes locking in.
//
// Built ENTIRELY on the Plinth module (mirrors ObsidianOrbit.tsx's contract) +
// the shared Chronixel engine. CSS-3D only, deterministic, no WebGL.
// ============================================================================
import React from "react";
import { AbsoluteFill, useCurrentFrame, useVideoConfig } from "remotion";
import {
Plinth,
OrbitWorld,
Extrude,
Callout,
DustMotes,
} from "../../plinth/plinth";
import { C, withAlpha, ACCENT_GRAD } from "../../chronixel/theme";
import { FONT, MONO } from "../../chronixel/fonts";
import {
beat,
clamp01,
mix,
osc,
enter,
springAt,
EASE,
SPRING,
} from "../../chronixel/motion";
import { Shockwave, GlowPulse, LightSweep } from "../../chronixel/fx";
import { TypeOn } from "../../chronixel/viz/text";
/* ----------------------------------------------------------------------------
A small mono eyebrow tag pinned top-left over the void — frames the beat.
-------------------------------------------------------------------------- */
const Eyebrow: React.FC<{ at: number; children: React.ReactNode }> = ({
at,
children,
}) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
return (
{children}
);
};
/* ============================================================================
CREATE EPISODE. Hero = the DASHBOARD artifact. A glass slab whose face is a
project dashboard: a glowing "+ Create Episode" button is clicked (ripple),
a name field types a title, then a directory skeleton self-assembles. The
world orbits in, callouts fire, and the scaffold locks in on the payoff.
========================================================================== */
export const MgOrbitScene08CreateEpisode: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const t = frame / fps;
// --- beat timeline (seconds) -------------------------------------------
const CLICK = 2.4; // the "+ Create Episode" button is pressed (ripple)
const TYPE = 3.0; // the name field begins typing the title
const SCAFFOLD = 5.6; // the directory skeleton starts self-assembling
const LOCK = 9.0; // the scaffold finishes — a calm settle payoff
// --- face choreography -------------------------------------------------
const dashIn = beat(frame, fps, 0.8, 2.0, EASE.out); // dashboard fades up
const btnGlow = 0.5 + 0.5 * osc(t, 1.4); // the create button breathes
const pressDip = 1 - 0.06 * clamp01(1 - Math.abs(t - CLICK) / 0.18); // squash
const ripple = clamp01((t - CLICK) / 0.7); // click ripple expands
const rippleLife = clamp01(1 - (t - CLICK) / 0.7);
// the name field appears right after the click, then the title types in.
const fieldP = beat(frame, fps, CLICK + 0.15, CLICK + 0.6, EASE.backOut);
// the directory tree rows assemble one at a time after SCAFFOLD.
const tree = [
{ label: "episode-08/", depth: 0 },
{ label: "scenes/", depth: 1 },
{ label: "scene-01.tsx", depth: 2 },
{ label: "scene-02.tsx", depth: 2 },
{ label: "assets/", depth: 1 },
{ label: "render.config.ts", depth: 1 },
];
// --- the lock-in: a gentle, weighty settle on the payoff ----------------
const stamp = springAt(frame, fps, LOCK, SPRING.HEAVY); // 0->1 settle
const stampScale = mix(1.04, 1, clamp01(stamp)); // a soft, calm seat
const flash = clamp01(1 - (t - LOCK) / 0.5) * (t >= LOCK ? 1 : 0);
const riser = beat(frame, fps, LOCK - 1.4, LOCK, EASE.out); // build into apex
const lockRing = clamp01(stamp);
const ringPulse = 0.5 + 0.5 * osc(t, 3);
// hold->ease orbit: shows the button face on the way in, turns to reveal the
// scaffolding, then a final short arc that settles to centre (0°) at the lock.
const legs = [
{ arc: -18, hold: 0.7, travel: 2.4 }, // ease left to read the button
{ arc: 26, hold: 1.6, travel: 2.8 }, // swing across as it types/scaffolds
{ arc: -8, hold: 0.6, travel: 1.8 }, // lands ~0° near LOCK — settle
];
const HeroFace = (
{/* dashboard chrome — title bar */}
chronixel · dashboard
new project
{/* main body: left = create + name field, right = scaffold tree */}
{/* LEFT — the glowing "+ Create Episode" button + the name field */}
{/* the create button (with click ripple) */}
+
Create Episode
{/* click ripple emanating from the press point */}
{t >= CLICK && (
)}
{/* the name field that types the episode title */}
{/* RIGHT — the back-end directory skeleton self-assembling */}
scaffolding
{tree.map((row, i) => {
const rp = beat(
frame,
fps,
SCAFFOLD + i * 0.28,
SCAFFOLD + i * 0.28 + 0.5,
EASE.backOut,
);
const isDir = row.label.endsWith("/");
return (
{/* node glyph: folder vs file */}
{row.label}
);
})}
{/* lock ring that snaps tight as the scaffold finishes (calm settle) */}
);
return (
One Click To Scaffold
{/* riser glow swelling behind the hero into the settle */}
{/* a soft flash + shockwave the instant the button is clicked */}
{/* a gentle settle flash as the scaffold locks in on the payoff */}
{HeroFace}
{/* billboarded spec callouts firing at orbit waypoints */}
{/* flat masked mirror sibling — re-renders the hero, simplified & matte */}
{HeroFace}
);
};
export default MgOrbitScene08CreateEpisode;