mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-29 11:34:40 +00:00
* feat(web-shell): approval and ask-user dialogs as in-flow sheets without covering messages When a tool approval or ask-user-question dialog is pending, the composer and the pane status area are hidden and the dialog occupies the composer slot in flow, so the message list shrinks above it instead of being covered by a floating overlay. The dialog width now matches the chat content width. Also, while an agent's launch approval is pending, its row stays compact and is not openable (there is nothing to show yet), and the launch dialog defaults its focus to the one-shot allow instead of the reject button. * fix(web-shell): don't fail background agents while their launch approval is pending The background-agent reconciliation probes /subagents/:callId for every active background agent. While the agent's launch approval is unanswered its subagent session cannot exist yet, so the probe 404s and, after the two-miss grace, the reconciliation marked the agent failed — painting a red error on the row while the permission dialog was still up. Skip reconciliation for callIds with an unresolved permission request, and resume probing once the permission resolves. * style(web-shell): drop notification bubble row margin * fix(web-shell): stabilize reconciliation dependency key; gate parallel agent rows under launch approval Address review findings on the approval-dialog and background-agent changes: - R1-1 (Critical): the pending-permission effect dependency was a fresh Set on every transcript delta, so the reconciliation effect re-ran on each streamed update, bypassing the retry backoff and re-introducing the false-failure the change set out to fix. Depend on a stable sorted-key string instead and rebuild the membership Set inside the effect. - R1-2: add a two-agent reconciliation test pinning that exclusion is per callId (the healthy sibling keeps probing while one agent is under approval). - R1-3: apply the non-openable-while-pending guard to ParallelAgentsGroup rows, which previously bypassed it when agents rendered grouped. - R1-4: add a test that an accumulated missing-agent miss is fully reset when a launch approval engages, so the post-approval grace restarts fresh. * fix(web-shell): address round-2 review findings - useMessages: pace the missing-agent grace by wall clock so a re-probe triggered by an unrelated permission change cannot collapse the retry ladder into two immediate misses, and never count misses or retry-budget errors for agents excluded by a pending permission (an in-flight round settles after the exclusion cleanup ran and would otherwise re-add them) - App.module.css: keep the approval sheet above portal-hosted DialogShell modals by restoring its z-index while preserving the in-flow layout - ParallelAgentsGroup: the pending-approval row keeps its status dot, shows a pending label instead of running, and is inert (no pointer, no hover highlight, no rowActive sweep); add a regression test for the inert row * fix(web-shell): pace background agent reconciliation --------- Co-authored-by: 钉萁 <dingqi.jww@alibaba-inc.com>
234 lines
5.2 KiB
CSS
234 lines
5.2 KiB
CSS
.pane {
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-width: 0;
|
|
min-height: 0;
|
|
height: 100%;
|
|
background: var(--background);
|
|
border: 1px solid var(--border);
|
|
border-radius: 10px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.paneEmbedded {
|
|
border: 0;
|
|
border-radius: 0;
|
|
}
|
|
|
|
.paneEmbedded .footer {
|
|
padding-bottom: 0;
|
|
}
|
|
|
|
.header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 8px 10px;
|
|
/* On a multi-workspace pane, `--ws-accent` is set by a shared accent class
|
|
(workspaceAccent.module.css) and colorizes this divider into a per-workspace
|
|
strip; it falls back to the neutral border everywhere else, so single-
|
|
workspace panes are unchanged. */
|
|
border-bottom: 1px solid var(--ws-accent, var(--border));
|
|
background: color-mix(in srgb, var(--foreground) 3%, transparent);
|
|
flex: 0 0 auto;
|
|
}
|
|
|
|
/* The `--ws-accent` variable that colorizes the divider above and the tag below
|
|
is set by a shared accent class from workspaceAccent.module.css (also used by
|
|
the composer chip), applied to `.header` when the pane has a workspace. */
|
|
|
|
/* Which workspace this pane's session lives in — a compact colored chip at the
|
|
start of the header, shown only on a multi-workspace daemon. The dot never
|
|
shrinks, so panes stay distinguishable by color even when the name and the
|
|
session title both ellipsize on a narrow split; the full cwd is in the
|
|
native title tooltip. */
|
|
.workspaceTag {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
flex: 0 1 auto;
|
|
min-width: 0;
|
|
max-width: 50%;
|
|
padding: 2px 8px 2px 6px;
|
|
border-radius: 999px;
|
|
background: color-mix(in srgb, var(--ws-accent) 14%, transparent);
|
|
color: color-mix(in srgb, var(--ws-accent) 70%, var(--foreground));
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.workspaceTagDot {
|
|
width: 7px;
|
|
height: 7px;
|
|
flex: 0 0 7px;
|
|
border-radius: 999px;
|
|
background: var(--ws-accent);
|
|
}
|
|
|
|
.workspaceTagText {
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* Per-pane connection-loss indicator: each pane has its own daemon connection,
|
|
so a drop on one shouldn't silently leave stale messages with no signal. */
|
|
.connectionError {
|
|
flex: 0 0 auto;
|
|
padding: 6px 10px;
|
|
background: var(--warning-bg);
|
|
border-bottom: 1px solid var(--warning-border);
|
|
color: var(--warning-color);
|
|
font-size: 12px;
|
|
}
|
|
.connectionErrorText {
|
|
display: block;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.title {
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--foreground);
|
|
}
|
|
|
|
.headerActions {
|
|
position: relative;
|
|
flex: 0 0 auto;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
min-width: 0;
|
|
}
|
|
|
|
.headerActionsInline {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
min-width: 0;
|
|
}
|
|
|
|
.headerActionSlot {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
}
|
|
|
|
/* Keep host actions mounted while collapsed so stateful actions survive
|
|
resize. Absolutely positioned so they do not affect flex layout. */
|
|
.headerActionsHostHidden {
|
|
position: absolute;
|
|
visibility: hidden;
|
|
pointer-events: none;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* The built-in maximize/close controls used to be direct children of `.header`
|
|
(gap 8px); keep that spacing now that they are grouped here. */
|
|
.headerTrailing {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
flex: 0 0 auto;
|
|
}
|
|
|
|
.headerOverflowPanel {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
gap: 4px;
|
|
padding: 2px;
|
|
}
|
|
|
|
.headerActionButton,
|
|
.closeButton,
|
|
.maximizeButton {
|
|
flex: 0 0 auto;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 26px;
|
|
height: 26px;
|
|
border: none;
|
|
border-radius: 6px;
|
|
background: transparent;
|
|
color: var(--muted-foreground);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.headerActionButton:hover,
|
|
.closeButton:hover,
|
|
.maximizeButton:hover {
|
|
color: var(--foreground);
|
|
background: color-mix(in srgb, var(--foreground) 8%, transparent);
|
|
}
|
|
|
|
/* The maximized pane keeps its toggle visually "active" so it's clear which
|
|
control returns to the tiled layout. */
|
|
.maximizeButton[aria-pressed='true'] {
|
|
color: var(--foreground);
|
|
background: color-mix(in srgb, var(--foreground) 10%, transparent);
|
|
}
|
|
|
|
/* The transcript takes the remaining height and scrolls independently per pane. */
|
|
.body {
|
|
flex: 1 1 auto;
|
|
min-height: 0;
|
|
overflow-y: auto;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.footer {
|
|
flex: 0 0 auto;
|
|
padding: 8px;
|
|
position: relative;
|
|
}
|
|
|
|
.approval {
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.composerHidden {
|
|
display: none;
|
|
}
|
|
|
|
.admissionUnknown {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 8px;
|
|
margin-bottom: 8px;
|
|
padding: 7px 9px;
|
|
border: 1px solid var(--warning-border);
|
|
border-radius: 8px;
|
|
background: var(--warning-bg);
|
|
color: var(--warning-color);
|
|
font-size: 12px;
|
|
}
|
|
|
|
.admissionUnknownActions {
|
|
display: inline-flex;
|
|
flex: 0 0 auto;
|
|
gap: 6px;
|
|
}
|
|
|
|
.admissionUnknownActions button {
|
|
border: 1px solid var(--warning-border);
|
|
border-radius: 6px;
|
|
padding: 3px 7px;
|
|
background: var(--background);
|
|
color: inherit;
|
|
cursor: pointer;
|
|
}
|