mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-07 06:19:43 -10:00
* feat(web-shell): collapse channel editor tuning behind advanced settings The shared channel editor renders every descriptor field flat, so a fresh GitHub instance shows roughly sixteen controls and the fields needed for first setup are buried under rarely changed tuning. Move the tuning fields into one collapsible advanced panel and hide the default message route until a route exists, keeping the default view to identity, credentials, conversation isolation, and access control. Collapsing never hides state: the panel opens automatically when any advanced field is configured, shows a count badge while collapsed, and forces itself open when a collapsed field fails validation. Save and validation semantics are unchanged because both already iterate every descriptor field regardless of rendering. The watch-events dependency UX is left to #16236 so the two changes stay complementary. * fix(web-shell): forward refs through the collapsible wrappers Review of #16239 showed the three new wrappers accept Radix props including ref, but plain function components drop external refs on React 18, and the repository supports both React 18 and 19. Use React.forwardRef for the root, trigger, and content, pass each ref to its Radix primitive, and pin the contract in react18-ref-compat.test.tsx with both forward-ref type and DOM-ref assertions; a plain-function mutation turns exactly the three new type assertions red. Record the contract in both design documents as well. * fix(web-shell): keep endpoints and operators visible, make collapsed errors reachable Review round 1 on #16239 found the disclosure went too far in three places and left dead scaffolding behind. Primary endpoints (baseUrl for GitHub Enterprise / self-hosted GitLab, wsUrl for WeCom) decide where a token is sent, so they stay in Connection instead of collapsing; a fresh enterprise channel could otherwise save without its endpoint and send the token to the public API. Operators is a permission grant and stays in Access control, a deliberate product decision rather than a consequence of the key set. The panel title becomes a real h3 wrapping the trigger (ARIA makes button descendants presentational, so the previous span-in-button was invisible to heading navigation), and a rejected submit now scrolls the first erroring advanced field into view instead of only opening the panel below the fold. The record rule is unified into one exported predicate shared by validation, the save path, and the visibility/auto-open readers; the unreachable routing section scaffolding is removed; a comment records why enum keys must stay out of the advanced set. Each new mechanism carries a mutation witness (endpoint placement, heading, boolean arm, record arm, scroll), and the visuals scenarios now anchor the heading, the configured-instance auto-open, and the count badge in a real browser. Both design documents are revised in lockstep.
433 lines
7.2 KiB
CSS
433 lines
7.2 KiB
CSS
.platformHeader {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
|
|
.platformMark {
|
|
display: inline-flex;
|
|
width: 42px;
|
|
height: 42px;
|
|
flex: 0 0 42px;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border: 1px solid var(--border);
|
|
border-radius: 13px;
|
|
background: var(--muted);
|
|
font-size: 13px;
|
|
font-weight: 750;
|
|
}
|
|
|
|
.form {
|
|
display: flex;
|
|
min-height: 0;
|
|
flex-direction: column;
|
|
gap: 0;
|
|
}
|
|
|
|
.body {
|
|
display: flex;
|
|
max-height: min(66vh, 650px);
|
|
flex-direction: column;
|
|
gap: 22px;
|
|
overflow-y: auto;
|
|
padding-block: 4px 8px;
|
|
padding-inline: 1px;
|
|
}
|
|
|
|
.section {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
}
|
|
|
|
.sectionHeading {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
color: var(--muted-foreground);
|
|
font-size: 11px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.08em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.sectionHeading::after {
|
|
height: 1px;
|
|
flex: 1;
|
|
background: var(--border);
|
|
content: '';
|
|
}
|
|
|
|
.settingsPanel {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 14px;
|
|
padding: 16px;
|
|
border: 1px solid var(--border);
|
|
border-radius: calc(var(--radius-lg) + 2px);
|
|
background: color-mix(in srgb, var(--card) 94%, var(--muted));
|
|
}
|
|
|
|
.settingsPanelHeader {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 3px;
|
|
}
|
|
|
|
.settingsPanelTitle {
|
|
margin: 0;
|
|
color: var(--foreground);
|
|
font-size: 14px;
|
|
font-weight: 700;
|
|
letter-spacing: -0.01em;
|
|
}
|
|
|
|
.settingsPanelDescription {
|
|
margin: 0;
|
|
color: var(--muted-foreground);
|
|
font-size: 11px;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.advancedTrigger {
|
|
display: flex;
|
|
width: 100%;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 0;
|
|
border: none;
|
|
background: none;
|
|
color: inherit;
|
|
font: inherit;
|
|
letter-spacing: inherit;
|
|
cursor: pointer;
|
|
text-align: left;
|
|
}
|
|
|
|
.advancedChevron {
|
|
flex: 0 0 auto;
|
|
color: var(--muted-foreground);
|
|
transition: transform 120ms ease;
|
|
}
|
|
|
|
.advancedTrigger[data-state='open'] .advancedChevron {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
.advancedBadge {
|
|
margin-left: auto;
|
|
padding: 2px 8px;
|
|
border: 1px solid var(--border);
|
|
border-radius: 999px;
|
|
color: var(--muted-foreground);
|
|
font-size: 11px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.advancedContent {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 14px;
|
|
}
|
|
|
|
.field {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 7px;
|
|
}
|
|
|
|
.fieldHeader {
|
|
display: flex;
|
|
min-height: 20px;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 10px;
|
|
}
|
|
|
|
.required {
|
|
color: var(--destructive);
|
|
}
|
|
|
|
.hint {
|
|
color: var(--muted-foreground);
|
|
font-size: 11px;
|
|
}
|
|
|
|
.fieldDescription {
|
|
margin: 0;
|
|
color: var(--muted-foreground);
|
|
font-size: 11px;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.optionGrid {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 6px;
|
|
}
|
|
|
|
.optionRow {
|
|
display: flex;
|
|
min-height: 34px;
|
|
cursor: pointer;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 6px 10px;
|
|
border: 1px solid var(--border);
|
|
border-radius: 999px;
|
|
background: var(--card);
|
|
user-select: none;
|
|
transition:
|
|
border-color 120ms ease,
|
|
background-color 120ms ease;
|
|
}
|
|
|
|
.optionRow:hover {
|
|
border-color: var(--foreground);
|
|
}
|
|
|
|
.optionRow[data-selected='true'] {
|
|
border-color: var(--foreground);
|
|
background: color-mix(in srgb, var(--muted) 62%, transparent);
|
|
}
|
|
|
|
.optionCheckbox {
|
|
width: 14px;
|
|
height: 14px;
|
|
flex: 0 0 auto;
|
|
margin: 0;
|
|
cursor: pointer;
|
|
accent-color: var(--primary);
|
|
}
|
|
|
|
.optionLabel {
|
|
font-size: 12px;
|
|
line-height: 1.35;
|
|
}
|
|
|
|
.recordFields {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
}
|
|
|
|
.recordRow {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 3px;
|
|
}
|
|
|
|
.recordLabel {
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
color: var(--muted-foreground);
|
|
}
|
|
|
|
.secretState {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 10px;
|
|
padding: 10px 11px;
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-lg);
|
|
background: color-mix(in srgb, var(--muted) 52%, transparent);
|
|
}
|
|
|
|
.secretStatus {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 7px;
|
|
color: var(--muted-foreground);
|
|
font-size: 12px;
|
|
}
|
|
|
|
.secretActions {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 4px;
|
|
}
|
|
|
|
.policyGrid {
|
|
display: grid;
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
gap: 9px;
|
|
}
|
|
|
|
.policyCard {
|
|
display: flex;
|
|
min-height: 76px;
|
|
cursor: pointer;
|
|
align-items: flex-start;
|
|
gap: 10px;
|
|
padding: 12px;
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-lg);
|
|
background: var(--card);
|
|
transition:
|
|
border-color 120ms ease,
|
|
background-color 120ms ease;
|
|
}
|
|
|
|
.policyCard[data-selected='true'] {
|
|
border-color: var(--foreground);
|
|
background: color-mix(in srgb, var(--muted) 62%, transparent);
|
|
}
|
|
|
|
.policyCopy {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
}
|
|
|
|
.policyTitle {
|
|
font-size: 13px;
|
|
font-weight: 650;
|
|
}
|
|
|
|
.policyDescription {
|
|
color: var(--muted-foreground);
|
|
font-size: 11px;
|
|
line-height: 1.45;
|
|
}
|
|
|
|
.sessionScopeField {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 9px;
|
|
}
|
|
|
|
.sessionScopeLabel {
|
|
color: var(--muted-foreground);
|
|
font-size: 11px;
|
|
font-weight: 650;
|
|
}
|
|
|
|
.sessionScopeControl {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
gap: 3px;
|
|
padding: 3px;
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-lg);
|
|
background: var(--muted);
|
|
}
|
|
|
|
.sessionScopeOption {
|
|
position: relative;
|
|
display: flex;
|
|
min-width: 0;
|
|
min-height: 36px;
|
|
cursor: pointer;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 7px 8px;
|
|
border: 1px solid transparent;
|
|
border-radius: calc(var(--radius-lg) - 3px);
|
|
color: var(--muted-foreground);
|
|
font-size: 12px;
|
|
font-weight: 650;
|
|
line-height: 1.25;
|
|
text-align: center;
|
|
transition:
|
|
color 120ms ease,
|
|
border-color 120ms ease,
|
|
background-color 120ms ease,
|
|
box-shadow 120ms ease;
|
|
}
|
|
|
|
.sessionScopeOption:hover {
|
|
color: var(--foreground);
|
|
}
|
|
|
|
.sessionScopeOption[data-selected='true'] {
|
|
border-color: var(--border);
|
|
background: var(--background);
|
|
color: var(--foreground);
|
|
box-shadow: 0 1px 3px color-mix(in srgb, var(--foreground) 12%, transparent);
|
|
}
|
|
|
|
@supports (selector(:has(*))) {
|
|
.sessionScopeOption:has(.sessionScopeRadio:focus-visible) {
|
|
outline: 2px solid var(--ring);
|
|
outline-offset: 1px;
|
|
}
|
|
}
|
|
|
|
.sessionScopeRadio {
|
|
position: absolute;
|
|
z-index: 1;
|
|
inset: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
padding: 0;
|
|
border: 0;
|
|
margin: 0;
|
|
cursor: pointer;
|
|
opacity: 0;
|
|
}
|
|
|
|
.sessionScopeDescription {
|
|
min-height: 18px;
|
|
margin: 0;
|
|
color: var(--muted-foreground);
|
|
font-size: 12px;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.repositoryList {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
}
|
|
|
|
.repositoryListTitle {
|
|
margin: 0;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
color: var(--muted-foreground);
|
|
}
|
|
|
|
.repositoryRow {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
padding: 10px 11px;
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
background: var(--card);
|
|
}
|
|
|
|
.repositoryName {
|
|
margin: 0;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
@media (max-width: 520px) {
|
|
.policyGrid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.sessionScopeControl {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.policyCard {
|
|
transition: none;
|
|
}
|
|
|
|
.sessionScopeOption {
|
|
transition: none;
|
|
}
|
|
|
|
.optionRow {
|
|
transition: none;
|
|
}
|
|
}
|