Files
claw3d/src/features/agents/components/RunningAvatarLoader.tsx
T
Luke The Dev a953c5fda6 feat: add company builder wizard with AI-powered org generation (#73)
* feat: add company builder wizard with AI-powered org generation

Adds a new "Build Your Company" step to the onboarding wizard that lets
users describe their business and generates a full agent org structure
using OpenClaw's AI. Includes company plan generation, role deduplication,
agent bootstrap with main-agent reuse, org chart preview, confetti on
success, CSS voxel running-avatar loader, amber theme unification, and
best-effort SSH workspace cleanup.

Made-with: Cursor

* fix: resolve lint errors in CompanyBuilderModal

Replace setState-in-effect pattern with a direct callback, escape
apostrophes in JSX text, and derive org chart hover state without
side effects.

Made-with: Cursor

---------

Co-authored-by: iamlukethedev <lucas.guilherme@smartwayslfl.com>
2026-03-27 12:59:44 -05:00

224 lines
6.3 KiB
TypeScript

"use client";
import type { CSSProperties } from "react";
type RunningAvatarLoaderProps = {
size?: number;
trackWidth?: number;
label?: string;
className?: string;
labelClassName?: string;
inline?: boolean;
};
const S = 4;
const box = (
w: number,
h: number,
color: string,
extra?: CSSProperties,
): CSSProperties => ({
position: "absolute",
width: w * S,
height: h * S,
background: color,
borderRadius: 1,
...extra,
});
export function RunningAvatarLoader({
label,
className = "",
labelClassName = "",
inline = false,
}: RunningAvatarLoaderProps) {
const charW = 14 * S;
const charH = 22 * S;
const totalH = charH + 8;
return (
<div
className={`flex ${inline ? "items-center gap-2" : "flex-col items-center gap-3"} ${className}`}
>
<div
className="relative"
style={{ width: charW + 16, height: totalH } as CSSProperties}
>
{/* Shadow. */}
<div
className="ra-shadow absolute"
style={{
left: "50%",
bottom: 0,
width: 10 * S,
height: 2 * S,
marginLeft: -5 * S,
borderRadius: "50%",
background: "rgba(0,0,0,0.2)",
}}
/>
{/* Character root — bounces. */}
<div
className="ra-bounce absolute"
style={{
left: "50%",
bottom: 2 * S,
marginLeft: (-7 * S),
width: charW,
height: charH,
}}
>
{/* Left leg. */}
<div
className="ra-leg-l"
style={{
position: "absolute",
left: 2 * S,
bottom: 0,
width: 3 * S,
transformOrigin: "50% 0",
}}
>
{/* Shorts. */}
<div style={box(3, 3, "#64748b", { top: 0, left: 0 })} />
{/* Skin. */}
<div style={box(2, 2, "#f5c9a5", { top: 3 * S, left: 0.5 * S })} />
{/* Shoe. */}
<div style={box(3, 2, "#f8fafc", { top: 5 * S, left: 0 })} />
</div>
{/* Right leg. */}
<div
className="ra-leg-r"
style={{
position: "absolute",
left: 8 * S,
bottom: 0,
width: 3 * S,
transformOrigin: "50% 0",
}}
>
<div style={box(3, 3, "#64748b", { top: 0, left: 0 })} />
<div style={box(2, 2, "#f5c9a5", { top: 3 * S, left: 0.5 * S })} />
<div style={box(3, 2, "#f8fafc", { top: 5 * S, left: 0 })} />
</div>
{/* Torso (yellow shirt). */}
<div style={box(10, 5, "#eab308", { left: 2 * S, bottom: 7 * S })} />
{/* Left arm. */}
<div
className="ra-arm-l"
style={{
position: "absolute",
left: 0,
bottom: 8 * S,
width: 2 * S,
transformOrigin: "50% 0",
}}
>
<div style={box(2, 4, "#eab308", { top: 0, left: 0 })} />
<div style={box(2, 2, "#f5c9a5", { top: 4 * S, left: 0 })} />
</div>
{/* Right arm. */}
<div
className="ra-arm-r"
style={{
position: "absolute",
left: 12 * S,
bottom: 8 * S,
width: 2 * S,
transformOrigin: "50% 0",
}}
>
<div style={box(2, 4, "#eab308", { top: 0, left: 0 })} />
<div style={box(2, 2, "#f5c9a5", { top: 4 * S, left: 0 })} />
</div>
{/* Neck. */}
<div style={box(3, 1, "#f5c9a5", { left: 5 * S, bottom: 12 * S })} />
{/* Head. */}
<div style={box(7, 5, "#f5c9a5", { left: 3 * S, bottom: 13 * S })} />
{/* Eyes. */}
<div style={box(1, 1, "#1e293b", { left: 4.5 * S, bottom: 15.5 * S })} />
<div style={box(1, 1, "#1e293b", { left: 8 * S, bottom: 15.5 * S })} />
{/* Mouth. */}
<div style={box(2, 0.5, "#ef4444", { left: 5.5 * S, bottom: 14 * S })} />
{/* Hair. */}
<div style={box(7, 1, "#111827", { left: 3 * S, bottom: 18 * S })} />
{/* Hat brim. */}
<div style={box(9, 1.5, "#fcd34d", { left: 2 * S, bottom: 19 * S })} />
{/* Hat top. */}
<div style={box(5, 1.5, "#0f172a", { left: 4 * S, bottom: 20.5 * S })} />
</div>
<style>{`
@keyframes ra-bounce-kf {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-${2 * S}px); }
}
@keyframes ra-shadow-kf {
0%, 100% { transform: scaleX(1); opacity: 0.2; }
50% { transform: scaleX(0.65); opacity: 0.12; }
}
@keyframes ra-leg-l-kf {
0% { transform: rotate(-25deg); }
50% { transform: rotate(25deg); }
100% { transform: rotate(-25deg); }
}
@keyframes ra-leg-r-kf {
0% { transform: rotate(25deg); }
50% { transform: rotate(-25deg); }
100% { transform: rotate(25deg); }
}
@keyframes ra-arm-l-kf {
0% { transform: rotate(30deg); }
50% { transform: rotate(-30deg); }
100% { transform: rotate(30deg); }
}
@keyframes ra-arm-r-kf {
0% { transform: rotate(-30deg); }
50% { transform: rotate(30deg); }
100% { transform: rotate(-30deg); }
}
.ra-bounce {
animation: ra-bounce-kf 0.32s ease-in-out infinite;
}
.ra-shadow {
animation: ra-shadow-kf 0.32s ease-in-out infinite;
}
.ra-leg-l {
animation: ra-leg-l-kf 0.32s ease-in-out infinite;
}
.ra-leg-r {
animation: ra-leg-r-kf 0.32s ease-in-out infinite;
}
.ra-arm-l {
animation: ra-arm-l-kf 0.32s ease-in-out infinite;
}
.ra-arm-r {
animation: ra-arm-r-kf 0.32s ease-in-out infinite;
}
`}</style>
</div>
{label ? (
<p
className={`${inline ? "" : "text-center"} font-mono text-[11px] tracking-[0.08em] text-white/55 ${labelClassName}`}
>
{label}
</p>
) : null}
</div>
);
}