Skip to content

Commit 4035f3c

Browse files
committed
feat: 优化配置侧边栏与输入区交互,增强用户体验
1 parent dda3009 commit 4035f3c

5 files changed

Lines changed: 369 additions & 189 deletions

File tree

docs/develop-guides/roadmap.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
<!-- 0.6.1 的内容请放在这里 -->
3434
- 合并知识库导航入口:左侧导航仅保留“知识库”,文档知识库与图知识库在页面 header 中通过同一组轻量切换入口切换,保留原有列表与图谱内容区交互。
35-
- 抽象页面轻量切换 header:知识库与扩展管理页直接共用 `ViewSwitchHeader`,通过统一切换样式和 actions slot 收敛文档知识库、知识图谱、Tools、MCP、Subagents、Skills 等入口的信息层级;扩展管理各列表的刷新入口下沉到搜索框右侧,并统一搜索框与工具按钮的边框和圆角。
35+
- 抽象页面轻量切换 header:知识库与扩展管理页直接共用 `ViewSwitchHeader`,通过统一切换样式和 actions slot 收敛文档知识库、知识图谱、Tools、MCP、Subagents、Skills 等入口的信息层级;扩展管理各列表的刷新入口下沉到搜索框右侧,并统一搜索框与工具按钮的边框和圆角,同时强化切换项选中态的边框、阴影与字重层级,提升 header 中当前视图的辨识度
3636
- 调整任务中心交互:入口移动到 GitHub 按钮下方,并将右侧抽屉展示改为居中弹窗,减少对主页面布局的占用。
3737
- 调整 backend Python 工作区依赖边界:将 `backend/package/yuxi` 明确为承载核心运行依赖的业务包,根 `backend/pyproject.toml` 仅保留工作区入口与开发/测试配置,减少依赖职责混淆。
3838
-`yuxi` 从 uv workspace 成员调整为 `backend/package` 下可独立构建的本地 Python 包,backend 通过 path dependency 以已安装包形式发现依赖,移除对 `PYTHONPATH=/app/package` 的运行时耦合。
@@ -53,6 +53,7 @@
5353
- 修复对话摘要中间件的工具结果卸载链路:摘要触发时改为将大体积 `ToolMessage` 写入当前 agent 可见的 sandbox outputs 路径,修正 `summary_offload` 路径拼接错误、`messages` 触发条件下不会真正裁剪历史的问题,并避免将 system message 重复纳入摘要与最终消息列表;补充对应单元测试覆盖。
5454
- 调整智能体对话中的工具调用展示:连续工具调用默认折叠为“调用了 N 个工具”的轻量摘要,展开后改为弱化时间线样式,减少工具结果卡片对正文阅读节奏的干扰。
5555
- 调整聊天首页的智能体切换入口:在无历史对话时,智能体数量 `<= 3``chat-main` 宽度不小于 `380px` 时继续使用横向 segmented;当智能体数量 `>= 4` 或内容区宽度小于 `380px` 时自动收敛为“当前智能体 + 下拉按钮”形式。
56+
- 调整对话配置入口与侧边栏头尾交互:输入区配置按钮改为轻量 dropdown 触发器,默认保持透明、hover 才出现背景;下拉中直接提供配置切换、新建配置,以及查看/编辑当前配置的入口。配置侧边栏则移除头部配置切换和新建入口,改为仅显示当前配置名称、可点击星标和关闭图标,并将删除操作下沉到底部保存按钮右侧。
5657

5758
---
5859

web/src/components/AgentConfigSidebar.vue

