mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-11 12:12:19 -10:00
* 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]>
212 lines
3.9 KiB
CSS
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;
|
|
}
|