// @ts-nocheck
import React, { type ReactNode, Suspense, useRef, useMemo } from "react";
import { useFrame } from "@react-three/fiber";
import {
Environment as DreiEnvironment,
OrbitControls as DreiOrbitControls,
PerspectiveCamera as DreiPerspectiveCamera,
Text as DreiText,
Text3D as DreiText3D,
Center as DreiCenter,
Gltf,
Splat as DreiSplat,
Sparkles as DreiSparkles,
Stars as DreiStars,
Sky as DreiSky,
Cloud as DreiCloud,
Clouds as DreiClouds,
Float as DreiFloat,
ContactShadows as DreiContactShadows,
MeshTransmissionMaterial,
MeshDistortMaterial,
MeshReflectorMaterial,
MeshPortalMaterial as DreiMeshPortalMaterial,
Html as DreiHtml,
RoundedBox as DreiRoundedBox,
Backdrop as DreiBackdrop,
CameraShake as DreiCameraShake,
} from "@react-three/drei";
// @ts-ignore
import * as postprocessing from "@react-three/postprocessing";
import * as THREE from "three";
import type { BaseComponentProps } from "@json-render/react";
import type { ThreeProps } from "./catalog";
// =============================================================================
// Helpers
// =============================================================================
type Vec3 = [number, number, number];
const DEFAULT_POS: Vec3 = [0, 0, 0];
const DEFAULT_ROT: Vec3 = [0, 0, 0];
const DEFAULT_SCALE: Vec3 = [1, 1, 1];
function pos(v: Vec3 | null | undefined): Vec3 {
return v ?? DEFAULT_POS;
}
function rot(v: Vec3 | null | undefined): Vec3 {
return v ?? DEFAULT_ROT;
}
function scl(v: Vec3 | null | undefined): Vec3 {
return v ?? DEFAULT_SCALE;
}
function MaterialComponent({
material,
}: {
material: ThreeProps<"Box">["material"] | null | undefined;
}) {
if (!material) return ;
return (
);
}
// =============================================================================
// Component Implementations
// =============================================================================
/**
* React Three Fiber component implementations for json-render.
*
* Pass to `defineRegistry()` from `@json-render/react` to create a
* component registry for rendering JSON specs as 3D scenes.
*
* @example
* ```ts
* import { defineRegistry } from "@json-render/react";
* import { threeComponents } from "@json-render/react-three-fiber";
*
* const { registry } = defineRegistry(catalog, {
* components: { ...threeComponents },
* });
* ```
*/
export const threeComponents = {
// ── Primitives ─────────────────────────────────────────────────────────
Box: ({ props }: BaseComponentProps>) => (
),
Sphere: ({ props }: BaseComponentProps>) => (
),
Cylinder: ({ props }: BaseComponentProps>) => (
),
Cone: ({ props }: BaseComponentProps>) => (
),
Torus: ({ props }: BaseComponentProps>) => (
),
Plane: ({
props,
children,
}: BaseComponentProps> & { children?: ReactNode }) => (
{children ?? }
),
Capsule: ({ props }: BaseComponentProps>) => (
),
// ── Lights ─────────────────────────────────────────────────────────────
AmbientLight: ({ props }: BaseComponentProps>) => (
),
DirectionalLight: ({
props,
}: BaseComponentProps>) => (
),
PointLight: ({ props }: BaseComponentProps>) => (
),
SpotLight: ({ props }: BaseComponentProps>) => (
),
// ── Container ──────────────────────────────────────────────────────────
Group: ({
props,
children,
}: BaseComponentProps> & { children?: ReactNode }) => (
{children}
),
// ── Model ──────────────────────────────────────────────────────────────
Model: ({ props }: BaseComponentProps>) => (
),
// ── Environment ────────────────────────────────────────────────────────
Environment: ({ props }: BaseComponentProps>) => (
),
Fog: ({ props }: BaseComponentProps>) => (
),
GridHelper: ({ props }: BaseComponentProps>) => (
),
// ── Text ───────────────────────────────────────────────────────────────
Text3D: ({ props }: BaseComponentProps>) => (
{props.text}
),
ExtrudedText: ({ props }: BaseComponentProps>) => {
const font =
props.font ??
"https://cdn.jsdelivr.net/npm/three/examples/fonts/helvetiker_regular.typeface.json";
const inner = (
{props.text}
);
return (
{(props.centered ?? true) ? {inner} : inner}
);
},
// ── Effects / Atmosphere ──────────────────────────────────────────────
Sparkles: ({ props }: BaseComponentProps>) => (
),
Stars: ({ props }: BaseComponentProps>) => (
),
Sky: ({ props }: BaseComponentProps>) => (
),
Cloud: ({ props }: BaseComponentProps>) => (
),
// ── Special Materials ───────────────────────────────────────────────
GlassSphere: ({ props }: BaseComponentProps>) => (
),
GlassBox: ({ props }: BaseComponentProps>) => (
),
DistortSphere: ({
props,
}: BaseComponentProps>) => (
),
// ── Extended Geometry ───────────────────────────────────────────────
TorusKnot: ({ props }: BaseComponentProps>) => (
),
RoundedBox: ({ props }: BaseComponentProps>) => (
),
// ── Shadows / Staging ───────────────────────────────────────────────
ContactShadows: ({
props,
}: BaseComponentProps>) => (
),
Float: ({
props,
children,
}: BaseComponentProps> & { children?: ReactNode }) => (
{children}
),
ReflectorPlane: ({
props,
}: BaseComponentProps>) => (
),
Backdrop: ({
props,
children,
}: BaseComponentProps> & { children?: ReactNode }) => (
{children}
),
// ── Crazy / Magic ─────────────────────────────────────────────────────
MeshPortalMaterial: ({
props,
children,
}: BaseComponentProps> & {
children?: ReactNode;
}) => (
{children}
),
HtmlLabel: ({ props }: BaseComponentProps>) => (
{props.text}
),
// ── Post-Processing ────────────────────────────────────────────────────
EffectComposer: ({
props,
children,
}: BaseComponentProps> & {
children?: ReactNode;
}) => {
if (props.enabled === false) return null;
return (
{children}
);
},
Bloom: ({ props }: BaseComponentProps>) => (
),
Glitch: ({ props }: BaseComponentProps>) => {
const delay = props.delay ? new THREE.Vector2(...props.delay) : undefined;
const duration = props.duration
? new THREE.Vector2(...props.duration)
: undefined;
const strength = props.strength
? new THREE.Vector2(...props.strength)
: undefined;
return (
);
},
Vignette: ({ props }: BaseComponentProps>) => (
),
// ── Animation Wrappers ──────────────────────────────────────────────
WarpTunnel: (() => {
function TunnelRing({
index,
ringCount,
radius,
length,
speed,
tubeRadius,
color,
}: {
index: number;
ringCount: number;
radius: number;
length: number;
speed: number;
tubeRadius: number;
color: THREE.Color;
}) {
const meshRef = useRef(null);
const spacing = length / ringCount;
useFrame((state) => {
if (!meshRef.current) return;
const t = state.clock.elapsedTime;
const z =
((((-index * spacing + t * speed) % length) + length) % length) -
length;
meshRef.current.position.z = z;
const distFactor = 1 - Math.abs(z) / length;
const s = 0.3 + distFactor * 0.7;
meshRef.current.scale.set(s, s, s);
});
return (
);
}
return ({ props }: BaseComponentProps>) => {
const ringCount = props.ringCount ?? 80;
const radius = props.radius ?? 3;
const length = props.length ?? 40;
const speed = props.speed ?? 8;
const tubeRadius = props.tubeRadius ?? 0.02;
const color1 = props.color1 ?? "#00ffff";
const color2 = props.color2 ?? "#ff00ff";
const colors = useMemo(() => {
const c1 = new THREE.Color(color1);
const c2 = new THREE.Color(color2);
return Array.from({ length: ringCount }, (_, i) =>
new THREE.Color().lerpColors(c1, c2, i / ringCount),
);
}, [ringCount, color1, color2]);
return (
{colors.map((color, i) => (
))}
);
};
})(),
Spin: ({
props,
children,
}: BaseComponentProps> & { children?: ReactNode }) => {
const ref = useRef(null);
const speed = props.speed ?? 1;
const axis = props.axis ?? "y";
useFrame((_, delta) => {
if (!ref.current) return;
if (axis === "x") ref.current.rotation.x += delta * speed;
else if (axis === "z") ref.current.rotation.z += delta * speed;
else ref.current.rotation.y += delta * speed;
});
return (
{children}
);
},
Orbit: ({
props,
children,
}: BaseComponentProps> & { children?: ReactNode }) => {
const ref = useRef(null);
const speed = props.speed ?? 1;
const radius = props.radius ?? 3;
const tilt = props.tilt ?? 0;
const baseY = props.position?.[1] ?? 0;
useFrame((state) => {
if (!ref.current) return;
const t = state.clock.elapsedTime * speed;
ref.current.position.x = Math.cos(t) * radius;
ref.current.position.z = Math.sin(t) * radius;
ref.current.position.y = baseY + Math.sin(t * 0.7) * tilt;
});
return {children};
},
Pulse: ({
props,
children,
}: BaseComponentProps> & { children?: ReactNode }) => {
const ref = useRef(null);
const speed = props.speed ?? 1;
const min = props.min ?? 0.8;
const max = props.max ?? 1.2;
useFrame((state) => {
if (!ref.current) return;
const t = (Math.sin(state.clock.elapsedTime * speed) + 1) / 2;
const s = min + t * (max - min);
ref.current.scale.setScalar(s);
});
return (
{children}
);
},
CameraShake: ({ props }: BaseComponentProps>) => (
),
// ── Camera / Controls ──────────────────────────────────────────────
PerspectiveCamera: ({
props,
}: BaseComponentProps>) => (
),
OrbitControls: ({
props,
}: BaseComponentProps>) => (
),
// ── Gaussian Splatting ──────────────────────────────────────────────
GaussianSplat: ({
props,
}: BaseComponentProps>) => (
),
};