🎨(ui) retour ui sources files (#37)

* (ui) fix retour global ui

correction retour ui

* 🎨(ui) retour ui sources files

fix retour ui source files

* 🐛(fix) fix front build

prettier - fix build front
This commit is contained in:
elvoisin
2025-09-23 13:29:52 +02:00
committed by GitHub
parent 9e4f0d3310
commit 623a951ca2
6 changed files with 119 additions and 52 deletions
+2 -1
View File
@@ -8,10 +8,11 @@ and this project adheres to
## [Unreleased]
- (ui) fix retour global ui
- 🎨(ui) retour ui sources files
### Changed
- ✨(ui) fix retour global ui
- 🐛(fix) broken staging css
- 🎨(alpha) adjustment for alpha version
- ✨(ui) delete flex message
@@ -38,7 +38,9 @@ export const AttachmentList = ({
onRemove(idx);
}
};
if (!name) {
return null;
}
return (
<Box
key={(name || 'attachment') + idx}
@@ -49,6 +51,7 @@ export const AttachmentList = ({
$background="var(--c--theme--colors--greyscale-050)"
$minWidth="200px"
$direction="row"
$gap="xs"
$align="center"
$padding="xs"
$css={`
@@ -80,7 +83,7 @@ export const AttachmentList = ({
</Text>
</Box>
<Text
$size="sm"
$size="xs"
$color="var(--c--theme--colors--greyscale-850)"
$css={`
overflow: hidden;
@@ -66,6 +66,28 @@ export const Chat = ({
setChatContainerRef(chatContainerRef);
}, []);
// Détecter si l'utilisateur scroll vers le haut
useEffect(() => {
const container = chatContainerRef.current;
if (!container) {
return;
}
const handleScroll = () => {
const { scrollTop, scrollHeight, clientHeight } = container;
const isAtBottom = scrollHeight - scrollTop - clientHeight < 50;
if (isAtBottom) {
setUserScrolledUp(false);
} else {
setUserScrolledUp(true);
}
};
container.addEventListener('scroll', handleScroll);
return () => container.removeEventListener('scroll', handleScroll);
}, []);
const [initialConversationMessages, setInitialConversationMessages] =
useState<Message[] | undefined>(undefined);
const [pendingFirstMessage, setPendingFirstMessage] = useState<{
@@ -78,6 +100,7 @@ export const Chat = ({
const [streamingMessageHeight, setStreamingMessageHeight] = useState<
number | null
>(null);
const [userScrolledUp, setUserScrolledUp] = useState(false);
const { mutate: createChatConversation } = useCreateChatConversation();
@@ -129,7 +152,7 @@ export const Chat = ({
const stopGeneration = async () => {
stopChat();
const response = await fetchAPI(`chats/${conversationId}/stop-steaming/`, {
const response = await fetchAPI(`chats/${conversationId}/stop-streaming/`, {
method: 'POST',
});
@@ -230,7 +253,8 @@ export const Chat = ({
if (
hasInitialized &&
(status === 'streaming' || status === 'submitted')
(status === 'streaming' || status === 'submitted') &&
!userScrolledUp
) {
const lastUserMessage = userMessages[userMessages.length - 1];
if (lastUserMessage) {
@@ -251,7 +275,13 @@ export const Chat = ({
}
}
}
}, [messages, status, hasInitialized, calculateStreamingHeight]);
}, [
messages,
status,
hasInitialized,
calculateStreamingHeight,
userScrolledUp,
]);
// Synchronize conversationId state with prop when it changes (e.g., after navigation)
useEffect(() => {
@@ -580,6 +610,7 @@ export const Chat = ({
$theme="greyscale"
$variation="550"
$size="16px"
className="action-chat-button-icon"
/>
{!isMobile && (
<Text $theme="greyscale" $variation="550">
@@ -589,41 +620,48 @@ export const Chat = ({
</Box>
{message.parts?.some(
(part) => part.type === 'source',
) && (
<Box
$direction="row"
$align="center"
$gap="4px"
className="c__button--neutral action-chat-button"
onClick={() => openSources(message.id)}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
openSources(message.id);
}
}}
role="button"
tabIndex={0}
>
<Icon
iconName="book"
$theme="greyscale"
$variation="550"
$size="16px"
/>
{!isMobile && (
<Text
$theme="greyscale"
$variation="550"
$weight="500"
) &&
(() => {
const sourceCount =
message.parts?.filter(
(part) => part.type === 'source',
).length || 0;
return (
<Box
$direction="row"
$align="center"
$gap="4px"
className={`c__button--neutral action-chat-button ${isSourceOpen ? 'action-chat-button--open' : ''}`}
onClick={() => openSources(message.id)}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
openSources(message.id);
}
}}
role="button"
tabIndex={0}
>
{!isSourceOpen
? t('Show sources')
: t('Hide sources')}
</Text>
)}
</Box>
)}
<Icon
iconName="book"
$theme="greyscale"
$variation="550"
$size="16px"
className="action-chat-button-icon"
/>
<Text
$theme="greyscale"
$variation="550"
$weight="500"
$size="12px"
>
{t('Show') +
` ${sourceCount} ` +
t('sources')}
</Text>
</Box>
);
})()}
</Box>
)}
{message.parts && isSourceOpen === message.id && (
@@ -8,6 +8,9 @@ const styles: Record<string, React.CSSProperties> = {
color: '#222631',
fontWeight: '500',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
display: 'block',
},
};
@@ -124,32 +127,44 @@ export const SourceItem: React.FC<SourceItemProps> = ({ url }) => {
return (
<Box $direction="row" $gap="4px" $align="center">
{renderFavicon()}
<Box $direction="row" $gap="4px" $align="center" $css="font-size: 14px;">
<Box
$direction="row"
$align="center"
$css="font-size: 14px;"
$width="100%"
>
{url.startsWith('http') ? (
<StyledLink
href={url}
target="_blank"
rel="noopener noreferrer"
$css={`
display: flex;
align-items: center;
gap: 0.4rem;
border-radius: 4px;
padding: 4px;
width: 100%;
text-decoration: none;
background-color: transparent;
transition: background-color 0.3s;
color: var(--c--theme--colors--greyscale-500);
&:hover {
color: var(--c--theme--colors--greyscale-700);
background-color: #EEF1F4;
}
`}
>
{renderFavicon()}
{new URL(url).hostname}
<Box
$direction="row"
$padding={{ left: '4px' }}
$padding={{ right: '4px' }}
$align="center"
style={styles.title}
>
{title && title.length > 100
? `${title.substring(0, 50)}...${title.substring(title.length - 20)}`
: title}
{title}
</Box>
</StyledLink>
) : (
@@ -19,7 +19,7 @@ export const SourceItemList: React.FC<SourceItemListProps> = ({ parts }) => {
$padding={{ all: 'sm' }}
$gap="4px"
$css={`
border: 1px solid var(--c--theme--colors--greyscale-200);
border: 1px solid var(--c--theme--colors--greyscale-100);
border-radius: 8px;
margin-top: 0.5rem;
overflow: hidden;
@@ -60,7 +60,8 @@ nextjs-portal {
}
.c__button--bordered,
.c__button--bordered:hover {
.c__button--bordered:hover,
.c__button--bordered:focus {
border: 1px solid var(--c--theme--colors--greyscale-200) !important;
background-color: #fff !important;
}
@@ -80,7 +81,8 @@ nextjs-portal {
font-weight: 500 !important;
cursor: pointer !important;
background-color: inherit !important;
z-index: 100 !important;
border: none !important;
z-index: 100;
font-size: 12px !important;
padding: 4px !important;
padding-right: 9px !important;
@@ -96,7 +98,7 @@ nextjs-portal {
padding: 9px !important;
}
.c__button--neutral.action-chat-button span,
.c__button--neutral.action-chat-button .action-chat-button-icon,
.c__button--neutral.research-web-button .c__button__icon span,
.c__button--neutral.attach-file-button .c__button__icon span {
@media (width <= 768px) {
@@ -104,8 +106,15 @@ nextjs-portal {
}
}
.c__button--neutral:hover {
.c__button--neutral:hover,
.c__button--neutral:active,
.action-chat-button--open {
background-color: #f2f5f4 !important;
border: none !important;
}
.c__button--neutral:active {
background-color: inherit !important;
}
ul a {
@@ -139,6 +148,7 @@ ul a:hover {
.c__button--info:hover,
.c__button--info:active,
.c__button--info:focus {
outline: none !important;
background-color: #b7d7ff !important;
color: #265eaa !important;
}