Spaces:
Sleeping
Sleeping
wuyiqunLu
commited on
Commit
β’
3a22cf3
1
Parent(s):
5d7d435
feat: adjust image to use next/image (#20)
Browse filesIgnore the image size from the video, it was using the unoptimized
property which renders the origin image
https://github.com/landing-ai/vision-agent-ui/assets/132986242/e90977b6-4ced-4ee7-bf02-df599e506631
<img width="970" alt="image"
src="https://github.com/landing-ai/vision-agent-ui/assets/132986242/531e5d7e-dca1-4f74-ad2c-8e60cdf1b178">
<img width="903" alt="image"
src="https://github.com/landing-ai/vision-agent-ui/assets/132986242/49456cf1-efc2-4383-a12b-7cd156caa660">
- components/chat-sidebar/ChatCard.tsx +1 -1
- components/chat/ChatList.tsx +1 -1
- components/chat/ChatMessage.tsx +23 -1
- components/chat/PromptForm.tsx +6 -2
- components/ui/Img.tsx +3 -3
- lib/hooks/useCleanedUpMessages.ts +10 -3
- lib/hooks/useVisionAgent.tsx +9 -8
- public/loading.gif +0 -0
components/chat-sidebar/ChatCard.tsx
CHANGED
@@ -44,7 +44,7 @@ const ChatCard: React.FC<ChatCardProps> = ({ chat }) => {
|
|
44 |
classNames={chatIdFromParam === id && 'border-gray-500'}
|
45 |
>
|
46 |
<div className="overflow-hidden flex items-center size-full">
|
47 |
-
<Img src={url} className="w-1/4 " />
|
48 |
<div className="flex items-start h-full">
|
49 |
<p className="text-sm w-3/4 ml-3">{title}</p>
|
50 |
</div>
|
|
|
44 |
classNames={chatIdFromParam === id && 'border-gray-500'}
|
45 |
>
|
46 |
<div className="overflow-hidden flex items-center size-full">
|
47 |
+
<Img src={url} alt={`chat-${id}-card-image`} className="w-1/4 " />
|
48 |
<div className="flex items-start h-full">
|
49 |
<p className="text-sm w-3/4 ml-3">{title}</p>
|
50 |
</div>
|
components/chat/ChatList.tsx
CHANGED
@@ -10,7 +10,7 @@ export interface ChatList {
|
|
10 |
|
11 |
export function ChatList({ messages }: ChatList) {
|
12 |
return (
|
13 |
-
<div className="relative mx-auto max-w-5xl px-8 pr-12">
|
14 |
{messages
|
15 |
// .filter(message => message.role !== 'system')
|
16 |
.map((message, index) => (
|
|
|
10 |
|
11 |
export function ChatList({ messages }: ChatList) {
|
12 |
return (
|
13 |
+
<div className="relative mx-auto max-w-5xl px-8 pr-12 pb-[100px]">
|
14 |
{messages
|
15 |
// .filter(message => message.role !== 'system')
|
16 |
.map((message, index) => (
|
components/chat/ChatMessage.tsx
CHANGED
@@ -11,6 +11,7 @@ import { IconOpenAI, IconUser } from '@/components/ui/Icons';
|
|
11 |
import { ChatMessageActions } from '@/components/chat/ChatMessageActions';
|
12 |
import { MessageBase } from '../../lib/types';
|
13 |
import { useCleanedUpMessages } from '@/lib/hooks/useCleanedUpMessages';
|
|
|
14 |
|
15 |
export interface ChatMessageProps {
|
16 |
message: MessageBase;
|
@@ -60,11 +61,32 @@ export function ChatMessage({ message, ...props }: ChatMessageProps) {
|
|
60 |
className="break-words"
|
61 |
remarkPlugins={[remarkGfm, remarkMath]}
|
62 |
components={{
|
63 |
-
p({ children }) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
return (
|
65 |
<p className="my-2 last:mb-0 whitespace-pre-line">{children}</p>
|
66 |
);
|
67 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
code({ node, inline, className, children, ...props }) {
|
69 |
if (children.length) {
|
70 |
if (children[0] == 'β') {
|
|
|
11 |
import { ChatMessageActions } from '@/components/chat/ChatMessageActions';
|
12 |
import { MessageBase } from '../../lib/types';
|
13 |
import { useCleanedUpMessages } from '@/lib/hooks/useCleanedUpMessages';
|
14 |
+
import Img from '../ui/Img';
|
15 |
|
16 |
export interface ChatMessageProps {
|
17 |
message: MessageBase;
|
|
|
61 |
className="break-words"
|
62 |
remarkPlugins={[remarkGfm, remarkMath]}
|
63 |
components={{
|
64 |
+
p({ children, ...props }) {
|
65 |
+
if (
|
66 |
+
props.node.children.some(
|
67 |
+
child => child.type === 'element' && child.tagName === 'img',
|
68 |
+
)
|
69 |
+
) {
|
70 |
+
return (
|
71 |
+
<p className="flex flex-wrap gap-2 items-start">{children}</p>
|
72 |
+
);
|
73 |
+
}
|
74 |
return (
|
75 |
<p className="my-2 last:mb-0 whitespace-pre-line">{children}</p>
|
76 |
);
|
77 |
},
|
78 |
+
img(props) {
|
79 |
+
return (
|
80 |
+
<Img
|
81 |
+
src={props.src ?? '/landing.png'}
|
82 |
+
alt={props.alt ?? 'answer-image'}
|
83 |
+
quality={100}
|
84 |
+
sizes="(min-width: 66em) 40vw,
|
85 |
+
(min-width: 44em) 66vw,
|
86 |
+
100vw"
|
87 |
+
/>
|
88 |
+
);
|
89 |
+
},
|
90 |
code({ node, inline, className, children, ...props }) {
|
91 |
if (children.length) {
|
92 |
if (children[0] == 'β') {
|
components/chat/PromptForm.tsx
CHANGED
@@ -60,10 +60,14 @@ export function PromptForm({
|
|
60 |
{url && (
|
61 |
<Tooltip>
|
62 |
<TooltipTrigger asChild>
|
63 |
-
<Img
|
|
|
|
|
|
|
|
|
64 |
</TooltipTrigger>
|
65 |
<TooltipContent>
|
66 |
-
<Img src={url} className="m-2" />
|
67 |
</TooltipContent>
|
68 |
</Tooltip>
|
69 |
)}
|
|
|
60 |
{url && (
|
61 |
<Tooltip>
|
62 |
<TooltipTrigger asChild>
|
63 |
+
<Img
|
64 |
+
alt="prompt-image"
|
65 |
+
src={url}
|
66 |
+
className="w-1/5 my-4 mx-2 cursor-zoom-in"
|
67 |
+
/>
|
68 |
</TooltipTrigger>
|
69 |
<TooltipContent>
|
70 |
+
<Img alt="prompt-hovered-image" src={url} className="m-2" />
|
71 |
</TooltipContent>
|
72 |
</Tooltip>
|
73 |
)}
|
components/ui/Img.tsx
CHANGED
@@ -10,8 +10,8 @@ const placeholder =
|
|
10 |
// const Props = Omit<React.ComponentPropsWithoutRef<typeof Image>, 'alt'>;
|
11 |
const Img = React.forwardRef<
|
12 |
React.ElementRef<typeof Image>,
|
13 |
-
|
14 |
-
>(({ src, onLoad, width, height, className, ...props }, ref) => {
|
15 |
const [dimensions, setDimensions] = React.useState({
|
16 |
width: width ?? 200,
|
17 |
height: height ?? 200,
|
@@ -24,7 +24,7 @@ const Img = React.forwardRef<
|
|
24 |
placeholder={placeholder}
|
25 |
width={dimensions.width}
|
26 |
height={dimensions.height}
|
27 |
-
alt=
|
28 |
ref={ref}
|
29 |
className={cn('rounded-md', className)}
|
30 |
onLoad={e => {
|
|
|
10 |
// const Props = Omit<React.ComponentPropsWithoutRef<typeof Image>, 'alt'>;
|
11 |
const Img = React.forwardRef<
|
12 |
React.ElementRef<typeof Image>,
|
13 |
+
React.ComponentPropsWithoutRef<typeof Image>
|
14 |
+
>(({ src, alt, onLoad, width, height, className, ...props }, ref) => {
|
15 |
const [dimensions, setDimensions] = React.useState({
|
16 |
width: width ?? 200,
|
17 |
height: height ?? 200,
|
|
|
24 |
placeholder={placeholder}
|
25 |
width={dimensions.width}
|
26 |
height={dimensions.height}
|
27 |
+
alt={alt ?? 'image'}
|
28 |
ref={ref}
|
29 |
className={cn('rounded-md', className)}
|
30 |
onLoad={e => {
|
lib/hooks/useCleanedUpMessages.ts
CHANGED
@@ -62,11 +62,18 @@ export const getCleanedUpMessages = ({
|
|
62 |
}
|
63 |
cleanedLogs.push(content.substring(left, right));
|
64 |
const [answerText, imagesStr = ''] = answer.split('<VIZ>');
|
65 |
-
const
|
|
|
|
|
|
|
|
|
66 |
return {
|
67 |
logs: cleanedLogs.join('').replace(/β/g, '|').split('|\n\n|').join('|\n|'),
|
68 |
-
content:
|
69 |
-
|
|
|
|
|
|
|
70 |
};
|
71 |
};
|
72 |
|
|
|
62 |
}
|
63 |
cleanedLogs.push(content.substring(left, right));
|
64 |
const [answerText, imagesStr = ''] = answer.split('<VIZ>');
|
65 |
+
const [imagesArrayStr, ...rest] = imagesStr.split('</VIZ>');
|
66 |
+
const images = imagesArrayStr
|
67 |
+
.split('</IMG>')
|
68 |
+
.map(str => str.replace('<IMG>', ''))
|
69 |
+
.slice(0, -1);
|
70 |
return {
|
71 |
logs: cleanedLogs.join('').replace(/β/g, '|').split('|\n\n|').join('|\n|'),
|
72 |
+
content:
|
73 |
+
answerText.replace('</</ANSWER>', '').replace('</ANSWER>', '') +
|
74 |
+
images.map((_, index) => `![answers-${index}](/loading.gif)`).join('') +
|
75 |
+
rest.join(''),
|
76 |
+
images: images,
|
77 |
};
|
78 |
};
|
79 |
|
lib/hooks/useVisionAgent.tsx
CHANGED
@@ -72,17 +72,18 @@ const useVisionAgent = (chat: ChatEntity) => {
|
|
72 |
uploadBase64(image, message.id, id, index),
|
73 |
),
|
74 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
const newMessage = {
|
76 |
...message,
|
77 |
-
content:
|
78 |
-
logs +
|
79 |
-
CLEANED_SEPARATOR +
|
80 |
-
content +
|
81 |
-
'\n' +
|
82 |
-
publicUrls
|
83 |
-
.map((url, index) => `![image-${index}](${url})`)
|
84 |
-
.join('\n'),
|
85 |
};
|
|
|
|
|
86 |
saveKVChatMessage(id, newMessage);
|
87 |
} else {
|
88 |
saveKVChatMessage(id, {
|
|
|
72 |
uploadBase64(image, message.id, id, index),
|
73 |
),
|
74 |
);
|
75 |
+
const newContent = publicUrls.reduce((accum, url, index) => {
|
76 |
+
return accum.replace(
|
77 |
+
`![answers-${index}](/loading.gif)`,
|
78 |
+
`![answers-${index}](${url})`,
|
79 |
+
);
|
80 |
+
}, content);
|
81 |
const newMessage = {
|
82 |
...message,
|
83 |
+
content: logs + CLEANED_SEPARATOR + newContent,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
};
|
85 |
+
console.log(messages);
|
86 |
+
setMessages([...messages, newMessage]);
|
87 |
saveKVChatMessage(id, newMessage);
|
88 |
} else {
|
89 |
saveKVChatMessage(id, {
|
public/loading.gif
ADDED