feat(assistant): the facts of a change as badges

The faint line under a change's name showed its facts as loose icon and
text pairs. They are now the planner sidebar's SoftPill badges, icon and
value in a small round pill, a changed value struck and followed by the
new one inside the same badge. The action stays plain faint text in
front of them, and the line wraps instead of cutting a badge off in a
narrow chat window.
This commit is contained in:
Maurice
2026-10-21 22:24:49 +00:00
parent 8a8ce4013b
commit bfec725e37
2 changed files with 19 additions and 14 deletions
@@ -295,8 +295,8 @@ function PendingRow({
{preview ? (
<>
<bdi className="block truncate text-body font-semibold leading-tight text-content">{preview.subject}</bdi>
<span className="mt-0.5 flex min-w-0 items-center gap-2.5 text-caption text-content-faint">
<span className="flex-shrink-0">{verbLabel(preview, t)}</span>
<span className="mt-1 flex min-w-0 flex-wrap items-center gap-x-1.5 gap-y-1 text-caption text-content-faint">
<span className="me-0.5 min-w-0 truncate">{verbLabel(preview, t)}</span>
{preview.domain && (
<span className="flex-shrink-0 rounded-full bg-surface-secondary px-1.5 text-content-secondary">
{t(`assistant.domain.${preview.domain}`)}
@@ -14,12 +14,14 @@ import {
} from 'lucide-react';
import { useTranslation } from '../../i18n';
import { SoftPill } from '../Planner/planParts';
/**
* The few facts that tell a change apart at a glance, on the faint line under
* its name the way the planner's day list shows a place: when, what kind,
* what it costs, a new order. A value that changes reads old to new. The rest
* of what the change sets is not listed: the planner shows it once applied.
* The few facts that tell a change apart at a glance, as badges on the line
* under its name the way the planner's day list shows a place: when, what
* kind, what it costs, a new order. A value that changes reads old to new.
* The rest of what the change sets is not listed: the planner shows it once
* applied.
*/
/** The fields worth a place on the line, each with its glyph, in this order. */
@@ -53,23 +55,26 @@ export default function AssistantChangeFields({ rows }: { rows: AssistantChangeR
.slice(0, MAX_FACTS);
if (facts.length === 0) return null;
return (
<span className="flex min-w-0 items-center gap-2.5">
<>
{facts.map((row, i) => {
const Icon = KEY_FIELDS[row.labelKey] ?? Tag;
const changed = row.op !== 'add' && !!row.before;
return (
<span key={`${row.labelKey}-${i}`} className="inline-flex min-w-0 items-center gap-1">
<Icon size={11} aria-hidden="true" className="flex-shrink-0" />
<SoftPill
key={`${row.labelKey}-${i}`}
className="max-w-full tabular-nums"
icon={<Icon size={10} strokeWidth={2.2} aria-hidden="true" className="flex-none text-content-faint" />}
>
{changed && (
<>
<bdi className="truncate line-through">{shown(row, row.before, dayWord)}</bdi>
<ArrowRight size={10} aria-hidden="true" className="flex-shrink-0 rtl:-scale-x-100" />
<bdi className="truncate text-content-faint line-through">{shown(row, row.before, dayWord)}</bdi>
<ArrowRight size={9} aria-hidden="true" className="flex-none text-content-faint rtl:-scale-x-100" />
</>
)}
<bdi className="truncate text-content-muted">{shown(row, row.after, dayWord)}</bdi>
</span>
<bdi className="truncate">{shown(row, row.after, dayWord)}</bdi>
</SoftPill>
);
})}
</span>
</>
);
}