Skip to content

Commit

Permalink
Merge branch 'main' into kevin/openai-compatibles
Browse files Browse the repository at this point in the history
  • Loading branch information
kevin-on committed Nov 20, 2024
2 parents aa50cc6 + d1bb01a commit fcb9028
Show file tree
Hide file tree
Showing 10 changed files with 279 additions and 62 deletions.
104 changes: 60 additions & 44 deletions src/components/chat-view/Chat.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useMutation } from '@tanstack/react-query'
import { History, Plus } from 'lucide-react'
import { CircleStop, History, Plus } from 'lucide-react'
import { App, Notice } from 'obsidian'
import {
forwardRef,
Expand Down Expand Up @@ -44,6 +44,7 @@ import { editorStateToPlainText } from './chat-input/utils/editor-state-to-plain
import { ChatListDropdown } from './ChatListDropdown'
import QueryProgress, { QueryProgressState } from './QueryProgress'
import ReactMarkdown from './ReactMarkdown'
import SimilaritySearchResults from './SimilaritySearchResults'

// Add an empty line here
const getNewInputMessage = (app: App): ChatUserMessage => {
Expand Down Expand Up @@ -118,9 +119,9 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
const [queryProgress, setQueryProgress] = useState<QueryProgressState>({
type: 'idle',
})

const preventAutoScrollRef = useRef(false)
const lastProgrammaticScrollRef = useRef<number>(0)

const activeStreamAbortControllersRef = useRef<AbortController[]>([])
const chatUserInputRefs = useRef<Map<string, ChatUserInputRef>>(new Map())
const chatMessagesRef = useRef<HTMLDivElement>(null)
Expand Down Expand Up @@ -540,48 +541,57 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
<div className="smtcmp-chat-messages" ref={chatMessagesRef}>
{chatMessages.map((message, index) =>
message.role === 'user' ? (
<ChatUserInput
key={message.id}
ref={(ref) => registerChatUserInputRef(message.id, ref)}
initialSerializedEditorState={message.content}
onChange={(content) => {
setChatMessages((prevChatHistory) =>
prevChatHistory.map((msg) =>
msg.role === 'user' && msg.id === message.id
? {
...msg,
content,
}
: msg,
),
)
}}
onSubmit={(content, useVaultSearch) => {
if (editorStateToPlainText(content).trim() === '') return
handleSubmit(
[
...chatMessages.slice(0, index),
{
...message,
content,
},
],
useVaultSearch,
)
chatUserInputRefs.current.get(inputMessage.id)?.focus()
}}
onFocus={() => {
setFocusedMessageId(message.id)
}}
mentionables={message.mentionables}
setMentionables={(mentionables) => {
setChatMessages((prevChatHistory) =>
prevChatHistory.map((msg) =>
msg.id === message.id ? { ...msg, mentionables } : msg,
),
)
}}
/>
<div key={message.id} className="smtcmp-chat-messages-user">
<ChatUserInput
ref={(ref) => registerChatUserInputRef(message.id, ref)}
initialSerializedEditorState={message.content}
onChange={(content) => {
setChatMessages((prevChatHistory) =>
prevChatHistory.map((msg) =>
msg.role === 'user' && msg.id === message.id
? {
...msg,
content,
}
: msg,
),
)
}}
onSubmit={(content, useVaultSearch) => {
if (editorStateToPlainText(content).trim() === '') return
handleSubmit(
[
...chatMessages.slice(0, index),
{
role: 'user',
content: content,
promptContent: null,
id: message.id,
mentionables: message.mentionables,
},
],
useVaultSearch,
)
chatUserInputRefs.current.get(inputMessage.id)?.focus()
}}
onFocus={() => {
setFocusedMessageId(message.id)
}}
mentionables={message.mentionables}
setMentionables={(mentionables) => {
setChatMessages((prevChatHistory) =>
prevChatHistory.map((msg) =>
msg.id === message.id ? { ...msg, mentionables } : msg,
),
)
}}
/>
{message.similaritySearchResults && (
<SimilaritySearchResults
similaritySearchResults={message.similaritySearchResults}
/>
)}
</div>
) : (
<ReactMarkdownItem
key={message.id}
Expand All @@ -595,6 +605,12 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
),
)}
<QueryProgress state={queryProgress} />
{submitMutation.isPending && (
<button onClick={abortActiveStreams} className="smtcmp-stop-gen-btn">
<CircleStop size={16} />
<div>Stop Generation</div>
</button>
)}
</div>
<ChatUserInput
key={inputMessage.id} // this is needed to clear the editor when the user submits a new message
Expand Down
2 changes: 1 addition & 1 deletion src/components/chat-view/ChatListDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function ChatListDropdown({
</DropdownMenu.Trigger>

<DropdownMenu.Portal>
<DropdownMenu.Content className="smtcmp-popover">
<DropdownMenu.Content className="smtcmp-popover smtcmp-chat-list-dropdown-content">
<ul>
{chatList.length === 0 ? (
<li className="smtcmp-chat-list-dropdown-empty">
Expand Down
68 changes: 68 additions & 0 deletions src/components/chat-view/SimilaritySearchResults.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import path from 'path'

import { ChevronDown, ChevronRight } from 'lucide-react'
import { useState } from 'react'

import { useApp } from '../../contexts/app-context'
import { SelectVector } from '../../database/schema'
import { openMarkdownFile } from '../../utils/obsidian'

function SimiliartySearchItem({
chunk,
}: {
chunk: Omit<SelectVector, 'embedding'> & {
similarity: number
}
}) {
const app = useApp()

const handleClick = () => {
openMarkdownFile(app, chunk.path, chunk.metadata.startLine)
}
return (
<div onClick={handleClick} className="smtcmp-similarity-search-item">
<div className="smtcmp-similarity-search-item__path">
{path.basename(chunk.path)}
</div>
<div className="smtcmp-similarity-search-item__line-numbers">
{`${chunk.metadata.startLine} - ${chunk.metadata.endLine}`}
</div>
</div>
)
}

export default function SimilaritySearchResults({
similaritySearchResults,
}: {
similaritySearchResults: (Omit<SelectVector, 'embedding'> & {
similarity: number
})[]
}) {
const [isOpen, setIsOpen] = useState(false)

return (
<div className="smtcmp-similarity-search-results">
<div
onClick={() => {
setIsOpen(!isOpen)
}}
className="smtcmp-similarity-search-results__trigger"
>
{isOpen ? <ChevronDown size={16} /> : <ChevronRight size={16} />}
<div>Show Referenced Documents ({similaritySearchResults.length})</div>
</div>
{isOpen && (
<div
style={{
display: 'flex',
flexDirection: 'column',
}}
>
{similaritySearchResults.map((chunk) => (
<SimiliartySearchItem key={chunk.id} chunk={chunk} />
))}
</div>
)}
</div>
)
}
6 changes: 5 additions & 1 deletion src/components/chat-view/chat-input/ChatUserInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,11 @@ const ChatUserInput = forwardRef<ChatUserInputRef, ChatUserInputProps>(
mentionableKey === displayedMentionableKey
) {
// open file on click again
openMarkdownFile(app, m.file.path)
openMarkdownFile(
app,
m.file.path,
m.type === 'block' ? m.startLine : undefined,
)
} else {
setDisplayedMentionableKey(mentionableKey)
}
Expand Down
4 changes: 2 additions & 2 deletions src/database/modules/vector/VectorRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export class VectorRepository {
}
const scopeCondition = getScopeCondition()

const similaritySearchResult = await this.db
const similaritySearchResults = await this.db
.select({
...(() => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand All @@ -158,6 +158,6 @@ export class VectorRepository {
.orderBy((t) => desc(t.similarity))
.limit(options.limit)

return similaritySearchResult
return similaritySearchResults
}
}
2 changes: 2 additions & 0 deletions src/hooks/useChatHistory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const serializeChatMessage = (message: ChatMessage): SerializedChatMessage => {
promptContent: message.promptContent,
id: message.id,
mentionables: message.mentionables.map(serializeMentionable),
similaritySearchResults: message.similaritySearchResults,
}
case 'assistant':
return {
Expand All @@ -50,6 +51,7 @@ const deserializeChatMessage = (
mentionables: message.mentionables
.map((m) => deserializeMentionable(m, app))
.filter((m): m is Mentionable => m !== null),
similaritySearchResults: message.similaritySearchResults,
}
}
case 'assistant':
Expand Down
8 changes: 8 additions & 0 deletions src/types/chat.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { SerializedEditorState } from 'lexical'

import { SelectVector } from '../database/schema'

import { Mentionable, SerializedMentionable } from './mentionable'

export type ChatUserMessage = {
Expand All @@ -8,6 +10,9 @@ export type ChatUserMessage = {
promptContent: string | null
id: string
mentionables: Mentionable[]
similaritySearchResults?: (Omit<SelectVector, 'embedding'> & {
similarity: number
})[]
}
export type ChatAssistantMessage = {
role: 'assistant'
Expand All @@ -22,6 +27,9 @@ export type SerializedChatUserMessage = {
promptContent: string | null
id: string
mentionables: SerializedMentionable[]
similaritySearchResults?: (Omit<SelectVector, 'embedding'> & {
similarity: number
})[]
}
export type SerializedChatAssistantMessage = {
role: 'assistant'
Expand Down
4 changes: 2 additions & 2 deletions src/utils/obsidian.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,12 @@ export function openMarkdownFile(

if (startLine) {
const view = existingLeaf.view as MarkdownView
view.setEphemeralState({ line: startLine })
view.setEphemeralState({ line: startLine - 1 }) // -1 because line is 0-indexed
}
} else {
const leaf = app.workspace.getLeaf('tab')
leaf.openFile(file, {
eState: startLine ? { line: startLine } : undefined,
eState: startLine ? { line: startLine - 1 } : undefined, // -1 because line is 0-indexed
})
}
}
Loading

0 comments on commit fcb9028

Please sign in to comment.