Redesign virtual office with desk workstations and spatial layout

Each agent sits at a workstation with a monitor, status-colored glow for
active agents, and a rich tooltip showing name, role, and current task.
Departments render as labeled rooms with subtle borders. The monitor
lights up when the agent has a running task run, giving a real sense of
who is working and who is idle.
This commit is contained in:
ZacharyZhang-NY committed 2026-06-30 21:42:50 -04:00
1 parent 453e47274a
commit 3a3c5b7ca3
2 files changed
+133 -119

No files matched your search

@@ -180,6 +180,7 @@ export function Shell({
<VirtualOffice
agents={snapshot.agents}
tasks={snapshot.tasks}
taskRuns={snapshot.taskRuns}
presence={snapshot.chatPresence}
orgId={organization.id}
/>
@@ -1,11 +1,12 @@
import { useState } from "react";
import type { AgentRecord, TaskRecord } from "../../../shared/domain";
import { useState, type CSSProperties } from "react";
import type { AgentRecord, TaskRecord, TaskRunRecord } from "../../../shared/domain";
import type { ChatPresenceRecord } from "../../../shared/chat-domain";
import { Avatar } from "./ui";
interface VirtualOfficeProps {
agents: AgentRecord[];
tasks: TaskRecord[];
taskRuns: TaskRunRecord[];
presence: ChatPresenceRecord[];
orgId: string;
}
@@ -15,29 +16,22 @@ type PresenceStatus = "active" | "thinking" | "busy" | "idle";
interface AgentDesk {
agent: AgentRecord;
status: PresenceStatus;
statusText: string;
currentTask: string | null;
runningNow: boolean;
}
function resolveDesks(props: VirtualOfficeProps): AgentDesk[] {
const { agents, tasks, presence, orgId } = props;
const orgAgents = agents.filter((a) => a.orgId === orgId);
return orgAgents.map((agent) => {
const { agents, tasks, taskRuns, presence, orgId } = props;
return agents.filter((a) => a.orgId === orgId).map((agent) => {
const p = presence.find((r) => r.principalType === "agent" && r.principalId === agent.id);
const activeTask = tasks.find(
(t) => t.orgId === orgId && t.assigneeAgentId === agent.id && t.status === "in_progress",
);
const status = mapStatus(p?.status);
return {
agent,
status,
statusText: p?.statusText ?? "",
currentTask: activeTask?.title ?? null,
};
const activeTask = tasks.find((t) => t.orgId === orgId && t.assigneeAgentId === agent.id && t.status === "in_progress");
const runningNow = taskRuns.some((r) => r.agentId === agent.id && r.state === "running");
return { agent, status: mapStatus(p?.status, runningNow), currentTask: activeTask?.title ?? null, runningNow };
});
}
function mapStatus(raw: string | undefined): PresenceStatus {
function mapStatus(raw: string | undefined, running: boolean): PresenceStatus {
if (running) return "active";
if (!raw) return "idle";
if (raw === "thinking") return "thinking";
if (raw === "waiting_approval" || raw === "busy") return "busy";
@@ -45,25 +39,39 @@ function mapStatus(raw: string | undefined): PresenceStatus {
return "idle";
}
const STATUS_RING: Record<PresenceStatus, string> = {
active: "var(--green)",
thinking: "var(--accent)",
busy: "var(--amber)",
idle: "var(--text-4)",
const STATUS_COLOR: Record<PresenceStatus, string> = {
active: "#34d399",
thinking: "#818cf8",
busy: "#fbbf24",
idle: "rgba(120,120,120,0.35)",
};
const DEPT_FLOOR: Record<string, { label: string; color: string }> = {
engineering: { label: "Engineering", color: "var(--accent-soft)" },
product: { label: "Product", color: "rgba(168, 130, 255, 0.08)" },
design: { label: "Design", color: "rgba(255, 170, 130, 0.08)" },
marketing: { label: "Marketing", color: "rgba(130, 200, 255, 0.08)" },
operations: { label: "Operations", color: "rgba(180, 180, 180, 0.06)" },
const DESK_BG: CSSProperties = {
background: "rgba(255,255,255,0.03)",
border: "1px solid rgba(255,255,255,0.06)",
borderRadius: 10,
padding: "10px 6px 8px",
display: "flex",
flexDirection: "column",
alignItems: "center",
position: "relative",
cursor: "default",
transition: "border-color 0.15s, box-shadow 0.15s",
};
function floorForDept(dept: string): { label: string; color: string } {
const key = dept.toLowerCase();
return DEPT_FLOOR[key] ?? { label: dept, color: "rgba(180, 180, 180, 0.06)" };
}
const MONITOR: CSSProperties = {
width: 28,
height: 18,
borderRadius: 3,
border: "1.5px solid rgba(255,255,255,0.12)",
marginBottom: 2,
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: 8,
position: "relative",
overflow: "hidden",
};
export function VirtualOffice(props: VirtualOfficeProps) {
const desks = resolveDesks(props);
@@ -76,127 +84,132 @@ export function VirtualOffice(props: VirtualOfficeProps) {
departments.get(dept)!.push(desk);
}
if (desks.length === 0) {
return <div style={{ padding: 24, color: "var(--text-3)", fontSize: 13 }}>No agents yet</div>;
}
if (desks.length === 0) return null;
return (
<div style={{ padding: "12px 8px", overflowY: "auto", height: "100%" }}>
{[...departments.entries()].map(([dept, deptDesks]) => {
const floor = floorForDept(dept);
return (
<div key={dept} style={{ marginBottom: 16 }}>
<div style={{
fontSize: 10,
fontWeight: 600,
textTransform: "uppercase",
letterSpacing: "0.05em",
color: "var(--text-3)",
padding: "0 8px 6px",
}}>
{floor.label}
</div>
<div style={{
background: floor.color,
borderRadius: 10,
padding: 8,
display: "grid",
gridTemplateColumns: "repeat(auto-fill, 64px)",
gap: 4,
justifyContent: "start",
}}>
{deptDesks.map((desk) => (
<DeskCell
key={desk.agent.id}
desk={desk}
hovered={hoveredId === desk.agent.id}
onHover={setHoveredId}
/>
))}
</div>
</div>
);
})}
<div style={{ padding: "12px 10px", overflowY: "auto", height: "100%" }}>
{[...departments.entries()].map(([dept, deptDesks]) => (
<Room key={dept} label={dept} desks={deptDesks} hoveredId={hoveredId} onHover={setHoveredId} />
))}
</div>
);
}
function DeskCell({
desk,
hovered,
onHover,
}: {
desk: AgentDesk;
hovered: boolean;
function Room({ label, desks, hoveredId, onHover }: {
label: string;
desks: AgentDesk[];
hoveredId: string | null;
onHover: (id: string | null) => void;
}) {
const ring = STATUS_RING[desk.status];
const isWorking = desk.status === "active" || desk.status === "thinking";
return (
<div style={{ marginBottom: 14 }}>
<div style={{
fontSize: 10,
fontWeight: 600,
textTransform: "uppercase",
letterSpacing: "0.06em",
color: "var(--text-4)",
padding: "0 4px 5px",
}}>
{label}
</div>
<div style={{
border: "1px solid rgba(255,255,255,0.05)",
borderRadius: 12,
padding: 6,
background: "rgba(255,255,255,0.015)",
display: "grid",
gridTemplateColumns: "repeat(auto-fill, 72px)",
gap: 4,
justifyContent: "start",
}}>
{desks.map((desk) => (
<Workstation key={desk.agent.id} desk={desk} hovered={hoveredId === desk.agent.id} onHover={onHover} />
))}
</div>
</div>
);
}
function Workstation({ desk, hovered, onHover }: { desk: AgentDesk; hovered: boolean; onHover: (id: string | null) => void }) {
const color = STATUS_COLOR[desk.status];
const glowing = desk.status === "active" || desk.status === "thinking";
return (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
padding: "6px 2px",
borderRadius: 8,
cursor: "default",
position: "relative",
transition: "background 0.15s",
background: hovered ? "rgba(255,255,255,0.06)" : "transparent",
...DESK_BG,
borderColor: hovered ? "rgba(255,255,255,0.14)" : undefined,
boxShadow: glowing ? `0 0 12px -4px ${color}` : undefined,
}}
onMouseEnter={() => onHover(desk.agent.id)}
onMouseLeave={() => onHover(null)}
>
<div style={{ ...MONITOR, borderColor: glowing ? color : undefined }}>
{glowing && <div style={{ width: "100%", height: "100%", background: `${color}22` }} />}
</div>
<div style={{ width: 6, height: 3, background: "rgba(255,255,255,0.08)", borderRadius: "0 0 2px 2px", marginBottom: 4 }} />
<div style={{ position: "relative" }}>
<Avatar name={desk.agent.name} size="lg" bot />
<Avatar name={desk.agent.name} size="sm" bot />
<div style={{
position: "absolute",
bottom: -1,
right: -1,
width: 10,
height: 10,
right: -2,
width: 7,
height: 7,
borderRadius: "50%",
background: ring,
border: "2px solid var(--panel)",
boxShadow: isWorking ? `0 0 4px ${ring}` : undefined,
background: color,
border: "1.5px solid var(--bg)",
boxShadow: glowing ? `0 0 5px ${color}` : undefined,
}} />
</div>
<div style={{
fontSize: 10,
color: "var(--text-2)",
marginTop: 4,
fontSize: 9,
color: "var(--text-3)",
marginTop: 3,
textAlign: "center",
lineHeight: 1.2,
maxWidth: 60,
lineHeight: 1.15,
maxWidth: 64,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
}}>
{desk.agent.name.split(" ")[0]}
</div>
{hovered && desk.currentTask && (
{hovered && (
<Tooltip name={desk.agent.name} role={desk.agent.role} task={desk.currentTask} status={desk.status} />
)}
</div>
);
}
function Tooltip({ name, role, task, status }: { name: string; role: string; task: string | null; status: PresenceStatus }) {
return (
<div style={{
position: "absolute",
bottom: "calc(100% + 6px)",
left: "50%",
transform: "translateX(-50%)",
background: "var(--panel)",
border: "1px solid var(--border)",
borderRadius: 8,
padding: "6px 10px",
zIndex: 30,
boxShadow: "0 4px 16px rgba(0,0,0,0.18)",
pointerEvents: "none",
minWidth: 120,
maxWidth: 200,
}}>
<div style={{ fontSize: 12, fontWeight: 560, color: "var(--text-1)", marginBottom: 1 }}>{name}</div>
<div style={{ fontSize: 10, color: "var(--text-3)", marginBottom: task ? 4 : 0 }}>{role}</div>
{task && (
<div style={{
position: "absolute",
bottom: "100%",
left: "50%",
transform: "translateX(-50%)",
background: "var(--panel)",
border: "1px solid var(--border)",
borderRadius: 6,
padding: "4px 8px",
fontSize: 11,
color: "var(--text-1)",
whiteSpace: "nowrap",
maxWidth: 200,
fontSize: 10,
color: STATUS_COLOR[status],
overflow: "hidden",
textOverflow: "ellipsis",
zIndex: 20,
boxShadow: "0 2px 8px rgba(0,0,0,0.12)",
marginBottom: 4,
pointerEvents: "none",
whiteSpace: "nowrap",
}}>
{desk.currentTask}
{task}
</div>
)}
</div>