Skip to content

Commit

Permalink
Merge pull request #471 from reorproject/split-screen
Browse files Browse the repository at this point in the history
Split screen
  • Loading branch information
samlhuillier authored Nov 3, 2024
2 parents 6ae0966 + 8a5b1ad commit ba9011f
Show file tree
Hide file tree
Showing 26 changed files with 271 additions and 204 deletions.
14 changes: 12 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
"react-icons": "^4.12.0",
"react-markdown": "^9.0.1",
"react-quill": "^2.0.0",
"react-resizable-panels": "^2.1.6",
"react-rnd": "^10.4.1",
"react-switch": "^7.0.0",
"react-toastify": "^10.0.4",
Expand Down
2 changes: 2 additions & 0 deletions src/components/Chat/ChatInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ const ChatInput: React.FC<ChatInputProps> = ({
name="Outlined"
placeholder="Follow up..."
rows={1}
// eslint-disable-next-line jsx-a11y/no-autofocus
autoFocus
style={{
backgroundColor: 'rgba(255, 255, 255, 0)',
color: 'rgb(212 212 212)',
Expand Down
34 changes: 20 additions & 14 deletions src/components/Chat/ChatMessages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const Message: React.FC<MessageProps> = ({ message, index, currentChat, setCurre
}

interface ChatMessagesProps {
currentChat: Chat
currentChat: Chat | undefined
setCurrentChat: React.Dispatch<React.SetStateAction<Chat | undefined>>
loadingState: LoadingState
handleNewChatMessage: (userTextFieldInput?: string, agentConfig?: AgentConfig) => void
Expand Down Expand Up @@ -76,9 +76,15 @@ const ChatMessages: React.FC<ChatMessagesProps> = ({
}
}

const handleSubmitNewMessage = () => {
const handleSubmitNewMessage = async () => {
if (userTextFieldInput) {
handleNewChatMessage(userTextFieldInput)
// this for v1 could just use the default agent config...
const agentConfigs = await window.electronStore.getAgentConfigs()
if (agentConfigs && agentConfigs.length > 0) {
handleNewChatMessage(userTextFieldInput, agentConfigs[0])
} else {
handleNewChatMessage(userTextFieldInput)
}
setUserTextFieldInput('')
setShouldAutoScroll(true)
}
Expand All @@ -103,7 +109,9 @@ const ChatMessages: React.FC<ChatMessagesProps> = ({
<div className="grow overflow-auto" ref={chatContainerRef} onScroll={handleScroll}>
<div className="flex flex-col items-center gap-3 p-4">
<div className="w-full max-w-3xl">
{currentChat?.messages?.length > 0 &&
{currentChat &&
currentChat.messages &&
currentChat.messages.length > 0 &&
currentChat.messages.map((message, index) => (
// eslint-disable-next-line react/no-array-index-key
<div key={index} ref={index === currentChat.messages.length - 1 ? lastMessageRef : null}>
Expand All @@ -120,16 +128,14 @@ const ChatMessages: React.FC<ChatMessagesProps> = ({
</div>
</div>

{currentChat && (
<div className="w-full p-4">
<ChatInput
userTextFieldInput={userTextFieldInput ?? ''}
setUserTextFieldInput={setUserTextFieldInput}
handleSubmitNewMessage={handleSubmitNewMessage}
loadingState={loadingState}
/>
</div>
)}
<div className="w-full p-4">
<ChatInput
userTextFieldInput={userTextFieldInput ?? ''}
setUserTextFieldInput={setUserTextFieldInput}
handleSubmitNewMessage={handleSubmitNewMessage}
loadingState={loadingState}
/>
</div>
</div>
)
}
Expand Down
37 changes: 29 additions & 8 deletions src/components/Chat/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,20 @@ import { useChatContext } from '@/contexts/ChatContext'
import StartChat from './StartChat'
import resolveLLMClient from '@/lib/llm/client'
import { appendToolCallsAndAutoExecuteTools, convertToolConfigToZodSchema } from '../../lib/llm/tools/utils'
import useResizeObserver from '@/lib/hooks/use-resize-observer'

const ChatComponent: React.FC = () => {
const [loadingState, setLoadingState] = useState<LoadingState>('idle')
const [defaultModelName, setDefaultLLMName] = useState<string>('')
const [containerWidth, setContainerWidth] = useState<number>(0)
const containerRef = useRef<HTMLDivElement>(null)
const { currentChat, setCurrentChat, saveChat } = useChatContext()
const abortControllerRef = useRef<AbortController | null>(null)

useResizeObserver(containerRef, (entry) => {
setContainerWidth(entry.contentRect.width)
})

useEffect(() => {
const fetchDefaultLLM = async () => {
const defaultName = await window.llm.getDefaultLLMName()
Expand Down Expand Up @@ -134,8 +141,8 @@ const ChatComponent: React.FC = () => {
)

return (
<div className="flex size-full items-center justify-center">
<div className="mx-auto flex size-full flex-col overflow-hidden bg-background">
<div ref={containerRef} className="flex size-full items-center justify-center">
<div className="mx-auto flex size-full flex-col overflow-hidden bg-background">
{currentChat && currentChat.messages && currentChat.messages.length > 0 ? (
<ChatMessages
currentChat={currentChat}
Expand All @@ -146,12 +153,26 @@ const ChatComponent: React.FC = () => {
}
/>
) : (
<StartChat
defaultModelName={defaultModelName}
handleNewChatMessage={(userTextFieldInput?: string, agentConfig?: AgentConfig) =>
handleNewChatMessage(undefined, userTextFieldInput, agentConfig)
}
/>
// eslint-disable-next-line react/jsx-no-useless-fragment
<>
{containerWidth > 600 ? (
<StartChat
defaultModelName={defaultModelName}
handleNewChatMessage={(userTextFieldInput?: string, agentConfig?: AgentConfig) =>
handleNewChatMessage(undefined, userTextFieldInput, agentConfig)
}
/>
) : (
<ChatMessages
currentChat={currentChat}
setCurrentChat={setCurrentChat}
loadingState={loadingState}
handleNewChatMessage={(userTextFieldInput?: string, chatFilters?: AgentConfig) =>
handleNewChatMessage(currentChat, userTextFieldInput, chatFilters)
}
/>
)}
</>
)}
</div>
</div>
Expand Down
5 changes: 4 additions & 1 deletion src/components/Common/CommonModals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ import SettingsModal from '../Settings/Settings'
import { useFileContext } from '@/contexts/FileContext'
import RenameNoteModal from '../File/RenameNote'
import RenameDirModal from '../File/RenameDirectory'
import NewDirectoryComponent from '../File/NewDirectory'

const CommonModals: React.FC = () => {
const { isSettingsModalOpen, setIsSettingsModalOpen } = useModalOpeners()
const { isNewDirectoryModalOpen, setIsNewDirectoryModalOpen, isSettingsModalOpen, setIsSettingsModalOpen } =
useModalOpeners()

const { noteToBeRenamed, fileDirToBeRenamed } = useFileContext()

return (
<div>
<NewDirectoryComponent isOpen={isNewDirectoryModalOpen} onClose={() => setIsNewDirectoryModalOpen(false)} />
{noteToBeRenamed && <RenameNoteModal />}
{fileDirToBeRenamed && <RenameDirModal />}
<SettingsModal isOpen={isSettingsModalOpen} onClose={() => setIsSettingsModalOpen(false)} />
Expand Down
7 changes: 3 additions & 4 deletions src/components/Common/EmptyPage.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { useState } from 'react'
import React from 'react'
import { ImFileEmpty } from 'react-icons/im'
import { useContentContext } from '@/contexts/ContentContext'
import NewDirectoryComponent from '../File/NewDirectory'
import { useModalOpeners } from '@/contexts/ModalContext'

const EmptyPage: React.FC = () => {
const [isNewDirectoryModalOpen, setIsNewDirectoryModalOpen] = useState(false)
const { setIsNewDirectoryModalOpen } = useModalOpeners()
const { createUntitledNote } = useContentContext()

return (
Expand All @@ -29,7 +29,6 @@ const EmptyPage: React.FC = () => {
>
Create a Folder
</button>
<NewDirectoryComponent isOpen={isNewDirectoryModalOpen} onClose={() => setIsNewDirectoryModalOpen(false)} />
</div>
</div>
)
Expand Down
72 changes: 1 addition & 71 deletions src/components/Editor/EditorManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@ const EditorManager: React.FC = () => {
const [contextMenuVisible, setContextMenuVisible] = useState(false)
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 })
const [editorFlex, setEditorFlex] = useState(true)
const [showPlaceholder, setShowPlaceholder] = useState(false)
const [writingAssistantTextPosition, setWritingAssistantTextPosition] = useState({ top: 0, left: 0 })

const { editor, suggestionsState, vaultFilesFlattened, currentlyOpenFilePath } = useFileContext()
const { editor, suggestionsState, vaultFilesFlattened } = useFileContext()
const [showDocumentStats, setShowDocumentStats] = useState(false)
const { openContent } = useContentContext()

Expand Down Expand Up @@ -69,65 +67,6 @@ const EditorManager: React.FC = () => {
window.ipcRenderer.on('show-doc-stats-changed', handleDocStatsChange)
}, [])

useEffect(() => {
if (!editor) return

const handleUpdate = () => {
try {
const { state } = editor
const { from, to } = state.selection

const $from = state.doc.resolve(from)
const $to = state.doc.resolve(to)
const start = $from.before()
const end = $to.after()

const currentLineText = state.doc.textBetween(start, end, '\n', ' ').trim()

if (currentLineText === '') {
const { node } = editor.view.domAtPos(from)
const rect = (node as HTMLElement).getBoundingClientRect()
const editorRect = editor.view.dom.getBoundingClientRect()
setWritingAssistantTextPosition({ top: rect.top - editorRect.top, left: rect.left - editorRect.left })
setShowPlaceholder(true)
} else {
setShowPlaceholder(false)
}
} catch (error) {
setShowPlaceholder(false)
}
}

editor.on('update', handleUpdate)
editor.on('selectionUpdate', handleUpdate)

// eslint-disable-next-line consistent-return
return () => {
editor.off('update', handleUpdate)
editor.off('selectionUpdate', handleUpdate)
}
}, [editor])

const handleInput = () => {
if (editor) {
const { state } = editor
const { from, to } = state.selection

const $from = state.doc.resolve(from)
const $to = state.doc.resolve(to)
const start = $from.before()
const end = $to.after()

const currentLineText = state.doc.textBetween(start, end, '\n', ' ').trim()
setShowPlaceholder(currentLineText === '')
}
}
useEffect(() => {
if (editor) {
editor.commands.focus()
}
}, [editor, currentlyOpenFilePath])

return (
<div
className="relative size-full cursor-text overflow-hidden bg-dark-gray-c-eleven py-4 text-slate-400 opacity-80"
Expand All @@ -154,17 +93,8 @@ const EditorManager: React.FC = () => {
}}
onContextMenu={handleContextMenu}
onClick={handleClick}
onInput={handleInput}
editor={editor}
/>
{showPlaceholder && (
<div
className="pointer-events-none absolute text-gray-500"
style={{ top: writingAssistantTextPosition.top, left: writingAssistantTextPosition.left }}
>
Press &apos;space&apos; for AI writing assistant
</div>
)}
</div>
</div>
{suggestionsState && (
Expand Down
Loading

0 comments on commit ba9011f

Please sign in to comment.