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
9 changes: 9 additions & 0 deletions src/main/presenter/deeplinkPresenter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,14 @@ export class DeeplinkPresenter implements IDeeplinkPresenter {
} else {
systemPrompt = ''
}
let mentions: string[] = []
const mentionsParam = params.get('mentions')
if (mentionsParam && mentionsParam.trim() !== '') {
mentions = decodeURIComponent(mentionsParam)
.split(',')
.map((mention) => mention.trim())
.filter((mention) => mention.length > 0)
}
// 如果用户增加了yolo=1或者yolo=true,则自动发送消息
const yolo = params.get('yolo')
const autoSend = yolo && yolo.trim() !== ''
Expand All @@ -204,6 +212,7 @@ export class DeeplinkPresenter implements IDeeplinkPresenter {
msg,
modelId,
systemPrompt,
mentions,
autoSend
})
}
Expand Down
107 changes: 103 additions & 4 deletions src/renderer/src/components/ChatInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,11 @@ import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import { Mention } from './editor/mention/mention'
import suggestion, { mentionData, setPromptFilesHandler } from './editor/mention/suggestion'
import suggestion, {
mentionData,
setPromptFilesHandler,
getPromptFilesHandler
} from './editor/mention/suggestion'
import { mentionSelected } from './editor/mention/suggestion'
import Placeholder from '@tiptap/extension-placeholder'
import HardBreak from '@tiptap/extension-hard-break'
Expand All @@ -213,6 +217,8 @@ import { ResourceListEntry } from '@shared/presenter'
import { searchHistory } from '@/lib/searchHistory'
import { useLanguageStore } from '@/stores/language'
import { useToast } from '@/components/ui/toast/use-toast'
import type { CategorizedData } from './editor/mention/suggestion'
import type { PromptListEntry } from '@shared/presenter'

const langStore = useLanguageStore()
const mcpStore = useMcpStore()
Expand Down Expand Up @@ -1018,17 +1024,110 @@ function onKeydown(e: KeyboardEvent) {
}
}

// 通过名称查找mention数据
const findMentionByName = (name: string): CategorizedData | null => {
// 在当前的mentionData中查找匹配的项目
const foundMention = mentionData.value.find(
(item) => item.type === 'item' && (item.label === name || item.id === name)
)

return foundMention || null
}

// 简化的插入mention到编辑器
const insertMentionToEditor = (mentionData: CategorizedData, position: number): boolean => {
try {
// 构建mention节点属性
const mentionAttrs = {
id: mentionData.id,
label: mentionData.label,
category: mentionData.category,
content: mentionData.mcpEntry ? JSON.stringify(mentionData.mcpEntry) : ''
}

// 使用TipTap命令插入mention
const success = editor
.chain()
.focus()
.setTextSelection(position)
.insertContent({
type: 'mention',
attrs: mentionAttrs
})
.insertContent(' ') // 默认添加空格
.run()

// 更新内部状态
if (success) {
inputText.value = editor.getText()
}

return success
} catch (error) {
console.error('Failed to insert mention to editor:', error)
return false
}
}
const handlePostInsertActions = async (mentionData: CategorizedData): Promise<void> => {
// 处理Prompt类型的特殊逻辑
if (mentionData.category === 'prompts' && mentionData.mcpEntry) {
const promptEntry = mentionData.mcpEntry as PromptListEntry

// 处理关联文件
if (promptEntry.files && Array.isArray(promptEntry.files) && promptEntry.files.length > 0) {
const handler = getPromptFilesHandler()
if (handler) {
await handler(promptEntry.files).catch((error) => {
console.error('Failed to handle prompt files:', error)
})
}
}
}
}

