Skip to content

Repository files navigation

Agent Composer

一个面向 Agent 场景的 Vue 3 对话输入组件。组件只负责输入与上下文收集,不包含消息列表、模型选择器或思考模式等外围界面。

底栏默认仅提供附件交互按钮,并显示 SHIFT + ENTER 换行 提示;发送按钮及其他操作可通过插槽自由组合。组件能够收集项目目录、@ 文件引用、附件和用户文本,并输出统一的 Agent 输入结构。

页面效果

Agent Composer 页面效果

截图使用默认浅色主题,展示项目目录输入、无边框消息输入框、附件按钮和换行快捷键提示。

技术栈

技术 用途
Vue 3 Composition API 与组件运行时
TypeScript 组件、事件及 Agent 内容结构类型
Vite 开发服务器与生产构建
Tailwind CSS v4 布局与主题样式
shadcn-vue Button、InputGroup、Command 等基础组件
Reka UI shadcn-vue 无障碍交互基础
ESLint + Prettier 代码检查与格式化

功能

  • 消息与项目目录双向绑定。
  • 输入 @ 后动态匹配项目文件。
  • 支持静态文件列表和异步文件解析器。
  • 支持方向键、Enter、Tab 与 Esc 操作文件候选列表。
  • 支持附件选择、拖放和从剪贴板粘贴文件。
  • 支持图片附件预览及附件移除。
  • Enter 提交,Shift + Enter 换行。
  • Textarea 无边框、无轮廓、无聚焦环,保留外层对话框边界。
  • 底栏默认仅包含附件交互按钮,并显示可隐藏或替换的换行快捷键提示。
  • 同时提供原始提交数据和 Agent 格式数据。

快速开始

npm install
npm run dev

生产构建:

npm run build

基本用法

<script setup lang="ts">
import { ref } from 'vue'
import {
  AgentComposer,
  type AgentComposerSubmitPayload,
  type AgentInputPayload,
  type ProjectFile,
} from '@/components/agent-composer'

const message = ref('')
const projectRoot = ref('/workspace/my-project')

const projectFiles: ProjectFile[] = [
  { path: 'src/App.vue' },
  { path: 'src/lib/api.ts' },
  { path: 'src/components', kind: 'directory' },
]

function handleSubmit(payload: AgentComposerSubmitPayload) {
  console.log('原始数据', payload)
}

function handleAgentSubmit(payload: AgentInputPayload) {
  console.log('Agent 数据', payload)
}
</script>

<template>
  <AgentComposer
    v-model="message"
    v-model:project-root="projectRoot"
    :project-files="projectFiles"
    @submit="handleSubmit"
    @agent-submit="handleAgentSubmit"
  />
</template>

隐藏或替换快捷键提示:

<!-- 隐藏提示 -->
<AgentComposer :show-shortcut-hint="false" />

<!-- 替换提示内容 -->
<AgentComposer>
  <template #shortcut-hint>
    <span>Shift + Enter 新起一行</span>
  </template>
</AgentComposer>

submitOnEnterfalse 时,Enter 本身即为换行,快捷键提示会自动隐藏。

组件入口位于 src/components/agent-composer/index.ts,全局主题样式由 src/style.css 提供。

动态文件匹配

浏览器不能根据文本路径直接读取本机目录。Web 项目通常需要从后端 API 获取文件列表;Electron 或 Tauri 项目可以在安全桥接层中读取目录。

通过 resolveFiles 传入同步或异步解析器:

<script setup lang="ts">
import type { ProjectFileResolver } from '@/components/agent-composer'

const resolveFiles: ProjectFileResolver = async (query, projectRoot) => {
  const params = new URLSearchParams({ query, projectRoot })
  const response = await fetch(`/api/project-files?${params}`)

  if (!response.ok) {
    throw new Error('项目文件读取失败')
  }

  return response.json()
}
</script>

<template>
  <AgentComposer
    v-model="message"
    v-model:project-root="projectRoot"
    :resolve-files="resolveFiles"
    @resolver-error="handleResolverError"
  />
</template>

解析器应返回 ProjectFile[]

interface ProjectFile {
  path: string
  name?: string
  kind?: 'file' | 'directory'
}

组件会根据文件名与路径进行二次排序和截断。匹配优先级依次为:文件名前缀、路径前缀、文件名包含、路径包含。

自定义底栏

底栏默认不会渲染发送按钮、模型选择器或其他 Agent 操作。附件按钮旁会显示 SHIFT + ENTER 换行,使用 actions 插槽可按需添加操作:

<script setup lang="ts">
import { ArrowUpIcon } from '@lucide/vue'
import { Button } from '@/components/ui/button'
</script>

<template>
  <AgentComposer v-model="message" v-model:project-root="projectRoot">
    <template #actions="{ submit, canSubmit }">
      <Button size="icon" :disabled="!canSubmit" aria-label="发送消息" @click="submit">
        <ArrowUpIcon />
      </Button>
    </template>
  </AgentComposer>
</template>

提交数据

原始数据

submit 事件输出组件内部的完整原始数据:

interface AgentComposerSubmitPayload {
  message: string
  projectRoot: string
  attachments: ComposerAttachment[]
  mentionedFiles: ProjectFile[]
}

Agent 数据

agent-submit 事件将所有有效内容转换为带判别类型的 role + content[] 结构:

