ouroboros/web/settings.css
Ouroboros ba261e4eeb Add host sign-in startup adapters and Behavior controls
Keep Windows registry behavior and add macOS LaunchAgent and Linux systemd/XDG adapters behind the immutable launcher version gate. Read OS state through the existing endpoint and expose lifecycle facts in model context.

Move the immediate host control into Behavior, document its contract, and cover automatic Panic/pause retention and adapter artifacts. Native Linux activation remains dependent on the separately owned stage-0 unit flag; its contract test intentionally exposes that missing integration.
2026-10-03 18:56:56 +03:00

991 lines
24 KiB
CSS

.settings-shell {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
gap: 0;
overflow: hidden;
padding: 0 20px 16px;
}
.settings-tabs-bar {
padding: 0;
background: transparent;
backdrop-filter: none;
}
.settings-mobile-back {
display: none;
}
.settings-scroll {
flex: 1;
min-height: 0;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 18px;
padding: 4px 2px 88px;
}
.settings-panel {
display: none;
flex-direction: column;
gap: 18px;
}
.settings-panel.active {
display: flex;
}
/* Design-system tokens only (docs/DESIGN.md); guarded by
tests/test_web_typography_static.py. */
.settings-section-copy {
color: var(--text-meta);
font-size: var(--type-body);
line-height: var(--line-body);
max-width: 900px;
}
.settings-shell .form-section,
.settings-card {
display: flex;
flex-direction: column;
gap: 14px;
padding: 18px;
}
.settings-shell .form-section {
margin-bottom: 0;
}
.settings-shell .form-section.compact {
margin-top: 0;
}
.settings-shell .form-section.danger {
border-color: rgba(220, 38, 38, 0.18);
background: rgba(239, 68, 68, 0.05);
}
.settings-shell .form-section h3 {
margin-bottom: 0;
}
.settings-card h3 {
margin: 0;
}
.settings-card-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 14px;
}
.settings-provider-card {
overflow: hidden;
}
.settings-provider-card summary {
list-style: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 16px 18px;
}
.settings-provider-card summary::-webkit-details-marker {
display: none;
}
.settings-provider-title {
display: flex;
align-items: center;
gap: 12px;
font-weight: 600;
color: var(--text-primary);
}
.settings-provider-icon {
width: 22px;
height: 22px;
object-fit: contain;
}
.settings-provider-hint {
color: var(--text-meta);
font-size: var(--type-meta);
line-height: var(--line-meta);
text-align: right;
}
.settings-provider-body {
display: flex;
flex-direction: column;
gap: var(--space-3);
padding: 0 18px 18px;
}
/* Collapsed "More providers" wrapper: rarely used provider cards (Cloud.ru,
GigaChat) stay mounted inside it whether it is open or not. The summary
reuses the provider-card visual grammar (shared ui-card border/background
variables) so it reads as a peer of the cards above it. */
.settings-more-providers > summary {
list-style: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 16px 18px;
border: 1px solid var(--ui-card-border);
border-radius: var(--radius);
background: var(--ui-card-bg);
font-weight: 600;
color: var(--text-primary);
}
.settings-more-providers > summary::-webkit-details-marker {
display: none;
}
.settings-more-providers-body {
display: flex;
flex-direction: column;
gap: 18px;
padding-top: 18px;
}
/* Collapsed subsection INSIDE a form section (Agents → Delegation → Advanced):
the same "More options" grammar as the provider wrapper above, one step
quieter because it sits within a section rather than beside cards. Two path
roots nobody edits in a normal week must not cost the tab its scannability. */
.settings-subsection > summary {
list-style: none;
cursor: pointer;
padding: 8px 0;
font-size: var(--type-meta);
line-height: var(--line-meta);
color: var(--text-meta);
border-top: 1px solid var(--divider);
}
.settings-subsection > summary::-webkit-details-marker {
display: none;
}
.settings-subsection > summary::before {
content: "▸ ";
}
.settings-subsection[open] > summary::before {
content: "▾ ";
}
.settings-subsection-body {
display: flex;
flex-direction: column;
gap: 12px;
padding: 4px 0 8px;
}
.settings-extension-sections {
display: flex;
flex-direction: column;
gap: 10px;
}
.settings-extension-section {
padding: 12px 14px;
}
.settings-extension-section-head {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 10px;
margin-bottom: 6px;
}
.settings-extension-components,
.settings-extension-form {
display: flex;
flex-direction: column;
gap: 10px;
}
.settings-extension-checkbox {
display: inline-flex;
align-items: center;
gap: 8px;
color: var(--text-primary);
font-size: var(--type-body);
line-height: var(--line-body);
}
.settings-shell .form-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 14px;
margin-bottom: 0;
}
.settings-shell .form-grid {
display: grid;
gap: 14px;
}
.settings-shell .form-grid.two {
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
.settings-shell .form-grid.four {
grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
}
.settings-shell .form-field {
min-width: 0;
gap: 8px;
}
/* Uppercase micro-label retired (docs/DESIGN.md "Hierarchy rule"). */
.settings-shell .form-field label {
font-size: var(--type-meta);
line-height: var(--line-meta);
font-weight: 600;
color: var(--text-meta);
text-transform: none;
letter-spacing: normal;
}
.mcp-servers-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.mcp-server-card {
display: flex;
flex-direction: column;
gap: 14px;
padding: 14px;
}
.mcp-server-card-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
}
.mcp-server-card-title {
display: flex;
flex-direction: column;
gap: 6px;
}
.mcp-server-card-actions,
.mcp-server-enabled {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
}
.mcp-server-status {
width: fit-content;
padding: 3px 8px;
font-size: var(--type-meta);
line-height: var(--line-meta);
background: var(--status-neutral-bg);
}
.mcp-server-status-ok { color: var(--status-ok-fg); background: var(--status-ok-bg); }
.mcp-server-status-warn { color: var(--status-warn-fg); background: var(--status-warn-bg); }
.mcp-server-status-danger { color: var(--status-error-fg); background: var(--status-error-bg); }
.mcp-server-status-muted { color: var(--status-neutral-fg); }
.mcp-tools-disclosure {
border-radius: 12px;
border: 1px solid rgba(var(--neutral-rgb), 0.06);
padding: 10px 12px;
}
.mcp-tools-disclosure summary {
cursor: pointer;
color: var(--text-secondary);
}
.mcp-tools-list {
display: flex;
flex-direction: column;
gap: 8px;
margin: 10px 0 0;
padding-left: 18px;
}
.mcp-tool-desc {
display: block;
color: var(--text-meta);
font-size: var(--type-meta);
line-height: var(--line-meta);
}
/* Was --text-muted (the DISABLED ink); load-bearing caveats are --text-meta. */
.settings-inline-note {
margin-top: 2px;
color: var(--text-meta);
font-size: var(--type-meta);
line-height: var(--line-body);
}
/* The note element carries a tone at a few call sites (the harness login card's
failure) but had no tone rule, so a failure rendered as an ordinary hint. */
.settings-inline-note[data-tone="ok"] { color: var(--status-ok-fg); }
.settings-inline-note[data-tone="warn"] { color: var(--status-warn-fg); }
.settings-inline-note[data-tone="error"] { color: var(--status-error-fg); }
/* Utility: hide elements that start as hidden and are revealed via JS */
.local-model-hidden {
display: none !important;
}
/* Local model download progress bar */
.local-model-progress-wrap {
margin-top: 8px;
height: 6px;
border-radius: var(--radius-xs);
background: rgba(var(--neutral-rgb), 0.08);
overflow: hidden;
}
.local-model-progress-bar {
height: 100%;
border-radius: var(--radius-xs);
background: var(--accent);
width: 0%;
transition: width 0.4s ease;
}
/* Install Local Runtime button */
.local-model-install-btn {
margin-top: 10px;
}
.settings-unsaved-indicator {
color: var(--status-warn-fg);
font-weight: 600;
min-width: 126px;
opacity: 0;
visibility: hidden;
text-align: right;
}
.settings-unsaved-indicator.is-visible {
opacity: 1;
visibility: visible;
}
.settings-toolbar {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
}
/* A single action with a durable result belongs to one row: the result stays
readable on the left while the action keeps a stable right edge. */
.settings-action-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
gap: var(--space-3);
min-width: 0;
}
.settings-action-row > .settings-inline-status {
min-width: 0;
}
.settings-action-row > .btn-default {
justify-self: end;
}
.settings-footer {
flex-shrink: 0;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: var(--space-3);
padding: var(--space-3) 2px 0;
border-top: 1px solid rgba(var(--neutral-rgb), 0.06);
background: linear-gradient(180deg, rgba(var(--shell-rgb), 0), rgba(var(--shell-rgb), 0.92) 55%);
}
.settings-footer-actions,
.settings-footer-status {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
}
.settings-footer-actions {
flex: 0 0 auto;
}
.settings-footer-status {
flex: 1 1 auto;
justify-content: flex-end;
min-width: 0;
}
.settings-footer-status #settings-status {
min-width: 0;
max-width: 52ch;
flex: 1 1 220px;
overflow-wrap: anywhere;
text-align: right;
}
.secret-input-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto auto;
gap: 10px;
align-items: stretch;
}
.secret-input-row input {
min-width: 0;
}
.settings-secret-value,
.settings-secret-source,
.settings-secret-status {
grid-column: 1 / -1;
min-width: 0;
overflow-wrap: anywhere;
}
.settings-secret-value {
white-space: pre-wrap;
-webkit-user-select: text;
user-select: text;
cursor: text;
}
.available-subagents-editor {
display: flex;
flex-direction: column;
gap: var(--space-2);
margin-top: var(--space-3);
}
.available-subagents-toolbar,
.available-subagent-head,
.available-subagent-actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--space-2);
}
/* The section's Add and the card's own actions dock right (docs/DESIGN.md §6). */
.available-subagents-toolbar .btn-default,
.available-subagent-actions {
margin-left: auto;
}
.available-subagents-count,
.available-subagents-source,
.available-subagents-diagnostics,
.available-subagents-empty,
.available-subagent-meta {
color: var(--text-meta);
font-size: var(--type-meta);
line-height: var(--line-body);
}
.available-subagents-diagnostics[data-tone="error"],
.available-subagent-meta[data-tone="error"] {
color: var(--status-error-fg);
}
.available-subagents-list {
display: flex;
flex-direction: column;
gap: var(--space-2);
}
/* Compact card (docs/DESIGN.md §6 row anatomy): head → Description → route
controls → one meta line. Inner rhythm is one space token and the card
padding three, so three cards fit a laptop-height Settings body. */
.available-subagent-row {
display: flex;
flex-direction: column;
gap: var(--space-1);
padding: var(--space-3);
border: 1px solid var(--surface-border);
border-radius: var(--radius);
background: var(--surface-card-soft);
}
/* An entry the owner tried to save with an error is emphasised, not dimmed. */
.available-subagent-row[data-invalid] {
border-color: var(--status-error-border);
background: var(--status-error-bg);
}
.available-subagent-route {
display: grid;
/* A track minimum yields to the container: the viewport query does not know
the Settings column width, so the floor is capped at 100% of the row. */
grid-template-columns: repeat(auto-fit, minmax(min(100%, 130px), 1fr));
gap: var(--space-2);
}
.available-subagent-heading {
margin: 0;
color: var(--text-primary);
font-size: var(--type-body);
font-weight: 600;
}
/* The per-row owner switch leads the card head. Padding buys a comfortable
pointer target around the 14px native box; the matching negative margin
keeps the head's own rhythm (docs/DESIGN.md §6). */
.available-subagent-enable {
display: inline-flex;
align-items: center;
padding: var(--space-2);
margin: calc(-1 * var(--space-2));
cursor: pointer;
}
/* Two short words on one line; the full sentence is the status's title. */
.available-subagent-head .settings-inline-status {
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.available-subagent-purpose {
display: flex;
flex-direction: column;
gap: var(--space-1);
color: var(--text-meta);
font-size: var(--type-meta);
}
/* One line that grows with its text up to four, then scrolls: the browser
sizes it (`field-sizing`); a shell without that property keeps the one-line
field and manual resize. 22px is the control's own padding and border. */
.available-subagent-purpose textarea {
width: 100%;
field-sizing: content;
line-height: var(--line-body);
min-height: calc(var(--type-body) * var(--line-body) + 22px);
max-height: calc(4 * var(--type-body) * var(--line-body) + 22px);
overflow-y: auto;
resize: vertical;
}
.available-subagent-meta {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.available-subagent-meta[data-run-history] {
white-space: normal;
overflow-wrap: anywhere;
}
.available-subagent-actions .btn-default {
min-height: 34px;
padding: 0 11px;
}
.settings-secret-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.settings-custom-secret-row {
display: grid;
/* A track minimum yields to the container: the viewport query does not know
the Settings column width, so the two fields shrink and Remove keeps its own. */
grid-template-columns: minmax(0, 0.65fr) minmax(0, 1.35fr) auto;
gap: 10px;
align-items: start;
padding: var(--space-3);
}
.settings-custom-secret-remove {
align-self: end;
padding-inline: var(--space-3);
}
.settings-model-grid,
.settings-effort-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 14px;
}
.settings-model-card,
.settings-effort-card {
display: flex;
flex-direction: column;
gap: 12px;
padding: 16px;
min-width: 0;
}
.settings-model-header {
display: flex;
justify-content: space-between;
gap: 12px;
align-items: flex-start;
}
.settings-model-header h4 {
font-size: var(--type-section);
line-height: var(--line-title);
font-weight: 600;
margin: 0 0 4px;
}
.settings-model-header p {
margin: 0;
color: var(--text-meta);
font-size: var(--type-meta);
line-height: var(--line-meta);
}
.settings-effort-card {
gap: 10px;
}
.settings-effort-card label {
font-size: var(--type-meta);
line-height: var(--line-meta);
font-weight: 600;
color: var(--text-meta);
text-transform: none;
letter-spacing: normal;
}
.settings-effort-group {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 8px;
}
/* .ui-segment — SSOT base look for a segmented single-select button (sibling of
.app-tab). Settings-specific overrides (column counts, accent colors) stay on
.settings-effort-*; the resting look + centering live here. The flex centering
fixes labels that sat top-left in taller cells (the effort-button bug). */
.ui-segment {
display: inline-flex;
align-items: center;
justify-content: center;
text-align: center;
min-height: 38px;
line-height: 1.2;
border: 1px solid rgba(var(--neutral-rgb), 0.08);
background: rgba(var(--neutral-rgb), 0.04);
color: var(--text-secondary);
border-radius: 12px;
padding: 10px 12px;
cursor: pointer;
font-size: var(--button-font-size);
font-weight: 600;
}
.settings-effort-btn.active,
.settings-effort-btn:hover {
color: var(--text-primary);
border-color: rgba(232, 93, 111, 0.35);
background: rgba(232, 93, 111, 0.12);
}
/* Review enforcement toggle: 2 columns. */
[data-enforcement-group].settings-effort-group {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
[data-update-channel-group].settings-effort-group {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
/* Task-result review keeps its compact 3-column shape. */
[data-task-review-group].settings-effort-group {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
/* Max review cycles: five short segments (1 / 2 / 3 / 5 / ∞) on one row. */
[data-review-cycles-group].settings-effort-group {
grid-template-columns: repeat(5, minmax(0, 1fr));
}
/* Shared disabled segmented-button style. */
.settings-effort-btn:disabled {
cursor: not-allowed;
opacity: 0.55;
}
.settings-effort-btn:disabled:hover {
background: rgba(var(--neutral-rgb), 0.04);
border-color: rgba(var(--neutral-rgb), 0.08);
color: var(--text-secondary);
transform: none;
}
/* The warn foreground, not the raw --amber: #f59e0b over the 12% amber wash is
~2:1 on the light surface and unreadable when selected. --status-warn-fg is
the token that already carries a legible warn tint per theme. */
[data-enforcement-group] .settings-effort-btn[data-effort-value="advisory"].active,
[data-enforcement-group] .settings-effort-btn[data-effort-value="advisory"]:hover {
color: var(--status-warn-fg);
border-color: var(--status-warn-border);
background: var(--status-warn-bg);
}
[data-enforcement-group] .settings-effort-btn[data-effort-value="blocking"].active,
[data-enforcement-group] .settings-effort-btn[data-effort-value="blocking"]:hover {
color: var(--text-primary);
border-color: rgba(232, 93, 111, 0.5);
background: var(--accent-18);
}
.local-toggle {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: var(--type-meta);
line-height: var(--line-meta);
color: var(--text-meta);
cursor: pointer;
white-space: nowrap;
}
.local-toggle input[type="checkbox"] {
accent-color: var(--accent);
width: 14px;
height: 14px;
cursor: pointer;
}
[data-autostart-settings] .local-toggle {
white-space: normal;
flex-wrap: nowrap;
}
.local-toggle[data-warning="1"] {
color: var(--status-warn-fg);
}
.settings-test-result {
display: none;
margin-top: 4px;
color: var(--text-meta);
font-size: var(--type-meta);
line-height: var(--line-meta);
}
@media (max-width: 980px) {
.settings-shell {
padding: 0 14px 14px;
}
.settings-model-grid,
.settings-effort-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 760px) {
.settings-provider-card summary,
.settings-footer {
align-items: stretch;
flex-direction: column;
}
.settings-footer-actions,
.settings-footer-status {
justify-content: flex-start;
align-items: stretch;
}
.settings-footer-status #settings-status,
.settings-unsaved-indicator {
text-align: left;
}
.settings-provider-hint {
text-align: left;
}
.secret-input-row {
grid-template-columns: minmax(0, 1fr) auto auto;
}
.secret-input-row > input { grid-column: 1 / -1; }
.secret-input-row > button:first-of-type { grid-column: 2; }
.settings-card-head,
.settings-custom-secret-row {
grid-template-columns: 1fr;
align-items: stretch;
}
.available-subagent-route {
grid-template-columns: 1fr;
}
.available-subagents-toolbar .btn-default {
margin-left: 0;
}
.settings-action-row {
grid-template-columns: 1fr;
align-items: start;
}
.settings-action-row > .settings-inline-status:empty {
display: none;
}
.settings-action-row > .btn-default {
justify-self: end;
}
.settings-effort-group {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
[data-update-channel-group].settings-effort-group,
[data-task-review-group].settings-effort-group {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
[data-review-cycles-group].settings-effort-group {
grid-template-columns: repeat(5, minmax(0, 1fr));
}
}
@media (max-width: 640px) {
/* Mobile settings keep horizontal tab pills; back element remains hidden compat. */
.settings-shell {
padding: 0 10px 10px;
}
.settings-tabs-bar {
padding: 0;
}
.settings-tabs {
flex-wrap: nowrap;
gap: 8px;
padding: 8px 2px 10px;
overflow-x: auto;
}
.settings-tab {
flex: 0 0 auto;
min-height: 38px;
padding: 8px 14px;
font-size: var(--pill-font-size);
white-space: nowrap;
border-radius: 999px;
}
.settings-mobile-back {
display: none !important;
}
.settings-shell .form-section {
padding: 14px;
}
.settings-provider-card summary,
.settings-more-providers > summary {
padding: 14px 14px;
}
.settings-provider-body {
padding: 0 14px 14px;
}
}
/* track-migration:begin — Removed after remaining form consumers adopt ui-control. */
.settings-shell .form-field input:not(.ui-control):not(.ui-checkbox),
.settings-shell .form-field select:not(.ui-control):not(.ui-checkbox),
.settings-shell .form-field textarea:not(.ui-control):not(.ui-checkbox),
.settings-shell .available-subagent-row input:not(.ui-control):not(.ui-checkbox),
.settings-shell .available-subagent-row select:not(.ui-control):not(.ui-checkbox),
.settings-shell .available-subagent-row textarea:not(.ui-control):not(.ui-checkbox),
.settings-shell .model-role-row input:not(.ui-control):not(.ui-checkbox),
.settings-shell .model-role-row select:not(.ui-control):not(.ui-checkbox) {
width: 100%;
min-width: 0;
padding: 10px 12px;
border-radius: 12px;
border: 1px solid var(--surface-border);
background: rgba(var(--composer-rgb), 0.88);
color: var(--text-primary);
font-size: var(--type-body);
font-family: inherit;
outline: none;
transition: border-color 0.2s, box-shadow 0.2s, background 0.2s;
}
.settings-shell .form-field input:not(.ui-control):not(.ui-checkbox)::placeholder,
.settings-shell .form-field textarea:not(.ui-control):not(.ui-checkbox)::placeholder,
.settings-shell .available-subagent-row input:not(.ui-control):not(.ui-checkbox)::placeholder,
.settings-shell .available-subagent-row textarea:not(.ui-control):not(.ui-checkbox)::placeholder,
.secret-input:not(.ui-control)::placeholder {
color: rgba(var(--neutral-rgb), 0.24);
}
.settings-shell .form-field input:not(.ui-control):not(.ui-checkbox):focus,
.settings-shell .form-field select:not(.ui-control):not(.ui-checkbox):focus,
.settings-shell .form-field textarea:not(.ui-control):not(.ui-checkbox):focus,
.settings-shell .available-subagent-row input:not(.ui-control):not(.ui-checkbox):focus,
.settings-shell .available-subagent-row select:not(.ui-control):not(.ui-checkbox):focus,
.settings-shell .available-subagent-row textarea:not(.ui-control):not(.ui-checkbox):focus,
.settings-shell .model-role-row input:not(.ui-control):not(.ui-checkbox):focus,
.settings-shell .model-role-row select:not(.ui-control):not(.ui-checkbox):focus {
border-color: var(--focus-accent-border);
box-shadow: 0 0 0 3px var(--focus-accent-ring);
}
.settings-shell .form-field textarea:not(.ui-control):not(.ui-checkbox) {
min-height: 84px;
resize: vertical;
}
.settings-shell .form-field select:not(.ui-control):not(.ui-checkbox),
.settings-shell .available-subagent-row select:not(.ui-control):not(.ui-checkbox) {
appearance: none;
padding-right: 36px;
background-image: var(--select-arrow);
background-repeat: no-repeat;
background-position: right 12px center;
}
/* track-migration:end */