Lines changed: 79 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,27 @@
33
<!-- 侧边栏头部 -->
44
<div class="sidebar-header">
55
<div class="header-top-row">
6-
<div v-if="selectedAgentId" class="config-manage-row">
7-
<a-select
8-
:value="selectedAgentConfigId"
9-
:options="configSwitchOptions"
10-
class="config-switch-select"
11-
placeholder="选择配置"
12-
@update:value="handleConfigSwitch"
13-
/>
6+
<div v-if="selectedAgentId" class="config-title-row">
7+
<span class="config-title-text">{{ currentConfigName }}</span>
148
</div>
159
<div class="header-actions">
1610
<a-tooltip
1711
v-if="!isEmptyConfig && userStore.isAdmin"
1812
:title="isCurrentDefault ? '当前已是默认配置' : '设为默认配置'"
1913
>
20-
<a-button
21-
type="text"
22-
shape="circle"
23-
class="icon-btn lucide-icon-btn"
14+
<button
15+
type="button"
16+
class="header-icon-action"
2417
:class="{ 'is-default': isCurrentDefault }"
2518
@click="setAsDefault"
2619
>
2720
<Star :size="18" :fill="isCurrentDefault ? 'currentColor' : 'none'" />
28-
</a-button>
29-
</a-tooltip>
30-
31-
<a-tooltip v-if="!isEmptyConfig && userStore.isAdmin" title="删除配置">
32-
<a-button
33-
type="text"
34-
shape="circle"
35-
danger
36-
class="icon-btn lucide-icon-btn"
37-
@click="confirmDeleteConfig"
38-
:disabled="isDeletingConfig"
39-
>
40-
<Trash2 :size="18" />
41-
</a-button>
21+
</button>
4222
</a-tooltip>
4323

44-
<a-button type="text" size="small" @click="closeSidebar" class="icon-btn lucide-icon-btn">
24+
<button type="button" @click="closeSidebar" class="header-icon-action">
4525
<X :size="16" />
46-
</a-button>
26+
</button>
4727
</div>
4828
</div>
4929
</div>
@@ -78,7 +58,6 @@
7858
<!-- 统一显示所有配置项 -->
7959
<template v-for="(value, key) in filteredConfigurableItems" :key="key">
8060
<a-form-item
81-
v-if="shouldShowConfig(key, value)"
8261
:label="getConfigLabel(key, value)"
8362
:name="key"
8463
class="config-item"
@@ -312,22 +291,20 @@
312291
>
313292
保存
314293
</a-button>
294+
295+
<a-tooltip v-if="!isEmptyConfig" title="删除配置">
296+
<button
297+
type="button"
298+
class="footer-icon-btn"
299+
@click="confirmDeleteConfig"
300+
:disabled="isDeletingConfig"
301+
>
302+
<Trash2 :size="16" />
303+
</button>
304+
</a-tooltip>
315305
</div>
316306
</div>
317307

