mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
Refine goal UI around memory theme tokens
Goal status needed to read like persistent agent state, not a success alert. Reuse the Memory surface language and expose goal-specific aliases so the visual treatment follows each supported desktop theme.\n\nConstraint: Must keep /goal visible in both active-session chrome and transcript events.\nRejected: Continue using success tokens | they made the UI look like a warning/success card and clashed with white/dark themes.\nConfidence: high\nScope-risk: narrow\nTested: cd desktop && bun run test -- --run src/pages/ActiveSession.test.tsx src/components/chat/MessageList.test.tsx src/theme/globals.test.ts\nTested: cd desktop && bun run lint\nTested: cd desktop && bun run build\nTested: Chrome screenshot fixture for light, white, and dark goal UI tokens.
This commit is contained in:
parent
d668a8e0d1
commit
291dd3e07d
@ -153,8 +153,7 @@ function GoalEventCard({ message }: { message: GoalEvent }) {
|
||||
const t = useTranslation()
|
||||
const titleKey = `chat.goalEvent.${message.action === 'status' ? 'statusTitle' : message.action}` as TranslationKey
|
||||
const title = t(titleKey) === titleKey ? t('chat.goalEvent.message') : t(titleKey)
|
||||
const details = [
|
||||
message.objective ? t('chat.goalEvent.objective', { value: message.objective }) : null,
|
||||
const metaDetails = [
|
||||
message.status ? t('chat.goalEvent.statusValue', { value: message.status }) : null,
|
||||
message.budget ? t('chat.goalEvent.budget', { value: message.budget }) : null,
|
||||
message.continuations ? t('chat.goalEvent.continuations', { value: message.continuations }) : null,
|
||||
@ -162,23 +161,33 @@ function GoalEventCard({ message }: { message: GoalEvent }) {
|
||||
|
||||
return (
|
||||
<div className="mb-3 flex justify-center">
|
||||
<div className="flex max-w-[min(680px,100%)] items-start gap-3 rounded-[8px] border border-[var(--color-success)]/25 bg-[var(--color-success-container)]/28 px-3.5 py-3 text-left shadow-sm">
|
||||
<div className="mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-[6px] bg-[var(--color-success)]/12 text-[var(--color-success)]">
|
||||
<div className="flex w-full max-w-[680px] items-start gap-3 rounded-lg border border-[var(--color-goal-border)] bg-[var(--color-goal-surface)] px-3 py-2.5 text-left">
|
||||
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md border border-[var(--color-goal-border)] bg-[var(--color-goal-icon-bg)] text-[var(--color-goal-accent)]">
|
||||
<Target size={15} strokeWidth={2.25} aria-hidden="true" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<div className="text-sm font-semibold text-[var(--color-text-primary)]">{title}</div>
|
||||
{details.length > 0 ? (
|
||||
<div className="mt-1 space-y-0.5 text-xs leading-5 text-[var(--color-text-secondary)]">
|
||||
{details.map((detail) => (
|
||||
<div key={detail} className="break-words">{detail}</div>
|
||||
))}
|
||||
{message.objective ? (
|
||||
<div className="mt-1 line-clamp-2 text-xs leading-5 text-[var(--color-text-secondary)]">
|
||||
{t('chat.goalEvent.objective', { value: message.objective })}
|
||||
</div>
|
||||
) : message.message ? (
|
||||
<div className="mt-1 whitespace-pre-wrap text-xs leading-5 text-[var(--color-text-secondary)]">
|
||||
{message.message}
|
||||
</div>
|
||||
) : null}
|
||||
{metaDetails.length > 0 && (
|
||||
<div className="mt-2 flex flex-wrap items-center gap-1.5">
|
||||
{metaDetails.map((detail) => (
|
||||
<span
|
||||
key={detail}
|
||||
className="rounded-[var(--radius-sm)] border border-[var(--color-goal-chip-border)] bg-[var(--color-goal-chip-bg)] px-1.5 py-0.5 text-[11px] font-medium text-[var(--color-text-secondary)]"
|
||||
>
|
||||
{detail}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -223,31 +223,46 @@ function GoalStatusPanel({
|
||||
data-testid="active-goal-panel"
|
||||
className={
|
||||
compact
|
||||
? 'border-b border-[var(--color-success)]/20 bg-[var(--color-success)]/6 px-4 py-2'
|
||||
: 'mx-auto w-full max-w-[860px] border-b border-[var(--color-success)]/20 px-8 py-2'
|
||||
? 'border-b border-[var(--color-border)] bg-[var(--color-surface-container-lowest)] px-4 py-2'
|
||||
: 'mx-auto w-full max-w-[900px] px-8 py-2.5'
|
||||
}
|
||||
>
|
||||
<div className="flex min-w-0 items-center gap-3 rounded-md border border-[var(--color-success)]/25 bg-[var(--color-success)]/8 px-3 py-2">
|
||||
<span className="material-symbols-outlined shrink-0 text-[18px] text-[var(--color-success)]">track_changes</span>
|
||||
<div className="flex min-w-0 items-center gap-3 rounded-lg border border-[var(--color-goal-border)] bg-[var(--color-goal-surface)] px-3 py-2.5">
|
||||
<span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md border border-[var(--color-goal-border)] bg-[var(--color-goal-icon-bg)] text-[var(--color-goal-accent)]">
|
||||
<span className="material-symbols-outlined text-[18px]">track_changes</span>
|
||||
</span>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
<span className="shrink-0 text-[11px] font-semibold uppercase tracking-normal text-[var(--color-success)]">
|
||||
<span className="shrink-0 text-[12px] font-semibold text-[var(--color-text-primary)]">
|
||||
{t('chat.activeGoal.title')}
|
||||
</span>
|
||||
<span className="shrink-0 rounded-sm bg-[var(--color-success)]/12 px-1.5 py-0.5 text-[10px] font-semibold text-[var(--color-success)]">
|
||||
<span className="inline-flex shrink-0 items-center gap-1 rounded-[var(--radius-sm)] border border-[var(--color-goal-border)] bg-[var(--color-goal-chip-bg)] px-1.5 py-0.5 text-[11px] font-semibold text-[var(--color-goal-accent)]">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-[var(--color-goal-accent)]" aria-hidden="true" />
|
||||
{stateLabel}
|
||||
</span>
|
||||
{goal.objective && (
|
||||
<span className="truncate text-xs font-semibold text-[var(--color-text-primary)]">
|
||||
<span className="truncate text-sm font-semibold text-[var(--color-text-primary)]">
|
||||
{goal.objective}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{(goal.budget || goal.continuations || goal.elapsed) && (
|
||||
<div className="mt-1 flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1 text-[10px] text-[var(--color-text-secondary)]">
|
||||
{goal.budget && <span>{t('chat.activeGoal.budget', { value: goal.budget })}</span>}
|
||||
{goal.continuations && <span>{t('chat.activeGoal.continuations', { value: goal.continuations })}</span>}
|
||||
{goal.elapsed && <span>{t('chat.activeGoal.elapsed', { value: goal.elapsed })}</span>}
|
||||
<div className="mt-1.5 flex min-w-0 flex-wrap items-center gap-1.5">
|
||||
{goal.budget && (
|
||||
<span className="rounded-[var(--radius-sm)] border border-[var(--color-goal-chip-border)] bg-[var(--color-goal-chip-bg)] px-1.5 py-0.5 text-[11px] font-medium text-[var(--color-text-secondary)]">
|
||||
{t('chat.activeGoal.budget', { value: goal.budget })}
|
||||
</span>
|
||||
)}
|
||||
{goal.continuations && (
|
||||
<span className="rounded-[var(--radius-sm)] border border-[var(--color-goal-chip-border)] bg-[var(--color-goal-chip-bg)] px-1.5 py-0.5 text-[11px] font-medium text-[var(--color-text-secondary)]">
|
||||
{t('chat.activeGoal.continuations', { value: goal.continuations })}
|
||||
</span>
|
||||
)}
|
||||
{goal.elapsed && (
|
||||
<span className="rounded-[var(--radius-sm)] border border-[var(--color-goal-chip-border)] bg-[var(--color-goal-chip-bg)] px-1.5 py-0.5 text-[11px] font-medium text-[var(--color-text-secondary)]">
|
||||
{t('chat.activeGoal.elapsed', { value: goal.elapsed })}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -370,6 +370,12 @@
|
||||
--color-memory-surface: #F6FAF8;
|
||||
--color-memory-border: #BCCDC8;
|
||||
--color-memory-icon-bg: #F3F8F7;
|
||||
--color-goal-accent: var(--color-memory-accent);
|
||||
--color-goal-surface: var(--color-memory-surface);
|
||||
--color-goal-border: var(--color-memory-border);
|
||||
--color-goal-icon-bg: var(--color-memory-icon-bg);
|
||||
--color-goal-chip-bg: var(--color-surface);
|
||||
--color-goal-chip-border: var(--color-border);
|
||||
--color-model-option-selected-bg: var(--color-primary-fixed);
|
||||
--color-model-option-selected-border: rgba(143, 72, 47, 0.2);
|
||||
--color-activity-heat-0: var(--color-surface-container);
|
||||
@ -571,6 +577,12 @@
|
||||
--color-memory-surface: #F3F8F7;
|
||||
--color-memory-border: #AECBC9;
|
||||
--color-memory-icon-bg: #EDF7F6;
|
||||
--color-goal-accent: var(--color-memory-accent);
|
||||
--color-goal-surface: var(--color-memory-surface);
|
||||
--color-goal-border: var(--color-memory-border);
|
||||
--color-goal-icon-bg: var(--color-memory-icon-bg);
|
||||
--color-goal-chip-bg: var(--color-surface);
|
||||
--color-goal-chip-border: var(--color-border);
|
||||
--color-model-option-selected-bg: #FFF0EA;
|
||||
--color-model-option-selected-border: rgba(143, 72, 47, 0.2);
|
||||
--color-activity-heat-0: #EEF2F6;
|
||||
@ -749,6 +761,12 @@
|
||||
--color-memory-surface: #1D2423;
|
||||
--color-memory-border: #334743;
|
||||
--color-memory-icon-bg: #22302E;
|
||||
--color-goal-accent: var(--color-memory-accent);
|
||||
--color-goal-surface: var(--color-memory-surface);
|
||||
--color-goal-border: var(--color-memory-border);
|
||||
--color-goal-icon-bg: var(--color-memory-icon-bg);
|
||||
--color-goal-chip-bg: var(--color-surface-container-low);
|
||||
--color-goal-chip-border: var(--color-border);
|
||||
--color-model-option-selected-bg: rgba(255, 181, 159, 0.13);
|
||||
--color-model-option-selected-border: rgba(255, 181, 159, 0.34);
|
||||
--color-activity-heat-0: #242322;
|
||||
|
||||
@ -49,6 +49,12 @@ describe('desktop theme tokens', () => {
|
||||
'--color-info',
|
||||
'--color-info-container',
|
||||
'--color-warning-container',
|
||||
'--color-goal-accent',
|
||||
'--color-goal-surface',
|
||||
'--color-goal-border',
|
||||
'--color-goal-icon-bg',
|
||||
'--color-goal-chip-bg',
|
||||
'--color-goal-chip-border',
|
||||
'--color-text-secondary-a72',
|
||||
'--color-text-secondary-a68',
|
||||
'--color-text-primary-a88',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user