feat(paliadin/agent-glyph): t-paliad-161 Slice E — ✨ alongside 👀
When a pending row was drafted by Paliadin (requester_kind='agent' on its in-flight approval_request), surface a sparkle ✨ next to the existing eye-pill 👀. The two glyphs are orthogonal: 👀 = "needs approval", ✨ = "Paliadin drafted this". Either can change without the other, so the visual taxonomy stays decomposable for any future autopilot mode where 👀 disappears but ✨ stays. Read-path: - DeadlineService.ListVisibleForUser + AppointmentService.ListVisibleForUser LEFT JOIN paliad.approval_requests on pending_request_id and project ar.requester_kind into the row. NULL when no request is pending. - models.DeadlineWithProject + AppointmentWithProject grow RequesterKind *string. The list-projection helpers (projectDeadline / projectAppointment in event_service.go) carry it into EventListItem. - /api/events response now includes requester_kind on every pending row; /api/inbox already does (Slice D extended approvalRequestViewColumns). Render-path: - frontend/src/client/events.ts — new AGENT_PILL_GLYPH constant ("✨"), agentPill rendered into the title cell next to the existing pendingPill when item.approval_status='pending' AND item.requester_kind='agent'. EventListItem TS shape gains `requester_kind?: "user" | "agent"`. - frontend/src/client/agenda.ts — same pattern, agendaItem TS shape + agentPill rendered next to pendingPill in the headline span. - frontend/src/client/inbox.ts — ApprovalRequestView gains requester_kind + agent_turn_id; the meta line replaces the requester's plain name with "Anna ✨ Paliadin" when the request was drafted by the agent. CSS: new .approval-pill--agent modifier in global.css using only existing tokens (--color-bg-lime-tint / --color-surface-2 / --color-text), mirroring the .approval-pill--icon shape so the two glyphs sit side-by-side at the same baseline. i18n: 3 new keys × 2 langs (approvals.agent.label / approvals.agent.byline / approvals.agent.suggestion_pending) — total 1966 → 1969. Build clean (frontend + go), tests green. Refs: docs/design-paliadin-inline-2026-05-08.md §8.
This commit is contained in:
@@ -14,6 +14,13 @@ import { projectIndent } from "./project-indent";
|
||||
// pill markup trivially short and inherits the user's emoji font.
|
||||
const APPROVAL_PILL_GLYPH = "👀";
|
||||
|
||||
// Sparkle glyph ✨ inside .approval-pill--agent (t-paliad-161). Renders
|
||||
// next to (not in place of) 👀 when the pending row originated from a
|
||||
// Paliadin chat suggestion. The two glyphs are orthogonal: 👀 = "needs
|
||||
// approval", ✨ = "Paliadin drafted this". Both can coexist; either can
|
||||
// appear alone in future autopilot states.
|
||||
const AGENT_PILL_GLYPH = "✨";
|
||||
|
||||
// EventsPage shared client (t-paliad-110). Drives /deadlines and
|
||||
// /appointments off the same shell — the route handler injects
|
||||
// `window.__PALIAD_EVENTS__ = { defaultType: "deadline" | "appointment" }`
|
||||
@@ -45,6 +52,9 @@ interface EventListItem {
|
||||
|
||||
// Approval workflow (t-paliad-138). "pending" → render the warning pill.
|
||||
approval_status?: "approved" | "pending" | "legacy";
|
||||
// t-paliad-161: when approval_status='pending', tells us whether the row
|
||||
// was drafted by a user or by Paliadin (✨ glyph). NULL when not pending.
|
||||
requester_kind?: "user" | "agent";
|
||||
|
||||
// deadline-only
|
||||
due_date?: string;
|
||||
@@ -515,17 +525,25 @@ function renderRow(item: EventListItem, showReopen: boolean): string {
|
||||
// Approval pending pill (t-paliad-138 / m's 2026-05-08 cosmetic ask).
|
||||
// Soft-tint the row + drop an eye-icon pill next to the title; hover
|
||||
// reveals the lifecycle label. Inbox surface shows the full detail.
|
||||
//
|
||||
// t-paliad-161 ✨: when the pending row came from a Paliadin
|
||||
// suggestion (requester_kind='agent'), drop a second pill next to 👀.
|
||||
// Two glyphs read together as "needs approval, drafted by Paliadin".
|
||||
const pendingClass = item.approval_status === "pending" ? " entity-row--pending-update" : "";
|
||||
const pendingLabel = item.approval_status === "pending" ? t("approvals.pending_update.label") : "";
|
||||
const pendingPill = item.approval_status === "pending"
|
||||
? `<span class="approval-pill approval-pill--icon" title="${esc(pendingLabel)}" aria-label="${esc(pendingLabel)}">${APPROVAL_PILL_GLYPH}</span>`
|
||||
: "";
|
||||
const agentLabel = t("approvals.agent.label");
|
||||
const agentPill = item.approval_status === "pending" && item.requester_kind === "agent"
|
||||
? `<span class="approval-pill approval-pill--agent" title="${esc(agentLabel)}" aria-label="${esc(agentLabel)}">${AGENT_PILL_GLYPH}</span>`
|
||||
: "";
|
||||
|
||||
return `<tr class="frist-row events-row events-row-${item.type}${pendingClass}" data-id="${esc(item.id)}" data-type="${item.type}">
|
||||
<td class="frist-col-check">${checkCell}</td>
|
||||
<td class="events-col-row-type">${rowTypeChip(item)}</td>
|
||||
<td class="frist-col-due ${urgency}"><span class="frist-due-dot"></span>${esc(dateLabel)}</td>
|
||||
<td class="frist-col-title ${titleClass}">${esc(item.title)}${pendingPill ? " " + pendingPill : ""}</td>
|
||||
<td class="frist-col-title ${titleClass}">${esc(item.title)}${pendingPill ? " " + pendingPill : ""}${agentPill ? " " + agentPill : ""}</td>
|
||||
<td class="frist-col-project">${projectCell}</td>
|
||||
<td class="frist-col-rule events-col-rule">${ruleLabel || "—"}</td>
|
||||
<td class="entity-col-event-type">${eventTypeCell || "—"}</td>
|
||||
|
||||
Reference in New Issue
Block a user