ui: round 5 — AGENT'S THINKING buys the 3ch rail; the standard chord; dashed model names
- THE 3CH RAIL: 'AGENT’S THINKING' (16ch, flush) − 'YOUR THINKING' (13ch) = 3ch of mono — the rail widens to 3ch (border + padding), keeping the chevrons aligned while giving the quote room to breathe (the 1ch rail read cramped). YOU row follows. - THE STANDARD CHORD everywhere: Enter submits, Shift+Enter is a newline, ⌘/Ctrl+Enter works too — the teaching thinking field drops the ⌘-only Gmail convention; the hint reads '↵ submits · ⇧↵ newline' in both docks, shown in text mode too (it Enter-submits as well). - The transcript-end optimistic placeholder joins the register: small mono caps THINKING… (was text-sm sentence case). - Model names keep their dashes: prettyModel renders 'glm-5.3-flash' exactly as written (no auto-caps, no dash→space join).
This commit is contained in:
@@ -137,7 +137,7 @@ const defaultGetThinkingMessage = (isStreaming: boolean) => {
|
|||||||
if (isStreaming) {
|
if (isStreaming) {
|
||||||
return <Shimmer duration={1}>Thinking...</Shimmer>;
|
return <Shimmer duration={1}>Thinking...</Shimmer>;
|
||||||
}
|
}
|
||||||
return <p>AGENT THINKING</p>;
|
return <p>AGENT’S THINKING</p>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ReasoningTrigger = memo(
|
export const ReasoningTrigger = memo(
|
||||||
|
|||||||
+4
-4
@@ -1259,7 +1259,7 @@ export function Chat({
|
|||||||
User text renders plain — the model's LaTeX pipeline is not
|
User text renders plain — the model's LaTeX pipeline is not
|
||||||
applied to learner-authored text ($ stays $, no surprises).
|
applied to learner-authored text ($ stays $, no surprises).
|
||||||
Rendered WITHOUT the Message wrapper — that is the bubble. */
|
Rendered WITHOUT the Message wrapper — that is the bubble. */
|
||||||
<div data-testid="user-message" className="my-1 w-full border-l-2 border-muted-foreground/30 pl-[calc(1ch-2px)] font-ui text-xs">
|
<div data-testid="user-message" className="my-1 w-full border-l-2 border-muted-foreground/30 pl-[calc(3ch-2px)] font-ui text-xs">
|
||||||
{/* The rail GROUPS (owner round 4, 2026-10-05): the YOU heading
|
{/* The rail GROUPS (owner round 4, 2026-10-05): the YOU heading
|
||||||
sits INSIDE the rail, same mono-xs register and same 1ch
|
sits INSIDE the rail, same mono-xs register and same 1ch
|
||||||
rail as the thinking rows — one quote geometry. */}
|
rail as the thinking rows — one quote geometry. */}
|
||||||
@@ -1365,10 +1365,10 @@ export function Chat({
|
|||||||
entered by external discovery (wake) / the poll
|
entered by external discovery (wake) / the poll
|
||||||
fallback. Never collapsible itself. */}
|
fallback. Never collapsible itself. */}
|
||||||
{(status === "submitted" || turnInFlight) && (
|
{(status === "submitted" || turnInFlight) && (
|
||||||
<span className="flex items-center gap-2">
|
<span className="flex items-center gap-2 font-ui text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
||||||
<BrailleSpinner label="Thinking" />
|
<BrailleSpinner label="Thinking" />
|
||||||
<Shimmer duration={1} className="text-sm">
|
<Shimmer duration={1}>
|
||||||
Thinking…
|
THINKING…
|
||||||
</Shimmer>
|
</Shimmer>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -820,9 +820,10 @@ function QuestionDockForm({
|
|||||||
onEscape();
|
onEscape();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// ⌘↵ on macOS, Ctrl↵ elsewhere — the Gmail convention; plain
|
// THE STANDARD CHORD (owner round 5, 2026-10-05): Enter
|
||||||
// Enter stays a newline (this field is for dumping).
|
// submits, Shift+Enter is a newline, ⌘/Ctrl+Enter works
|
||||||
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
// too — identical to the practice dock and the composer.
|
||||||
|
if (e.key === "Enter" && (!e.shiftKey || e.metaKey || e.ctrlKey)) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
submit();
|
submit();
|
||||||
}
|
}
|
||||||
@@ -838,11 +839,9 @@ function QuestionDockForm({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div className="flex items-center justify-between gap-2 p-1">
|
<div className="flex items-center justify-between gap-2 p-1">
|
||||||
{qd.mode !== "text" && (
|
|
||||||
<span aria-hidden className="pl-1.5 text-xs text-muted-foreground">
|
<span aria-hidden className="pl-1.5 text-xs text-muted-foreground">
|
||||||
⌘/Ctrl + ↵ submits
|
↵ submits · ⇧↵ newline
|
||||||
</span>
|
</span>
|
||||||
)}
|
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={!canSubmit}
|
disabled={!canSubmit}
|
||||||
|
|||||||
@@ -33,16 +33,18 @@ export function ThinkingBlock({
|
|||||||
return (
|
return (
|
||||||
/* The rail GROUPS and the rail IS THE MISSING CHARACTER (owner
|
/* The rail GROUPS and the rail IS THE MISSING CHARACTER (owner
|
||||||
rounds, 2026-10-05): the heading sits INSIDE the rail, and the
|
rounds, 2026-10-05): the heading sits INSIDE the rail, and the
|
||||||
rail's full width (border + padding) is exactly ONE CHARACTER of
|
rail's full width (border + padding) is exactly THREE CHARACTERS of
|
||||||
the mono heading font — 'YOUR THINKING' is one char shorter than
|
the mono heading font — 'YOUR THINKING' is three chars shorter
|
||||||
'AGENT THINKING', so with the user row offset by the rail and the
|
than 'AGENT'S THINKING', so with the user row offset by the rail
|
||||||
agent row flush, the chevrons line up vertically. The heading
|
and the agent row flush, the chevrons line up vertically. Three
|
||||||
|
(not one, owner round 5): the wider gap reads better than the
|
||||||
|
cramped single-char rail. The heading
|
||||||
register is the card eyebrows' small mono caps (text-xs = the
|
register is the card eyebrows' small mono caps (text-xs = the
|
||||||
11px rung). */
|
11px rung). */
|
||||||
<div
|
<div
|
||||||
data-testid={testId}
|
data-testid={testId}
|
||||||
data-commit={dataCommit === undefined ? undefined : dataCommit ? "true" : "false"}
|
data-commit={dataCommit === undefined ? undefined : dataCommit ? "true" : "false"}
|
||||||
className="lh-card-enter my-1 w-full border-l-2 border-muted-foreground/30 pl-[calc(1ch-2px)] font-ui text-xs"
|
className="lh-card-enter my-1 w-full border-l-2 border-muted-foreground/30 pl-[calc(3ch-2px)] font-ui text-xs"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ describe("render smoke tests", () => {
|
|||||||
assert.match(html, /Why this answer\? What are you unsure about\?/, "placeholder invites, not apologizes");
|
assert.match(html, /Why this answer\? What are you unsure about\?/, "placeholder invites, not apologizes");
|
||||||
assert.ok(!html.includes("optional"), "the field is never called optional");
|
assert.ok(!html.includes("optional"), "the field is never called optional");
|
||||||
assert.match(html, /Submit answer/, "send becomes Submit answer");
|
assert.match(html, /Submit answer/, "send becomes Submit answer");
|
||||||
assert.match(html, /⌘\/Ctrl \+ ↵ submits/, "the ⌘/Ctrl+Enter shortcut is discoverable");
|
assert.match(html, /↵ submits · ⇧↵ newline/, "the standard chord is discoverable (owner round 5)");
|
||||||
// The v1 desync bug: options are buttons with ARIA roles, not native inputs.
|
// The v1 desync bug: options are buttons with ARIA roles, not native inputs.
|
||||||
assert.ok(!html.includes('type="radio"'), "no native radio inputs");
|
assert.ok(!html.includes('type="radio"'), "no native radio inputs");
|
||||||
assert.ok(html.includes('role="radio"'), "button-based radio role present");
|
assert.ok(html.includes('role="radio"'), "button-based radio role present");
|
||||||
|
|||||||
Reference in New Issue
Block a user