ui: adjust styling and rm logs

This commit is contained in:
nick-delirium 2025-05-13 14:59:12 +02:00
parent dca5e54811
commit 9fec22319b
No known key found for this signature in database
GPG key ID: 93ABD695DF5FDBA0
7 changed files with 129 additions and 75 deletions

View file

@ -103,7 +103,8 @@ const HIGHLIGHTS_PATH = routes.highlights();
const KAI_PATH = routes.kai();
function PrivateRoutes() {
const { projectsStore, userStore, integrationsStore, searchStore } = useStore();
const { projectsStore, userStore, integrationsStore, searchStore } =
useStore();
const onboarding = userStore.onboarding;
const scope = userStore.scopeState;
const { tenantId } = userStore.account;
@ -127,8 +128,12 @@ function PrivateRoutes() {
React.useEffect(() => {
if (!searchStore.urlParsed) return;
debounceCall(() => searchStore.fetchSessions(true), 250)()
}, [searchStore.urlParsed, searchStore.instance.filters, searchStore.instance.eventsOrder]);
debounceCall(() => searchStore.fetchSessions(true), 250)();
}, [
searchStore.urlParsed,
searchStore.instance.filters,
searchStore.instance.eventsOrder,
]);
return (
<Suspense fallback={<Loader loading className="flex-1" />}>
@ -166,13 +171,13 @@ function PrivateRoutes() {
case '/integrations/slack':
client.post('integrations/slack/add', {
code: location.search.split('=')[1],
state: tenantId
state: tenantId,
});
break;
case '/integrations/msteams':
client.post('integrations/msteams/add', {
code: location.search.split('=')[1],
state: tenantId
state: tenantId,
});
break;
}
@ -197,7 +202,7 @@ function PrivateRoutes() {
withSiteId(DASHBOARD_PATH, siteIdList),
withSiteId(DASHBOARD_SELECT_PATH, siteIdList),
withSiteId(DASHBOARD_METRIC_CREATE_PATH, siteIdList),
withSiteId(DASHBOARD_METRIC_DETAILS_PATH, siteIdList)
withSiteId(DASHBOARD_METRIC_DETAILS_PATH, siteIdList),
]}
component={enhancedComponents.Dashboard}
/>
@ -258,7 +263,7 @@ function PrivateRoutes() {
withSiteId(FFLAG_READ_PATH, siteIdList),
withSiteId(FFLAG_CREATE_PATH, siteIdList),
withSiteId(NOTES_PATH, siteIdList),
withSiteId(BOOKMARKS_PATH, siteIdList)
withSiteId(BOOKMARKS_PATH, siteIdList),
]}
component={enhancedComponents.SessionsOverview}
/>
@ -274,12 +279,14 @@ function PrivateRoutes() {
path={withSiteId(LIVE_SESSION_PATH, siteIdList)}
component={enhancedComponents.LiveSession}
/>
{hasAi ? <Route
exact
strict
path={withSiteId(KAI_PATH, siteIdList)}
component={enhancedComponents.Kai}
/> : null}
{hasAi ? (
<Route
exact
strict
path={withSiteId(KAI_PATH, siteIdList)}
component={enhancedComponents.Kai}
/>
) : null}
{Object.entries(routes.redirects).map(([fr, to]) => (
<Redirect key={fr} exact strict from={fr} to={to} />
))}

View file

@ -27,6 +27,13 @@ function KaiChat() {
const params = new URLSearchParams(location.search);
const threadIdFromUrl = params.get('threadId');
React.useEffect(() => {
// Reset chat state and clear URL params when project changes
setSection('intro');
setThreadId(null);
history.replace({ search: '' });
}, [activeSiteId]);
const openChats = () => {
showModal(
<ChatsModal

View file

@ -1,53 +1,66 @@
import AiService from "@/services/AiService";
import AiService from '@/services/AiService';
export default class KaiService extends AiService {
getKaiChats = async (projectId: string): Promise<{ title: string, threadId: string }[]> => {
getKaiChats = async (
projectId: string,
): Promise<{ title: string; threadId: string }[]> => {
const r = await this.client.get(`/kai/${projectId}/chats`);
if (!r.ok) {
throw new Error('Failed to fetch chats');
}
const data = await r.json();
return data;
}
};
deleteKaiChat = async (projectId: string, threadId: string): Promise<boolean> => {
deleteKaiChat = async (
projectId: string,
threadId: string,
): Promise<boolean> => {
const r = await this.client.delete(`/kai/${projectId}/chats/${threadId}`);
if (!r.ok) {
throw new Error('Failed to delete chat');
}
return true;
}
};
getKaiChat = async (projectId: string, threadId: string): Promise<{ role: string, content: string, message_id: any, duration?: number }[]> => {
getKaiChat = async (
projectId: string,
threadId: string,
): Promise<
{ role: string; content: string; message_id: any; duration?: number }[]
> => {
const r = await this.client.get(`/kai/${projectId}/chats/${threadId}`);
if (!r.ok) {
throw new Error('Failed to fetch chat');
}
const data = await r.json();
return data;
}
};
createKaiChat = async (projectId: string): Promise<number> => {
const r = await this.client.get(`/kai/${projectId}/chat/new`)
const r = await this.client.get(`/kai/${projectId}/chat/new`);
if (!r.ok) {
throw new Error('Failed to create chat');
}
const data = await r.json();
return data;
}
};
feedback = async (positive: boolean | null, messageId: string, projectId: string) => {
feedback = async (
positive: boolean | null,
messageId: string,
projectId: string,
) => {
const r = await this.client.post(`/kai/${projectId}/messages/feedback`, {
message_id: messageId,
value: positive,
user_id: userId,
message_id: messageId,
value: positive,
});
if (!r.ok) {
throw new Error('Failed to send feedback');
}
return await r.json()
}
return await r.json();
};
cancelGeneration = async (projectId: string, threadId: string) => {
const r = await this.client.post(`/kai/${projectId}/cancel/${threadId}`);
@ -57,5 +70,5 @@ export default class KaiService extends AiService {
const data = await r.json();
return data;
}
};
}

View file

@ -42,7 +42,6 @@ export class ChatManager {
socket.on('error', (err) => {
console.error('Socket error:', err);
});
socket.onAny((e) => console.log('event', e));
this.socket = socket;
}
@ -68,11 +67,9 @@ export class ChatManager {
titleCallback: (title: string) => void;
}) => {
this.socket.on('chunk', (msg: BotChunk) => {
console.log('Received message:', msg);
msgCallback(msg);
});
this.socket.on('title', (msg: { content: string }) => {
console.log('Received title:', msg);
titleCallback(msg.content);
});
this.socket.on(

View file

@ -2,8 +2,15 @@ import React from 'react';
import { Icon, CopyButton } from 'UI';
import cn from 'classnames';
import Markdown from 'react-markdown';
import remarkGfm from 'remark-gfm'
import { Loader, ThumbsUp, ThumbsDown, ListRestart, FileDown, Clock } from 'lucide-react';
import remarkGfm from 'remark-gfm';
import {
Loader,
ThumbsUp,
ThumbsDown,
ListRestart,
FileDown,
Clock,
} from 'lucide-react';
import { Button, Tooltip } from 'antd';
import { kaiStore } from '../KaiStore';
import { toast } from 'react-toastify';
@ -27,36 +34,37 @@ export function ChatMsg({
const [isProcessing, setIsProcessing] = React.useState(false);
const bodyRef = React.useRef<HTMLDivElement>(null);
const onRetry = () => {
kaiStore.editMessage(text)
}
kaiStore.editMessage(text);
};
const onFeedback = (feedback: 'like' | 'dislike', messageId: string) => {
kaiStore.sendMsgFeedback(feedback, messageId);
};
const onExport = () => {
setIsProcessing(true);
import('jspdf').then(({ jsPDF }) => {
const doc = new jsPDF();
import('jspdf')
.then(({ jsPDF }) => {
const doc = new jsPDF();
doc.html(bodyRef.current, {
callback: function(doc) {
doc.save('document.pdf');
},
margin: [10, 10, 10, 10],
x: 0,
y: 0,
width: 190, // Target width
windowWidth: 675 // Window width for rendering
doc.html(bodyRef.current, {
callback: function (doc) {
doc.save('document.pdf');
},
margin: [10, 10, 10, 10],
x: 0,
y: 0,
width: 190, // Target width
windowWidth: 675, // Window width for rendering
});
})
.catch((e) => {
console.error('Error exporting message:', e);
toast.error('Failed to export message');
})
.finally(() => {
setIsProcessing(false);
});
})
.catch(e => {
console.error('Error exporting message:', e);
toast.error('Failed to export message');
})
.finally(() => {
setIsProcessing(false);
});
}
};
return (
<div
className={cn(
@ -82,7 +90,7 @@ export function ChatMsg({
</div>
)}
<div className={'mt-1 flex flex-col'}>
<div className='markdown-body' ref={bodyRef}>
<div className="markdown-body" ref={bodyRef}>
<Markdown remarkPlugins={[remarkGfm]}>{text}</Markdown>
</div>
{isUser ? (
@ -99,18 +107,31 @@ export function ChatMsg({
) : null
) : (
<div className={'flex items-center gap-2'}>
{duration ? (
<MsgDuration duration={duration} />
) : null}
<div className='ml-auto' />
<IconButton tooltip="Like this answer" onClick={() => onFeedback('like', messageId)}>
{duration ? <MsgDuration duration={duration} /> : null}
<div className="ml-auto" />
<IconButton
tooltip="Like this answer"
onClick={() => onFeedback('like', messageId)}
>
<ThumbsUp size={16} />
</IconButton>
<IconButton tooltip="Dislike this answer" onClick={() => onFeedback('dislike', messageId)}>
<IconButton
tooltip="Dislike this answer"
onClick={() => onFeedback('dislike', messageId)}
>
<ThumbsDown size={16} />
</IconButton>
<CopyButton getHtml={() => bodyRef.current?.innerHTML} content={text} isIcon format={'text/html'} />
<IconButton processing={isProcessing} tooltip="Export as PDF" onClick={onExport}>
<CopyButton
getHtml={() => bodyRef.current?.innerHTML}
content={text}
isIcon
format={'text/html'}
/>
<IconButton
processing={isProcessing}
tooltip="Export as PDF"
onClick={onExport}
>
<FileDown size={16} />
</IconButton>
</div>
@ -133,23 +154,35 @@ function IconButton({
}) {
return (
<Tooltip title={tooltip}>
<Button onClick={onClick} type="text" icon={children} size='small' loading={processing} />
<Button
onClick={onClick}
type="text"
icon={children}
size="small"
loading={processing}
/>
</Tooltip>
);
}
export function ChatNotice({ content, duration }: { content: string, duration?: number }) {
export function ChatNotice({
content,
duration,
}: {
content: string;
duration?: number;
}) {
const startTime = React.useRef(duration ? Date.now() - duration : Date.now());
const [activeDuration, setDuration] = React.useState(duration ?? 0);
React.useEffect(() => {
const interval = setInterval(() => {
setDuration(Math.round((Date.now() - startTime.current)));
setDuration(Math.round(Date.now() - startTime.current));
}, 250);
return () => clearInterval(interval);
}, []);
return (
<div className='flex flex-col gap-1 items-start p-2 rounded-lg bg-gray-lightest border-gray-light w-fit '>
<div className="flex flex-col gap-1 items-start p-2 rounded-lg bg-gray-lightest border-gray-light w-fit ">
<div className="flex gap-2 items-start">
<div className={'animate-spin mt-1'}>
<Loader size={14} />
@ -165,9 +198,7 @@ function MsgDuration({ duration }: { duration: number }) {
return (
<div className="text-disabled-text text-sm flex items-center gap-1">
<Clock size={14} />
<span className="leading-none">
{durationFormatted(duration)}
</span>
<span className="leading-none">{durationFormatted(duration)}</span>
</div>
)
);
}

View file

@ -15,7 +15,7 @@ import {
import { MODULES } from 'Components/Client/Modules';
import { Icon } from 'UI';
import SVG from 'UI/SVG';
import { hasAi } from 'App/utils/split-utils'
import { hasAi } from 'App/utils/split-utils';
import { useStore } from 'App/mstore';
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';

View file

@ -398,7 +398,6 @@ class SearchStore {
force: boolean = false,
bookmarked: boolean = false,
): Promise<void> => {
console.log(this.searchInProgress)
if (this.searchInProgress) return;
const filter = this.instance.toSearch();