Skip to content

Commit

Permalink
Refactor AIChat header and loading components for improved UI structure
Browse files Browse the repository at this point in the history
  • Loading branch information
trheyi committed Feb 9, 2025
1 parent 054548c commit c7e0c3e
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 120 deletions.
5 changes: 4 additions & 1 deletion packages/xgen/components/chat/loading/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ const Index = (props: IProps) => {
</span>
)}
{placeholder && <span className={styles.placeholder}>{placeholder}</span>}
<span className={styles.dots}>{dots}</span>
<span className={styles.dots}>
{dots}
<span style={{ opacity: 0 }}>...</span>
</span>
</div>
)
}
Expand Down
71 changes: 69 additions & 2 deletions packages/xgen/layouts/components/Neo/components/AIChat/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -150,18 +150,85 @@
font-size: 12px;
color: var(--color_title);

.pageInfo {
/* Left Section - Assistant & Page Info */
.leftSection {
display: flex;
align-items: center;
gap: 4px;
flex: 1;
gap: 12px;

/* Assistant Info */
.assistantInfo {
display: flex;
align-items: center;
gap: 8px;
padding-right: 12px;
border-right: 1px solid var(--color_border_light);
position: relative;

.avatarWrapper {
display: flex;
align-items: center;
gap: 8px;

.avatar {
width: 20px;
height: 20px;
border-radius: 50%;
object-fit: cover;
background: var(--color_border_light);
}

.assistantName {
font-size: 12px;
color: var(--color_text);
font-weight: 500;
}
}

.deleteBtn {
cursor: pointer;
color: var(--color_text_grey);
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
border-radius: 50%;

&:hover {
background: var(--color_danger_bg);
color: var(--color_danger);
}
}

&:hover {
.deleteBtn {
background: var(--color_border_light);
}
}
}

/* Page Info */
.pageInfo {
display: flex;
align-items: center;
gap: 4px;
color: var(--color_text_grey);
font-size: 11px;
}
}

/* Status Indicator */
.loadingContainer {
color: var(--color_warning);
animation: breathing 1.5s ease-in-out infinite;
display: flex;
align-items: center;
justify-content: center;
margin-left: auto;
padding-left: 12px;
}
}