defineExpose({
setText: (text: string) => {
inputText.value = text
clearContent: () => {
inputText.value = ''
editor.chain().clearContent().run()
editor.view.updateState(editor.state)
},
appendText: (text: string) => {
inputText.value += text
nextTick(() => {
editor.chain().clearContent().insertContent(text).run()
editor.chain().insertContent(text).run()
editor.view.updateState(editor.state)
setTimeout(() => {
const docSize = editor.state.doc.content.size
editor.chain().focus().setTextSelection(docSize).run()
}, 10)
})
},
Comment on lines +1089 to +1104
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Review the text appending logic for potential reactivity issues.

The appendText method directly modifies inputText.value before updating the editor, which might cause reactivity inconsistencies.

Consider letting TipTap manage the text state and sync back to inputText:

 appendText: (text: string) => {
-  inputText.value += text
   nextTick(() => {
     editor.chain().insertContent(text).run()
+    inputText.value = editor.getText()
     editor.view.updateState(editor.state)
     setTimeout(() => {
       const docSize = editor.state.doc.content.size
       editor.chain().focus().setTextSelection(docSize).run()
     }, 10)
   })
 },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
clearContent: () => {
inputText.value = ''
editor.chain().clearContent().run()
editor.view.updateState(editor.state)
},
appendText: (text: string) => {
inputText.value += text
nextTick(() => {
editor.chain().clearContent().insertContent(text).run()
editor.chain().insertContent(text).run()
editor.view.updateState(editor.state)
setTimeout(() => {
const docSize = editor.state.doc.content.size
editor.chain().focus().setTextSelection(docSize).run()
}, 10)
})
},
appendText: (text: string) => {
nextTick(() => {
editor.chain().insertContent(text).run()
inputText.value = editor.getText()
editor.view.updateState(editor.state)
setTimeout(() => {
const docSize = editor.state.doc.content.size
editor.chain().focus().setTextSelection(docSize).run()
}, 10)
})
},
🤖 Prompt for AI Agents
In src/renderer/src/components/ChatInput.vue between lines 1088 and 1103, the
appendText method directly modifies inputText.value before updating the editor,
which can cause reactivity issues. To fix this, avoid directly changing
inputText.value; instead, update the editor content first using TipTap's API,
then sync the editor's content back to inputText.value to keep the state
consistent and reactive.

appendMention: async (name: string) => {
try {
// 通过name在各个数据源中查找匹配的mention
const mentionData = findMentionByName(name)

if (!mentionData) {
console.warn(`Mention not found: ${name}`)
return false
}

// 计算插入位置(默认为光标位置)
const insertPosition = editor.state.selection.anchor

// 执行TipTap插入操作
const insertSuccess = insertMentionToEditor(mentionData, insertPosition)

// 处理后续操作(如文件关联、参数处理等)
if (insertSuccess) {
await handlePostInsertActions(mentionData)
}

return insertSuccess
} catch (error) {
console.error('Failed to append mention:', error)
return false
}
}
})
</script>
Expand Down
14 changes: 12 additions & 2 deletions src/renderer/src/components/NewThread.vue
Original file line number Diff line number Diff line change
Expand Up @@ -302,8 +302,18 @@ watch(
handleModelUpdate(matchedModel.model, matchedModel.providerId)
}
}
if (newCache.msg && chatInputRef.value) {
chatInputRef.value.setText(newCache.msg)
if (chatInputRef.value && (newCache.msg || newCache.mentions)) {
const chatInput = chatInputRef.value // 将引用存储在局部变量中
chatInput.clearContent()
if (newCache.mentions) {
// 优先处理 mentions
newCache.mentions.forEach((mention) => {
chatInput.appendMention(mention)
})
}
if (newCache.msg) {
chatInput.appendText(newCache.msg)
}
}
if (newCache.systemPrompt) {
systemPrompt.value = newCache.systemPrompt
Expand Down
4 changes: 3 additions & 1 deletion src/renderer/src/stores/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export const useChatStore = defineStore('chat', () => {
modelId?: string
systemPrompt?: string
autoSend?: boolean
mentions?: string[]
} | null>(null)

// Getters
Expand Down Expand Up @@ -955,7 +956,8 @@ export const useChatStore = defineStore('chat', () => {
msg: data.msg,
modelId: data.modelId,
systemPrompt: data.systemPrompt,
autoSend: data.autoSend
autoSend: data.autoSend,
mentions: data.mentions
}
}
})
Expand Down