diff --git a/docs/develop-guides/roadmap.md b/docs/develop-guides/roadmap.md
index f7cbb9bf..aa458a16 100644
--- a/docs/develop-guides/roadmap.md
+++ b/docs/develop-guides/roadmap.md
@@ -32,7 +32,7 @@
- 合并知识库导航入口:左侧导航仅保留“知识库”,文档知识库与图知识库在页面 header 中通过同一组轻量切换入口切换,保留原有列表与图谱内容区交互。
-- 抽象页面轻量切换 header:知识库与扩展管理页直接共用 `ViewSwitchHeader`,通过统一切换样式和 actions slot 收敛文档知识库、知识图谱、Tools、MCP、Subagents、Skills 等入口的信息层级;扩展管理各列表的刷新入口下沉到搜索框右侧,并统一搜索框与工具按钮的边框和圆角。
+- 抽象页面轻量切换 header:知识库与扩展管理页直接共用 `ViewSwitchHeader`,通过统一切换样式和 actions slot 收敛文档知识库、知识图谱、Tools、MCP、Subagents、Skills 等入口的信息层级;扩展管理各列表的刷新入口下沉到搜索框右侧,并统一搜索框与工具按钮的边框和圆角,同时强化切换项选中态的边框、阴影与字重层级,提升 header 中当前视图的辨识度。
- 调整任务中心交互:入口移动到 GitHub 按钮下方,并将右侧抽屉展示改为居中弹窗,减少对主页面布局的占用。
- 调整 backend Python 工作区依赖边界:将 `backend/package/yuxi` 明确为承载核心运行依赖的业务包,根 `backend/pyproject.toml` 仅保留工作区入口与开发/测试配置,减少依赖职责混淆。
- 将 `yuxi` 从 uv workspace 成员调整为 `backend/package` 下可独立构建的本地 Python 包,backend 通过 path dependency 以已安装包形式发现依赖,移除对 `PYTHONPATH=/app/package` 的运行时耦合。
@@ -53,6 +53,7 @@
- 修复对话摘要中间件的工具结果卸载链路:摘要触发时改为将大体积 `ToolMessage` 写入当前 agent 可见的 sandbox outputs 路径,修正 `summary_offload` 路径拼接错误、`messages` 触发条件下不会真正裁剪历史的问题,并避免将 system message 重复纳入摘要与最终消息列表;补充对应单元测试覆盖。
- 调整智能体对话中的工具调用展示:连续工具调用默认折叠为“调用了 N 个工具”的轻量摘要,展开后改为弱化时间线样式,减少工具结果卡片对正文阅读节奏的干扰。
- 调整聊天首页的智能体切换入口:在无历史对话时,智能体数量 `<= 3` 且 `chat-main` 宽度不小于 `380px` 时继续使用横向 segmented;当智能体数量 `>= 4` 或内容区宽度小于 `380px` 时自动收敛为“当前智能体 + 下拉按钮”形式。
+- 调整对话配置入口与侧边栏头尾交互:输入区配置按钮改为轻量 dropdown 触发器,默认保持透明、hover 才出现背景;下拉中直接提供配置切换、新建配置,以及查看/编辑当前配置的入口。配置侧边栏则移除头部配置切换和新建入口,改为仅显示当前配置名称、可点击星标和关闭图标,并将删除操作下沉到底部保存按钮右侧。
---
diff --git a/web/src/components/AgentConfigSidebar.vue b/web/src/components/AgentConfigSidebar.vue
index 0d4e3553..1ed66c02 100644
--- a/web/src/components/AgentConfigSidebar.vue
+++ b/web/src/components/AgentConfigSidebar.vue
@@ -3,47 +3,27 @@
-
-
-
-
-
-
{
return !!selectedConfigSummary.value?.is_default
})
+const currentConfigName = computed(() => {
+ return selectedConfigSummary.value?.name || '当前配置'
+})
+
const isReadOnlyConfig = computed(() => !userStore.isAdmin)
const isSavingConfig = ref(false)
@@ -578,24 +555,6 @@ const filteredConfigurableItems = computed(() => {
return filtered
})
-const configSwitchOptions = computed(() => {
- if (!selectedAgentId.value) return []
- const list = agentConfigs.value[selectedAgentId.value] || []
- const options = list.map((cfg) => ({
- label: cfg.is_default ? `${cfg.name}(默认)` : cfg.name,
- value: cfg.id
- }))
-
- if (userStore.isAdmin) {
- options.push({
- label: '+ 新建配置',
- value: CREATE_CONFIG_OPTION_VALUE
- })
- }
-
- return options
-})
-
const loadLiveSkillOptions = async (force = false) => {
if (!userStore.isAdmin) {
liveSkillOptions.value = []
@@ -834,21 +793,6 @@ const filteredOptions = computed(() => {
})
// 方法
-const handleConfigSwitch = async (configId) => {
- if (configId === CREATE_CONFIG_OPTION_VALUE) {
- openCreateConfigModal()
- return
- }
-
- if (!configId || configId === selectedAgentConfigId.value) return
- try {
- await agentStore.selectAgentConfig(configId)
- } catch (error) {
- console.error('切换配置出错:', error)
- message.error('切换配置失败')
- }
-}
-
const updateConfigValue = (key, value) => {
if (isReadOnlyConfig.value) return
agentStore.updateAgentConfig({
@@ -856,47 +800,6 @@ const updateConfigValue = (key, value) => {
})
}
-const openCreateConfigModal = () => {
- if (!userStore.isAdmin) return
- createConfigName.value = ''
- createConfigModalOpen.value = true
-}
-
-const closeCreateConfigModal = () => {
- createConfigModalOpen.value = false
- createConfigName.value = ''
-}
-
-const handleCreateConfig = async () => {
- if (!userStore.isAdmin) return
- if (!selectedAgentId.value) return
- const name = createConfigName.value.trim()
- if (!name) {
- message.error('请输入配置名称')
- return
- }
-
- createConfigLoading.value = true
- try {
- await agentStore.createAgentConfigProfile({
- name,
- setDefault: false,
- fromCurrent: false
- })
- closeCreateConfigModal()
- message.success('配置已创建')
- } catch (error) {
- console.error('创建配置出错:', error)
- message.error(error.message || '创建配置失败')
- } finally {
- createConfigLoading.value = false
- }
-}
-
-const shouldShowConfig = () => {
- return true
-}
-
const closeSidebar = () => {
emit('close')
}
@@ -1185,81 +1088,61 @@ const confirmDeleteConfig = async () => {
width: 100%;
}
- .config-manage-row {
+ .config-title-row {
display: flex;
align-items: center;
flex: 1;
min-width: 0;
+ }
- .config-switch-select {
- flex: 1;
- min-width: 0;
-
- :deep(.ant-select-selector) {
- height: 32px;
- border-radius: 8px;
- border-color: var(--gray-200);
- padding: 0 10px;
- transition: border-color 0.2s ease;
- }
-
- :deep(.ant-select-selection-search-input),
- :deep(.ant-select-selection-item),
- :deep(.ant-select-selection-placeholder) {
- line-height: 30px;
- font-size: 13px;
- }
-
- :deep(.ant-select.ant-select-focused .ant-select-selector),
- :deep(.ant-select-selector:hover) {
- border-color: var(--main-color);
- box-shadow: none;
- }
- }
+ .config-title-text {
+ min-width: 0;
+ font-size: 14px;
+ font-weight: 600;
+ color: var(--gray-900);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
.header-actions {
display: flex;
align-items: center;
- gap: 8px;
+ gap: 4px;
margin-left: auto;
}
}
- .icon-btn {
- width: 32px;
- height: 32px;
- border-radius: 8px;
- color: var(--gray-600);
- border: 1px solid var(--gray-200);
- background: var(--gray-0);
+ .header-icon-action {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 28px;
+ height: 28px;
padding: 0;
+ border: none;
+ background: transparent;
+ border-radius: 6px;
+ color: var(--gray-600);
transition:
color 0.2s ease,
- border-color 0.2s ease,
background-color 0.2s ease;
+ cursor: pointer;
&:hover:not(:disabled) {
color: var(--main-600);
- border-color: var(--main-200);
- background: var(--main-10);
+ background: var(--gray-100);
}
- &.is-default {
+ &.is-default,
+ &.is-default:hover:not(:disabled) {
color: var(--color-warning-500);
}
- &.ant-btn-dangerous:hover:not(:disabled) {
- color: var(--color-error-700);
- border-color: var(--color-error-100);
- background: var(--color-error-50);
- }
-
&:disabled {
cursor: not-allowed;
background: transparent;
color: var(--gray-400);
- border-color: var(--gray-200);
&.is-default {
opacity: 1;
@@ -1450,7 +1333,7 @@ const confirmDeleteConfig = async () => {
align-items: center;
.footer-main-btn {
- width: 100%;
+ flex: 1;
height: 36px;
border-radius: 8px;
font-size: 14px;
@@ -1484,6 +1367,36 @@ const confirmDeleteConfig = async () => {
color: var(--gray-400);
}
}
+
+ .footer-icon-btn {
+ width: 36px;
+ height: 36px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ border: 1px solid var(--gray-200);
+ border-radius: 8px;
+ background: var(--gray-0);
+ color: var(--gray-500);
+ cursor: pointer;
+ transition:
+ color 0.2s ease,
+ border-color 0.2s ease,
+ background-color 0.2s ease;
+
+ &:hover:not(:disabled) {
+ color: var(--color-error-700);
+ border-color: var(--color-error-100);
+ background: var(--color-error-50);
+ }
+
+ &:disabled {
+ cursor: not-allowed;
+ color: var(--gray-400);
+ background: var(--gray-50);
+ }
+ }
}
}
}
diff --git a/web/src/components/AgentInputArea.vue b/web/src/components/AgentInputArea.vue
index 942230b1..051b505e 100644
--- a/web/src/components/AgentInputArea.vue
+++ b/web/src/components/AgentInputArea.vue
@@ -252,7 +252,7 @@ const getTodoStatusLabel = (status) => {
padding: 6px 8px;
// height: 28px;
border-radius: 8px;
- font-size: 14px;
+ font-size: 13px;
color: var(--gray-600);
cursor: pointer;
transition: all 0.2s ease;
diff --git a/web/src/components/ViewSwitchHeader.vue b/web/src/components/ViewSwitchHeader.vue
index 5cd22974..1ec5fd2e 100644
--- a/web/src/components/ViewSwitchHeader.vue
+++ b/web/src/components/ViewSwitchHeader.vue
@@ -83,10 +83,10 @@ const selectItem = (item) => {
align-items: center;
height: 28px;
padding: 0 10px;
- border: 0;
+ border: 1px solid transparent;
border-radius: 6px;
background: transparent;
- color: var(--gray-700);
+ color: var(--gray-600);
font-size: 15px;
font-weight: 500;
line-height: 1;
@@ -94,16 +94,26 @@ const selectItem = (item) => {
cursor: pointer;
transition:
background-color 0.2s ease,
- color 0.2s ease;
+ color 0.2s ease,
+ border-color 0.2s ease,
+ box-shadow 0.2s ease;
&:hover {
- color: var(--main-color);
+ color: var(--gray-900);
background-color: var(--gray-50);
}
+ &:focus-visible {
+ outline: 2px solid var(--main-200);
+ outline-offset: 2px;
+ }
+
&.active {
- color: var(--main-color);
- background-color: var(--main-40);
+ color: var(--main-700);
+ font-weight: 600;
+ background-color: var(--main-50);
+ border-color: var(--main-200);
+ box-shadow: 0 1px 2px rgba(1, 21, 31, 0.06);
}
}
diff --git a/web/src/views/AgentView.vue b/web/src/views/AgentView.vue
index a1b23ba1..ba21a25e 100644
--- a/web/src/views/AgentView.vue
+++ b/web/src/views/AgentView.vue
@@ -9,18 +9,67 @@
@thread-change="handleThreadChange"
>
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -50,6 +99,17 @@
:agent-id="selectedAgentId"
/>
+
+
+
+
@@ -78,10 +138,10 @@