Expand Down
239 changes: 122 additions & 117 deletions packages/xgen/layouts/components/Neo/components/AIChat/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -556,139 +556,144 @@ const AIChat = (props: AIChatProps) => {

{/* Input Area */}
<div className={styles.inputArea}>
{((showCurrentPage && currentPage) || attachments.length > 0) && (
<div className={styles.contextArea}>
{showCurrentPage && currentPage && (
<div className={styles.currentPage}>
<div className={styles.contextArea}>
<div className={styles.currentPage}>
{/* Assistant Info Section */}
<div className={styles.leftSection}>
<div className={styles.assistantInfo}>
<div className={styles.avatarWrapper}>
<img
src='https://api.dicebear.com/7.x/bottts/svg?seed=Neo'
alt='Assistant'
className={styles.avatar}
/>
<div className={styles.assistantName}>Neo Assistant</div>
</div>
<div className={styles.deleteBtn}>
<Icon name='material-close' size={12} />
</div>
</div>

{/* Current Page Info */}
{showCurrentPage && currentPage && (
<div className={styles.pageInfo}>
<Icon name='icon-link-2' size={12} className='pageIcon' />
{currentPage}
</div>
{loading && (
<div className={styles.loadingContainer}>
<svg
width='8'
height='8'
viewBox='0 0 8 8'
fill='currentColor'
xmlns='http://www.w3.org/2000/svg'
>
<circle cx='4' cy='4' r='4' />
</svg>
</div>
)}
)}
</div>

{/* Status Indicator */}
{loading && (
<div className={styles.loadingContainer}>
<svg
width='8'
height='8'
viewBox='0 0 8 8'
fill='currentColor'
xmlns='http://www.w3.org/2000/svg'
>
<circle cx='4' cy='4' r='4' />
</svg>
</div>
)}
</div>

{attachments.length > 0 && (
<div className={styles.attachmentsArea}>
<div className={styles.attachmentsList}>
{attachments.map((attachment, index) => (
<div key={index} className={clsx(styles.attachmentItem)}>
{/* Attachments Area */}
{attachments.length > 0 && (
<div className={styles.attachmentsArea}>
<div className={styles.attachmentsList}>
{attachments.map((attachment, index) => (
<div key={index} className={clsx(styles.attachmentItem)}>
<div
className={styles.deleteBtn}
onClick={(e) => {
e.stopPropagation()
removeAttachment(attachment)
}}
>
<Icon name='material-close' size={12} />
</div>
<div
className={clsx(styles.attachmentContent, {
[styles.uploading]:
attachment.status === 'uploading'
})}
onClick={() => handleFileClick(attachment)}
>
<div className={styles.attachmentThumb}>
{attachment.type === 'URL' ? (
<div
className={
styles.attachmentTypeIcon
}
>
<Icon name='icon-link' size={10} />
</div>
) : attachment.thumbUrl ? (
<img
src={attachment.thumbUrl}
alt={attachment.name}
/>
) : (
<div
className={clsx(
styles.attachmentTypeIcon,
{
[styles.longType]:
attachment.type
.length >= 4
}
)}
>
{attachment.type.slice(0, 3)}
</div>
)}
{attachment.status === 'uploading' && (
<div className={styles.uploadingOverlay}>
<Icon
name='icon-loader'
size={16}
className={styles.spinner}
/>
</div>
)}
</div>
<div
className={styles.deleteBtn}
onClick={(e) => {
e.stopPropagation()
removeAttachment(attachment)
}}
className={styles.attachmentName}
title={attachment.name}
>
<Icon name='material-close' size={12} />
{attachment.name.length > 15
? `${attachment.name.slice(0, 12)}...`
: attachment.name}
</div>
<div
className={clsx(styles.attachmentContent, {
[styles.uploading]:
attachment.status === 'uploading'
className={clsx(styles.pinBtn, {
[styles.pinned]: attachment.pinned
})}
onClick={() => handleFileClick(attachment)}
onClick={(e) => {
e.stopPropagation()
const updatedAttachments =
attachments.map((att) =>
att === attachment
? {
...att,
pinned: !att.pinned
}
: att
)
setAttachments(updatedAttachments)
}}
>
<div className={styles.attachmentThumb}>
{attachment.type === 'URL' ? (
<div
className={
styles.attachmentTypeIcon
}
>
<Icon
name='icon-link'
size={10}
/>
</div>
) : attachment.thumbUrl ? (
<img
src={attachment.thumbUrl}
alt={attachment.name}
/>
) : (
<div
className={clsx(
styles.attachmentTypeIcon,
{
[styles.longType]:
attachment
.type
.length >=
4
}
)}
>
{attachment.type.slice(0, 3)}
</div>
)}
{attachment.status === 'uploading' && (
<div
className={
styles.uploadingOverlay
}
>
<Icon
name='icon-loader'
size={16}
className={
styles.spinner
}
/>
</div>
)}
</div>
<div
className={styles.attachmentName}
title={attachment.name}
>
{attachment.name.length > 15
? `${attachment.name.slice(
0,
12
)}...`
: attachment.name}
</div>
<div
className={clsx(styles.pinBtn, {
[styles.pinned]: attachment.pinned
})}
onClick={(e) => {
e.stopPropagation()
const updatedAttachments =
attachments.map((att) =>
att === attachment
? {
...att,
pinned: !att.pinned
}
: att
)
setAttachments(updatedAttachments)
}}
>
<Icon name='material-keep' size={12} />
</div>
<Icon name='material-keep' size={12} />
</div>
</div>
))}
</div>
</div>
))}
</div>
)}
</div>
)}
</div>
)}
</div>

<div className={styles.inputWrapper}>
<MentionTextArea
Expand Down

0 comments on commit c7e0c3e

Please sign in to comment.