Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/app/app/virtual-lab/(free)/explore/layout.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.main {
display: grid;
grid-template-columns: 1fr auto;
gap: 0;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: hidden;
}

.main > div {
min-height: 0;
position: relative;
}
12 changes: 11 additions & 1 deletion src/app/app/virtual-lab/(free)/explore/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { ErrorBoundary } from 'react-error-boundary';
import { useSetAtom } from 'jotai/index';
import SimpleErrorComponent from '@/components/GenericErrorFallback';
import { sectionAtom } from '@/state/application';
import LitteratureSuggestions from '@/components/literature-suggestions';

import styles from './layout.module.css';

type GenericLayoutProps = {
children: ReactNode;
Expand All @@ -15,5 +18,12 @@ export default function ExploreLayout({ children }: GenericLayoutProps) {

useEffect(() => setSection('explore'), [setSection]);

return <ErrorBoundary FallbackComponent={SimpleErrorComponent}>{children}</ErrorBoundary>;
return (
<ErrorBoundary FallbackComponent={SimpleErrorComponent}>
<div className={styles.main}>
<div className={styles.content}>{children}</div>
<LitteratureSuggestions />
</div>
</ErrorBoundary>
);
}
5 changes: 0 additions & 5 deletions src/components/explore-section/ExploreInteractive/index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
'use client';

import dynamic from 'next/dynamic';

import DataTypeTabs from './DataTypeTabs';
import SelectedBrainRegionMETypes from './SelectedBrainRegionMETypes';
import DataTypeStatPanel from './DataTypeStatPanel';
import ThreeDeeBrain from '@/components/ThreeDeeBrain';
import { VirtualLabInfo } from '@/types/virtual-lab/common';

const LitteratureSuggestions = dynamic(() => import('@/components/litterature-suggestions'));

type ExploreInteractivePanelProps = {
virtualLabInfo?: VirtualLabInfo;
};
Expand Down Expand Up @@ -44,7 +40,6 @@ export default function ExploreInteractivePanel({ virtualLabInfo }: ExploreInter
</div>
</div>
</div>
<LitteratureSuggestions className="flex-0" />
</div>
);
}
1 change: 1 addition & 0 deletions src/components/literature-suggestions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './literature-suggestions';
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
.literatureSuggestions {
width: max(320px, 25vw);
height: 100%;
background-color: #000;
color: #eee;
padding: 1em;
display: grid;
grid-template-rows: auto 1fr auto;
gap: 1em;
}

.literatureSuggestions.collapsed {
width: 48px;
}

.literatureSuggestions > button.header {
display: flex;
flex-wrap: nowrap;
flex-direction: row;
justify-content: space-between;
align-items: center;
gap: 1em;
padding: 0 1em;
font-size: 24px;
}

.literatureSuggestions.collapsed > button.header {
flex-direction: column;
padding: 0;
}

.literatureSuggestions > button.header > h1 {
font-size: inherit;
font-weight: bold;
}

.literatureSuggestions.collapsed > button.header > h1 {
order: 9;
writing-mode: vertical-lr;
}

.articles {
overflow: auto;
}

.error {
padding: 1em;
background-color: #f30;
color: #fffe;
border: 1px solid currentColor;
white-space: prewrap;
}

.bottom {
content: '';
height: 5em;
}
90 changes: 90 additions & 0 deletions src/components/literature-suggestions/literature-suggestions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
'use client';

import React from 'react';

import { MinusOutlined, PlusOutlined } from '@ant-design/icons';
import Spinner from '../Spinner';
// import { useLitteratureCrawler, useThreadId } from './hooks';
import SuggestedQuestions from './suggested-questions';
import MessageItem from './message-item';
import Prompt from './prompt';
import { classNames } from '@/util/utils';
import { useServiceAiAgentChat, useServiceAiAgentThread } from '@/services/ai-agent';

import styles from './literature-suggestions.module.css';

export interface LiteratureSuggestionsProps {
className?: string;
}

export default function LiteratureSuggestions({ className }: LiteratureSuggestionsProps) {
const [collapsedPanel, setCollapsedPanel] = React.useState(false);
const refChatBottom = React.useRef<HTMLDivElement | null>(null);
const threadId = useServiceAiAgentThread();
const [prompt, setPrompt] = React.useState('');
const { messages, status, append, error } = useServiceAiAgentChat(threadId ?? '');
const handleQuery = React.useCallback(
(content: string) => {
append({
role: 'user',
content,
});
setPrompt('');
},
[append]
);
React.useEffect(() => {
refChatBottom.current?.scrollIntoView();
}, [messages]);

return (
<div
className={classNames(
className,
styles.literatureSuggestions,
collapsedPanel && styles.collapsed
)}
>
<button
className={styles.header}
type="button"
onClick={() => setCollapsedPanel(!collapsedPanel)}
>
<h1>Explore AI</h1>
{collapsedPanel ? <PlusOutlined /> : <MinusOutlined />}
</button>
{!collapsedPanel && (
<>
{threadId ? (
<>
{error ? (
<div className={styles.error}>{JSON.stringify(error, null, ' ')}</div>
) : (
<div className={styles.articles}>
{messages.map((item) => (
<MessageItem key={item.id} value={item} />
))}
{status !== 'ready' && <Spinner />}
<div ref={refChatBottom} className={styles.bottom} />
</div>
)}
<footer>
{messages.length === 0 && (
<SuggestedQuestions
onClick={(selectedPrompt) => {
setPrompt(selectedPrompt);
handleQuery(selectedPrompt);
}}
/>
)}
<Prompt value={prompt} onChange={setPrompt} onClick={handleQuery} />
</footer>
</>
) : (
<Spinner>Connecting AI service...</Spinner>
)}
</>
)}
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
flex-wrap: nowrap;
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
align-items: stretch;
gap: 0;
color: #fffe;
margin: 8px 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
}

.suggestedQuestions > button {
border-radius: 4px;
padding: 0.5em 1em;
background-color: #888;
border: 1px solid #888;
color: #000;
padding: 0;
background-color: transparent;
border: none;
color: #1890ff;
transition: all 0.3s;
text-align: right;
margin: 0.25em 0;
}

.suggestedQuestions > button:hover {
background-color: #333;
color: #fff;
color: #79beff;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

import React from 'react';

import { usePromptSuggestions } from '../hooks';
import { classNames } from '@/util/utils';
import { userJourneyTracker } from '@/components/explore-section/Literature/user-journey';
import { useGenericEventListener } from '@/util/generic-event';
import { useServiceAiAgentSuggestionFromUserJourney } from '@/services/ai-agent';

import styles from './suggested-questions.module.css';

Expand All @@ -15,7 +15,7 @@ export interface SuggestedQuestionsProps {
}

export default function SuggestedQuestions({ className, onClick }: SuggestedQuestionsProps) {
const [suggestions, clearSuggestions] = usePromptSuggestions();
const [suggestions, clearSuggestions] = useServiceAiAgentSuggestionFromUserJourney();
const extraSuggestion = useMophologySuggestion();

return (
Expand Down
Loading