Compare commits

...
Author SHA1 Message Date
weichen a54022c438 Constrain the chat column height so the message stream scrolls
Why: .chat is a column-flex item of .main without min-height:0, so its
content-based automatic minimum height grew past the viewport; .chat-body
never overflowed (never scrolled) and the input bar was pushed off-screen.
Evidence: headless Chromium on mock serve, 1024x620: before .chat=2130px
and input-bar bottom=717 (>viewport), chat-body scrollable=false; after
.chat=620, chat-body clientH 501 / scrollH 2011 with wheel scroll moving
600px up and back to bottom, input-bar bottom=620. Variants (empty
conversation, single short message, 1024x400, 720x600) keep the input
visible. Full combo make test green.
Impact: chat page was unusable beyond one screen of content; single CSS
rule chain, no behavior change elsewhere.
Refs: Agent-System-Dev task card feature/chat-scroll-fix.
2026-09-30 09:15:45 +00:00
weichen a617504268 Merge pull request 'Add the tidy action and collapsed-rail pending badge (T5)' (#8) from feature/polish-r1 into main 2026-09-30 08:18:01 +00:00
weichen 4f4711a22a Add the tidy action and collapsed-rail pending badge (T5)
Why: T5 notification strategy - the collapsed right rail must still surface the pending count (badge), and the inbox needs a one-key tidy (done cards dismissed; resolved verdicts keep their stamp).
Evidence: tsc clean; vitest 9/9; vite build ok; delegation-only logic.
Impact: App collapsed rail badge, store tidyCards (done->dismiss via existing cardDismiss), Workbench tidy button, styles.
Refs: docs/proposals/2026-09-30-ui-inbox-command-workspace.md T5; task card feature/polish-r1
2026-09-30 08:13:13 +00:00
weichen e5b65ca79a Merge pull request 'Make the right rail pure inbox and move deep views into overlay workspaces (T4)' (#7) from feature/workspaces-r1 into main 2026-09-30 08:08:25 +00:00
4 changed files with 22 additions and 3 deletions
+4 -1
View File
@@ -51,6 +51,7 @@ export function App() {
const toast = useStore(s => s.toast)
const trackerRunId = useStore(s => s.ui.trackerRunId)
const viewingArtifact = useStore(s => s.ui.viewingArtifact)
const pendingBadge = useStore(s => s.cards.filter(c => c.status === 'action_required').length)
// authState:checking → ok(放行)| required(登录门)。
const [authState, setAuthState] = useState<'checking' | 'required' | 'ok'>('checking')
@@ -140,7 +141,9 @@ export function App() {
) : (
<div className="right-panel collapsed" style={{ width: 44 }}>
<div className="icon-only">
<button title="展开工作台" onClick={store.toggleRight}>◧</button>
<button title="展开收件箱" onClick={store.toggleRight}>◧
{pendingBadge > 0 && <span className="rail-badge" data-testid="rail-badge">{pendingBadge}</span>}
</button>
</div>
</div>
)}
+1
View File
@@ -23,6 +23,7 @@ export function Workbench() {
<>
<div className="tabs">
<span className="tab active">收件箱{pending > 0 ? ` ${pending}` : ''}</span>
<button className="tab tab-action" title="收纳已完成卡(done→关闭;已确认卡留痕不动)" onClick={() => store.tidyCards()}>🧹 收纳</button>
<button className="tab" title="收起收件箱" onClick={store.toggleRight}>»</button>
</div>
<div className="tab-body">
+8
View File
@@ -418,6 +418,14 @@ export const store = {
toggleLeft() { setUI({ leftCollapsed: !state.ui.leftCollapsed }) },
toggleRight() { setUI({ rightCollapsed: !state.ui.rightCollapsed }) },
openWorkspace(id: string) { setUI({ activeWorkspace: id }) },
/** 收纳策略(T5):一键收纳已完成卡(done→dismissed;resolved 盖章留痕不动)。 */
async tidyCards() {
const done = state.cards.filter(c => c.status === 'done')
for (const c of done) {
try { const out = await api.cardDismiss(c.id); store.upsertCard(out.card) } catch { /* 单卡失败不阻断 */ }
}
if (done.length > 0) store.toast(`已收纳 ${done.length} 张完成卡`)
},
closeWorkspace() { setUI({ activeWorkspace: null }) },
openInbox(cardId?: string) {
setUI({ rightCollapsed: false, rightTab: 'inbox' })
+9 -2
View File
@@ -39,10 +39,12 @@ button { font-family: inherit; }
.icon-only button:hover { background: var(--bg-hover); color: var(--text); }
/* ---- 中栏对话 ---- */
.chat { flex: 1; display: flex; flex-direction: column; min-width: 0; }
/* min-height:0 须随列向 flex 链下传(.main→.chat→.chat-body):flex 项的自动最小
高度是内容高度,缺失时消息会撑破 .chat——chat-body 永不滚动、输入栏被顶出视口。 */
.chat { flex: 1; display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.chat-header { padding: 10px 18px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 10px; background: var(--bg-raised); }
.chat-title { font-weight: 600; flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-body { flex: 1; overflow-y: auto; padding: 18px max(18px, 12%); }
.chat-body { flex: 1; overflow-y: auto; min-height: 0; padding: 18px max(18px, 12%); }
.empty-state { height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; color: var(--text-faint); gap: 8px; }
.msg { margin-bottom: 18px; }
.msg-role { font-size: 12px; color: var(--text-faint); margin-bottom: 4px; display: flex; gap: 8px; align-items: center; }
@@ -196,3 +198,8 @@ button { font-family: inherit; }
.project-badge{font-size:11px;padding:1px 8px;border-radius:8px;background:rgba(120,160,255,.15);color:var(--accent);white-space:nowrap}
.tab-hint{font-size:10px;color:var(--text-faint);align-self:center}
.empty-note{color:var(--text-faint);font-size:12px;padding:12px 4px}
/* 收纳策略(T5):收起态角标 + 收纳按钮 */
.rail-badge{position:absolute;top:-4px;right:-6px;font-size:10px;background:var(--warn,#d29922);color:#fff;border-radius:8px;padding:0 5px;min-width:14px}
.right-panel.collapsed .icon-only button{position:relative}
.tab-action{font-size:11px;color:var(--text-faint)}