mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-11 17:43:43 -10:00
* feat(web-shell): add a Workspaces overview panel Layer B2 of #10399: a full-page table of every registered workspace — name with primary/untrusted badges, path, active session counts, MCP health (unknown while the runtime is not initialized, never zero), branch with a dirty counter, and last activity — with New task and Remove actions per row. Entries: a Manage workspaces row at the end of the Projects section (hidden for a locked sidebar) and an opt-in workspacesOverview footer item outside the default set. Daemon-owned live runtimes are not rows. The sidebar's inline removal state machine moves verbatim into a shared useWorkspaceRemoval hook plus WorkspaceRemovalDialog so both surfaces run the same confirm/busy/force/mismatch/in-progress flow; the sidebar keeps its reconciliation as the hook's onRemoved callback. Cells reuse layer-A plumbing: catalog-store session counts deduplicated with the sidebar's queries, useWorkspaceOverview for MCP, and the git chip's fetch discipline for the branch column. Untrusted rows fetch nothing. * fix(web-shell): align the Workspaces panel's cells and gates with review round 1 Three behavioural fixes: a capabilities-refresh rejection after a daemon-confirmed removal is no longer misreported as a removal failure (the panel's onRemoved guards the refresh the way the sidebar's reconcile does); the MCP fraction excludes disabled servers from its denominator, matching the sidebar chip's connected/enabled convention; and last activity falls back to createdAt like the daemon's getSummaryActivityTime, so a live session that has not published a terminal still counts. Smaller alignments: session counts treat a nextCursor page as a lower bound (the sidebar's union); the opt-in footer entry honours the locked gate and carries a testid; the panel gets its aria-label and heading focus when opened; the dedup comment states what actually dedupes. Coverage the review asked for: the in-progress retry loop (converges, busy mid-retry, dismissed, exhausted), submitting reset across sequential removals, the active-session force block through the panel, the per-row query and facet shape, the New-task double-submit guard and its App wiring, the footer entry, an MCP-cell-scoped unknown assertion, and a cross-workspace facet witness in the e2e spec. * fix(web-shell): stabilize the removal controller and pin review round 2 The one behavioural change: useWorkspaceRemoval reads its options through a ref and memoizes the controller it returns, and App passes createNewSession directly, so the panel's column definitions stop rebuilding on every render — a rebuilt column set is a new component type per cell, which unmounted every row cell and re-fired their git fetches on any App re-render while the panel was open. The rest is coverage round 2 asked for: both remaining retry-loop exits (mid-retry mismatch reconciles, a non-transient error surfaces itself rather than the synthetic exhaustion message), the dismissed flag reset and stale busy-state reset in request(), the reconcile-before-clear ordering (a hung reconcile keeps the dialog open), the allow direction of the panel's force gate, hook-level blockForce at both call sites past the disabled attribute (invoking the button's props directly, as a stale reference would), the global cross-row New-task guard, the wait:true git contract, the truncated-without-cursor lower bound, the unwired footer opt-in rendering nothing, heading focus on panel open, and an honest scope note on the e2e cross-wiring witness. * test(web-shell): pin fresh option reads and submitting across the reconcile Two review-round-3 pins on the removal hook. Options are read through a ref so blockForce observes the connection state at confirm time — a busy dialog whose candidate gains the active session between renders must refuse the forced retry; the new test re-renders the probe with a flipped blockForce and goes red if the ref assignment is dropped. And submitting stays true for the whole reconcile window, keeping every dialog gate locked during the caller's capability round-trip; the hung-reconcile test now asserts it mid-flight and goes red if confirm stops awaiting handleRemoved. * test(web-shell): pin the busy report across a blocked forced confirm force derives from activity, so a block branch that cleared it would silently downgrade the next click to a non-forced attempt and restart the consent flow; the blocked-confirm test now asserts the report and candidate both survive the refusal.
106 lines
1.5 KiB
CSS
106 lines
1.5 KiB
CSS
.page {
|
|
display: flex;
|
|
min-height: 100%;
|
|
flex-direction: column;
|
|
margin: -16px -20px 0;
|
|
}
|
|
|
|
.pageHeader {
|
|
display: flex;
|
|
min-height: 46px;
|
|
flex: 0 0 auto;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 10px 20px;
|
|
border-block-end: 1px solid var(--border);
|
|
}
|
|
|
|
.backButton {
|
|
width: 30px;
|
|
height: 30px;
|
|
flex: 0 0 30px;
|
|
color: var(--muted-foreground);
|
|
}
|
|
|
|
.title {
|
|
min-width: 0;
|
|
color: var(--foreground);
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
line-height: 1.35;
|
|
outline: none;
|
|
}
|
|
|
|
.pageBody {
|
|
display: flex;
|
|
min-width: 0;
|
|
flex-direction: column;
|
|
gap: 16px;
|
|
padding: 20px 24px 32px;
|
|
}
|
|
|
|
.toolbar {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
}
|
|
|
|
.count {
|
|
min-width: 0;
|
|
color: var(--muted-foreground);
|
|
font-size: 13px;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.toolbarActions {
|
|
display: flex;
|
|
flex: 0 0 auto;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.nameCell {
|
|
display: flex;
|
|
min-width: 0;
|
|
align-items: center;
|
|
gap: 6px;
|
|
}
|
|
|
|
.nameLabel {
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
font-weight: 500;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.pathCell {
|
|
display: block;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
color: var(--muted-foreground);
|
|
font-size: 12px;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.statCell {
|
|
display: flex;
|
|
min-width: 0;
|
|
align-items: center;
|
|
gap: 6px;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.muted {
|
|
color: var(--muted-foreground);
|
|
}
|
|
|
|
.actionsCell {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: flex-end;
|
|
gap: 6px;
|
|
}
|