Skip to content

Commit d19b0ff

Browse files
committed
feat(web): add project session filter
1 parent c53ff23 commit d19b0ff

3 files changed

Lines changed: 33 additions & 11 deletions

File tree

apps/web/static/app.js

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,9 +1644,10 @@ function renderChat(){
16441644
<div class="rail-title">会话库</div>
16451645
<input id="sessionSearch" placeholder="搜索会话" value="${esc(chatSessionFilter.q)}" oninput="chatSessionFilter.q=this.value;renderSessionList()" aria-label="搜索会话"/>
16461646
<div class="session-filter" role="tablist" aria-label="会话状态">
1647-
<button class="${chatSessionFilter.status==='active'?'active':''}" onclick="setSessionFilter('active',this)">活跃</button>
1648-
<button class="${chatSessionFilter.status==='archived'?'active':''}" onclick="setSessionFilter('archived',this)">归档</button>
1649-
<button class="${chatSessionFilter.status==='all'?'active':''}" onclick="setSessionFilter('all',this)">全部</button>
1647+
<button data-status="active" class="${chatSessionFilter.status==='active'?'active':''}" onclick="setSessionFilter('active',this)">活跃</button>
1648+
<button data-status="project" class="${chatSessionFilter.status==='project'?'active':''}" onclick="setSessionFilter('project',this)">项目</button>
1649+
<button data-status="archived" class="${chatSessionFilter.status==='archived'?'active':''}" onclick="setSessionFilter('archived',this)">归档</button>
1650+
<button data-status="all" class="${chatSessionFilter.status==='all'?'active':''}" onclick="setSessionFilter('all',this)">全部</button>
16501651
</div>
16511652
</div>
16521653
<div id="chatSessionList">${inlineLoading('正在读取会话')}</div>
@@ -1890,6 +1891,7 @@ function syncContextPackPanel(){
18901891
const panel=document.getElementById('contextPackPanel');
18911892
if(panel) panel.outerHTML=renderContextPackPanel();
18921893
syncChatContextBar();
1894+
renderSessionList();
18931895
}
18941896
function persistContextPack(opts={}){
18951897
contextPack=normalizeContextPack(contextPack);
@@ -2520,22 +2522,26 @@ function selectedChatContext(){
25202522
}
25212523
function setSessionFilter(status,btn){
25222524
chatSessionFilter.status=status;
2523-
document.querySelectorAll('.session-filter button').forEach(b=>b.classList.toggle('active',b===btn||b.textContent.includes(status==='active'?'活跃':status==='archived'?'归档':'全部')));
2525+
document.querySelectorAll('.session-filter button').forEach(b=>b.classList.toggle('active',b===btn||b.dataset.status===status));
25242526
renderSessionList();
25252527
}
25262528
function sessionMatches(s){
25272529
const status=chatSessionFilter.status||'active';
25282530
const q=(chatSessionFilter.q||'').trim().toLowerCase();
2529-
const okStatus=status==='all' || (s.status||'active')===status;
2530-
const haystack=[s.title,s.id,s.agent_id,s.status].filter(Boolean).join(' ').toLowerCase();
2531+
const pack=normalizeContextPack(contextPack);
2532+
const okStatus=status==='all'
2533+
|| (status==='project' ? Boolean(pack.sessionId && s.id===pack.sessionId) : (s.status||'active')===status);
2534+
const haystack=[s.title,s.id,s.agent_id,s.status,pack.sessionId===s.id?'project context pack':''].filter(Boolean).join(' ').toLowerCase();
25312535
return okStatus && (!q || haystack.includes(q));
25322536
}
25332537
function sessionCard(s){
25342538
const archived=(s.status||'active')==='archived';
2535-
return `<article class="session-item ${s.id===activeSessionId?'active':''} ${archived?'archived':''}">
2536-
<button class="session-open" onclick="loadChatSession('${jsArg(s.id)}')"><b>${esc(sessionTitle(s))}</b><span>${esc(timeText(s.updated_at))} · ${esc(s.agent_id||'auto')}</span></button>
2539+
const linked=normalizeContextPack(contextPack).sessionId===s.id;
2540+
return `<article class="session-item ${s.id===activeSessionId?'active':''} ${archived?'archived':''} ${linked?'linked':''}">
2541+
<button class="session-open" onclick="loadChatSession('${jsArg(s.id)}')"><b>${esc(sessionTitle(s))}</b><span>${esc(timeText(s.updated_at))} · ${esc(s.agent_id||'auto')}${linked?' · 工作包':''}</span></button>
25372542
<div class="session-actions">
25382543
<button title="重命名会话" onclick="renameChatSession('${jsArg(s.id)}',this)">改名</button>
2544+
<button title="${linked?'已绑定到工作包':'绑定到当前工作包'}" onclick="bindSessionToContextPack('${jsArg(s.id)}',this)" ${linked?'disabled':''}>${linked?'已绑定':'绑定'}</button>
25392545
<button title="${archived?'恢复会话':'归档会话'}" onclick="toggleChatSessionArchive('${jsArg(s.id)}',${archived},this)">${archived?'恢复':'归档'}</button>
25402546
</div>
25412547
</article>`;
@@ -2544,7 +2550,10 @@ function renderSessionList(){
25442550
const box=document.getElementById('chatSessionList');
25452551
if(!box) return;
25462552
const filtered=chatSessions.filter(sessionMatches);
2547-
box.innerHTML=filtered.length?filtered.slice(0,40).map(sessionCard).join(''):emptyState('暂无匹配会话','调整搜索或切换活跃/归档状态。');
2553+
const empty=chatSessionFilter.status==='project'
2554+
? emptyState('暂无项目会话','在任一会话卡片点击“绑定”,或加载含来源会话的工作包预设。')
2555+
: emptyState('暂无匹配会话','调整搜索或切换活跃/归档状态。');
2556+
box.innerHTML=filtered.length?filtered.slice(0,40).map(sessionCard).join(''):empty;
25482557
}
25492558
async function refreshChatSessions(){
25502559
chatSessions=await api('/api/sessions').catch(()=>[]);
@@ -2598,6 +2607,17 @@ async function toggleChatSessionArchive(id,isArchived,btn){
25982607
toast('会话状态更新失败:'+e.message);
25992608
}
26002609
}
2610+
function bindSessionToContextPack(id,btn){
2611+
if(!id) return;
2612+
setBusy(btn,true);
2613+
try{
2614+
contextPack.sessionId=id;
2615+
contextPack.updatedAt=new Date().toISOString();
2616+
persistContextPack({toast:'会话已绑定到当前工作包'});
2617+
}finally{
2618+
setBusy(btn,false);
2619+
}
2620+
}
26012621
function startNewChat(){
26022622
activeSessionId='';
26032623
activeChatMessages=[];

apps/web/static/styles.css

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,11 +602,12 @@ button.ghost,.prompt-pill{background:#fff;color:#2d2d2a;border-color:#e1ded6}
602602
.rail-title{font-size:12px;font-weight:900;color:#7d786d;margin:2px 4px;text-transform:uppercase}
603603
.session-controls{display:grid;gap:8px}
604604
.session-controls input{min-height:36px;padding:8px 10px}
605-
.session-filter{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px}
605+
.session-filter{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}
606606
.session-filter button{background:#fff;color:#4b4842;border:1px solid var(--line);border-radius:999px;padding:7px 8px;box-shadow:none;font-size:12px}
607607
.session-filter button.active{background:#171717;color:#fff;border-color:#171717}
608608
.session-item{display:grid;gap:8px;width:100%;text-align:left;background:#fff;color:#2d2d2a;border:1px solid transparent;border-radius:12px;padding:8px;box-shadow:none}
609609
.session-item:hover,.session-item.active{background:#f5f4ef;border-color:#e1ded6;transform:none;filter:none}
610+
.session-item.linked{background:#f6fbf7;border-color:#cfe8dc}
610611
.session-item.archived{opacity:.76}
611612
.session-open{display:grid;gap:3px;width:100%;text-align:left;background:transparent;color:#2d2d2a;border:0;border-radius:8px;padding:2px;box-shadow:none}
612613
.session-open:hover{background:transparent;color:#2d2d2a;filter:none;transform:none}
@@ -615,6 +616,7 @@ button.ghost,.prompt-pill{background:#fff;color:#2d2d2a;border-color:#e1ded6}
615616
.session-actions{display:flex;gap:6px;flex-wrap:wrap}
616617
.session-actions button{background:#fff;color:#6b6b62;border:1px solid var(--line);border-radius:999px;padding:5px 8px;box-shadow:none;font-size:11px}
617618
.session-actions button:hover{background:#171717;color:#fff;border-color:#171717;filter:none;transform:none}
619+
.session-actions button:disabled{color:#0d6d58;background:#eef8f3;border-color:#cfe8dc;cursor:not-allowed}
618620
.context-card{border-top:1px solid var(--line);padding-top:12px;margin-top:auto}
619621
.context-card>b{display:block;margin-bottom:8px}
620622
.context-pack-card{display:grid;gap:9px;border:1px solid var(--line);border-radius:14px;background:#fbfaf7;padding:12px;min-width:0}

docs/ui-function-map.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
1. **控制塔**:总体状态、Agent/Trace/SQL/Codex 指标、关键链路、快捷分析入口、任务 composer、最近会话/报告继续工作面板和最近任务队列 Trace 下钻。
66
2. **Agent Studio**:按能力目录选择 Agent,支持名称/ID/Adapter 搜索、能力类型和风险筛选,右侧 inspector 查看风险、版本、知识绑定,并直接进入问数/研究。
7-
3. **智能问数**:左侧场景问题、可搜索/重命名/归档的会话库和 Project-style 工作包,中间对话与图表/表格/报告,右侧 Trace 证据链和可编辑工作 Canvas;composer 显示 Agent/数据集/工具/证据/工作包上下文。工作包支持搜索并加入数据集、知识库和报告资产,可逐项打开或移除上下文,可保存/加载/删除本地项目预设,可设置项目内/默认记忆边界,可把 Canvas 草稿随 `context_pack.canvas_markdown` 提交给问数请求,可把知识库随 `context_pack.knowledge_base_ids` 一起提交给问数请求,并提供解释指标、生成图表、打开数据资产、转深度研究、创建 Codex 任务、发送中停止生成、用户问题编辑/重问/分支/写入 Canvas/复制、会话 Brief 生成/复制/下载、回答写入/追加 Canvas、提取下一步、回答后续追问建议、捕获上下文、应用工作包、生成分析大纲/SQL 草稿/报告要点、Canvas Markdown 加粗/斜体/标题/项目列表/编号列表格式化、Canvas 选区润色/补证据/压缩/展开/追问、本地版本回退、版本变化对比、保存报告草稿和下载 Markdown 的快捷动作。
7+
3. **智能问数**:左侧场景问题、可搜索/重命名/归档/项目筛选的会话库和 Project-style 工作包,中间对话与图表/表格/报告,右侧 Trace 证据链和可编辑工作 Canvas;composer 显示 Agent/数据集/工具/证据/工作包上下文。工作包支持搜索并加入数据集、知识库和报告资产,可逐项打开或移除上下文,可绑定来源会话并在会话库中只看项目会话,可保存/加载/删除本地项目预设,可设置项目内/默认记忆边界,可把 Canvas 草稿随 `context_pack.canvas_markdown` 提交给问数请求,可把知识库随 `context_pack.knowledge_base_ids` 一起提交给问数请求,并提供解释指标、生成图表、打开数据资产、转深度研究、创建 Codex 任务、发送中停止生成、用户问题编辑/重问/分支/写入 Canvas/复制、会话 Brief 生成/复制/下载、回答写入/追加 Canvas、提取下一步、回答后续追问建议、捕获上下文、应用工作包、生成分析大纲/SQL 草稿/报告要点、Canvas Markdown 加粗/斜体/标题/项目列表/编号列表格式化、Canvas 选区润色/补证据/压缩/展开/追问、本地版本回退、版本变化对比、保存报告草稿和下载 Markdown 的快捷动作。
88
4. **深度研究**:研究 brief、可审批计划、历史研究任务队列、证据 Trace、报告草稿入口和取消/刷新任务。
99
5. **分析面板**:物化经营总览面板、创建面板 brief、添加 Widget,并通过 SQL Guard 预校验后刷新面板证据。
1010
6. **数据能力**:可搜索/筛选的数据资产库、数据集详情、字段/指标、只读 SQL Workbench、数据画像、业务规则运行、质量历史复核,以及从数据集直接进入问数/研究/Codex 治理任务的上下文动作。

0 commit comments

Comments
 (0)