diff --git a/src/renderer/src/components/chat/ChatInputBox.vue b/src/renderer/src/components/chat/ChatInputBox.vue index 140380027..864b02f3e 100644 --- a/src/renderer/src/components/chat/ChatInputBox.vue +++ b/src/renderer/src/components/chat/ChatInputBox.vue @@ -4,8 +4,8 @@ 'w-full overflow-hidden rounded-xl border bg-card/30 shadow-sm backdrop-blur-lg', props.maxWidthClass ]" - @dragover.prevent - @drop.prevent="onDrop" + @dragover="onDragOver" + @drop="onDrop" > @@ -83,6 +83,10 @@ import { TextSelection } from '@tiptap/pm/state' import { Icon } from '@iconify/vue' import type { MessageFile } from '@shared/types/agent-interface' import { useI18n } from 'vue-i18n' +import { + buildChatInputWorkspaceReferenceText, + getChatInputWorkspaceItemDragData +} from '@/lib/chatInputWorkspaceReference' import { useChatInputMentions } from './composables/useChatInputMentions' import { useChatInputFiles } from './composables/useChatInputFiles' import { useSkillsData } from '@/components/chat-input/composables/useSkillsData' @@ -329,7 +333,44 @@ function onPaste(event: ClipboardEvent) { void files.handlePaste(event, true) } +function onDragOver(event: DragEvent) { + event.preventDefault() + if (event.dataTransfer) { + event.dataTransfer.dropEffect = 'copy' + } +} + +function insertWorkspaceReference(targetPath: string) { + const referenceText = buildChatInputWorkspaceReferenceText( + targetPath, + props.workspacePath, + targetPath.split(/[/\\]/).pop() + ) + if (!referenceText) { + return false + } + + const { from, to } = editor.state.selection + const docSize = editor.state.doc.content.size + const before = + from > 0 ? editor.state.doc.textBetween(Math.max(0, from - 1), from, '\n', '\n') : '' + const after = + to < docSize ? editor.state.doc.textBetween(to, Math.min(docSize, to + 1), '\n', '\n') : '' + const prefix = before && !/\s/.test(before) ? ' ' : '' + const suffix = after && /\s/.test(after) ? '' : ' ' + + editor.chain().focus().insertContent(`${prefix}${referenceText}${suffix}`).run() + return true +} + function onDrop(event: DragEvent) { + event.preventDefault() + + const workspaceItem = getChatInputWorkspaceItemDragData(event.dataTransfer) + if (workspaceItem && insertWorkspaceReference(workspaceItem.path)) { + return + } + if (!event.dataTransfer?.files || event.dataTransfer.files.length === 0) { return } diff --git a/src/renderer/src/components/chat/composables/useChatInputMentions.ts b/src/renderer/src/components/chat/composables/useChatInputMentions.ts index 849a9f763..92f1de34a 100644 --- a/src/renderer/src/components/chat/composables/useChatInputMentions.ts +++ b/src/renderer/src/components/chat/composables/useChatInputMentions.ts @@ -7,6 +7,10 @@ import { ACP_WORKSPACE_EVENTS } from '@/events' import { usePresenter } from '@/composables/usePresenter' import { useMcpStore } from '@/stores/mcp' import { useSkillsStore } from '@/stores/skillsStore' +import { + buildChatInputWorkspaceReferenceText, + resolveChatInputWorkspaceReferencePath +} from '@/lib/chatInputWorkspaceReference' import SuggestionList from '../mentions/SuggestionList.vue' import { buildCommandText, @@ -154,8 +158,11 @@ export function useChatInputMentions(options: UseChatInputMentionsOptions) { ([] as WorkspaceFileNode[]) return result.slice(0, 20).map((file) => { - const relativePath = window.api.toRelativePath?.(file.path, workspacePath) ?? '' - const displayPath = relativePath || file.name + const displayPath = resolveChatInputWorkspaceReferencePath( + file.path, + workspacePath, + file.name + ) return { id: `file:${file.path}`, category: 'file' as const, @@ -163,7 +170,7 @@ export function useChatInputMentions(options: UseChatInputMentionsOptions) { description: file.path, payload: { path: file.path, - insertText: `@${displayPath} ` + insertText: `${buildChatInputWorkspaceReferenceText(file.path, workspacePath, file.name)} ` } } }) diff --git a/src/renderer/src/components/workspace/WorkspaceFileNode.vue b/src/renderer/src/components/workspace/WorkspaceFileNode.vue index 3f32d365f..1e46ce67d 100644 --- a/src/renderer/src/components/workspace/WorkspaceFileNode.vue +++ b/src/renderer/src/components/workspace/WorkspaceFileNode.vue @@ -3,10 +3,12 @@