mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-13 09:01:54 +00:00
Tighten conversation rhythm, flatten the tool list, and smooth streaming text
Conversation rhythm: - Single `--paragraph-gap` knob drives paragraph spacing both inside a markdown block and between consecutive prose parts, out-specifying Tailwind Typography's prose margins. Code cards carry the same gap themselves so it holds at any Streamdown nesting depth. - Two-tier vertical rhythm: `--turn-block-gap` separates scaffolding (tools / thinking) from the reply; `--tool-row-gap` keeps a tool run tight. - Drop the prose indent so prose, tools, todos, and thinking share one left edge. `---` renders as quiet spacing, not a heavy rule. Flat tool list: - Tools always render as a standalone-row stack, never a "Tool actions · N steps" group. assistant-ui slices the tool range unstably (interleaved live vs. reconstructed-consecutive when settled), so grouping reshuffled the whole turn the instant it settled. Flat rows are pixel-identical either way. - Inline approvals can no longer be buried in a collapsed group body. - Remove the now-dead grouping helpers from tool-fallback-model. Empty thinking: - Suppress reasoning disclosures with no visible text (encrypted / spinner- coerced reasoning) instead of leaving an empty "Thinking" header. - Tail stall indicator returns "thinking" when a running turn goes quiet. Streaming cadence: - Smooth character-reveal decouples visible cadence from bursty arrival. - Flush queued text deltas before applying tool events so a tool row can't jump ahead of its preceding text. - Disable Nagle on the GUI WebSocket so per-token frames aren't coalesced. Polish: clarify/patch/vision_analyze tool meta, queue-panel + diff-lines spacing, sticky human bubble expands on focus (not hover).
This commit is contained in:
parent
6bbc5eefa0
commit
9d31577590
12 changed files with 287 additions and 355 deletions
|
|
@ -278,11 +278,21 @@
|
|||
--composer-shell-pad-block-end: 0.625rem;
|
||||
--message-text-indent: 0.75rem;
|
||||
--conversation-text-font-size: 0.8125rem;
|
||||
--conversation-tool-font-size: var(--conversation-text-font-size);
|
||||
--conversation-tool-font-size: 0.6875rem;
|
||||
--conversation-caption-font-size: 0.75rem;
|
||||
--conversation-line-height: 1.125rem;
|
||||
--conversation-caption-line-height: 1rem;
|
||||
--conversation-turn-gap: 0.375rem;
|
||||
/* Gap between top-level turn blocks (prose ↔ tools ↔ thinking) — enough air
|
||||
that scaffolding reads as separate from the reply, not crammed into it. */
|
||||
--turn-block-gap: 0.75rem;
|
||||
/* Tight gap between tool rows inside a single action group, so a back-to-back
|
||||
run still reads as one cohesive sequence. */
|
||||
--tool-row-gap: 0.375rem;
|
||||
/* Paragraph spacing — vertical gap between prose paragraphs, both inside a
|
||||
markdown block and between consecutive prose parts. Single knob; tweak
|
||||
freely. */
|
||||
--paragraph-gap: 0.45rem;
|
||||
--sticky-human-top: 0.23rem;
|
||||
--file-tree-row-height: 1.375rem;
|
||||
|
||||
|
|
@ -798,14 +808,27 @@ canvas {
|
|||
font-size: inherit;
|
||||
}
|
||||
|
||||
/* Streamed prose hangs slightly indented from the tool/todo column so the
|
||||
reading column reads as a "reply" within the conversation gutter. Tools,
|
||||
todos, and thinking blocks keep the existing --message-text-indent so they
|
||||
remain flush with the user message text above them. */
|
||||
[data-slot='aui_assistant-message-content'] > .aui-md {
|
||||
padding-inline-start: var(--md-text-indent, 0.5rem);
|
||||
/* Tailwind Typography sets `.prose :where(p) { margin: 1.25em }` (~16px). That
|
||||
selector ties our `my-*` utility on specificity and wins on source order, so
|
||||
paragraph spacing must be reclaimed here at higher specificity. One tight
|
||||
top-margin (bottom zeroed to avoid doubling), first child reset to flush. */
|
||||
[data-slot='aui_assistant-message-content'] .aui-md :where(p) {
|
||||
margin-block: var(--paragraph-gap) 0;
|
||||
}
|
||||
|
||||
/* First rendered element of a prose block is flush — the block-level gap above
|
||||
(tool / paragraph) already provides the separation. Reach one level deep too:
|
||||
Streamdown wraps blocks in a `div.space-y-*`, so the real first line is the
|
||||
first child's first child. */
|
||||
[data-slot='aui_assistant-message-content'] .aui-md > :first-child,
|
||||
[data-slot='aui_assistant-message-content'] .aui-md > :first-child > :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Prose, tools, todos, and thinking all share one left edge (the message
|
||||
content's --message-text-indent). No extra prose indent — a single gutter
|
||||
reads cleaner than a ragged tool-vs-reply column. */
|
||||
|
||||
[data-slot='aui_user-message-root'] {
|
||||
top: var(--sticky-human-top);
|
||||
}
|
||||
|
|
@ -816,12 +839,13 @@ canvas {
|
|||
}
|
||||
|
||||
/* Sticky human bubbles clamp to ~2 lines with a soft bottom fade so a long
|
||||
prompt doesn't dominate the viewport while you read the response stuck
|
||||
beneath it. The clamp lifts on hover / focus (clicking the bubble opens the
|
||||
edit composer, which already shows the full text). --human-msg-full is the
|
||||
measured content height (set in UserMessage) so expand/collapse animates to
|
||||
the real height instead of overshooting the cap. */
|
||||
prompt doesn't dominate the viewport. The clamp lifts on focus only (clicking
|
||||
opens the edit composer, which shows the full text) — not on hover, so the
|
||||
bubble doesn't jump as the pointer passes over it. --human-msg-full is the
|
||||
measured content height (set in UserMessage) so it animates to the real
|
||||
height instead of overshooting the cap. */
|
||||
.sticky-human-clamp {
|
||||
cursor: pointer;
|
||||
max-height: calc(2 * var(--dt-line-height) * var(--conversation-text-font-size) + 0.15rem);
|
||||
overflow: hidden;
|
||||
transition: max-height 0.08s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
|
@ -832,7 +856,6 @@ canvas {
|
|||
mask-image: linear-gradient(to bottom, #000 55%, transparent);
|
||||
}
|
||||
|
||||
.composer-human-message:hover .sticky-human-clamp,
|
||||
.composer-human-message:focus-within .sticky-human-clamp {
|
||||
max-height: min(var(--human-msg-full, 24rem), 24rem);
|
||||
overflow-y: auto;
|
||||
|
|
@ -992,7 +1015,7 @@ canvas {
|
|||
[data-slot='aui_assistant-message-content'] .aui-md [data-streamdown='code-block'] {
|
||||
contain: none;
|
||||
overflow: visible;
|
||||
margin-block: 0.375rem !important;
|
||||
margin-block: var(--paragraph-gap) 0 !important;
|
||||
padding: 0 !important;
|
||||
gap: 0 !important;
|
||||
border: 0 !important;
|
||||
|
|
@ -1006,6 +1029,11 @@ canvas {
|
|||
}
|
||||
|
||||
[data-slot='aui_assistant-message-content'] .aui-md [data-slot='code-card'] {
|
||||
/* Streamdown nests blocks, so the container's child-combinator rhythm can't
|
||||
reach the card. Carry the paragraph gap on the card itself (top-owned);
|
||||
collapses cleanly with the wrapper's margin when one is present, and the
|
||||
first-child reset still flushes a leading code block. */
|
||||
margin-block: var(--paragraph-gap) 0;
|
||||
position: relative;
|
||||
transition:
|
||||
border-color 180ms ease-out,
|
||||
|
|
@ -1075,34 +1103,25 @@ canvas {
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Conversation block rhythm. Consecutive tool calls stay tight so a step
|
||||
sequence reads as one action group; the gap between any scaffolding
|
||||
block and adjacent prose bumps up so the model's reply visually
|
||||
separates from its scaffolding. */
|
||||
[data-slot='tool-block'] + [data-slot='tool-block'] {
|
||||
margin-top: 0.375rem;
|
||||
}
|
||||
|
||||
[data-slot='tool-block']:has(> :nth-child(2)) + [data-slot='tool-block'] {
|
||||
margin-top: 0.625rem;
|
||||
}
|
||||
|
||||
/* Conversation block rhythm. assistant-ui renders each range as a direct child
|
||||
of the message content with no per-part wrapper, so adjacency rules cover
|
||||
every pairing — first block needs no reset, nested tool rows are untouched.
|
||||
Two tiers: scaffolding (tool / thinking) gets a roomy block gap so it reads
|
||||
as separate from the reply; consecutive prose collapses to a tight paragraph
|
||||
rhythm so split-out text parts don't look like a big gap. */
|
||||
/* Scaffolding adjacent to anything → roomy block gap. */
|
||||
[data-slot='aui_assistant-message-content']
|
||||
:is([data-slot='tool-block'], [data-slot='aui_thinking-disclosure'])
|
||||
+ .aui-md,
|
||||
> :is([data-slot='tool-block'], [data-slot='aui_thinking-disclosure'])
|
||||
+ :is([data-slot='tool-block'], [data-slot='aui_thinking-disclosure'], .aui-md),
|
||||
[data-slot='aui_assistant-message-content']
|
||||
.aui-md
|
||||
> .aui-md
|
||||
+ :is([data-slot='tool-block'], [data-slot='aui_thinking-disclosure']) {
|
||||
margin-top: 1rem;
|
||||
margin-top: var(--turn-block-gap);
|
||||
}
|
||||
|
||||
[data-slot='aui_assistant-message-content'] [data-slot='aui_thinking-disclosure'] + [data-slot='tool-block'],
|
||||
[data-slot='aui_assistant-message-content'] [data-slot='tool-block'] + [data-slot='aui_thinking-disclosure'] {
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
[data-slot='aui_assistant-message-content'] > [data-slot='tool-block']:first-child {
|
||||
margin-top: 0;
|
||||
/* Prose ↔ prose → tight paragraph rhythm, matching in-block paragraph spacing. */
|
||||
[data-slot='aui_assistant-message-content'] > .aui-md + .aui-md {
|
||||
margin-top: var(--paragraph-gap);
|
||||
}
|
||||
|
||||
/* Message action bars — flat icon hits with default dim; only the hovered/focused control is full-strength. */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue