-
+
-
{label}
-
{detail}
+
{label}
+
{detail}
{"command" in rest ? (
-
+
{rest.command}
) : null}
@@ -54,7 +54,7 @@ export const PrerequisitesStep = () => (
href={rest.link}
target="_blank"
rel="noopener noreferrer"
- className="mt-1.5 inline-flex items-center gap-1 text-[11px] text-emerald-400 hover:text-emerald-300"
+ className="mt-1 inline-flex items-center gap-1 text-[10px] leading-4 text-amber-300 hover:text-amber-200"
>
{rest.linkLabel ?? "Learn more"}
@@ -65,13 +65,13 @@ export const PrerequisitesStep = () => (
))}
-
+
Need help? Check{" "}
docs.openclaw.ai
{" "}
@@ -80,7 +80,7 @@ export const PrerequisitesStep = () => (
href="https://discord.com/invite/clawd"
target="_blank"
rel="noopener noreferrer"
- className="text-emerald-400/70 hover:text-emerald-300"
+ className="text-amber-300/70 hover:text-amber-200"
>
join Discord
diff --git a/src/features/onboarding/components/WelcomeStep.tsx b/src/features/onboarding/components/WelcomeStep.tsx
index 0710f45..d2e6fe5 100644
--- a/src/features/onboarding/components/WelcomeStep.tsx
+++ b/src/features/onboarding/components/WelcomeStep.tsx
@@ -48,7 +48,7 @@ export const WelcomeStep = () => (
className="rounded-lg border border-white/8 bg-white/[0.03] px-3.5 py-3"
>
-
+
{title}
diff --git a/src/features/onboarding/types.ts b/src/features/onboarding/types.ts
index da0d317..afeac63 100644
--- a/src/features/onboarding/types.ts
+++ b/src/features/onboarding/types.ts
@@ -11,6 +11,7 @@ export type OnboardingStepId =
| "prerequisites"
| "connect"
| "agents"
+ | "company"
| "complete";
export type OnboardingStep = {
@@ -57,6 +58,12 @@ export const ONBOARDING_STEPS: OnboardingStep[] = [
description: "Meet your AI team",
skippable: true,
},
+ {
+ id: "company",
+ title: "Build Your Company",
+ description: "Generate your org structure",
+ skippable: true,
+ },
{
id: "complete",
title: "You're All Set",
diff --git a/src/features/retro-office/RetroOffice3D.tsx b/src/features/retro-office/RetroOffice3D.tsx
index 56e8d73..7a46e72 100644
--- a/src/features/retro-office/RetroOffice3D.tsx
+++ b/src/features/retro-office/RetroOffice3D.tsx
@@ -2295,6 +2295,7 @@ const getAgentInitials = (name: string | null | undefined): string => {
export function RetroOffice3D({
agents,
+ officeCenterSignal = 0,
animationState = null,
readOnly = false,
storageNamespace = "default",
@@ -2365,6 +2366,7 @@ export function RetroOffice3D({
onJukeboxInteract,
}: {
agents: OfficeAgent[];
+ officeCenterSignal?: number;
animationState?: Pick<
OfficeAnimationState,
| "cleaningCues"
@@ -4958,6 +4960,21 @@ export function RetroOffice3D({
? DISTRICT_CAMERA_TARGET
: LOCAL_CAMERA_TARGET;
const cameraZoom = remoteOfficeEnabled ? DISTRICT_CAMERA_ZOOM : 56;
+ const lastOfficeCenterSignalRef = useRef(officeCenterSignal);
+
+ useEffect(() => {
+ cameraPresetRef.current = {
+ pos: CAM_POS,
+ target: cameraTarget,
+ zoom: cameraZoom,
+ };
+ }, [CAM_POS, cameraTarget, cameraZoom]);
+
+ useEffect(() => {
+ if (officeCenterSignal === lastOfficeCenterSignalRef.current) return;
+ lastOfficeCenterSignalRef.current = officeCenterSignal;
+ cameraPresetRef.current = CAMERA_PRESET_MAP.overview;
+ }, [officeCenterSignal]);
return (
diff --git a/src/features/retro-office/objects/agents.tsx b/src/features/retro-office/objects/agents.tsx
index e06839b..ee0ae46 100644
--- a/src/features/retro-office/objects/agents.tsx
+++ b/src/features/retro-office/objects/agents.tsx
@@ -14,6 +14,16 @@ import type {
} from "@/features/retro-office/core/types";
import { AgentModelProps } from "@/features/retro-office/objects/types";
+const MAX_NAMEPLATE_TEXT_LENGTH = 10;
+
+const formatAgentNameplateText = (value: string): string => {
+ const normalized = value.replace(/\s+/g, " ").trim();
+ if (!normalized) return "";
+ if (normalized.length <= MAX_NAMEPLATE_TEXT_LENGTH) return normalized;
+ const [firstName] = normalized.split(" ");
+ return firstName || normalized;
+};
+
export const AgentModel = memo(function AgentModel({
agentId,
name,
@@ -111,21 +121,23 @@ export const AgentModel = memo(function AgentModel({
: workoutStyle === "box"
? 0.04
: 0.02
- : agent.pingPongUntil
- ? 0.08
- : 0;
+ : agent.pingPongUntil
+ ? 0.08
+ : 0;
const bounce =
agent.state === "walking"
? Math.sin(frameValue * WALK_ANIM_SPEED) * 0.04
: isDancing
- ? 0.03 + Math.abs(Math.sin(agent.frame * 0.22 + (agent.phaseOffset ?? 0))) * 0.05
+ ? 0.03 +
+ Math.abs(Math.sin(agent.frame * 0.22 + (agent.phaseOffset ?? 0))) *
+ 0.05
: isWorkout
- ? workoutStyle === "stretch"
- ? 0.012 + Math.abs(workoutPhase) * 0.018
- : workoutStyle === "row"
- ? 0.015 + Math.abs(workoutPhase) * 0.028
- : 0.02 + Math.abs(workoutPhase) * 0.04
- : 0;
+ ? workoutStyle === "stretch"
+ ? 0.012 + Math.abs(workoutPhase) * 0.018
+ : workoutStyle === "row"
+ ? 0.015 + Math.abs(workoutPhase) * 0.028
+ : 0.02 + Math.abs(workoutPhase) * 0.04
+ : 0;
const breathe =
agent.state === "standing" || isWorkout || agent.pingPongUntil
? Math.sin(frameValue * 0.03) * 0.01
@@ -142,8 +154,10 @@ export const AgentModel = memo(function AgentModel({
} else if (agent.state === "walking") {
leftArmRef.current.rotation.x = walkPhase * 0.4;
} else if (isDancing) {
- leftArmRef.current.rotation.x = -0.8 + Math.sin(agent.frame * 0.22) * 0.9;
- leftArmRef.current.rotation.z = -0.45 + Math.cos(agent.frame * 0.16) * 0.18;
+ leftArmRef.current.rotation.x =
+ -0.8 + Math.sin(agent.frame * 0.22) * 0.9;
+ leftArmRef.current.rotation.z =
+ -0.45 + Math.cos(agent.frame * 0.16) * 0.18;
leftArmRef.current.rotation.y = -0.08;
groupRef.current.rotation.z = Math.sin(agent.frame * 0.12) * 0.08;
} else if (isWorkout) {
@@ -199,8 +213,10 @@ export const AgentModel = memo(function AgentModel({
} else if (agent.state === "walking") {
rightArmRef.current.rotation.x = -walkPhase * 0.4;
} else if (isDancing) {
- rightArmRef.current.rotation.x = -0.8 - Math.sin(agent.frame * 0.22) * 0.9;
- rightArmRef.current.rotation.z = 0.45 - Math.cos(agent.frame * 0.16) * 0.18;
+ rightArmRef.current.rotation.x =
+ -0.8 - Math.sin(agent.frame * 0.22) * 0.9;
+ rightArmRef.current.rotation.z =
+ 0.45 - Math.cos(agent.frame * 0.16) * 0.18;
rightArmRef.current.rotation.y = 0.08;
groupRef.current.rotation.z = Math.sin(agent.frame * 0.12) * 0.08;
} else if (isWorkout) {
@@ -252,18 +268,18 @@ export const AgentModel = memo(function AgentModel({
: isDancing
? Math.sin(agent.frame * 0.22 + (agent.phaseOffset ?? 0)) * 0.35
: isWorkout
- ? workoutStyle === "run"
- ? workoutPhase * 0.7
- : workoutStyle === "bike"
- ? workoutPhase * 0.82
- : workoutStyle === "row"
- ? 0.14 + Math.max(0, workoutPhase) * 0.42
- : workoutStyle === "stretch"
- ? -0.2 + Math.abs(workoutPhase) * 0.08
- : workoutStyle === "box"
- ? 0.06 + workoutPhase * 0.14
- : workoutPhase * 0.18
- : 0;
+ ? workoutStyle === "run"
+ ? workoutPhase * 0.7
+ : workoutStyle === "bike"
+ ? workoutPhase * 0.82
+ : workoutStyle === "row"
+ ? 0.14 + Math.max(0, workoutPhase) * 0.42
+ : workoutStyle === "stretch"
+ ? -0.2 + Math.abs(workoutPhase) * 0.08
+ : workoutStyle === "box"
+ ? 0.06 + workoutPhase * 0.14
+ : workoutPhase * 0.18
+ : 0;
}
if (rightLegRef.current) {
rightLegRef.current.rotation.x =
@@ -272,22 +288,25 @@ export const AgentModel = memo(function AgentModel({
: isDancing
? -Math.sin(agent.frame * 0.22 + (agent.phaseOffset ?? 0)) * 0.35
: isWorkout
- ? workoutStyle === "run"
- ? -workoutPhase * 0.7
- : workoutStyle === "bike"
- ? -workoutPhase * 0.82
- : workoutStyle === "row"
- ? 0.14 + Math.max(0, -workoutPhase) * 0.42
- : workoutStyle === "stretch"
- ? -0.12 + Math.abs(workoutPhase) * 0.08
- : workoutStyle === "box"
- ? 0.06 - workoutPhase * 0.14
- : -workoutPhase * 0.18
- : 0;
+ ? workoutStyle === "run"
+ ? -workoutPhase * 0.7
+ : workoutStyle === "bike"
+ ? -workoutPhase * 0.82
+ : workoutStyle === "row"
+ ? 0.14 + Math.max(0, -workoutPhase) * 0.42
+ : workoutStyle === "stretch"
+ ? -0.12 + Math.abs(workoutPhase) * 0.08
+ : workoutStyle === "box"
+ ? 0.06 - workoutPhase * 0.14
+ : -workoutPhase * 0.18
+ : 0;
}
const working =
- agent.state === "sitting" || isWorkout || isDancing || agent.status === "working";
+ agent.state === "sitting" ||
+ isWorkout ||
+ isDancing ||
+ agent.status === "working";
const isError = agent.status === "error";
const isAway = agent.state === "away";
@@ -620,6 +639,9 @@ export const AgentModel = memo(function AgentModel({
: "#8dc4ff"
: "transparent";
const speechBubbleBorderInset = activeSpeechBubble ? 0.03 : 0;
+ const nameplateText = name ? formatAgentNameplateText(name) : "";
+ const nameplateFontSize =
+ nameplateText.length > 9 ? 0.118 : nameplateText.length > 7 ? 0.13 : 0.144;
return (
- {!activeSpeechBubble && name ? (
+ {!activeSpeechBubble && nameplateText ? (
@@ -1061,14 +1083,14 @@ export const AgentModel = memo(function AgentModel({
- {name}
+ {nameplateText}
) : null}
diff --git a/src/features/spotify-jukebox/components/JukeboxPanel.tsx b/src/features/spotify-jukebox/components/JukeboxPanel.tsx
index c084fa9..a0eee37 100644
--- a/src/features/spotify-jukebox/components/JukeboxPanel.tsx
+++ b/src/features/spotify-jukebox/components/JukeboxPanel.tsx
@@ -1,6 +1,7 @@
"use client";
import { useEffect, useRef, useState } from "react";
+import { RunningAvatarLoader } from "@/features/agents/components/RunningAvatarLoader";
import { useJukeboxStore } from "../store";
import {
startSpotifyAuth,
@@ -302,7 +303,7 @@ function PlayerView() {
{isLoadingPlayer && !track ? (
) : track ? (
@@ -374,7 +375,7 @@ function PlayerView() {
/>
{isSearching && (
)}
diff --git a/src/lib/studio/settings.ts b/src/lib/studio/settings.ts
index 8e15666..013b855 100644
--- a/src/lib/studio/settings.ts
+++ b/src/lib/studio/settings.ts
@@ -70,6 +70,13 @@ export type StudioOfficePreference = {
remoteOfficePresenceUrl: string;
remoteOfficeGatewayUrl: string;
remoteOfficeToken: string;
+ companyName: string;
+ companyPrompt: string;
+ companyImprovedBrief: string;
+ companySummary: string;
+ companyGeneratedAt: string | null;
+ companyRoleTitles: string[];
+ companyPlanJson: string;
};
export type StudioOfficePreferencePublic = {
@@ -80,6 +87,13 @@ export type StudioOfficePreferencePublic = {
remoteOfficePresenceUrl: string;
remoteOfficeGatewayUrl: string;
remoteOfficeTokenConfigured: boolean;
+ companyName: string;
+ companyPrompt: string;
+ companyImprovedBrief: string;
+ companySummary: string;
+ companyGeneratedAt: string | null;
+ companyRoleTitles: string[];
+ companyPlanJson: string;
};
export type StudioOfficePreferencePatch = {
@@ -90,6 +104,13 @@ export type StudioOfficePreferencePatch = {
remoteOfficePresenceUrl?: string | null;
remoteOfficeGatewayUrl?: string | null;
remoteOfficeToken?: string | null;
+ companyName?: string | null;
+ companyPrompt?: string | null;
+ companyImprovedBrief?: string | null;
+ companySummary?: string | null;
+ companyGeneratedAt?: string | null;
+ companyRoleTitles?: string[] | null;
+ companyPlanJson?: string | null;
};
export type StudioDeskAssignments = Record