feat: 3d ofis deneyimini ve etkilesimleri gelistir
This commit is contained in:
BIN
web/public/ata-cropped.png
Normal file
BIN
web/public/ata-cropped.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 331 KiB |
@@ -17,6 +17,8 @@ export default function App() {
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [teamView, setTeamView] = useState("board");
|
||||
const [officeAgents, setOfficeAgents] = useState(() => createInitialOfficeAgents());
|
||||
const [selectedOfficeObject, setSelectedOfficeObject] = useState(null);
|
||||
const [dismissedSpeech, setDismissedSpeech] = useState({});
|
||||
const autoStartedRef = useRef(false);
|
||||
|
||||
async function runAction(action) {
|
||||
@@ -59,7 +61,7 @@ export default function App() {
|
||||
...current,
|
||||
[command.agentId]: {
|
||||
...current[command.agentId],
|
||||
zoneId: command.zoneId,
|
||||
targetZoneId: command.zoneId,
|
||||
targetPosition: zone.approachPosition
|
||||
}
|
||||
}));
|
||||
@@ -68,12 +70,88 @@ export default function App() {
|
||||
}
|
||||
|
||||
function handleAgentArrive(agentId, position) {
|
||||
setOfficeAgents((current) => ({
|
||||
...current,
|
||||
[agentId]: {
|
||||
...current[agentId],
|
||||
currentPosition: position
|
||||
let arrivedZoneId = null;
|
||||
|
||||
setOfficeAgents((current) => {
|
||||
arrivedZoneId = current[agentId]?.targetZoneId ?? null;
|
||||
return {
|
||||
...current,
|
||||
[agentId]: {
|
||||
...current[agentId],
|
||||
currentPosition: position,
|
||||
currentZoneId: arrivedZoneId
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
if (String(arrivedZoneId ?? "").endsWith("Desk")) {
|
||||
setSelectedOfficeObject((current) =>
|
||||
current?.type === "agent" && current.id === agentId ? null : current
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function handleOfficeAgentSelect(agentId) {
|
||||
setSelectedOfficeObject(() => ({ type: "agent", id: agentId }));
|
||||
}
|
||||
|
||||
function handleOfficeObjectSelect(type, id) {
|
||||
setSelectedOfficeObject(() => ({ type, id }));
|
||||
}
|
||||
|
||||
function handleOfficeFloorSelect(position) {
|
||||
if (!selectedOfficeObject || selectedOfficeObject.type !== "agent") {
|
||||
return;
|
||||
}
|
||||
|
||||
const agentId = selectedOfficeObject.id;
|
||||
setOfficeAgents((current) => {
|
||||
if (!current[agentId]) {
|
||||
return current;
|
||||
}
|
||||
|
||||
return {
|
||||
...current,
|
||||
[agentId]: {
|
||||
...current[agentId],
|
||||
targetZoneId: null,
|
||||
targetPosition: [position[0], 0, position[2]]
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function handleOfficeZoneSelect(zoneId) {
|
||||
if (!selectedOfficeObject || selectedOfficeObject.type !== "agent") {
|
||||
return;
|
||||
}
|
||||
|
||||
const zone = getZoneById(zoneId);
|
||||
if (!zone) {
|
||||
return;
|
||||
}
|
||||
|
||||
const agentId = selectedOfficeObject.id;
|
||||
setOfficeAgents((current) => {
|
||||
if (!current[agentId]) {
|
||||
return current;
|
||||
}
|
||||
|
||||
return {
|
||||
...current,
|
||||
[agentId]: {
|
||||
...current[agentId],
|
||||
targetZoneId: zoneId,
|
||||
targetPosition: zone.approachPosition
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function handleDismissSpeech(agentId, speechKey) {
|
||||
setDismissedSpeech((current) => ({
|
||||
...current,
|
||||
[agentId]: speechKey
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -110,6 +188,13 @@ export default function App() {
|
||||
onViewChange={setTeamView}
|
||||
officeAgents={officeAgents}
|
||||
onAgentArrive={handleAgentArrive}
|
||||
selectedOfficeObject={selectedOfficeObject}
|
||||
onAgentSelect={handleOfficeAgentSelect}
|
||||
onOfficeObjectSelect={handleOfficeObjectSelect}
|
||||
onFloorSelect={handleOfficeFloorSelect}
|
||||
onZoneSelect={handleOfficeZoneSelect}
|
||||
dismissedSpeech={dismissedSpeech}
|
||||
onDismissSpeech={handleDismissSpeech}
|
||||
/>
|
||||
</div>
|
||||
<div className="console-grid__main">
|
||||
|
||||
@@ -37,9 +37,33 @@ function TeamCard({ member }) {
|
||||
);
|
||||
}
|
||||
|
||||
export default function TeamBoard({ chat, view = "board", onViewChange, officeAgents, onAgentArrive }) {
|
||||
export default function TeamBoard({
|
||||
chat,
|
||||
view = "board",
|
||||
onViewChange,
|
||||
officeAgents,
|
||||
onAgentArrive,
|
||||
selectedOfficeObject,
|
||||
onAgentSelect,
|
||||
onOfficeObjectSelect,
|
||||
onFloorSelect,
|
||||
onZoneSelect,
|
||||
dismissedSpeech,
|
||||
onDismissSpeech
|
||||
}) {
|
||||
const members = parseTeamFeed(chat);
|
||||
const isOfficeView = view === "office";
|
||||
const speechByAgent = {
|
||||
teamLead: members.find((member) => member.name === "Mazlum")?.messages.at(-1)?.text ?? "",
|
||||
frontend: members.find((member) => member.name === "Berkecan")?.messages.at(-1)?.text ?? "",
|
||||
backend: members.find((member) => member.name === "Simsar")?.messages.at(-1)?.text ?? "",
|
||||
uiux: members.find((member) => member.name === "Aybuke")?.messages.at(-1)?.text ?? "",
|
||||
ios: members.find((member) => member.name === "Ive")?.messages.at(-1)?.text ?? "",
|
||||
trainee: members.find((member) => member.name === "Irgatov")?.messages.at(-1)?.text ?? ""
|
||||
};
|
||||
const speechEntries = Object.fromEntries(
|
||||
Object.entries(speechByAgent).map(([agentId, text]) => [agentId, { text, key: `${agentId}:${text}` }])
|
||||
);
|
||||
|
||||
return (
|
||||
<PanelFrame
|
||||
@@ -73,7 +97,19 @@ export default function TeamBoard({ chat, view = "board", onViewChange, officeAg
|
||||
{isOfficeView ? (
|
||||
<div className="team-board__office" aria-label="Office view">
|
||||
<Suspense fallback={<div className="team-board__office-fallback" />}>
|
||||
<OfficeCanvas debug={false} agents={officeAgents} onAgentArrive={onAgentArrive} />
|
||||
<OfficeCanvas
|
||||
debug={false}
|
||||
agents={officeAgents}
|
||||
onAgentArrive={onAgentArrive}
|
||||
speechByAgent={speechEntries}
|
||||
selectedOfficeObject={selectedOfficeObject}
|
||||
onAgentSelect={onAgentSelect}
|
||||
onOfficeObjectSelect={onOfficeObjectSelect}
|
||||
onFloorSelect={onFloorSelect}
|
||||
onZoneSelect={onZoneSelect}
|
||||
dismissedSpeech={dismissedSpeech}
|
||||
onDismissSpeech={onDismissSpeech}
|
||||
/>
|
||||
</Suspense>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import { Html, RoundedBox } from "@react-three/drei";
|
||||
import { Billboard, Html, RoundedBox } from "@react-three/drei";
|
||||
import { useFrame } from "@react-three/fiber";
|
||||
import { Vector3 } from "three";
|
||||
|
||||
@@ -78,10 +78,24 @@ function HairStyle({ style, color }) {
|
||||
);
|
||||
}
|
||||
|
||||
export default function OfficeAgent({ agent, onArrive }) {
|
||||
export default function OfficeAgent({
|
||||
agent,
|
||||
onArrive,
|
||||
selected = false,
|
||||
speechText = "",
|
||||
speechKey = "",
|
||||
onSelect,
|
||||
onDismissSpeech
|
||||
}) {
|
||||
const groupRef = useRef(null);
|
||||
const bodyRef = useRef(null);
|
||||
const lastArrivalKeyRef = useRef("");
|
||||
const targetRef = useRef(new Vector3(...agent.targetPosition));
|
||||
const leftArmRef = useRef(null);
|
||||
const rightArmRef = useRef(null);
|
||||
const leftLegRef = useRef(null);
|
||||
const rightLegRef = useRef(null);
|
||||
const walkCycleRef = useRef(0);
|
||||
const appearance = {
|
||||
skinColor: "#f0c6a9",
|
||||
hairColor: "#171717",
|
||||
@@ -95,7 +109,8 @@ export default function OfficeAgent({ agent, onArrive }) {
|
||||
};
|
||||
|
||||
const isSkirt = appearance.lowerType === "skirt";
|
||||
const isShorts = appearance.lowerType === "shorts";
|
||||
const isMoving = !positionsMatch(agent.currentPosition, agent.targetPosition);
|
||||
const isSeated = !isMoving && String(agent.currentZoneId ?? "").endsWith("Desk");
|
||||
|
||||
useEffect(() => {
|
||||
if (!groupRef.current) {
|
||||
@@ -114,9 +129,63 @@ export default function OfficeAgent({ agent, onArrive }) {
|
||||
return;
|
||||
}
|
||||
|
||||
const speed = Math.min(1, delta * 2.4);
|
||||
const speed = Math.min(1, delta * 1.44);
|
||||
groupRef.current.position.lerp(targetRef.current, speed);
|
||||
|
||||
const directionX = targetRef.current.x - groupRef.current.position.x;
|
||||
const directionZ = targetRef.current.z - groupRef.current.position.z;
|
||||
if (bodyRef.current) {
|
||||
if (isMoving && (Math.abs(directionX) > 0.01 || Math.abs(directionZ) > 0.01)) {
|
||||
const targetYaw = Math.atan2(directionX, directionZ);
|
||||
bodyRef.current.rotation.y += (targetYaw - bodyRef.current.rotation.y) * Math.min(1, delta * 8);
|
||||
} else if (isSeated) {
|
||||
bodyRef.current.rotation.y += (0 - bodyRef.current.rotation.y) * Math.min(1, delta * 8);
|
||||
}
|
||||
}
|
||||
|
||||
if (isMoving) {
|
||||
walkCycleRef.current += delta * 6;
|
||||
const swing = Math.sin(walkCycleRef.current) * 0.45;
|
||||
if (leftArmRef.current) {
|
||||
leftArmRef.current.rotation.x = swing;
|
||||
}
|
||||
if (rightArmRef.current) {
|
||||
rightArmRef.current.rotation.x = -swing;
|
||||
}
|
||||
if (leftLegRef.current) {
|
||||
leftLegRef.current.rotation.x = -swing;
|
||||
}
|
||||
if (rightLegRef.current) {
|
||||
rightLegRef.current.rotation.x = swing;
|
||||
}
|
||||
} else if (isSeated) {
|
||||
if (leftArmRef.current) {
|
||||
leftArmRef.current.rotation.x = -0.18;
|
||||
}
|
||||
if (rightArmRef.current) {
|
||||
rightArmRef.current.rotation.x = -0.18;
|
||||
}
|
||||
if (leftLegRef.current) {
|
||||
leftLegRef.current.rotation.x = Math.PI / 2.7;
|
||||
}
|
||||
if (rightLegRef.current) {
|
||||
rightLegRef.current.rotation.x = Math.PI / 2.7;
|
||||
}
|
||||
} else {
|
||||
if (leftArmRef.current) {
|
||||
leftArmRef.current.rotation.x = 0;
|
||||
}
|
||||
if (rightArmRef.current) {
|
||||
rightArmRef.current.rotation.x = 0;
|
||||
}
|
||||
if (leftLegRef.current) {
|
||||
leftLegRef.current.rotation.x = 0;
|
||||
}
|
||||
if (rightLegRef.current) {
|
||||
rightLegRef.current.rotation.x = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const current = [
|
||||
groupRef.current.position.x,
|
||||
groupRef.current.position.y,
|
||||
@@ -131,111 +200,177 @@ export default function OfficeAgent({ agent, onArrive }) {
|
||||
});
|
||||
|
||||
return (
|
||||
<group ref={groupRef}>
|
||||
<group
|
||||
ref={groupRef}
|
||||
onPointerDown={(event) => {
|
||||
event.stopPropagation();
|
||||
onSelect?.(agent.id);
|
||||
}}
|
||||
>
|
||||
{selected ? (
|
||||
<mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, 0.01, 0]}>
|
||||
<ringGeometry args={[0.36, 0.48, 32]} />
|
||||
<meshBasicMaterial color="#7ff7ff" transparent opacity={0.8} />
|
||||
</mesh>
|
||||
) : null}
|
||||
<group ref={bodyRef}>
|
||||
<HairStyle style={appearance.hairStyle} color={appearance.hairColor} />
|
||||
<mesh castShadow position={[0, 1.55, 0]}>
|
||||
<mesh castShadow position={[0, isSeated ? 1.47 : 1.55, 0]}>
|
||||
<boxGeometry args={[0.42, 0.42, 0.42]} />
|
||||
<meshStandardMaterial color={appearance.skinColor} roughness={0.95} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[-0.08, 1.58, 0.222]}>
|
||||
<mesh castShadow position={[-0.08, isSeated ? 1.5 : 1.58, 0.222]}>
|
||||
<boxGeometry args={[0.08, 0.012, 0.016]} />
|
||||
<meshStandardMaterial color="#101010" roughness={0.4} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[0.08, 1.58, 0.222]}>
|
||||
<mesh castShadow position={[0.08, isSeated ? 1.5 : 1.58, 0.222]}>
|
||||
<boxGeometry args={[0.08, 0.012, 0.016]} />
|
||||
<meshStandardMaterial color="#101010" roughness={0.4} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[-0.08, 1.65, 0.214]}>
|
||||
<mesh castShadow position={[-0.08, isSeated ? 1.57 : 1.65, 0.214]}>
|
||||
<boxGeometry args={[0.1, 0.018, 0.02]} />
|
||||
<meshStandardMaterial color="#222222" roughness={0.7} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[0.08, 1.65, 0.214]}>
|
||||
<mesh castShadow position={[0.08, isSeated ? 1.57 : 1.65, 0.214]}>
|
||||
<boxGeometry args={[0.1, 0.018, 0.02]} />
|
||||
<meshStandardMaterial color="#222222" roughness={0.7} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[0, 1.5, 0.22]} rotation={[Math.PI / 2.4, 0, 0]}>
|
||||
<mesh castShadow position={[0, isSeated ? 1.42 : 1.5, 0.22]} rotation={[Math.PI / 2.4, 0, 0]}>
|
||||
<coneGeometry args={[0.035, 0.13, 4]} />
|
||||
<meshStandardMaterial color="#deaf90" roughness={0.88} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[0, 1.39, 0.222]}>
|
||||
<mesh castShadow position={[0, isSeated ? 1.31 : 1.39, 0.222]}>
|
||||
<boxGeometry args={[0.12, 0.012, 0.016]} />
|
||||
<meshStandardMaterial color="#101010" roughness={0.5} />
|
||||
</mesh>
|
||||
<RoundedBox castShadow receiveShadow position={[0, 1.13, 0]} args={[0.62, 0.58, 0.32]} radius={0.04} smoothness={2}>
|
||||
<RoundedBox castShadow receiveShadow position={[0, isSeated ? 1.02 : 1.13, 0]} args={[0.62, 0.58, 0.32]} radius={0.04} smoothness={2}>
|
||||
<meshStandardMaterial color={appearance.shirtColor} roughness={0.82} />
|
||||
</RoundedBox>
|
||||
{appearance.tieColor ? (
|
||||
<mesh castShadow position={[0, 1.12, 0.17]}>
|
||||
<mesh castShadow position={[0, isSeated ? 1.01 : 1.12, 0.17]}>
|
||||
<boxGeometry args={[0.11, 0.48, 0.03]} />
|
||||
<meshStandardMaterial color={appearance.tieColor} roughness={0.64} />
|
||||
</mesh>
|
||||
) : null}
|
||||
<mesh castShadow position={[-0.44, 1.15, 0]}>
|
||||
<boxGeometry args={[0.22, 0.16, 0.18]} />
|
||||
<meshStandardMaterial color={appearance.shirtColor} roughness={0.82} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[0.44, 1.15, 0]}>
|
||||
<boxGeometry args={[0.22, 0.16, 0.18]} />
|
||||
<meshStandardMaterial color={appearance.shirtColor} roughness={0.82} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[-0.44, 0.93, 0]}>
|
||||
<boxGeometry args={[0.14, 0.28, 0.14]} />
|
||||
<meshStandardMaterial color={appearance.skinColor} roughness={0.92} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[0.44, 0.93, 0]}>
|
||||
<boxGeometry args={[0.14, 0.28, 0.14]} />
|
||||
<meshStandardMaterial color={appearance.skinColor} roughness={0.92} />
|
||||
</mesh>
|
||||
<group ref={leftArmRef} position={[-0.44, isSeated ? 1.12 : 1.24, 0]}>
|
||||
<mesh castShadow position={[0, -0.09, 0]}>
|
||||
<boxGeometry args={[0.22, 0.16, 0.18]} />
|
||||
<meshStandardMaterial color={appearance.shirtColor} roughness={0.82} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[0, -0.31, 0]}>
|
||||
<boxGeometry args={[0.14, 0.28, 0.14]} />
|
||||
<meshStandardMaterial color={appearance.skinColor} roughness={0.92} />
|
||||
</mesh>
|
||||
</group>
|
||||
<group ref={rightArmRef} position={[0.44, isSeated ? 1.12 : 1.24, 0]}>
|
||||
<mesh castShadow position={[0, -0.09, 0]}>
|
||||
<boxGeometry args={[0.22, 0.16, 0.18]} />
|
||||
<meshStandardMaterial color={appearance.shirtColor} roughness={0.82} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[0, -0.31, 0]}>
|
||||
<boxGeometry args={[0.14, 0.28, 0.14]} />
|
||||
<meshStandardMaterial color={appearance.skinColor} roughness={0.92} />
|
||||
</mesh>
|
||||
</group>
|
||||
{isSkirt ? (
|
||||
<mesh castShadow position={[0, 0.76, 0]}>
|
||||
<mesh castShadow position={[0, isSeated ? 0.7 : 0.76, 0]}>
|
||||
<boxGeometry args={[0.54, 0.22, 0.34]} />
|
||||
<meshStandardMaterial color={appearance.lowerColor} roughness={0.86} />
|
||||
</mesh>
|
||||
) : (
|
||||
<mesh castShadow position={[0, 0.76, 0]}>
|
||||
<mesh castShadow position={[0, isSeated ? 0.72 : 0.76, 0]}>
|
||||
<boxGeometry args={[0.5, 0.22, 0.3]} />
|
||||
<meshStandardMaterial color={appearance.lowerColor} roughness={0.86} />
|
||||
</mesh>
|
||||
)}
|
||||
<mesh castShadow position={[-0.14, 0.41, 0]}>
|
||||
<boxGeometry args={[0.14, 0.48, 0.14]} />
|
||||
<meshStandardMaterial color={appearance.skinColor} roughness={0.9} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[0.14, 0.41, 0]}>
|
||||
<boxGeometry args={[0.14, 0.48, 0.14]} />
|
||||
<meshStandardMaterial color={appearance.skinColor} roughness={0.9} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[-0.14, 0.62, 0]}>
|
||||
<boxGeometry args={[0.16, 0.08, 0.16]} />
|
||||
<meshStandardMaterial color={appearance.skinColor} roughness={0.9} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[0.14, 0.62, 0]}>
|
||||
<boxGeometry args={[0.16, 0.08, 0.16]} />
|
||||
<meshStandardMaterial color={appearance.skinColor} roughness={0.9} />
|
||||
</mesh>
|
||||
{isShorts ? (
|
||||
{isSeated ? (
|
||||
<>
|
||||
<mesh castShadow position={[-0.14, 0.58, 0]}>
|
||||
<boxGeometry args={[0.16, 0.08, 0.16]} />
|
||||
<meshStandardMaterial color={appearance.skinColor} roughness={0.9} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[0.14, 0.58, 0]}>
|
||||
<boxGeometry args={[0.16, 0.08, 0.16]} />
|
||||
<meshStandardMaterial color={appearance.skinColor} roughness={0.9} />
|
||||
</mesh>
|
||||
<group ref={leftLegRef} position={[-0.14, 0.73, 0.03]}>
|
||||
<mesh castShadow position={[0, -0.07, 0.11]}>
|
||||
<boxGeometry args={[0.14, 0.14, 0.34]} />
|
||||
<meshStandardMaterial color={appearance.lowerColor} roughness={0.88} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[0, -0.39, 0.24]}>
|
||||
<boxGeometry args={[0.14, 0.42, 0.14]} />
|
||||
<meshStandardMaterial color={isSkirt ? appearance.skinColor : appearance.lowerColor} roughness={0.9} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[0, -0.65, 0.32]}>
|
||||
<boxGeometry args={[0.16, 0.12, 0.24]} />
|
||||
<meshStandardMaterial color={appearance.shoeColor} roughness={0.9} />
|
||||
</mesh>
|
||||
</group>
|
||||
<group ref={rightLegRef} position={[0.14, 0.73, 0.03]}>
|
||||
<mesh castShadow position={[0, -0.07, 0.11]}>
|
||||
<boxGeometry args={[0.14, 0.14, 0.34]} />
|
||||
<meshStandardMaterial color={appearance.lowerColor} roughness={0.88} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[0, -0.39, 0.24]}>
|
||||
<boxGeometry args={[0.14, 0.42, 0.14]} />
|
||||
<meshStandardMaterial color={isSkirt ? appearance.skinColor : appearance.lowerColor} roughness={0.9} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[0, -0.65, 0.32]}>
|
||||
<boxGeometry args={[0.16, 0.12, 0.24]} />
|
||||
<meshStandardMaterial color={appearance.shoeColor} roughness={0.9} />
|
||||
</mesh>
|
||||
</group>
|
||||
</>
|
||||
) : null}
|
||||
<mesh castShadow position={[-0.14, 0.11, 0.02]}>
|
||||
<boxGeometry args={[0.16, 0.12, 0.24]} />
|
||||
<meshStandardMaterial color={appearance.shoeColor} roughness={0.9} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[0.14, 0.11, 0.02]}>
|
||||
<boxGeometry args={[0.16, 0.12, 0.24]} />
|
||||
<meshStandardMaterial color={appearance.shoeColor} roughness={0.9} />
|
||||
</mesh>
|
||||
<Html position={[0, 2.08, 0]} center distanceFactor={10}>
|
||||
) : (
|
||||
<>
|
||||
<group ref={leftLegRef} position={[-0.14, 0.66, 0]}>
|
||||
<mesh castShadow position={[0, -0.25, 0]}>
|
||||
<boxGeometry args={[0.14, 0.48, 0.14]} />
|
||||
<meshStandardMaterial color={isSkirt ? appearance.skinColor : appearance.lowerColor} roughness={0.9} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[0, -0.04, 0]}>
|
||||
<boxGeometry args={[0.16, 0.08, 0.16]} />
|
||||
<meshStandardMaterial color={isSkirt ? appearance.skinColor : appearance.lowerColor} roughness={0.9} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[0, -0.55, 0.02]}>
|
||||
<boxGeometry args={[0.16, 0.12, 0.24]} />
|
||||
<meshStandardMaterial color={appearance.shoeColor} roughness={0.9} />
|
||||
</mesh>
|
||||
</group>
|
||||
<group ref={rightLegRef} position={[0.14, 0.66, 0]}>
|
||||
<mesh castShadow position={[0, -0.25, 0]}>
|
||||
<boxGeometry args={[0.14, 0.48, 0.14]} />
|
||||
<meshStandardMaterial color={isSkirt ? appearance.skinColor : appearance.lowerColor} roughness={0.9} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[0, -0.04, 0]}>
|
||||
<boxGeometry args={[0.16, 0.08, 0.16]} />
|
||||
<meshStandardMaterial color={isSkirt ? appearance.skinColor : appearance.lowerColor} roughness={0.9} />
|
||||
</mesh>
|
||||
<mesh castShadow position={[0, -0.55, 0.02]}>
|
||||
<boxGeometry args={[0.16, 0.12, 0.24]} />
|
||||
<meshStandardMaterial color={appearance.shoeColor} roughness={0.9} />
|
||||
</mesh>
|
||||
</group>
|
||||
</>
|
||||
)}
|
||||
<Html position={[0, 2.02, 0]} center distanceFactor={10}>
|
||||
<div className="office-agent__label">{agent.name}</div>
|
||||
</Html>
|
||||
</group>
|
||||
{speechText ? (
|
||||
<Billboard position={[0, 3.03, 0]} follow lockX={false} lockY={false} lockZ={false}>
|
||||
<Html center distanceFactor={10}>
|
||||
<button
|
||||
type="button"
|
||||
className="office-agent__bubble"
|
||||
onWheel={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.currentTarget.scrollTop += event.deltaY;
|
||||
}}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onDismissSpeech?.(agent.id, speechKey);
|
||||
}}
|
||||
>
|
||||
{speechText}
|
||||
</button>
|
||||
</Html>
|
||||
</Billboard>
|
||||
) : null}
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,11 +7,9 @@ export default function OfficeCamera() {
|
||||
enableDamping
|
||||
dampingFactor={0.08}
|
||||
minDistance={12}
|
||||
maxDistance={20}
|
||||
maxDistance={30}
|
||||
minPolarAngle={0.7}
|
||||
maxPolarAngle={1.02}
|
||||
minAzimuthAngle={-0.85}
|
||||
maxAzimuthAngle={0.85}
|
||||
target={[0.5, 0.8, 0.2]}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,11 +1,35 @@
|
||||
import { Canvas } from "@react-three/fiber";
|
||||
import OfficeScene from "./OfficeScene.jsx";
|
||||
|
||||
export default function OfficeCanvas({ debug = false, agents = {}, onAgentArrive }) {
|
||||
export default function OfficeCanvas({
|
||||
debug = false,
|
||||
agents = {},
|
||||
onAgentArrive,
|
||||
speechByAgent = {},
|
||||
selectedOfficeObject,
|
||||
onAgentSelect,
|
||||
onOfficeObjectSelect,
|
||||
onFloorSelect,
|
||||
onZoneSelect,
|
||||
dismissedSpeech,
|
||||
onDismissSpeech
|
||||
}) {
|
||||
return (
|
||||
<div className="office-canvas">
|
||||
<Canvas shadows dpr={[1, 1.5]} camera={{ position: [12, 12, 12], fov: 38 }}>
|
||||
<OfficeScene debug={debug} agents={agents} onAgentArrive={onAgentArrive} />
|
||||
<OfficeScene
|
||||
debug={debug}
|
||||
agents={agents}
|
||||
onAgentArrive={onAgentArrive}
|
||||
speechByAgent={speechByAgent}
|
||||
selectedOfficeObject={selectedOfficeObject}
|
||||
onAgentSelect={onAgentSelect}
|
||||
onOfficeObjectSelect={onOfficeObjectSelect}
|
||||
onFloorSelect={onFloorSelect}
|
||||
onZoneSelect={onZoneSelect}
|
||||
dismissedSpeech={dismissedSpeech}
|
||||
onDismissSpeech={onDismissSpeech}
|
||||
/>
|
||||
</Canvas>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
export default function OfficeFloor() {
|
||||
export default function OfficeFloor({ onSelect }) {
|
||||
return (
|
||||
<mesh receiveShadow rotation={[-Math.PI / 2, 0, 0]} position={[0, -0.001, 0]}>
|
||||
<mesh
|
||||
receiveShadow
|
||||
rotation={[-Math.PI / 2, 0, 0]}
|
||||
position={[0, -0.001, 0]}
|
||||
onPointerDown={(event) => {
|
||||
event.stopPropagation();
|
||||
onSelect?.(event.point.toArray());
|
||||
}}
|
||||
>
|
||||
<planeGeometry args={[24, 18]} />
|
||||
<meshStandardMaterial color="#efe0c5" roughness={0.95} metalness={0.02} />
|
||||
</mesh>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { OFFICE_ZONES } from "./officeZones.js";
|
||||
function AccentWallPanels() {
|
||||
const steveTexture = useLoader(TextureLoader, "/steve.png");
|
||||
const monaTexture = useLoader(TextureLoader, "/mona.png");
|
||||
const ataTexture = useLoader(TextureLoader, "/ata-cropped.png");
|
||||
const panels = [
|
||||
{ position: [-8.2, 1.8, -8.78], color: "#495b8a" },
|
||||
{ position: [0, 2.15, -8.76], color: "#6b4c78" },
|
||||
@@ -34,12 +35,15 @@ function AccentWallPanels() {
|
||||
toneMapped={false}
|
||||
/>
|
||||
</mesh>
|
||||
{[panels[2]].map((panel, index) => (
|
||||
<mesh key={index} position={panel.position}>
|
||||
<boxGeometry args={[1, 0.55, 0.05]} />
|
||||
<meshStandardMaterial color={panel.color} roughness={0.55} />
|
||||
</mesh>
|
||||
))}
|
||||
<mesh position={[6.2, 1.47, -8.73]}>
|
||||
<planeGeometry args={[1.62, 2.184]} />
|
||||
<meshBasicMaterial
|
||||
map={ataTexture}
|
||||
transparent
|
||||
alphaTest={0.05}
|
||||
toneMapped={false}
|
||||
/>
|
||||
</mesh>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
@@ -59,18 +63,24 @@ function FloorDecals() {
|
||||
);
|
||||
}
|
||||
|
||||
export default function OfficeLayout() {
|
||||
export default function OfficeLayout({ selectedOfficeObject, onOfficeObjectSelect, onZoneSelect }) {
|
||||
const coffeeMachineSelected = selectedOfficeObject?.type === "object" && selectedOfficeObject?.id === "coffeeMachine";
|
||||
|
||||
return (
|
||||
<group>
|
||||
<FloorDecals />
|
||||
<AccentWallPanels />
|
||||
<Desk position={OFFICE_ZONES[0].position} nameplateColor="#d6c15d" />
|
||||
<Desk position={OFFICE_ZONES[1].position} nameplateColor="#49c2f1" />
|
||||
<Desk position={OFFICE_ZONES[2].position} nameplateColor="#7bd87a" />
|
||||
<Desk position={OFFICE_ZONES[3].position} nameplateColor="#f48cc7" />
|
||||
<Desk position={OFFICE_ZONES[4].position} nameplateColor="#8aa4ff" />
|
||||
<Desk position={OFFICE_ZONES[0].position} nameplateColor="#d6c15d" onSelect={() => onZoneSelect?.("teamLeadDesk")} />
|
||||
<Desk position={OFFICE_ZONES[1].position} nameplateColor="#49c2f1" onSelect={() => onZoneSelect?.("frontendDesk")} />
|
||||
<Desk position={OFFICE_ZONES[2].position} nameplateColor="#7bd87a" onSelect={() => onZoneSelect?.("backendDesk")} />
|
||||
<Desk position={OFFICE_ZONES[3].position} nameplateColor="#f48cc7" onSelect={() => onZoneSelect?.("uiuxDesk")} />
|
||||
<Desk position={OFFICE_ZONES[4].position} nameplateColor="#8aa4ff" onSelect={() => onZoneSelect?.("iosDesk")} />
|
||||
<MeetingTable position={OFFICE_ZONES[5].position} />
|
||||
<CoffeeMachine position={OFFICE_ZONES[6].position} />
|
||||
<CoffeeMachine
|
||||
position={OFFICE_ZONES[6].position}
|
||||
selected={coffeeMachineSelected}
|
||||
onSelect={() => onOfficeObjectSelect?.("object", "coffeeMachine")}
|
||||
/>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
export default function OfficeLighting() {
|
||||
return (
|
||||
<>
|
||||
<ambientLight intensity={1.6} />
|
||||
<hemisphereLight args={["#fff4de", "#17110d", 1.15]} />
|
||||
<ambientLight intensity={2.1} />
|
||||
<hemisphereLight args={["#fff8ea", "#2a2018", 1.45]} />
|
||||
<directionalLight
|
||||
castShadow
|
||||
intensity={1.9}
|
||||
intensity={2.45}
|
||||
position={[7, 12, 5]}
|
||||
shadow-mapSize-width={1024}
|
||||
shadow-mapSize-height={1024}
|
||||
|
||||
@@ -7,17 +7,42 @@ import OfficeDebugLabels from "./OfficeDebugLabels.jsx";
|
||||
import OfficeAgent from "./OfficeAgent.jsx";
|
||||
import { OFFICE_ZONES } from "./officeZones.js";
|
||||
|
||||
export default function OfficeScene({ debug = false, agents = {}, onAgentArrive }) {
|
||||
export default function OfficeScene({
|
||||
debug = false,
|
||||
agents = {},
|
||||
onAgentArrive,
|
||||
speechByAgent = {},
|
||||
selectedOfficeObject,
|
||||
onAgentSelect,
|
||||
onOfficeObjectSelect,
|
||||
onFloorSelect,
|
||||
onZoneSelect,
|
||||
dismissedSpeech = {},
|
||||
onDismissSpeech
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<color attach="background" args={["#120d0a"]} />
|
||||
<fog attach="fog" args={["#120d0a", 18, 34]} />
|
||||
<color attach="background" args={["#1a130f"]} />
|
||||
<fog attach="fog" args={["#1a130f", 42, 72]} />
|
||||
<OfficeLighting />
|
||||
<OfficeFloor />
|
||||
<OfficeFloor onSelect={onFloorSelect} />
|
||||
<OfficeWalls />
|
||||
<OfficeLayout />
|
||||
<OfficeLayout
|
||||
selectedOfficeObject={selectedOfficeObject}
|
||||
onOfficeObjectSelect={onOfficeObjectSelect}
|
||||
onZoneSelect={onZoneSelect}
|
||||
/>
|
||||
{Object.values(agents).map((agent) => (
|
||||
<OfficeAgent key={agent.id} agent={agent} onArrive={onAgentArrive} />
|
||||
<OfficeAgent
|
||||
key={agent.id}
|
||||
agent={agent}
|
||||
onArrive={onAgentArrive}
|
||||
selected={selectedOfficeObject?.type === "agent" && selectedOfficeObject?.id === agent.id}
|
||||
speechText={dismissedSpeech[agent.id] === speechByAgent[agent.id]?.key ? "" : (speechByAgent[agent.id]?.text ?? "")}
|
||||
speechKey={speechByAgent[agent.id]?.key ?? ""}
|
||||
onSelect={onAgentSelect}
|
||||
onDismissSpeech={onDismissSpeech}
|
||||
/>
|
||||
))}
|
||||
<OfficeCamera />
|
||||
{debug ? <OfficeDebugLabels zones={OFFICE_ZONES} /> : null}
|
||||
|
||||
@@ -15,7 +15,8 @@ function createAgent({
|
||||
name,
|
||||
role,
|
||||
color,
|
||||
zoneId,
|
||||
currentZoneId: zoneId,
|
||||
targetZoneId: zoneId,
|
||||
currentPosition: zone?.approachPosition ?? [0, 0, 0],
|
||||
targetPosition: zone?.approachPosition ?? [0, 0, 0],
|
||||
appearance
|
||||
@@ -38,7 +39,7 @@ export function createInitialOfficeAgents() {
|
||||
tieColor: "#ba1d2f",
|
||||
lowerColor: "#75777d",
|
||||
shoeColor: "#111111",
|
||||
lowerType: "shorts"
|
||||
lowerType: "pants"
|
||||
}
|
||||
}),
|
||||
frontend: createAgent({
|
||||
|
||||
@@ -28,15 +28,27 @@ function findMatch(normalizedText, map) {
|
||||
return Object.entries(map).find(([, aliases]) => aliases.some((alias) => normalizedText.includes(alias)))?.[0] ?? null;
|
||||
}
|
||||
|
||||
function inferZoneFromIntent(normalizedText) {
|
||||
if (/\b(kahve al|kahve alsin|kahve alsın|kahve getir|kahve makinesi|kahve makinasi|kahve alanina|kahve alanına)\b/.test(normalizedText)) {
|
||||
return "coffeeMachine";
|
||||
}
|
||||
|
||||
if (/\b(toplantiya git|toplanti masasi|toplantiya|toplanti alanina|toplanti alanina|masaya gec|masaya geç)\b/.test(normalizedText)) {
|
||||
return "meetingTable";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function parseOfficeCommand(prompt) {
|
||||
const normalized = normalizeText(prompt);
|
||||
|
||||
if (!/\b(git|gidebilir|gitsin|gidin|toplan|yuru|yurusun|gitmesi)\b/.test(normalized)) {
|
||||
if (!/\b(git|gidebilir|gitsin|gidin|toplan|yuru|yurusun|gitmesi|al|alsin|alsın|getir)\b/.test(normalized)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const agentId = findMatch(normalized, AGENT_ALIASES);
|
||||
const zoneId = findMatch(normalized, ZONE_ALIASES);
|
||||
const zoneId = findMatch(normalized, ZONE_ALIASES) ?? inferZoneFromIntent(normalized);
|
||||
|
||||
if (!agentId || !zoneId) {
|
||||
return null;
|
||||
|
||||
@@ -5,7 +5,7 @@ export const OFFICE_ZONES = [
|
||||
role: "Team Lead",
|
||||
type: "desk",
|
||||
position: [0, 0, -4.2],
|
||||
approachPosition: [0, 0, -2.9]
|
||||
approachPosition: [0, 0, -5.25]
|
||||
},
|
||||
{
|
||||
id: "frontendDesk",
|
||||
@@ -13,7 +13,7 @@ export const OFFICE_ZONES = [
|
||||
role: "Frontend Dev",
|
||||
type: "desk",
|
||||
position: [-3.8, 0, -0.9],
|
||||
approachPosition: [-2.7, 0, 0.3]
|
||||
approachPosition: [-3.8, 0, -1.95]
|
||||
},
|
||||
{
|
||||
id: "backendDesk",
|
||||
@@ -21,7 +21,7 @@ export const OFFICE_ZONES = [
|
||||
role: "Backend Dev",
|
||||
type: "desk",
|
||||
position: [3.8, 0, -0.9],
|
||||
approachPosition: [2.7, 0, 0.3]
|
||||
approachPosition: [3.8, 0, -1.95]
|
||||
},
|
||||
{
|
||||
id: "uiuxDesk",
|
||||
@@ -29,7 +29,7 @@ export const OFFICE_ZONES = [
|
||||
role: "UI/UX Designer",
|
||||
type: "desk",
|
||||
position: [-3.8, 0, 2.9],
|
||||
approachPosition: [-2.7, 0, 1.8]
|
||||
approachPosition: [-3.8, 0, 1.85]
|
||||
},
|
||||
{
|
||||
id: "iosDesk",
|
||||
@@ -37,7 +37,7 @@ export const OFFICE_ZONES = [
|
||||
role: "iOS Dev",
|
||||
type: "desk",
|
||||
position: [3.8, 0, 2.9],
|
||||
approachPosition: [2.7, 0, 1.8]
|
||||
approachPosition: [3.8, 0, 1.85]
|
||||
},
|
||||
{
|
||||
id: "meetingTable",
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Html } from "@react-three/drei";
|
||||
|
||||
function Cup({ position = [0, 0, 0], scale = 1, sleeve = false }) {
|
||||
return (
|
||||
<group position={position} scale={scale}>
|
||||
@@ -57,9 +59,30 @@ function FrontCups() {
|
||||
);
|
||||
}
|
||||
|
||||
export default function CoffeeMachine({ position = [0, 0, 0] }) {
|
||||
export default function CoffeeMachine({ position = [0, 0, 0], selected = false, onSelect }) {
|
||||
return (
|
||||
<group position={position} scale={0.82}>
|
||||
<mesh
|
||||
position={[0, 0.7, 0.12]}
|
||||
onPointerDown={(event) => {
|
||||
event.stopPropagation();
|
||||
onSelect?.();
|
||||
}}
|
||||
>
|
||||
<boxGeometry args={[2.3, 1.7, 1.8]} />
|
||||
<meshBasicMaterial transparent opacity={0} />
|
||||
</mesh>
|
||||
{selected ? (
|
||||
<>
|
||||
<mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, 0.015, 0.12]}>
|
||||
<ringGeometry args={[1.15, 1.35, 40]} />
|
||||
<meshBasicMaterial color="#8fe7ff" transparent opacity={0.75} />
|
||||
</mesh>
|
||||
<Html position={[0, 2.15, 0.12]} center sprite>
|
||||
<div className="office-object__label">Kahve Makinesi</div>
|
||||
</Html>
|
||||
</>
|
||||
) : null}
|
||||
<mesh receiveShadow position={[0, 0.22, 0.12]}>
|
||||
<boxGeometry args={[2.25, 0.36, 1.65]} />
|
||||
<meshStandardMaterial color="#6e4024" roughness={0.98} />
|
||||
|
||||
@@ -3,11 +3,21 @@ import { TextureLoader } from "three";
|
||||
import { DoubleSide } from "three";
|
||||
import Chair from "./Chair.jsx";
|
||||
|
||||
export default function Desk({ position = [0, 0, 0], nameplateColor = "#52b6ff" }) {
|
||||
export default function Desk({ position = [0, 0, 0], nameplateColor = "#52b6ff", onSelect }) {
|
||||
const appleLogoTexture = useLoader(TextureLoader, "/apple-logo.png");
|
||||
|
||||
return (
|
||||
<group position={position}>
|
||||
<group
|
||||
position={position}
|
||||
onPointerDown={(event) => {
|
||||
event.stopPropagation();
|
||||
onSelect?.();
|
||||
}}
|
||||
>
|
||||
<mesh position={[0, 0.72, -0.08]}>
|
||||
<boxGeometry args={[2.1, 1.5, 2.1]} />
|
||||
<meshBasicMaterial transparent opacity={0} />
|
||||
</mesh>
|
||||
<mesh castShadow receiveShadow position={[0, 0.78, 0]}>
|
||||
<boxGeometry args={[1.9, 0.16, 1.15]} />
|
||||
<meshStandardMaterial color="#b78256" roughness={0.86} />
|
||||
@@ -34,8 +44,8 @@ export default function Desk({ position = [0, 0, 0], nameplateColor = "#52b6ff"
|
||||
<boxGeometry args={[0.62, 0.03, 0.4]} />
|
||||
<meshStandardMaterial color="#c8ced6" roughness={0.28} metalness={0.58} />
|
||||
</mesh>
|
||||
<mesh position={[0, 0.016, 0.2]} rotation={[-Math.PI / 2, 0, Math.PI]}>
|
||||
<planeGeometry args={[0.114, 0.114]} />
|
||||
<mesh position={[0, 0.022, 0.2]} rotation={[-Math.PI / 2, 0, Math.PI]}>
|
||||
<planeGeometry args={[0.18, 0.18]} />
|
||||
<meshBasicMaterial
|
||||
map={appleLogoTexture}
|
||||
transparent
|
||||
|
||||
@@ -237,7 +237,7 @@
|
||||
|
||||
.console-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.55fr) minmax(320px, 0.9fr);
|
||||
grid-template-columns: minmax(0, 2.15fr) minmax(220px, 0.45fr);
|
||||
gap: 16px;
|
||||
align-items: start;
|
||||
}
|
||||
@@ -279,6 +279,36 @@
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.chat-panel .panel-frame__header {
|
||||
padding: 12px 12px 0;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.chat-panel .panel-frame__eyebrow {
|
||||
margin-bottom: 6px;
|
||||
font-size: 0.48rem;
|
||||
letter-spacing: 0.14em;
|
||||
}
|
||||
|
||||
.chat-panel .panel-frame__title {
|
||||
font-size: 0.96rem;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.chat-panel .session-toolbar {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.chat-panel .pixel-button {
|
||||
min-width: 94px;
|
||||
}
|
||||
|
||||
.chat-panel .pixel-button span {
|
||||
padding: 9px 8px;
|
||||
font-size: 0.44rem;
|
||||
letter-spacing: 0.09em;
|
||||
}
|
||||
|
||||
.chat-panel .panel-frame__body {
|
||||
height: 62vh;
|
||||
min-height: 62vh;
|
||||
@@ -289,7 +319,7 @@
|
||||
min-height: 420px;
|
||||
max-height: 62vh;
|
||||
overflow: auto;
|
||||
padding: 16px;
|
||||
padding: 12px;
|
||||
border: 2px solid var(--border-mid);
|
||||
background:
|
||||
linear-gradient(180deg, rgba(4, 8, 5, 0.88) 0%, rgba(8, 12, 9, 0.92) 100%);
|
||||
@@ -299,6 +329,13 @@
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
max-height: none;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.chat-panel .chat-stream::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.chat-stream pre,
|
||||
@@ -311,7 +348,8 @@
|
||||
|
||||
.chat-stream pre {
|
||||
color: var(--accent-green);
|
||||
line-height: 1.55;
|
||||
font-size: 0.84rem;
|
||||
line-height: 1.42;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
@@ -322,27 +360,29 @@
|
||||
text-align: center;
|
||||
color: var(--text-dim);
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.62rem;
|
||||
line-height: 1.9;
|
||||
font-size: 0.52rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.empty-state--small {
|
||||
font-size: 0.56rem;
|
||||
font-size: 0.5rem;
|
||||
}
|
||||
|
||||
.prompt-composer {
|
||||
padding: 14px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.prompt-composer textarea {
|
||||
width: 100%;
|
||||
min-height: 110px;
|
||||
min-height: 96px;
|
||||
resize: vertical;
|
||||
border: 3px solid var(--border-dark);
|
||||
box-shadow: inset 0 0 0 2px var(--border-mid);
|
||||
background: rgba(5, 10, 6, 0.95);
|
||||
color: var(--text-main);
|
||||
padding: 14px;
|
||||
padding: 10px;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.35;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@@ -357,7 +397,7 @@
|
||||
align-items: center;
|
||||
margin-top: 12px;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.78rem;
|
||||
font-size: 0.68rem;
|
||||
}
|
||||
|
||||
.team-board {
|
||||
@@ -444,17 +484,63 @@
|
||||
}
|
||||
|
||||
.office-agent__label {
|
||||
padding: 6px 10px;
|
||||
padding: 6.61px 13.23px;
|
||||
border: 1px solid rgba(99, 245, 255, 0.45);
|
||||
background: rgba(6, 11, 8, 0.92);
|
||||
color: var(--text-main);
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.58rem;
|
||||
font-size: 0.8745rem;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.office-agent__bubble,
|
||||
.office-object__label {
|
||||
max-width: 504px;
|
||||
padding: 6px 8px;
|
||||
border: 1px solid rgba(165, 172, 176, 0.72);
|
||||
background: rgba(8, 8, 8, 0.94);
|
||||
color: #f3f6f3;
|
||||
font-family: var(--font-body);
|
||||
font-size: 1.314144rem;
|
||||
line-height: 1.28;
|
||||
white-space: pre-wrap;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.22);
|
||||
pointer-events: auto;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.office-agent__bubble {
|
||||
appearance: none;
|
||||
text-align: left;
|
||||
width: 504px;
|
||||
min-height: 155px;
|
||||
max-height: 155px;
|
||||
padding: 2px 8px 6px;
|
||||
display: block;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
white-space: pre-wrap;
|
||||
align-content: start;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.office-agent__bubble::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.office-object__label {
|
||||
max-width: none;
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.58rem;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.office-scene__svg {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
Reference in New Issue
Block a user