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 @@