feat(ui): 자매 프로필 사진 통합
- OfficeFloor: 4자매 책상에 SisterAvatar (60px gradient frame) - SubTaskTree: 노드 row 에 작은 SisterAvatar (20px) 표시 - SubTaskDetailDrawer: 헤더에 큰 SisterAvatar (40px) + role badge 함께 이미 있는 백엔드 /api/sisters/:name/avatar 엔드포인트 재활용 (SSH 로 ~/.openclaw/avatar.png 가져오는 AvatarService)
This commit is contained in:
@@ -1 +1 @@
|
||||
1775811234
|
||||
1775812327
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import styled, { keyframes } from 'styled-components';
|
||||
import type { RailsSubTaskNode, RailsPipelineSummary } from '@/lib/useRailsSocket';
|
||||
import SisterAvatar from '@/components/common/SisterAvatar';
|
||||
|
||||
const SISTERS = [
|
||||
{ key: 'harang', label: '하랑', role: 'Planner', color: '#3b82f6', accent: '#60a5fa', x: 0, y: 0 },
|
||||
@@ -114,17 +115,15 @@ const SisterBadge = styled.div<{ $color: string }>`
|
||||
gap: 12px;
|
||||
`;
|
||||
|
||||
const Avatar = styled.div<{ $color: string; $running: boolean }>`
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 14px;
|
||||
background: linear-gradient(135deg, ${({ $color }) => $color}, ${({ $color }) => `${$color}80`});
|
||||
const AvatarFrame = styled.div<{ $color: string; $running: boolean }>`
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
padding: 3px;
|
||||
background: linear-gradient(135deg, ${({ $color }) => $color}, ${({ $color }) => `${$color}60`});
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
flex-shrink: 0;
|
||||
box-shadow: ${({ $color, $running }) =>
|
||||
$running ? `0 4px 16px ${$color}60` : `0 2px 8px ${$color}30`};
|
||||
@@ -309,9 +308,9 @@ export default function OfficeFloor({
|
||||
>
|
||||
<DeskHead>
|
||||
<SisterBadge $color={sister.color}>
|
||||
<Avatar $color={sister.color} $running={running}>
|
||||
{sister.label[0]}
|
||||
</Avatar>
|
||||
<AvatarFrame $color={sister.color} $running={running}>
|
||||
<SisterAvatar name={sister.key} size={54} />
|
||||
</AvatarFrame>
|
||||
<NameBlock>
|
||||
<Name>{sister.label}</Name>
|
||||
<Role>{sister.role}</Role>
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { API_URL } from '@/lib/config';
|
||||
import SisterAvatar from '@/components/common/SisterAvatar';
|
||||
|
||||
const SISTER_NAMES = new Set(['harang', 'narang', 'darang', 'erang']);
|
||||
|
||||
interface SubTaskEvent {
|
||||
id: number;
|
||||
@@ -375,7 +378,12 @@ export default function SubTaskDetailDrawer({ subTaskId, onClose }: Props) {
|
||||
<>
|
||||
<Header>
|
||||
<TopRow>
|
||||
<RoleBadge $role={detail.role}>{detail.role}</RoleBadge>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
{SISTER_NAMES.has(detail.agentName) && (
|
||||
<SisterAvatar name={detail.agentName} size={40} />
|
||||
)}
|
||||
<RoleBadge $role={detail.role}>{detail.role}</RoleBadge>
|
||||
</div>
|
||||
<Close onClick={onClose}>닫기 ESC</Close>
|
||||
</TopRow>
|
||||
<Title>{detail.title}</Title>
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import type { RailsSubTaskNode } from '@/lib/useRailsSocket';
|
||||
import SisterAvatar from '@/components/common/SisterAvatar';
|
||||
|
||||
const SISTER_NAMES = new Set(['harang', 'narang', 'darang', 'erang']);
|
||||
|
||||
const Wrap = styled.div`
|
||||
font-family: var(--font-sans);
|
||||
@@ -160,6 +163,9 @@ function NodeRow({
|
||||
<Node $state={node.state} onClick={() => onSelect?.(node.id)}>
|
||||
<Row>
|
||||
<StateDot $state={node.state} />
|
||||
{SISTER_NAMES.has(node.agentName) && (
|
||||
<SisterAvatar name={node.agentName} size={20} style={{ marginRight: 8 }} />
|
||||
)}
|
||||
<RoleBadge $role={node.role}>{node.role}</RoleBadge>
|
||||
<Title>{node.title.slice(0, 100)}</Title>
|
||||
<Model>{node.model || '—'}</Model>
|
||||
|
||||
Reference in New Issue
Block a user