318-
<!-- 通用选择弹窗 -->
319-
320-
<a-modal
321-
v-model:open="createConfigModalOpen"
322-
title="新建配置"
323-
:width="360"
324-
:confirmLoading="createConfigLoading"
325-
@ok="handleCreateConfig"
326-
@cancel="closeCreateConfigModal"
327-
>
328-
<a-input v-model:value="createConfigName" placeholder="请输入配置名称" allow-clear />
329-
</a-modal>
330-
331308
<a-modal
332309
v-model:open="selectionModalOpen"
333310
:title="`选择${configurableItems[currentConfigKey]?.name || '项目'}`"
@@ -526,10 +503,6 @@ const liveSkillOptions = ref([])
526503
const liveMcpOptions = ref([])
527504
const liveSubagentOptions = ref([])
528505
const toolOptionsFromApi = ref([])
529-
const createConfigModalOpen = ref(false)
530-
const createConfigLoading = ref(false)
531-
const createConfigName = ref('')
532-
const CREATE_CONFIG_OPTION_VALUE = '__create_config__'
533506
const currentSegment = ref('model')
534507
const segmentOptions = [
535508
{ label: '模型', value: 'model' },
@@ -545,6 +518,10 @@ const isCurrentDefault = computed(() => {
545518
return !!selectedConfigSummary.value?.is_default
546519
})
547520
521+
const currentConfigName = computed(() => {
522+
return selectedConfigSummary.value?.name || '当前配置'
523+
})
524+
548525
const isReadOnlyConfig = computed(() => !userStore.isAdmin)
549526
550527
const isSavingConfig = ref(false)
@@ -578,24 +555,6 @@ const filteredConfigurableItems = computed(() => {
578555
return filtered
579556
})
580557
581-
const configSwitchOptions = computed(() => {
582-
if (!selectedAgentId.value) return []
583-
const list = agentConfigs.value[selectedAgentId.value] || []
584-
const options = list.map((cfg) => ({
585-
label: cfg.is_default ? `${cfg.name}(默认)` : cfg.name,
586-
value: cfg.id
587-
}))
588-
589-
if (userStore.isAdmin) {
590-
options.push({
591-
label: '+ 新建配置',
592-
value: CREATE_CONFIG_OPTION_VALUE
593-
})
594-
}
595-
596-
return options
597-
})
598-
599558
const loadLiveSkillOptions = async (force = false) => {
600559
if (!userStore.isAdmin) {
601560
liveSkillOptions.value = []
@@ -834,69 +793,13 @@ const filteredOptions = computed(() => {
834793
})
835794
836795
// 方法
837-
const handleConfigSwitch = async (configId) => {
838-
if (configId === CREATE_CONFIG_OPTION_VALUE) {
839-
openCreateConfigModal()
840-
return
841-
}
842-
843-
if (!configId || configId === selectedAgentConfigId.value) return
844-
try {
845-
await agentStore.selectAgentConfig(configId)
846-
} catch (error) {
847-
console.error('切换配置出错:', error)
848-
message.error('切换配置失败')
849-
}
850-
}
851-
852796
const updateConfigValue = (key, value) => {
853797
if (isReadOnlyConfig.value) return
854798
agentStore.updateAgentConfig({
855799
[key]: value
856800
})
857801
}
858802
859-
const openCreateConfigModal = () => {
860-
if (!userStore.isAdmin) return
861-
createConfigName.value = ''
862-
createConfigModalOpen.value = true
863-
}
864-
865-
const closeCreateConfigModal = () => {
866-
createConfigModalOpen.value = false
867-
createConfigName.value = ''
868-
}
869-
870-
const handleCreateConfig = async () => {
871-
if (!userStore.isAdmin) return
872-
if (!selectedAgentId.value) return
873-
const name = createConfigName.value.trim()
874-
if (!name) {
875-
message.error('请输入配置名称')
876-
return
877-
}
878-
879-
createConfigLoading.value = true
880-
try {
881-
await agentStore.createAgentConfigProfile({
882-
name,
883-
setDefault: false,
884-
fromCurrent: false
885-
})
886-
closeCreateConfigModal()
887-
message.success('配置已创建')
888-
} catch (error) {
889-
console.error('创建配置出错:', error)
890-
message.error(error.message || '创建配置失败')
891-
} finally {
892-
createConfigLoading.value = false
893-
}
894-
}
895-
896-
const shouldShowConfig = () => {
897-
return true
898-
}
899-
900803
const closeSidebar = () => {
901804
emit('close')
902805
}
@@ -1185,81 +1088,61 @@ const confirmDeleteConfig = async () => {
11851088
width: 100%;
11861089
}
11871090
1188-
.config-manage-row {
1091+
.config-title-row {
11891092
display: flex;
11901093
align-items: center;
11911094
flex: 1;
11921095
min-width: 0;
1096+
}
11931097
1194-
.config-switch-select {
1195-
flex: 1;
1196-
min-width: 0;
1197-
1198-
:deep(.ant-select-selector) {
1199-
height: 32px;
1200-
border-radius: 8px;
1201-
border-color: var(--gray-200);
1202-
padding: 0 10px;
1203-
transition: border-color 0.2s ease;
1204-
}
1205-
1206-
:deep(.ant-select-selection-search-input),
1207-
:deep(.ant-select-selection-item),
1208-
:deep(.ant-select-selection-placeholder) {
1209-
line-height: 30px;
1210-
font-size: 13px;
1211-
}
1212-
1213-
:deep(.ant-select.ant-select-focused .ant-select-selector),
1214-
:deep(.ant-select-selector:hover) {
1215-
border-color: var(--main-color);
1216-
box-shadow: none;
1217-
}
1218-
}
1098+
.config-title-text {
1099+
min-width: 0;
1100+
font-size: 14px;
1101+
font-weight: 600;
1102+
color: var(--gray-900);
1103+
overflow: hidden;
1104+
text-overflow: ellipsis;
1105+
white-space: nowrap;
12191106
}
12201107
12211108
.header-actions {
12221109
display: flex;
12231110
align-items: center;
1224-
gap: 8px;
1111+
gap: 4px;
12251112
margin-left: auto;
12261113
}
12271114
}
12281115
1229-
.icon-btn {
1230-
width: 32px;
1231-
height: 32px;
1232-
border-radius: 8px;
1233-
color: var(--gray-600);
1234-
border: 1px solid var(--gray-200);
1235-
background: var(--gray-0);
1116+
.header-icon-action {
1117+
display: inline-flex;
1118+
align-items: center;
1119+
justify-content: center;
1120+
width: 28px;
1121+
height: 28px;
12361122
padding: 0;
1123+
border: none;
1124+
background: transparent;
1125+
border-radius: 6px;
1126+
color: var(--gray-600);
12371127
transition:
12381128
color 0.2s ease,
1239-
border-color 0.2s ease,
12401129
background-color 0.2s ease;
1130+
cursor: pointer;
12411131
12421132
&:hover:not(:disabled) {
12431133
color: var(--main-600);
1244-
border-color: var(--main-200);
1245-
background: var(--main-10);
1134+
background: var(--gray-100);
12461135
}
12471136
1248-
&.is-default {
1137+
&.is-default,
1138+
&.is-default:hover:not(:disabled) {
12491139
color: var(--color-warning-500);
12501140
}
12511141
1252-
&.ant-btn-dangerous:hover:not(:disabled) {
1253-
color: var(--color-error-700);
1254-
border-color: var(--color-error-100);
1255-
background: var(--color-error-50);
1256-
}
1257-
12581142
&:disabled {
12591143
cursor: not-allowed;
12601144
background: transparent;
12611145
color: var(--gray-400);
1262-
border-color: var(--gray-200);
12631146
12641147
&.is-default {
12651148
opacity: 1;
@@ -1450,7 +1333,7 @@ const confirmDeleteConfig = async () => {
14501333
align-items: center;
14511334
14521335
.footer-main-btn {
1453-
width: 100%;
1336+
flex: 1;
14541337
height: 36px;
14551338
border-radius: 8px;
14561339
font-size: 14px;
@@ -1484,6 +1367,36 @@ const confirmDeleteConfig = async () => {
14841367
color: var(--gray-400);
14851368
}
14861369
}
1370+
1371+
.footer-icon-btn {
1372+
width: 36px;
1373+
height: 36px;
1374+
display: inline-flex;
1375+
align-items: center;
1376+
justify-content: center;
1377+
flex-shrink: 0;
1378+
border: 1px solid var(--gray-200);
1379+
border-radius: 8px;
1380+
background: var(--gray-0);
1381+
color: var(--gray-500);
1382+
cursor: pointer;
1383+
transition:
1384+
color 0.2s ease,
1385+
border-color 0.2s ease,
1386+
background-color 0.2s ease;
1387+
1388+
&:hover:not(:disabled) {
1389+
color: var(--color-error-700);
1390+
border-color: var(--color-error-100);
1391+
background: var(--color-error-50);
1392+
}
1393+
1394+
&:disabled {
1395+
cursor: not-allowed;
1396+
color: var(--gray-400);
1397+
background: var(--gray-50);
1398+
}
1399+
}
14871400
}
14881401
}
14891402
}

web/src/components/AgentInputArea.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ const getTodoStatusLabel = (status) => {
252252
padding: 6px 8px;
253253
// height: 28px;
254254
border-radius: 8px;
255-
font-size: 14px;
255+
font-size: 13px;
256256
color: var(--gray-600);
257257
cursor: pointer;
258258
transition: all 0.2s ease;

0 commit comments

Comments
 (0)