From 28ce14f70de10765c9b4d991c7bff9e89960b002 Mon Sep 17 00:00:00 2001 From: Volker Christian Date: Fri, 28 Aug 2026 20:07:12 +0200 Subject: [PATCH] Emphasize final Codex answers --- docs/ui-behavior.md | 4 ++-- src/codex/middle/ConversationCards.cpp | 24 ++++++++++++++---------- src/codex/ui/UiStyle.cpp | 8 ++++---- tests/codex/ConversationCardsTest.cpp | 20 ++++++++++---------- ui-review/UX-DESIGN-DECISIONS.md | 6 +++--- web/src/styles.css | 10 +++++----- 6 files changed, 38 insertions(+), 34 deletions(-) diff --git a/docs/ui-behavior.md b/docs/ui-behavior.md index 86392c4..c350c82 100644 --- a/docs/ui-behavior.md +++ b/docs/ui-behavior.md @@ -126,8 +126,8 @@ Generated-image items show the app-server-saved image as a bounded thumbnail. Selecting it opens the shared non-modal image viewer; encoded image data is never displayed as generic activity text. -User messages use the canonical soft-violet identity surface. Final Codex -messages use the canonical soft-blue identity surface, while interim Codex +User messages use the canonical soft-blue identity surface. Final Codex +messages use the canonical soft-violet identity surface, while interim Codex updates remain neutral and identify their phase in the header. Process cards also remain neutral so they support rather than dominate the primary exchange. Status text alone uses canonical semantic state colors. diff --git a/src/codex/middle/ConversationCards.cpp b/src/codex/middle/ConversationCards.cpp index 3b7615f..0158832 100644 --- a/src/codex/middle/ConversationCards.cpp +++ b/src/codex/middle/ConversationCards.cpp @@ -981,8 +981,10 @@ class ConversationCard::Impl final { const bool waiting = prompt->state == PromptState::Queued || prompt->state == PromptState::InFlight; const bool failed = prompt->state == PromptState::Failed; - const QString foreground = failed ? QStringLiteral("#982f3d") - : QStringLiteral("#53389e"); + const QString foreground = waiting || transitioning + ? QStringLiteral("#536b8f") + : failed ? QStringLiteral("#982f3d") + : QStringLiteral("#1d2633"); const QString style = QStringLiteral("background:transparent;color:%1;").arg(foreground); bool changed = false; @@ -1089,11 +1091,13 @@ void ConversationCard::paintEvent(QPaintEvent *event) { const bool transitioning = acceptedTransitionActive(*prompt, now); const bool failed = prompt->state == PromptState::Failed; const QColor background = waiting || transitioning - ? QColor(QStringLiteral("#eee8fb")) + ? QColor(QStringLiteral("#dbe7f8")) : failed ? QColor(QStringLiteral("#fff0f2")) - : QColor(QStringLiteral("#f4f0ff")); - const QColor border = failed ? QColor(QStringLiteral("#efb8c0")) - : QColor(QStringLiteral("#d4c5f2")); + : QColor(QStringLiteral("#eaf2ff")); + const QColor border = waiting || transitioning + ? QColor(QStringLiteral("#9eb9df")) + : failed ? QColor(QStringLiteral("#efb8c0")) + : QColor(QStringLiteral("#bfd3f9")); painter.setBrush(background); painter.setPen(QPen(border, 1.0)); painter.drawRoundedRect(bounds, 8.0, 8.0); @@ -1113,9 +1117,9 @@ void ConversationCard::paintEvent(QPaintEvent *event) { const qreal center = bounds.left() + position * bounds.width(); const qreal radius = std::max(28.0, bounds.width() * 0.24); QLinearGradient sweep(center - radius, 0.0, center + radius, 0.0); - sweep.setColorAt(0.0, QColor(105, 65, 198, 0)); - sweep.setColorAt(0.5, QColor(155, 128, 214, 105)); - sweep.setColorAt(1.0, QColor(105, 65, 198, 0)); + sweep.setColorAt(0.0, QColor(47, 111, 235, 0)); + sweep.setColorAt(0.5, QColor(117, 160, 239, 105)); + sweep.setColorAt(1.0, QColor(47, 111, 235, 0)); QPainterPath clip; clip.addRoundedRect(bounds, 8.0, 8.0); painter.save(); @@ -1124,7 +1128,7 @@ void ConversationCard::paintEvent(QPaintEvent *event) { painter.restore(); painter.setBrush(Qt::NoBrush); - painter.setPen(QPen(QColor(QStringLiteral("#b49bdf")), 1.5)); + painter.setPen(QPen(QColor(QStringLiteral("#79a0d7")), 1.5)); painter.drawRoundedRect(bounds, 8.0, 8.0); } diff --git a/src/codex/ui/UiStyle.cpp b/src/codex/ui/UiStyle.cpp index 7a6ef1e..f579454 100644 --- a/src/codex/ui/UiStyle.cpp +++ b/src/codex/ui/UiStyle.cpp @@ -204,10 +204,10 @@ QString applicationStyleSheet() { QFrame[kind="panel"] { background: #ffffff; } QFrame[kind="raised"] { background: #ffffff; border: 1px solid #d7dee8; border-radius: 10px; } QFrame[kind="raised"][tone="warning"] { background: #fff6df; border-color: #e5c77d; } - QFrame[messageRole="user"] { background: #f4f0ff; border: 1px solid #d4c5f2; border-radius: 8px; } - QFrame[messageRole="user"] QLabel[kind="title"] { color: #53389e; } - QFrame[messageRole="agent"][messagePhase="final"] { background: #eaf2ff; border: 1px solid #bfd3f9; border-radius: 8px; } - QFrame[messageRole="agent"][messagePhase="final"] QLabel[kind="title"] { color: #285fca; } + QFrame[messageRole="user"] { background: #eaf2ff; border: 1px solid #bfd3f9; border-radius: 8px; } + QFrame[messageRole="user"] QLabel[kind="title"] { color: #285fca; } + QFrame[messageRole="agent"][messagePhase="final"] { background: #f4f0ff; border: 1px solid #d4c5f2; border-radius: 8px; } + QFrame[messageRole="agent"][messagePhase="final"] QLabel[kind="title"] { color: #53389e; } QFrame[messageRole="agent"][messagePhase="update"] { background: #ffffff; border: 1px solid #d7dee8; border-radius: 8px; } QFrame[kind="summary"] { background: #f8fafc; border: 1px solid #d7dee8; border-radius: 7px; } QFrame[kind="standardDivider"] { background: #d7dee8; border: none; } diff --git a/tests/codex/ConversationCardsTest.cpp b/tests/codex/ConversationCardsTest.cpp index a1ed8e4..0e29807 100644 --- a/tests/codex/ConversationCardsTest.cpp +++ b/tests/codex/ConversationCardsTest.cpp @@ -223,15 +223,15 @@ bool testMessageIdentityPalette() { }; const bool result = expect( titleColor(user) == - QColor(QString::fromLatin1(codexui::UiStyle::purpleText)) && - surfaceColor(user) == - QColor(QString::fromLatin1(codexui::UiStyle::purpleSurface)) && + QColor(QString::fromLatin1(codexui::UiStyle::blueHover)) && + surfaceColor(user) == QColor(QStringLiteral("#eaf2ff")) && surfaceColor(update) == QColor(QString::fromLatin1(codexui::UiStyle::panel)) && titleColor(final) == - QColor(QString::fromLatin1(codexui::UiStyle::blueHover)) && - surfaceColor(final) == QColor(QStringLiteral("#eaf2ff")), - "You is violet, interim Codex is neutral, and final Codex is blue"); + QColor(QString::fromLatin1(codexui::UiStyle::purpleText)) && + surfaceColor(final) == + QColor(QString::fromLatin1(codexui::UiStyle::purpleSurface)), + "You is blue, interim Codex is neutral, and final Codex is violet"); qApp->setStyleSheet(originalStyleSheet); return result; } @@ -1560,13 +1560,13 @@ bool testPendingPromptAnimation() { const QImage second = card.grab().toImage(); bool result = expect(first != second, - "an unacknowledged prompt visibly animates its violet sweep"); + "an unacknowledged prompt visibly animates its blue sweep"); result &= expect( first.pixelColor(10, first.height() - 10).blue() > - first.pixelColor(10, first.height() - 10).green() && - first.pixelColor(10, first.height() - 10).red() > + first.pixelColor(10, first.height() - 10).red() && + first.pixelColor(10, first.height() - 10).blue() > first.pixelColor(10, first.height() - 10).green(), - "the temporary You card stays in the violet identity family"); + "the temporary You card stays in the blue identity family"); auto &accepted = std::get(pending.payload); accepted.state = PromptState::Accepted; diff --git a/ui-review/UX-DESIGN-DECISIONS.md b/ui-review/UX-DESIGN-DECISIONS.md index 2ccc738..8dcec12 100644 --- a/ui-review/UX-DESIGN-DECISIONS.md +++ b/ui-review/UX-DESIGN-DECISIONS.md @@ -10,9 +10,9 @@ This document records the implemented CodexUI visual and interaction contract. white-text contrast. - Hover, focus, selection, disabled, warning, error, pending, and active states remain visually distinct. -- User messages use the violet identity family. Final Codex narrative uses the - canonical blue identity family; interim Codex updates remain neutral and are - identified by their phase label. Commands, tool activity, files, and +- User messages use the blue identity family. Final Codex narrative uses the + more prominent violet identity family; interim Codex updates remain neutral + and are identified by their phase label. Commands, tool activity, files, and collaboration activity also use neutral raised cards. - Scrollbars use one compact application style across conversation, nested output, State, Protocol, and Inspector surfaces. diff --git a/web/src/styles.css b/web/src/styles.css index 0b65ced..a87e092 100644 --- a/web/src/styles.css +++ b/web/src/styles.css @@ -64,13 +64,13 @@ h1, h2, h3, p { margin: 0; } .conversation-card > header small { font-size: 9px; opacity: .55; } .card-meta { display: flex; align-items: center; gap: 6px; }.card-meta button { width: 20px; height: 20px; padding: 0; border: 1px solid #dce2eb; border-radius: 5px; background: #fff; color: #778194; cursor: pointer; } .conversation-card.collapsed > header { margin-bottom: 0; } -.conversation-card.userMessage, .conversation-card.localPrompt { margin-left: 12%; background: #f4f0ff; border-color: #d4c5f2; } -.conversation-card.userMessage > header span, .conversation-card.localPrompt > header span { color: #53389e; } -.conversation-card.localPrompt { background: linear-gradient(100deg, #f4f0ff, #e8def8, #f4f0ff); background-size: 200% 100%; animation: awaiting 1.7s linear infinite; } +.conversation-card.userMessage, .conversation-card.localPrompt { margin-left: 12%; background: #eaf2ff; border-color: #bfd3f9; } +.conversation-card.userMessage > header span, .conversation-card.localPrompt > header span { color: #285fca; } +.conversation-card.localPrompt { background: linear-gradient(100deg, #eaf2ff, #dbe7f8, #eaf2ff); background-size: 200% 100%; animation: awaiting 1.7s linear infinite; } .conversation-card.reasoning { border-left: 3px solid #7896df; } .conversation-card.agentMessage.update { background: #fff; border-color: #dce2eb; } -.conversation-card.agentMessage.final { background: #eaf2ff; border-color: #bfd3f9; } -.conversation-card.agentMessage.final > header span { color: #285fca; } +.conversation-card.agentMessage.final { background: #f4f0ff; border-color: #d4c5f2; } +.conversation-card.agentMessage.final > header span { color: #53389e; } .conversation-card.fileChanges { border-left: 3px solid #28a56c; } @keyframes awaiting { to { background-position: -200% 0; } } .card-text { white-space: pre-wrap; overflow-wrap: anywhere; font-size: 14px; line-height: 1.55; }