{
  role: 'user',
  content: [
    { type: 'project_directory', path: '/workspace/my-project' },
    {
      type: 'file_reference',
      path: 'src/App.vue',
      name: 'App.vue',
      kind: 'file',
    },
    {
      type: 'input_file',
      id: 'attachment-id',
      name: 'design.png',
      mediaType: 'image/png',
      size: 2048,
      file: File,
    },
    { type: 'input_text', text: '请检查 @src/App.vue' },
  ],
}

内容块类型:

type 内容
project_directory 当前项目目录
file_reference 用户通过 @ 选择的项目文件或目录
input_file 用户添加的附件及原始 File 对象
input_text 去除首尾空白后的用户消息

input_file.file 是原始浏览器 File,需要在 API 适配层中上传、转换为 Base64,或替换成服务端文件 ID。该结构是通用 Agent 输入协议,不与某一家模型供应商的请求格式强绑定。

也可以在组件外单独转换:

import { formatAgentInput } from '@/components/agent-composer'

const agentInput = formatAgentInput(rawPayload)

组件 API

双向绑定

绑定 类型 默认值 说明
v-model string '' 当前消息内容
v-model:project-root string '' 当前项目目录

Props

属性 类型 默认值 说明
projectFiles ProjectFile[] [] 用于本地匹配的静态文件列表
resolveFiles ProjectFileResolver undefined 动态文件解析器;提供后将优先于 projectFiles
placeholder string 输入消息,使用 @ 引用项目文件… 消息占位文字
directoryPlaceholder string /path/to/project 项目目录占位文字
accept string undefined 原生文件输入的 accept
multiple boolean true 是否允许一次选择多个附件
disabled boolean false 是否禁用输入与操作
autoFocus boolean false 是否自动聚焦消息输入框
submitOnEnter boolean true 是否使用 Enter 提交
clearOnSubmit boolean true 提交后是否清空消息、引用和附件
showShortcutHint boolean true Enter 提交启用时是否显示换行快捷键提示
mentionLimit number 8 文件候选列表最大数量
maxAttachments number 10 最大附件数量

Vue 模板中使用 kebab-case,例如 submit-on-enterclear-on-submitmax-attachments

Events

事件 参数 触发时机
submit AgentComposerSubmitPayload 消息通过 Enter 或插槽中的 submit() 提交时
agent-submit AgentInputPayload submit 同时触发,输出 Agent 格式数据
files-selected ComposerAttachment[] 新附件成功加入时,仅包含本次新增附件
resolver-error unknown 异步文件解析器抛出错误时

Slots

插槽 参数 说明
actions submitcanSubmitattachmentsmentionedFiles 自定义底栏右侧操作
attachment attachmentremove(id) 自定义单个附件预览
mention-item fileactive 自定义文件候选项内容
directory-prefix 自定义项目目录输入框前缀
shortcut-hint 自定义换行快捷键提示

键盘交互

按键 行为
@ 打开项目文件候选列表
ArrowUp / ArrowDown 切换活动候选项
Enter / Tab 插入当前活动文件引用
Esc 关闭候选列表
Enter 候选列表关闭时提交消息
Shift + Enter 插入换行

组件会在输入法组合输入期间忽略提交快捷键,避免中文输入被误提交。

附件行为

  • 点击底栏附件按钮打开系统文件选择器。
  • 文件可以直接拖入组件。
  • 从剪贴板粘贴图片或其他文件时,会将其转换为附件。
  • 图片使用 URL.createObjectURL() 生成本地预览,并在移除、提交清空或组件卸载时释放。
  • 超过 maxAttachments 的文件不会被加入。

样式与主题

组件使用 shadcn-vue 语义化主题变量,例如 backgroundforegroundmutedborderring。可以在 src/style.css 中修改对应 CSS 变量,而不需要改写组件颜色类。

Textarea 自身不显示边框、outline、focus ring 或阴影;焦点层次由外层对话框容器表达。

项目结构

src/
├── components/
│   ├── agent-composer/
│   │   ├── AgentComposer.vue
│   │   ├── AttachmentPreview.vue
│   │   ├── formatAgentInput.ts
│   │   ├── index.ts
│   │   └── types.ts
│   └── ui/                    # shadcn-vue 基础组件
├── lib/
│   └── utils.ts
├── App.vue                    # 示例页面
├── main.ts
└── style.css                  # Tailwind 与主题变量

开发命令

命令 说明
npm run dev 启动 Vite 开发服务器
npm run build 执行 Vue 类型检查并构建生产文件
npm run preview 预览生产构建
npm run lint 运行 ESLint
npm run lint:fix 自动修复可修复的 ESLint 问题
npm run format 使用 Prettier 格式化项目
npm run format:check 检查代码格式但不修改文件

使用限制

  • 浏览器无法只凭项目目录字符串遍历本地文件,必须提供 projectFilesresolveFiles
  • 当前 @ 查询以空白字符作为引用边界;包含空格的文件路径需要在数据源中使用可插入的无空格表示,或扩展引用解析规则。
  • Agent 内容块不会自动读取项目文件正文,也不会自动上传附件;这些操作应由具有相应权限的宿主应用完成。
  • crypto.randomUUID()FileURL.createObjectURL() 需要现代浏览器环境。

变更记录

参见 CHANGELOG.md

About

Vue 3 agent composer with shadcn-vue, file mentions, attachments, and agent-ready output

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages