Suggestions: right-margin comment rail + Mandarin explanations

Surface every outstanding suggestion as a card in the right-hand
whitespace, vertically aligned to the text it flags — so the writer sees
the whole queue at once instead of hovering each highlight. Cards stack
with collision avoidance, link both ways with their highlight (hover/click
↔ soft text wash, driven through the decoration plugin so it survives
edit repaints), and carry the same Accept / Dismiss / Ask Petal actions.
The rail is a progressive enhancement: it mounts only when there's room
beside the editor, otherwise the existing inline hover card is unchanged.
Stacked cards that reach the bottom-right corner tuck behind the
companion mascot (z-order).

When a card is expanded, the Ask Petal bubble now opens with the
Simplified-Chinese translation of the explanation (the English stays in
the card body) instead of repeating the same text twice — a new
POST /api/suggestions/{id}/translate one-shot LLM endpoint, loaded
lazily on open with an English fallback.

Verified live against the local LLM via the uitest harness: rail
stacking, hover↔text wash, expand/Ask Petal, accept-from-rail, narrow
fallback, and the Mandarin bubble.

Claude-Session: https://claude.ai/code/session_016Yr6jELuRc7hyzYLccQKZd
This commit is contained in:
prosolis
2026-06-26 12:23:02 -07:00
parent 035dcf5d25
commit 82e2bcc777
12 changed files with 629 additions and 41 deletions

View File

@@ -223,6 +223,67 @@ button, a, input {
animation: petal-suggestion-in 200ms ease both;
}
/* Text emphasized from its margin card (or a hover) — a soft wash so the
card↔text link reads at a glance, both directions. */
.petal-suggestion-active {
background: var(--color-surface-alt);
box-shadow: 0 0 0 3px var(--color-surface-alt);
border-radius: 3px;
}
/* --- Suggestion margin rail -------------------------------------------------
The right-hand "comment column": every outstanding suggestion as a card,
vertically aligned to the text it flags, so the whole queue is visible at a
glance instead of one-highlight-at-a-time on hover. It floats in the whitespace
just right of the editor column and is only mounted when there's room for it
(EditorCore measures the gap). Cards are absolutely positioned by a resolved
top (anchor + collision avoidance), so the container only anchors the column. */
.petal-rail {
position: absolute;
top: 0;
left: 100%;
margin-left: 32px;
width: 300px;
/* Below the companion mascot (z-40): where a stacked card reaches the
bottom-right corner it simply tucks behind the sleeping cat. */
z-index: 10;
}
.petal-rail-card {
position: absolute;
left: 0;
right: 0;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-left: 3px solid var(--color-border); /* type color set inline */
border-radius: var(--radius-card);
box-shadow: var(--shadow-soft);
padding: 0.7rem 0.85rem;
/* `top` eases so re-stacking (accept/dismiss/edit) glides instead of jumping.
The entrance uses fill `backwards` so its translateY doesn't linger and fight
the active-state transform once it's done. */
transition: top 240ms cubic-bezier(0.2, 0.7, 0.3, 1), box-shadow 200ms ease,
transform 200ms ease, border-color 200ms ease;
animation: petal-suggestion-in 220ms ease backwards;
}
.petal-rail-card-active {
transform: translateX(-5px);
box-shadow: 0 8px 26px rgba(180, 130, 160, 0.24);
border-top-color: var(--color-accent);
border-right-color: var(--color-accent);
border-bottom-color: var(--color-accent);
}
.petal-rail-x {
color: var(--color-muted);
font-size: 0.72rem;
line-height: 1;
padding: 2px 5px;
border-radius: var(--radius-pill);
}
.petal-rail-x:hover {
background: var(--color-surface-alt);
color: var(--color-plum);
}
/* --- Find & Replace ---------------------------------------------------------
In-document search (Ctrl/Cmd+F). Every match gets a soft honey wash; the
current match is brighter with a rose ring so it stands out as you step