Files
qwen-code/packages/web-shell/client/components/SplitView.module.css
2SinceJulyand2SinceJuly 3a4a077362 fix(web-shell): keep "+ Add session" clickable when the catalog is empty (#15835)
* fix(web-shell): keep + Add session clickable when the catalog is empty

The add button was gated on the cached session catalog already containing an
addable session, so the picker could never open to refresh itself in the one
case its reload-on-open effect exists for. Gate the button on pane capacity
alone and let the picker report an empty list after reloading.

Closes #15678

* test(web-shell): pin the empty-picker guard with a negative assertion

* test(web-shell): assert add-session button disables at MAX_PANES

The PR narrowed canAdd to the capacity term only, and the new test
pinned just the removed half (empty catalog stays enabled). Without a
cap assertion, dropping the pane-count check would leave every test
green while the picker could open a seventh pane.

* test(web-shell): pin the add button stays enabled below MAX_PANES

---------

Co-authored-by: 2SinceJuly <[email protected]>
2026-10-30 23:40:51 +08:00

212 lines
3.9 KiB
CSS

.split {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
color: var(--foreground);
}
.toolbar {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 14px;
border-bottom: 1px solid var(--border);
flex: 0 0 auto;
}
.backButton {
display: inline-flex;
align-items: center;
justify-content: center;
width: 30px;
height: 30px;
border: none;
border-radius: 6px;
background: transparent;
color: var(--foreground);
cursor: pointer;
}
.backButton:hover {
background: color-mix(in srgb, var(--foreground) 8%, transparent);
}
.title {
font-size: 14px;
font-weight: 600;
}
.count {
color: var(--muted-foreground);
font-size: 12px;
}
.addWrap {
position: relative;
margin-left: auto;
}
.pendingButton {
flex: 0 0 auto;
padding: 4px 10px;
border: 1px solid var(--warning-border);
border-radius: 8px;
background: var(--warning-bg);
color: var(--warning-color);
font-size: 12px;
cursor: pointer;
}
.addButton {
appearance: none;
padding: 4px 12px;
border: 1px solid var(--primary);
border-radius: 8px;
background: var(--primary);
color: var(--background);
font-size: 13px;
font-weight: 500;
cursor: pointer;
}
.addButton:disabled {
opacity: 0.55;
cursor: default;
border-color: var(--border);
background: transparent;
color: var(--muted-foreground);
}
.picker {
position: absolute;
top: calc(100% + 6px);
right: 0;
z-index: 30;
list-style: none;
margin: 0;
padding: 4px;
min-width: 220px;
max-height: 320px;
overflow-y: auto;
border: 1px solid var(--border);
border-radius: 8px;
background: var(--background);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.28);
}
.pickerItem {
display: flex;
align-items: baseline;
gap: 8px;
width: 100%;
text-align: left;
padding: 7px 10px;
border: none;
border-radius: 6px;
background: transparent;
color: var(--foreground);
font-size: 13px;
cursor: pointer;
}
.pickerEmpty {
padding: 7px 10px;
color: var(--muted-foreground);
font-size: 13px;
}
.pickerItemLabel {
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Which workspace a session lives in — only shown on a multi-workspace daemon,
so the single-workspace picker is unchanged. */
.pickerItemWorkspace {
flex: 0 0 auto;
max-width: 45%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 11px;
color: var(
--muted-foreground,
color-mix(in srgb, var(--foreground) 55%, transparent)
);
}
.pickerItem:hover {
background: color-mix(in srgb, var(--foreground) 8%, transparent);
}
/* Panes fill the remaining height and share the width; each pane scrolls its
own transcript. Below the fit width they keep a minimum and scroll sideways. */
.panes {
flex: 1 1 auto;
min-height: 0;
display: flex;
gap: 10px;
padding: 10px;
overflow-x: auto;
}
.paneSlot {
flex: 1 1 0;
min-width: 340px;
height: 100%;
min-height: 0;
}
/* While one pane is maximized, its siblings are hidden (but stay mounted, so
their sessions keep streaming). The lone visible slot then fills the row. */
.paneSlot[data-pane-hidden] {
display: none;
}
.empty {
flex: 1 1 auto;
display: flex;
align-items: center;
justify-content: center;
color: var(--muted-foreground);
font-size: 13px;
}
.paneError {
display: flex;
flex-direction: column;
gap: 8px;
height: 100%;
padding: 16px;
border: 1px solid var(--error-color);
border-radius: 10px;
background: color-mix(in srgb, var(--error-color) 6%, transparent);
color: var(--foreground);
overflow: auto;
}
.paneErrorTitle {
font-size: 13px;
font-weight: 600;
}
.paneErrorMessage {
font-size: 12px;
color: var(--muted-foreground);
}
.paneErrorClose {
align-self: flex-start;
padding: 4px 12px;
border: 1px solid var(--border);
border-radius: 6px;
background: transparent;
color: var(--foreground);
font-size: 12px;
cursor: pointer;
}