-
+
—
-
+
no agent calls in this run
diff --git a/frontend/src/apps/software_factory/projects/projects.css b/frontend/src/apps/software_factory/projects/projects.css
index fb373bd4d..d2a5be1f1 100644
--- a/frontend/src/apps/software_factory/projects/projects.css
+++ b/frontend/src/apps/software_factory/projects/projects.css
@@ -1,4 +1,4 @@
-.pj-refresh-error { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; margin-bottom: 20px; color: var(--outcome-failed); }
+.pj-refresh-error { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; margin-bottom: 20px; color: var(--danger); }
.pj-refresh-error p { margin: 0; }
.page-projects .page-shell-body { padding: var(--page-padding); }
@@ -11,69 +11,69 @@
.pj-project-head h2 { margin: 0; min-width: 0; }
.pj-disclosure { display: flex; align-items: center; gap: 16px; min-width: 0; padding: 4px; text-align: left; font-size: 32px; font-weight: 500; letter-spacing: -0.025em; line-height: 1.25; }
.pj-disclosure span { overflow-wrap: anywhere; }
-.pj-disclosure svg { flex-shrink: 0; color: var(--text-dim); transition: transform 140ms ease-out; }
+.pj-disclosure svg { flex-shrink: 0; color: var(--text-tertiary); transition: transform 140ms ease-out; }
.pj-disclosure[aria-expanded="false"] svg { transform: rotate(-90deg); }
-.pj-icon-button { display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 4px; color: var(--text-dim); cursor: pointer; }
+.pj-icon-button { display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 4px; color: var(--text-tertiary); cursor: pointer; }
.pj-icon-button:hover { background: var(--surface-2); color: var(--text); }
.pj-actions { position: relative; flex-shrink: 0; }
.pj-actions summary { list-style: none; }
.pj-actions summary::-webkit-details-marker { display: none; }
.pj-actions[open] summary { background: var(--surface-2); }
-.pj-action-list { position: absolute; right: 0; top: calc(100% + 4px); z-index: 10; width: 192px; padding: 6px; border: 1px solid var(--border-loud); border-radius: 6px; background: var(--surface-3); }
-.pj-action-list button { display: flex; align-items: center; gap: 12px; width: 100%; padding: 10px; border-radius: 3px; text-align: left; font-size: 14px; color: var(--text-mid); }
+.pj-action-list { position: absolute; right: 0; top: calc(100% + 4px); z-index: 10; width: 192px; padding: 6px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface-3); }
+.pj-action-list button { display: flex; align-items: center; gap: 12px; width: 100%; padding: 10px; border-radius: 3px; text-align: left; font-size: 14px; color: var(--text-secondary); }
.pj-action-list button:hover { color: var(--text); background: var(--surface-2); }
-.pj-action-list .pj-danger { color: var(--outcome-failed); border-top: 1px solid var(--border); border-radius: 0; margin-top: 4px; }
-.pj-repos { border-top: 1px solid var(--border-loud); }
+.pj-action-list .pj-danger { color: var(--danger); border-top: 1px solid var(--border); border-radius: 0; margin-top: 4px; }
+.pj-repos { border-top: 1px solid var(--border-soft); }
.pj-repo { border-bottom: 1px solid var(--border-soft); }
.pj-repo-row { display: grid; grid-template-columns: var(--pj-repo-column) minmax(0, 1fr) auto; gap: 28px; align-items: center; width: 100%; min-height: 56px; padding: 16px 4px; text-align: left; }
.pj-repo-row:hover { background: var(--surface); }
.pj-repo-row[aria-expanded="true"] .pj-repo-name { color: var(--accent); }
.pj-repo-name { min-width: 0; font-family: var(--font-mono); font-size: 14px; overflow-wrap: anywhere; }
-.pj-repo-org { color: var(--text-faint); }
-.pj-repo-purpose { font-size: 15px; line-height: 1.5; color: var(--text-mid); overflow-wrap: anywhere; }
-.pj-profile-status { display: inline-flex; align-items: center; gap: 8px; justify-self: end; color: var(--text-dim); font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; white-space: nowrap; }
-.pj-profile-status::before { content: ''; width: 5px; height: 5px; border-radius: 50%; background: var(--text-faint); }
-.pj-profile-status[data-state="ready"]::before { background: var(--outcome-merged); }
-.pj-profile-status[data-state="running"]::before { background: var(--bucket-run); }
-.pj-profile-status[data-state="failed"] { color: var(--outcome-failed); }
+.pj-repo-org { color: var(--text-tertiary); }
+.pj-repo-purpose { font-size: 15px; line-height: 1.5; color: var(--text-secondary); overflow-wrap: anywhere; }
+.pj-profile-status { display: inline-flex; align-items: center; gap: 8px; justify-self: end; color: var(--text-tertiary); font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; white-space: nowrap; }
+.pj-profile-status::before { content: ''; width: 5px; height: 5px; border-radius: 50%; background: var(--text-tertiary); }
+.pj-profile-status[data-state="ready"]::before { background: var(--success); }
+.pj-profile-status[data-state="running"]::before { background: var(--info); }
+.pj-profile-status[data-state="failed"] { color: var(--danger); }
.pj-profile-status[data-state="failed"]::before { background: currentColor; }
.pj-repo-details { display: grid; grid-template-columns: var(--pj-repo-column) minmax(0, 1fr); gap: 28px; padding: 16px 4px 36px; }
.pj-repo-rail { display: flex; flex-direction: column; align-items: flex-start; gap: 20px; }
.pj-profile-facts { display: grid; gap: 16px; margin: 0; }
-.pj-profile-facts dt, .pj-profile-line h3 { font-family: var(--font-mono); font-size: 11px; font-weight: 400; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-faint); }
-.pj-profile-facts dd { margin: 4px 0 0; font-family: var(--font-mono); font-size: 13px; color: var(--text-mid); }
+.pj-profile-facts dt, .pj-profile-line h3 { font-family: var(--font-mono); font-size: 11px; font-weight: 400; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-tertiary); }
+.pj-profile-facts dd { margin: 4px 0 0; font-family: var(--font-mono); font-size: 13px; color: var(--text-secondary); }
.pj-repo-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.pj-profile-content { display: grid; align-content: start; gap: 28px; min-width: 0; }
.pj-profile-content p { margin: 0; }
-.pj-profile-summary { max-width: 72ch; font-size: 15px; line-height: 1.7; color: var(--text-mid); overflow-wrap: anywhere; }
+.pj-profile-summary { max-width: 72ch; font-size: 15px; line-height: 1.7; color: var(--text-secondary); overflow-wrap: anywhere; }
.pj-profile-line { display: grid; grid-template-columns: 72px minmax(0, 1fr); gap: 20px; align-items: baseline; }
.pj-profile-line h3 { margin: 0; }
-.pj-profile-line p { font-size: 13px; line-height: 1.8; color: var(--text-mid); overflow-wrap: anywhere; }
+.pj-profile-line p { font-size: 13px; line-height: 1.8; color: var(--text-secondary); overflow-wrap: anywhere; }
.pj-profile-commands { display: grid; gap: 8px; list-style: none; margin: 0; padding: 0 0 0 16px; border-left: 1px solid var(--border); }
.pj-profile-commands li { display: flex; gap: 12px; min-width: 0; }
-.pj-profile-commands li::before { content: '$'; color: var(--text-faint); font-family: var(--font-mono); font-size: 13px; }
-.pj-profile-commands code { font-family: var(--font-mono); font-size: 13px; color: var(--text-mid); overflow-wrap: anywhere; white-space: pre-wrap; }
-.pj-text-button { display: inline-flex; align-items: center; gap: 4px; min-height: 32px; margin-top: 4px; font-size: 13px; color: var(--text-dim); }
+.pj-profile-commands li::before { content: '$'; color: var(--text-tertiary); font-family: var(--font-mono); font-size: 13px; }
+.pj-profile-commands code { font-family: var(--font-mono); font-size: 13px; color: var(--text-secondary); overflow-wrap: anywhere; white-space: pre-wrap; }
+.pj-text-button { display: inline-flex; align-items: center; gap: 4px; min-height: 32px; margin-top: 4px; font-size: 13px; color: var(--text-tertiary); }
.pj-text-button:hover { color: var(--text); }
.pj-text-button[aria-expanded="true"] svg { transform: rotate(180deg); }
-.pj-muted { color: var(--text-faint); }
-.pj-error { color: var(--outcome-failed); font-size: 14px; line-height: 1.5; overflow-wrap: anywhere; }
-.pj-empty-project { border-top: 1px solid var(--border); padding: 24px 4px; color: var(--text-mid); }
+.pj-muted { color: var(--text-tertiary); }
+.pj-error { color: var(--danger); font-size: 14px; line-height: 1.5; overflow-wrap: anywhere; }
+.pj-empty-project { border-top: 1px solid var(--border); padding: 24px 4px; color: var(--text-secondary); }
.pj-empty-project p { margin: 0 0 16px; }
.pj-purpose-form { display: grid; gap: 12px; }
.pj-form-actions { display: flex; flex-wrap: wrap; gap: 8px; }
-.pj-field { display: grid; gap: 8px; color: var(--text-mid); font-size: 14px; }
-.pj-dialog { width: min(520px, calc(100vw - 32px)); max-height: calc(100dvh - 32px); padding: 0; margin: auto; border: 1px solid var(--border-loud); border-radius: 6px; background: var(--surface); color: var(--text); overflow: auto; }
-.pj-dialog::backdrop { background: rgb(3 5 9 / 0.75); }
+.pj-field { display: grid; gap: 8px; color: var(--text-secondary); font-size: 14px; }
+.pj-dialog { width: min(520px, calc(100vw - 32px)); max-height: calc(100dvh - 32px); padding: 0; margin: auto; border: 1px solid var(--border); border-radius: 6px; background: var(--surface); color: var(--text); overflow: auto; }
+.pj-dialog::backdrop { background: var(--scrim); }
.pj-dialog-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 16px 24px; border-bottom: 1px solid var(--border); }
.pj-dialog-head h2 { margin: 0; font-size: 18px; font-weight: 500; overflow-wrap: anywhere; }
.pj-dialog-head .pj-icon-button { flex-shrink: 0; }
.pj-dialog-body { display: grid; gap: 20px; padding: 24px; }
-.pj-dialog-body p { margin: 0; font-size: 14px; line-height: 1.5; color: var(--text-mid); }
-.pj-dialog-body .pj-error { color: var(--outcome-failed); }
+.pj-dialog-body p { margin: 0; font-size: 14px; line-height: 1.5; color: var(--text-secondary); }
+.pj-dialog-body .pj-error { color: var(--danger); }
.pj-dialog-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 16px 24px; background: var(--surface-2); border-top: 1px solid var(--border); }
.pj-repo-choices { min-width: 0; max-height: 280px; overflow: auto; border: 0; padding: 0; margin: 0; }
-.pj-repo-choices legend { color: var(--text-dim); font-size: 14px; margin-bottom: 12px; }
+.pj-repo-choices legend { color: var(--text-tertiary); font-size: 14px; margin-bottom: 12px; }
.pj-repo-choice { display: flex; align-items: center; gap: 12px; padding: 12px 8px; border-radius: 3px; cursor: pointer; }
.pj-repo-choice:hover, .pj-repo-choice:has(:checked) { background: var(--surface-2); }
.pj-repo-choice input { flex-shrink: 0; accent-color: var(--accent); }
diff --git a/frontend/src/chat.css b/frontend/src/chat.css
index 20c542998..a73a86319 100644
--- a/frontend/src/chat.css
+++ b/frontend/src/chat.css
@@ -1,5 +1,4 @@
.page-chat {
- --chat-accent: #83d8b8;
--chat-gutter: clamp(20px, 3vw, 40px);
}
@@ -14,61 +13,61 @@
.chat-list { background: var(--surface); border-right: 1px solid var(--border); }
.chat-thread { grid-column: 2; }
.chat-list-head { display: flex; align-items: center; gap: 8px; padding: 12px; flex-shrink: 0; }
-.chat-search { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1; padding: 0 10px; border: 1px solid var(--border-loud); border-radius: 6px; color: var(--text-dim); }
+.chat-search { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1; padding: 0 10px; border: 1px solid var(--border-strong); border-radius: 6px; background: var(--input); color: var(--text-tertiary); }
.chat-search svg { flex-shrink: 0; }
.chat-search input { width: 100%; min-width: 0; height: 36px; border: 0; background: transparent; color: var(--text); font: inherit; font-size: 13px; }
-.chat-search input::placeholder { color: var(--text-dim); }
-.chat-search:focus-within { outline: 2px solid var(--chat-accent); outline-offset: 2px; }
+.chat-search input::placeholder { color: var(--text-tertiary); }
+.chat-search:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
.page-chat .chat-search input:focus-visible { outline: none; }
.chat-list-body { flex: 1; min-height: 0; overflow-y: auto; padding: 0 8px; }
-.chat-day { display: flex; align-items: center; gap: 12px; margin: 24px 10px 8px; color: var(--text-dim); font-size: 12px; font-weight: 500; }
+.chat-day { display: flex; align-items: center; gap: 12px; margin: 24px 10px 8px; color: var(--text-tertiary); font-size: 12px; font-weight: 500; }
.chat-day::after { content: ''; flex: 1; height: 1px; background: var(--border); }
.chat-list-row { position: relative; border: 1px solid transparent; border-radius: 6px; }
.chat-list-row:hover { background: var(--surface-2); }
-.chat-list-row[data-selected="true"] { background: var(--surface-3); border-color: var(--border-loud); }
-.chat-list-item { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 5px 8px; padding: 10px 12px; color: var(--text-mid); text-decoration: none; border-radius: 6px; }
+.chat-list-row[data-selected="true"] { background: var(--surface-3); border-color: var(--border); }
+.chat-list-item { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 5px 8px; padding: 10px 12px; color: var(--text-secondary); text-decoration: none; border-radius: 6px; }
.chat-list-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; }
.chat-list-item[aria-current="page"] .chat-list-title { color: var(--text); }
.chat-list-item time { font-family: var(--font-mono); font-size: 11px; font-variant-numeric: tabular-nums; align-self: center; }
-.chat-list-status { display: flex; align-items: center; gap: 8px; grid-column: 1 / -1; padding-right: 30px; color: var(--text-dim); font-size: 12px; }
+.chat-list-status { display: flex; align-items: center; gap: 8px; grid-column: 1 / -1; padding-right: 30px; color: var(--text-tertiary); font-size: 12px; }
.chat-list-row > .chat-pin { position: absolute; right: 2px; bottom: 0; width: 30px; min-height: 30px; opacity: 0; }
.chat-list-row:hover > .chat-pin, .chat-list-row:focus-within > .chat-pin, .chat-list-row > .chat-pin[aria-pressed="true"] { opacity: 1; }
-.chat-button, .chat-icon-button { display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-height: 36px; flex-shrink: 0; border-radius: 6px; color: var(--text-mid); text-decoration: none; font: inherit; line-height: 1.25; transition: background-color 150ms ease-out, color 150ms ease-out; }
-.chat-button { padding: 7px 12px; border: 1px solid var(--border-loud); background: var(--surface); font-size: 13px; }
+.chat-button, .chat-icon-button { display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-height: 36px; flex-shrink: 0; border-radius: 6px; color: var(--text-secondary); text-decoration: none; font: inherit; line-height: 1.25; transition: background-color 150ms ease-out, color 150ms ease-out; }
+.chat-button { padding: 7px 12px; border: 1px solid var(--border-strong); background: var(--surface); font-size: 13px; }
.chat-icon-button { width: 36px; padding: 8px; }
.chat-button:hover, .chat-icon-button:hover { background: var(--surface-3); color: var(--text); }
.chat-icon-button:active, .chat-button:active { background: var(--surface-2); }
.chat-button:disabled, .chat-icon-button:disabled { opacity: .5; cursor: not-allowed; }
-.chat-new, .chat-send:not(:disabled) { background: var(--chat-accent); color: var(--bg); }
-.chat-new:hover, .chat-send:not(:disabled):hover { background: color-mix(in srgb, var(--chat-accent) 85%, white); color: var(--bg); }
-.chat-send:disabled { background: var(--surface-3); color: var(--text-dim); }
-.chat-pin[aria-pressed="true"] { color: var(--chat-accent); }
-.page-chat :is(button, a, summary, textarea, input):focus-visible { outline: 2px solid var(--chat-accent); outline-offset: 2px; }
-.page-chat ::selection { background: color-mix(in srgb, var(--chat-accent) 28%, var(--surface)); color: var(--text); }
-.page-chat :is(textarea, input) { caret-color: var(--chat-accent); }
-.page-chat :is(.chat-list-body, .chat-messages, pre, textarea) { scrollbar-width: thin; scrollbar-color: var(--border-loud) transparent; }
+.chat-new, .chat-send:not(:disabled) { background: var(--accent); color: var(--on-accent); }
+.chat-new:hover, .chat-send:not(:disabled):hover { background: color-mix(in oklch, var(--accent) 92%, var(--on-accent)); color: var(--on-accent); }
+
+.chat-send:disabled { background: var(--surface-3); color: var(--text-tertiary); }
+.chat-pin[aria-pressed="true"] { color: var(--text-tertiary); }
+.page-chat :is(button, a, summary, textarea, input):focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
+.page-chat :is(textarea, input) { caret-color: var(--accent); }
+.page-chat :is(.chat-list-body, .chat-messages, pre, textarea) { scrollbar-width: thin; scrollbar-color: var(--border-soft) transparent; }
.chat-back { display: none; }
.chat-thread-head { display: flex; align-items: center; gap: 12px; min-height: 64px; padding: 12px var(--chat-gutter); border-bottom: 1px solid var(--border); flex-shrink: 0; }
.chat-thread-title { min-width: 0; flex: 1; }
.chat-thread-title h1 { margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 15px; line-height: 1.5; font-weight: 500; }
-.chat-thread-title p { margin: 2px 0 0; color: var(--text-dim); font-family: var(--font-mono); font-size: 11px; line-height: 1.5; }
+.chat-thread-title p { margin: 2px 0 0; color: var(--text-tertiary); font-family: var(--font-mono); font-size: 11px; line-height: 1.5; }
.chat-thread-title p > span { margin-inline: 5px; }
-.chat-connection { color: var(--text-mid); font-size: 12px; }
+.chat-connection { color: var(--text-secondary); font-size: 12px; }
.chat-messages { flex: 1; min-height: 0; overflow: auto; overscroll-behavior: contain; }
.chat-messages[hidden] { display: none; }
.chat-messages-content { display: flex; flex-direction: column; justify-content: flex-end; min-height: 100%; padding: 24px var(--chat-gutter) 12px; }
.chat-turn + .chat-turn { margin-top: 28px; }
.chat-user { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }
-.chat-message-meta { display: flex; align-items: center; gap: 10px; color: var(--text-dim); font-size: 11px; text-transform: uppercase; letter-spacing: .06em; }
+.chat-message-meta { display: flex; align-items: center; gap: 10px; color: var(--text-tertiary); font-size: 11px; text-transform: uppercase; letter-spacing: .06em; }
.chat-message-meta time { font-family: var(--font-mono); letter-spacing: 0; font-variant-numeric: tabular-nums; }
-.chat-agent-meta { color: var(--chat-accent); }
-.chat-agent-meta time { color: var(--text-dim); }
-.chat-user-body { max-width: 75ch; line-height: 1.65; white-space: pre-wrap; overflow-wrap: anywhere; color: var(--text-mid); }
+.chat-agent-meta { color: var(--text-tertiary); }
+.chat-agent-meta time { color: var(--text-tertiary); }
+.chat-user-body { max-width: 75ch; line-height: 1.65; white-space: pre-wrap; overflow-wrap: anywhere; color: var(--text-secondary); }
.chat-user-transcript { font-style: italic; }
-.chat-user.is-internal .chat-message-meta { color: var(--chat-accent); }
-.chat-user > a { color: var(--chat-accent); text-underline-offset: 3px; overflow-wrap: anywhere; }
-.chat-queued { font-size: 12px; color: var(--text-mid); border: 1px solid var(--border-loud); border-radius: 4px; padding: 3px 8px; }
+.chat-user.is-internal .chat-message-meta { color: var(--text-tertiary); }
+.chat-user > a { color: var(--accent); text-underline-offset: 3px; overflow-wrap: anywhere; }
+.chat-queued { font-size: 12px; color: var(--text-secondary); border: 1px solid var(--border); border-radius: 4px; padding: 3px 8px; }
.chat-reply { display: flex; flex-direction: column; gap: 12px; margin-top: 26px; min-width: 0; line-height: 1.65; }
.chat-reply:empty { display: none; }
.chat-reply .markdown-content { max-width: 75ch; min-width: 0; overflow-wrap: anywhere; }
@@ -77,13 +76,13 @@
.chat-reply .markdown-content pre { max-width: 100%; overflow: auto; }
.chat-reply .markdown-content table { display: block; max-width: 100%; overflow: auto; overflow-wrap: normal; }
.chat-reply .markdown-content img { max-width: 100%; }
-.chat-reply .markdown-content a { color: var(--chat-accent); text-underline-offset: 3px; }
-.chat-waiting { display: flex; align-items: center; gap: 10px; margin: 0; font-size: 13px; color: var(--text-mid); }
-.chat-activity-dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--chat-accent); flex-shrink: 0; animation: chat-pulse 1.8s ease-in-out infinite; }
+.chat-reply .markdown-content a { color: var(--accent); text-underline-offset: 3px; }
+.chat-waiting { display: flex; align-items: center; gap: 10px; margin: 0; font-size: 13px; color: var(--text-secondary); }
+.chat-activity-dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--info); flex-shrink: 0; animation: chat-pulse 1.8s ease-in-out infinite; }
@keyframes chat-pulse { 50% { opacity: .35; } }
-.chat-notice { color: var(--text-mid); padding: 16px 12px; font-size: 14px; }
+.chat-notice { color: var(--text-secondary); padding: 16px 12px; font-size: 14px; }
.chat-notice p { margin-top: 0; }
-.chat-tool { border: 1px solid var(--border-loud); border-radius: 6px; overflow: hidden; }
+.chat-tool { border: 1px solid var(--border); border-radius: 6px; overflow: hidden; }
.chat-tool summary { display: flex; align-items: center; gap: 12px; padding: 12px 16px; cursor: pointer; list-style: none; background: var(--surface); }
.chat-tool summary::-webkit-details-marker { display: none; }
.chat-tool summary:hover { background: var(--surface-2); }
@@ -91,44 +90,44 @@
.chat-tool-chevron { flex-shrink: 0; transition: transform 150ms ease-out; }
.chat-tool[open] .chat-tool-chevron { transform: rotate(90deg); }
.chat-tool-label { min-width: 0; overflow-wrap: anywhere; line-height: 1.4; }
-.chat-tool-status { display: flex; align-items: center; gap: 5px; margin-left: auto; flex-shrink: 0; color: var(--text-mid); font-size: 13px; }
-.chat-tool-status.is-completed { color: var(--outcome-merged); }
-.chat-tool-status.is-failed { color: var(--outcome-failed); }
+.chat-tool-status { display: flex; align-items: center; gap: 5px; margin-left: auto; flex-shrink: 0; color: var(--text-secondary); font-size: 13px; }
+.chat-tool-status.is-completed { color: var(--success); }
+.chat-tool-status.is-failed { color: var(--danger); }
.chat-tool-details { padding: 14px 16px; border-top: 1px solid var(--border); }
.chat-tool-details h3 { margin: 0 0 6px; font-size: 13px; font-weight: 500; }
.chat-tool-details pre { margin: 0 0 14px; padding: 12px; background: var(--surface); border-radius: 4px; font-family: var(--font-mono); font-size: 12px; line-height: 1.5; white-space: pre-wrap; overflow-wrap: anywhere; max-height: 320px; overflow: auto; }
.chat-tool-details pre:last-child, .chat-tool-details p:last-child { margin-bottom: 0; }
-.chat-tool-details p { color: var(--text-mid); font-size: 13px; }
+.chat-tool-details p { color: var(--text-secondary); font-size: 13px; }
.chat-plan { margin: 0; padding-left: 22px; }
.chat-plan li { padding: 3px 0; }
-.chat-plan-status { margin-left: 12px; font-size: 12px; color: var(--text-mid); }
-.chat-plan li[data-state="completed"] .chat-plan-status { color: var(--outcome-merged); }
+.chat-plan-status { margin-left: 12px; font-size: 12px; color: var(--text-secondary); }
+.chat-plan li[data-state="completed"] .chat-plan-status { color: var(--success); }
.chat-interrupted { font-size: 14px; }
-.chat-interrupted strong { font-weight: 500; color: var(--accent-amber); }
-.chat-interrupted p { margin: 4px 0 12px; color: var(--text-mid); }
-.chat-interrupted > span { display: block; margin-top: 10px; color: var(--text-mid); font-size: 12px; }
+.chat-interrupted strong { font-weight: 500; color: var(--warning); }
+.chat-interrupted p { margin: 4px 0 12px; color: var(--text-secondary); }
+.chat-interrupted > span { display: block; margin-top: 10px; color: var(--text-secondary); font-size: 12px; }
.chat-jump { display: flex; justify-content: center; padding: 6px 0; }
-.chat-error { margin: 0 var(--chat-gutter) 12px; color: var(--outcome-failed); font-size: 14px; }
+.chat-error { margin: 0 var(--chat-gutter) 12px; color: var(--danger); font-size: 14px; }
.chat-error p { margin: 0 0 8px; }
-.chat-pin-error { display: flex; align-items: center; gap: 12px; grid-column: 1 / -1; grid-row: 1; padding: 10px 16px; color: var(--outcome-failed); border-bottom: 1px solid var(--border); font-size: 14px; }
+.chat-pin-error { display: flex; align-items: center; gap: 12px; grid-column: 1 / -1; grid-row: 1; padding: 10px 16px; color: var(--danger); border-bottom: 1px solid var(--border); font-size: 14px; }
.chat-composer { padding: 10px var(--chat-gutter) 18px; flex-shrink: 0; }
-.chat-composer-controls { display: flex; align-items: flex-end; gap: 8px; padding: 8px; border: 1px solid var(--border-loud); border-radius: 10px; background: var(--surface); }
-.chat-composer-controls:focus-within { border-color: var(--chat-accent); }
+.chat-composer-controls { display: flex; align-items: flex-end; gap: 8px; padding: 8px; border: 1px solid var(--border-strong); border-radius: 10px; background: var(--surface); }
+.chat-composer-controls:focus-within { border-color: var(--accent); }
.chat-composer textarea { flex: 1; width: 100%; min-width: 0; min-height: 36px; max-height: 180px; field-sizing: content; resize: vertical; padding: 6px 8px; border: 0; background: transparent; color: var(--text); font: inherit; line-height: 1.6; }
-.chat-composer textarea::placeholder { color: var(--text-dim); }
+.chat-composer textarea::placeholder { color: var(--text-tertiary); }
.page-chat .chat-composer textarea:focus-visible { outline: none; }
.chat-composer-actions { display: flex; align-items: center; gap: 8px; }
-.chat-composer > p { margin: 7px 0 0; color: var(--text-dim); font-size: 11px; text-align: right; }
+.chat-composer > p { margin: 7px 0 0; color: var(--text-tertiary); font-size: 11px; text-align: right; }
.chat-thread.is-new { overflow-y: auto; }
.chat-thread.is-new .chat-thread-head { border-bottom: 0; }
.chat-intro { margin: clamp(16px, 5vh, 56px) var(--chat-gutter) 12px; max-width: 65ch; }
.chat-intro h1 { margin: 0 0 12px; font-size: clamp(24px, 2.5vw, 30px); line-height: 1.2; font-weight: 500; letter-spacing: -.02em; text-wrap: balance; }
-.chat-intro p { margin: 0 0 8px; color: var(--text-mid); font-size: 15px; line-height: 1.65; }
+.chat-intro p { margin: 0 0 8px; color: var(--text-secondary); font-size: 15px; line-height: 1.65; }
.chat-starters { margin: 0 var(--chat-gutter) 32px; }
-.chat-starters > button { display: flex; align-items: center; justify-content: space-between; gap: 24px; width: 100%; min-height: 60px; padding: 16px 2px; border-top: 1px solid var(--border); text-align: left; color: var(--text-mid); font-size: 14px; line-height: 1.6; }
-.chat-starters > button:hover { color: var(--chat-accent); }
+.chat-starters > button { display: flex; align-items: center; justify-content: space-between; gap: 24px; width: 100%; min-height: 60px; padding: 16px 2px; border-top: 1px solid var(--border); text-align: left; color: var(--text-secondary); font-size: 14px; line-height: 1.6; }
+.chat-starters > button:hover { color: var(--accent); }
.chat-starters > button svg { flex-shrink: 0; }
-.chat-apps { border-top: 1px solid var(--border); margin: 0; padding-top: 16px; color: var(--text-dim); font-size: 12px; line-height: 1.65; }
+.chat-apps { border-top: 1px solid var(--border); margin: 0; padding-top: 16px; color: var(--text-tertiary); font-size: 12px; line-height: 1.65; }
@media (max-width: 1100px) {
.page-chat > .page-shell-body { grid-template-columns: 260px minmax(0, 1fr); }
diff --git a/frontend/src/command-center.css b/frontend/src/command-center.css
index f422bd35b..005026100 100644
--- a/frontend/src/command-center.css
+++ b/frontend/src/command-center.css
@@ -32,13 +32,13 @@
overflow-wrap: anywhere;
}
-.command-breadcrumb > span { color: var(--text-dim); }
+.command-breadcrumb > span { color: var(--text-tertiary); }
.command-breadcrumb strong { font-weight: 500; }
-.command-app-settings { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 32px; height: 32px; margin-left: 4px; border-radius: 5px; color: var(--text-mid); text-decoration: none; }
-.command-app-settings:visited { color: var(--text-mid); }
+.command-app-settings { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 32px; height: 32px; margin-left: 4px; border-radius: 5px; color: var(--text-secondary); text-decoration: none; }
+.command-app-settings:visited { color: var(--text-secondary); }
.command-app-settings:hover, .command-app-settings[aria-current="page"] { color: var(--text); background: var(--surface-3); }
.command-utilities { display: flex; align-items: center; gap: 20px; margin-left: auto; flex-shrink: 0; }
-.command-timezone { color: var(--text-mid); }
+.command-timezone { color: var(--text-secondary); }
.app-main { display: flex; flex: 1; flex-direction: column; min-width: 0; min-height: 0; overflow: hidden; }
.sidebar {
@@ -53,47 +53,46 @@
}
.sidebar-desktop { position: fixed; inset: 0 auto 0 0; z-index: 15; }
-.sidebar-brand { display: flex; align-items: center; gap: 10px; min-height: 44px; margin: 0 10px 24px; font-size: 23px; font-weight: 600; letter-spacing: -0.04em; }
+.sidebar-brand { display: flex; align-items: center; min-height: 48px; margin: 0 4px 24px; }
.sidebar-brand, .sidebar-brand:visited { color: var(--text); text-decoration: none; }
-.sidebar-brand .brand-glyph { width: 24px; height: 24px; }
.sidebar-work { display: flex; flex-direction: column; gap: 3px; }
-.sidebar-link { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 44px; padding: 9px 10px; border: 1px solid transparent; border-radius: 5px; color: var(--text-mid); text-decoration: none; font-size: 15px; text-align: left; }
-.sidebar-link:visited { color: var(--text-mid); }
+.sidebar-link { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 44px; padding: 9px 10px; border: 1px solid transparent; border-radius: 5px; color: var(--text-secondary); text-decoration: none; font-size: 15px; text-align: left; }
+.sidebar-link:visited { color: var(--text-secondary); }
.sidebar-link:hover { background: var(--surface-2); color: var(--text); }
-.sidebar-link[aria-current="page"] { color: var(--text); background: var(--surface-3); border-color: var(--border-loud); }
+.sidebar-link[aria-current="page"] { color: var(--text); background: var(--surface-3); border-color: var(--border); }
.sidebar-link[aria-current="page"] > svg { color: var(--text); }
.sidebar-link > svg { flex-shrink: 0; }
.sidebar-link > span { min-width: 0; overflow-wrap: anywhere; }
.sidebar-apps { display: flex; flex: 1; flex-direction: column; min-height: 0; margin-top: 16px; }
-.sidebar-group-title { display: flex; align-items: baseline; justify-content: space-between; padding: 0 10px 8px; color: var(--text-dim); font-size: 13px; }
-.sidebar-search { display: flex; flex: 0 0 auto; align-items: center; min-height: 40px; gap: 8px; padding: 0 10px; margin-bottom: 8px; border: 1px solid var(--border-loud); border-radius: 5px; background: var(--bg); color: var(--text-dim); }
-.sidebar-search:focus-within { outline: 2px solid var(--text-mid); outline-offset: 2px; }
+.sidebar-group-title { display: flex; align-items: baseline; justify-content: space-between; padding: 0 10px 8px; color: var(--text-tertiary); font-size: 13px; }
+.sidebar-search { display: flex; flex: 0 0 auto; align-items: center; min-height: 40px; gap: 8px; padding: 0 10px; margin-bottom: 8px; border: 1px solid var(--border-strong); border-radius: 5px; background: var(--input); color: var(--text-tertiary); }
+.sidebar-search:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
.sidebar-search > svg { flex-shrink: 0; }
.sidebar-search input { width: 100%; min-width: 0; border: 0; outline: 0; background: transparent; color: var(--text); font: inherit; padding: 8px 0; }
-.sidebar-search input::placeholder { color: var(--text-dim); }
-.sidebar-app-list { display: flex; flex-direction: column; gap: 2px; min-height: 0; overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--border-loud) transparent; }
-.sidebar-message { margin: 12px 10px; color: var(--text-dim); font-size: 13px; overflow-wrap: anywhere; }
+.sidebar-search input::placeholder { color: var(--text-tertiary); }
+.sidebar-app-list { display: flex; flex-direction: column; gap: 2px; min-height: 0; overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--border-soft) transparent; }
+.sidebar-message { margin: 12px 10px; color: var(--text-tertiary); font-size: 13px; overflow-wrap: anywhere; }
.sidebar-retry { text-decoration: underline; min-height: 44px; color: var(--text); }
.sidebar-footer { padding-top: 12px; margin-top: 12px; border-top: 1px solid var(--border); }
-.sidebar-account { flex: 0 0 auto; padding: 16px 10px 0; font-size: 13px; color: var(--text-mid); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
-.navigation-toggle, .navigation-close { display: none; align-items: center; justify-content: center; width: 44px; height: 44px; flex-shrink: 0; color: var(--text-mid); border-radius: 5px; }
+.sidebar-account { flex: 0 0 auto; padding: 16px 10px 0; font-size: 13px; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+.navigation-toggle, .navigation-close { display: none; align-items: center; justify-content: center; width: 44px; height: 44px; flex-shrink: 0; color: var(--text-secondary); border-radius: 5px; }
.sidebar-drawer { inset: 0 auto 0 0; width: min(300px, calc(100vw - 44px)); height: 100dvh; max-width: none; max-height: none; margin: 0; padding: 0; border: 0; background: var(--surface); }
.sidebar-drawer .sidebar { width: 100%; }
-.sidebar-drawer::backdrop { background: rgb(0 0 0 / 60%); }
+.sidebar-drawer::backdrop { background: var(--scrim); }
.command-app-navigation { display: flex; flex: 0 0 auto; gap: 20px; padding: 0 var(--page-gutter); border-bottom: 1px solid var(--border); overflow-x: auto; scrollbar-width: thin; }
-.command-tab { display: inline-flex; align-items: center; min-height: 46px; padding: 8px 0; color: var(--text-dim); border-bottom: 2px solid transparent; text-decoration: none; text-transform: capitalize; white-space: nowrap; }
+.command-tab { display: inline-flex; align-items: center; min-height: 46px; padding: 8px 0; color: var(--text-tertiary); border-bottom: 2px solid transparent; text-decoration: none; text-transform: capitalize; white-space: nowrap; }
.command-tab:hover, .command-tab[aria-current="page"] { color: var(--text); }
-.command-tab[aria-current="page"] { border-bottom-color: var(--text); }
+.command-tab[aria-current="page"] { border-bottom-color: var(--accent); }
.skip-navigation { position: fixed; top: 8px; left: 8px; z-index: 100; transform: translateY(-200%); padding: 12px; background: var(--surface-3); color: var(--text); }
.skip-navigation:focus { transform: none; }
-:is(.sidebar, .command-header, .command-app-navigation) :is(a, button, input):focus-visible { outline: 2px solid var(--text); outline-offset: 3px; }
+:is(.sidebar, .command-header, .command-app-navigation) :is(a, button, input):focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.sidebar-search input:focus-visible { outline: none; }
.command-center :is(.review-question legend, .review-option, .review-note, .review-btn, .ins-needs-body) { font-family: var(--font-sans); font-size: 15px; }
.command-center :is(.review-artifact-title, .review-helper, .review-error, .review-recommended) { font-family: var(--font-sans); font-size: 13px; }
.command-center .review-option { min-height: 44px; }
.command-center .subject-run { font-family: var(--font-sans); }
-.command-center :is(.wic-op, .wic-call)[role="button"]:focus-visible { outline: 2px solid var(--text); outline-offset: -2px; }
+.command-center :is(.wic-op, .wic-call)[role="button"]:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
@media (max-width: 1024px) {
:root { --page-gutter: 24px; }
diff --git a/frontend/src/components/AuthedApp.tsx b/frontend/src/components/AuthedApp.tsx
index 36ea93dda..41825cc0c 100644
--- a/frontend/src/components/AuthedApp.tsx
+++ b/frontend/src/components/AuthedApp.tsx
@@ -27,7 +27,7 @@ function QueryCacheMount({ account }: { account: Account }) {
)
return (
-
+
diff --git a/frontend/src/components/ChatChannels.test.tsx b/frontend/src/components/ChatChannels.test.tsx
index bbb1631bb..c8bee98ec 100644
--- a/frontend/src/components/ChatChannels.test.tsx
+++ b/frontend/src/components/ChatChannels.test.tsx
@@ -4,6 +4,7 @@ import { cleanup, render, screen } from '@testing-library/react'
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
import { Router } from 'wouter'
+import { UserPreferencesProvider } from '../lib/preferences'
import { SettingsPages } from './SettingsPages'
const account = { id: 'operator', username: 'ana@example.com', isDefault: true }
@@ -12,9 +13,9 @@ let channels = ['whatsapp']
function mount(path: string, appName?: string) {
window.history.replaceState(null, '', path)
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } })
- render(
+ render(
- )
+ )
}
beforeEach(() => {
@@ -28,7 +29,7 @@ beforeEach(() => {
defaultHarness: 'claude', defaultModel: '', defaultBilling: 'subscription',
defaultEffort: '', defaultTimeout: 1800, fastMode: false,
}
- if (url === '/api/settings/personal') response = { timezone: 'UTC', gateParkDestinationId: null, isTagRequired: false }
+ if (url === '/api/settings/personal') response = { timezone: 'UTC', theme: 'system', gateParkDestinationId: null, isTagRequired: false }
if (url === '/api/settings/apps') response = {
allowedEfforts: [], channels, apps: [{
name: 'chat', description: 'Live agent conversations.', icon: 'messages-square',
diff --git a/frontend/src/components/SettingsPages.test.tsx b/frontend/src/components/SettingsPages.test.tsx
index 85a45b4e6..94e024209 100644
--- a/frontend/src/components/SettingsPages.test.tsx
+++ b/frontend/src/components/SettingsPages.test.tsx
@@ -4,6 +4,7 @@ import { afterEach, describe, expect, it, onTestFinished, vi } from 'vitest'
import { App } from '../App'
import { api } from '../api/client'
+import { UserPreferencesProvider } from '../lib/preferences'
import { SETTINGS_FIELDS } from './settings'
import type {
AgentSetting,
@@ -145,6 +146,7 @@ const providerDirectory = [
const patched: Record
[] = []
const personalPatched: Record[] = []
+let rejectPersonalPatch = false
const appSettings: AppsSettingsResponse = {
allowedEfforts: ['low', 'medium', 'high'],
@@ -342,12 +344,14 @@ function stubFetch(
if (path === '/api/settings/personal') {
const preferences = savedPersonal ?? {
timezone: 'UTC',
+ theme: 'system',
gateParkDestinationId: savedSettings.gateParkDestinationId,
isTagRequired: false,
}
if (init?.method === 'PATCH') {
const changes = JSON.parse(String(init.body))
personalPatched.push(changes)
+ if (rejectPersonalPatch) return new Response('{}', { status: 503 })
savedPersonal = { ...preferences, ...changes }
}
return new Response(JSON.stringify(savedPersonal ?? preferences), { status: 200 })
@@ -439,14 +443,17 @@ function stubFetch(
function renderSettings(workPath?: string) {
window.history.replaceState(null, '', workPath ?? '/settings/personal')
- vi.stubGlobal(
- 'matchMedia',
- vi.fn(() => ({ matches: true, addEventListener: vi.fn(), removeEventListener: vi.fn() })),
- )
+ vi.stubGlobal('matchMedia', vi.fn(() => ({
+ matches: true,
+ addEventListener: vi.fn(),
+ removeEventListener: vi.fn(),
+ })))
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } })
render(
-
+
+
+
,
)
}
@@ -457,6 +464,8 @@ afterEach(() => {
vi.restoreAllMocks()
patched.length = 0
personalPatched.length = 0
+ rejectPersonalPatch = false
+ window.localStorage.removeItem('druks:theme:operator')
})
describe('SettingsPages app fields', () => {
@@ -837,6 +846,42 @@ describe('SettingsPages agents', () => {
})
describe('settings drafts and navigation', () => {
+ it('previews theme choices and persists them with Personal Settings', async () => {
+ stubFetch(false)
+ renderSettings('/settings/personal')
+
+ const theme = await screen.findByRole('combobox', { name: 'Theme' })
+ expect((theme as HTMLSelectElement).value).toBe('system')
+ expect(document.documentElement.dataset.theme).toBeUndefined()
+
+ fireEvent.change(theme, { target: { value: 'light' } })
+ await waitFor(() => expect(document.documentElement.dataset.theme).toBe('light'))
+ expect(document.documentElement.dataset.theme).toBe('light')
+ expect((theme as HTMLSelectElement).value).toBe('light')
+ expect(personalPatched).toEqual([])
+ fireEvent.click(screen.getByRole('button', { name: 'Save changes' }))
+ await waitFor(() => expect(personalPatched).toEqual([{ theme: 'light' }]))
+
+ fireEvent.change(theme, { target: { value: 'dark' } })
+ await waitFor(() => expect(document.documentElement.dataset.theme).toBe('dark'))
+ fireEvent.click(screen.getByRole('button', { name: 'Save changes' }))
+ await waitFor(() => expect(personalPatched).toEqual([{ theme: 'light' }, { theme: 'dark' }]))
+ })
+
+ it('keeps a failed theme save as a draft', async () => {
+ stubFetch(false)
+ rejectPersonalPatch = true
+ renderSettings('/settings/personal')
+
+ const theme = await screen.findByRole('combobox', { name: 'Theme' })
+ fireEvent.change(theme, { target: { value: 'dark' } })
+ fireEvent.click(screen.getByRole('button', { name: 'Save changes' }))
+
+ await screen.findByRole('alert')
+ expect(document.documentElement.dataset.theme).toBe('dark')
+ expect((theme as HTMLSelectElement).value).toBe('dark')
+ })
+
it('keeps shared drafts across pages and saves only the current page', async () => {
stubFetch(false)
renderSettings()
@@ -989,7 +1034,7 @@ describe('settings resource and keyboard behavior', () => {
expect(update).toHaveBeenCalledTimes(1)
expect(update).toHaveBeenCalledWith({ timezone: 'Europe/Madrid' })
await act(async () => {
- finish({ timezone: 'Europe/Madrid', gateParkDestinationId: null, isTagRequired: false } as PersonalSettings)
+ finish({ timezone: 'Europe/Madrid', theme: 'system', gateParkDestinationId: null, isTagRequired: false } as PersonalSettings)
})
expect(
(screen.getByRole('button', { name: 'Save changes' }) as HTMLButtonElement).disabled,
@@ -1035,7 +1080,9 @@ it('restores the work return URL when a settings fragment route reloads', async
-
+
+
+
,
)
await screen.findByRole('combobox', { name: 'Timezone' })
@@ -1699,7 +1746,7 @@ it('focuses a personal field after preferences finish loading', async () => {
renderSettings('/settings/personal?field=timezone')
await screen.findByRole('heading', { name: 'Preferences' })
expect(screen.queryByRole('combobox', { name: 'Timezone' })).toBeNull()
- await act(async () => { finish({ timezone: 'UTC', gateParkDestinationId: null, isTagRequired: false }) })
+ await act(async () => { finish({ timezone: 'UTC', theme: 'system', gateParkDestinationId: null, isTagRequired: false }) })
await waitFor(() => expect(document.activeElement?.id).toBe('personal-timezone'))
})
diff --git a/frontend/src/components/SettingsPages.tsx b/frontend/src/components/SettingsPages.tsx
index a60e1ce5e..1aefa23d1 100644
--- a/frontend/src/components/SettingsPages.tsx
+++ b/frontend/src/components/SettingsPages.tsx
@@ -25,6 +25,7 @@ import { appLabel } from '../apps/registry'
import { useTicker } from '../lib/useTicker'
import { absTime } from '../lib/format'
import { harnessColors } from '../lib/harnessColors'
+import { useThemePreference } from '../lib/preferences'
import { Page } from './Page'
import { Sidebar } from './Sidebar'
import { BrowserProfilesPane } from './BrowserProfilesPane'
@@ -174,6 +175,8 @@ export function SettingsPages({
const errorNotice = useRef(null)
const effectiveDefaults = defaults ?? savedDefaults
const personalTimezone = personalEdits.timezone ?? personalQuery.data?.timezone ?? 'UTC'
+ const { theme, previewTheme } = useThemePreference()
+ const personalTheme = personalEdits.theme ?? theme
const personalChanges = Object.fromEntries(
Object.entries(personalEdits).filter(([field, value]) =>
value !== personalQuery.data?.[field as keyof UpdatePersonalSettingsRequest],
@@ -284,7 +287,10 @@ export function SettingsPages({
}, [dirtyPages.length])
function discard(page: string) {
- if (page === 'personal') setPersonalEdits({})
+ if (page === 'personal') {
+ setPersonalEdits({})
+ previewTheme(null)
+ }
else if (page === 'agents') setDefaults(null)
else
setAppEdits((current) => {
@@ -691,6 +697,11 @@ export function SettingsPages({
setPersonalEdits((current) => ({ ...current, timezone: value }))}
+ theme={personalTheme}
+ setTheme={(value) => {
+ setPersonalEdits((current) => ({ ...current, theme: value }))
+ previewTheme(value)
+ }}
timezones={timezones}
clock={clock}
busy={saving}
diff --git a/frontend/src/components/SettingsPanes.tsx b/frontend/src/components/SettingsPanes.tsx
index 20e07aa6d..8f8c25af6 100644
--- a/frontend/src/components/SettingsPanes.tsx
+++ b/frontend/src/components/SettingsPanes.tsx
@@ -25,6 +25,7 @@ import {
type Billing,
type McpServer,
type Pat,
+ type PersonalSettings,
type Provider,
type ProviderCatalog,
type ProviderDirectoryEntry,
@@ -428,12 +429,16 @@ function InheritCell({
export function PreferencesPane({
timezone,
setTimezone,
+ theme,
+ setTheme,
timezones,
clock,
busy,
}: {
timezone: string
setTimezone: (timezone: string) => void
+ theme: PersonalSettings['theme']
+ setTheme: (theme: PersonalSettings['theme']) => void
timezones: string[]
clock: string
busy: boolean
@@ -469,6 +474,24 @@ export function PreferencesPane({