mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-13 14:02:16 +00:00
opentui(copy): mask chrome/gutters so free-form copy is clean
Audit selectable masking (free-code noSelect model) so a free-form drag over an
agent turn yields CLEAN pasteable content — no labels, summaries, carets, or
annotations. Newly masked (selectable={false}):
- toolPart: the whole collapsed header row (name + args-preview + duration +
"(N lines)") summary; the "args"/"output" section labels; the args overflow
"… +N more"; the "… omitted N" / "… +N more lines" truncation notes.
- messageLine: the streaming caret (▍) — a cursor glyph, not content.
- reasoningPart: the collapsible-section header label (Thinking/Thought + title).
- composer: the completion dropdown rows + the "Tab complete · Esc dismiss" hint.
Kept selectable (real content): assistant markdown, tool args values + output
body, user/system message text.
This commit is contained in:
parent
eb4821127c
commit
60c5a82c85
4 changed files with 28 additions and 10 deletions
|
|
@ -178,15 +178,20 @@ export function Composer(props: {
|
|||
paddingRight: 1
|
||||
}}
|
||||
>
|
||||
{/* the completion dropdown is transient input chrome (menu rows + the
|
||||
key-hint) — not transcript content — so it's excluded from mouse
|
||||
selection (item 4). */}
|
||||
<For each={completions().slice(0, 8)}>
|
||||
{(c, i) => (
|
||||
<text fg={i() === 0 ? theme().color.accent : theme().color.text}>
|
||||
<text selectable={false} fg={i() === 0 ? theme().color.accent : theme().color.text}>
|
||||
{c.display || c.text}
|
||||
{c.meta ? ` ${c.meta}` : ''}
|
||||
</text>
|
||||
)}
|
||||
</For>
|
||||
<text fg={theme().color.muted}>Tab complete · Esc dismiss</text>
|
||||
<text selectable={false} fg={theme().color.muted}>
|
||||
Tab complete · Esc dismiss
|
||||
</text>
|
||||
</box>
|
||||
</Show>
|
||||
{/* prompt glyph + textarea — the glyph (item 3) marks the input line so the
|
||||
|
|
|
|||
|
|
@ -61,7 +61,8 @@ export function MessageLine(props: { message: Message }) {
|
|||
</text>
|
||||
}
|
||||
>
|
||||
<text>
|
||||
<text selectable={false}>
|
||||
{/* streaming caret — a cursor glyph, not content (item 4) */}
|
||||
<span style={{ fg: theme().color.muted }}>▍</span>
|
||||
</text>
|
||||
</Show>
|
||||
|
|
|
|||
|
|
@ -50,7 +50,10 @@ export function ReasoningPart(props: { text: string; streaming?: boolean }) {
|
|||
<span style={{ fg: theme().color.accent }}>{expanded() ? '▼' : '▶'}</span>
|
||||
</text>
|
||||
</box>
|
||||
<text>
|
||||
{/* the header is a collapsible-section LABEL (Thinking/Thought + title)
|
||||
— chrome, not the reasoning body — so a free-form drag yields only
|
||||
the markdown body below, not the section label (item 4). */}
|
||||
<text selectable={false}>
|
||||
{/* accent chevron marks it; muted label keeps reasoning in the dim,
|
||||
secondary tier alongside tool calls (Ink hierarchy). */}
|
||||
<span style={{ fg: theme().color.muted }}>{label()}</span>
|
||||
|
|
|
|||
|
|
@ -103,7 +103,11 @@ export function ToolPart(props: { part: ToolPartState }) {
|
|||
</text>
|
||||
</box>
|
||||
<box style={{ flexDirection: 'row', flexGrow: 1, minWidth: 0 }}>
|
||||
<text>
|
||||
{/* the whole header row is a collapsed SUMMARY (tool name + args-preview
|
||||
+ duration + "(N lines)") — chrome, not the copyable body — so a
|
||||
free-form drag over a tool yields only the expanded output/args
|
||||
content, never the header label (item 4). */}
|
||||
<text selectable={false}>
|
||||
<span style={{ fg: theme().color.muted }}>{props.part.name}</span>
|
||||
<Show when={running()}>
|
||||
<span style={{ fg: theme().color.muted }}> …</span>
|
||||
|
|
@ -134,7 +138,8 @@ export function ToolPart(props: { part: ToolPartState }) {
|
|||
>
|
||||
<box style={{ flexDirection: 'column', flexGrow: 1, minWidth: 0 }}>
|
||||
<Show when={showArgs()}>
|
||||
<text>
|
||||
{/* section label — chrome, not content (item 4) */}
|
||||
<text selectable={false}>
|
||||
<span style={{ fg: theme().color.label }}>args</span>
|
||||
</text>
|
||||
{/* parsed key: value lines (tidy), or raw argsText when unparseable */}
|
||||
|
|
@ -158,14 +163,16 @@ export function ToolPart(props: { part: ToolPartState }) {
|
|||
)}
|
||||
</For>
|
||||
<Show when={argEntries().length > ARGS_MAX}>
|
||||
<text>
|
||||
{/* overflow annotation — chrome, not content (item 4) */}
|
||||
<text selectable={false}>
|
||||
<span style={{ fg: theme().color.accent }}>{`… +${argEntries().length - ARGS_MAX} more`}</span>
|
||||
</text>
|
||||
</Show>
|
||||
</Show>
|
||||
</Show>
|
||||
<Show when={showArgs() && hasOutput()}>
|
||||
<text>
|
||||
{/* section label — chrome, not content (item 4) */}
|
||||
<text selectable={false}>
|
||||
<span style={{ fg: theme().color.label }}>output</span>
|
||||
</text>
|
||||
</Show>
|
||||
|
|
@ -176,13 +183,15 @@ export function ToolPart(props: { part: ToolPartState }) {
|
|||
</text>
|
||||
)}
|
||||
</For>
|
||||
{/* truncation annotations — chrome (the "… omitted N" / "… +N more
|
||||
lines" notes are not part of the real output body) (item 4). */}
|
||||
<Show when={props.part.omittedNote}>
|
||||
<text>
|
||||
<text selectable={false}>
|
||||
<span style={{ fg: theme().color.muted }}>{`… omitted ${props.part.omittedNote}`}</span>
|
||||
</text>
|
||||
</Show>
|
||||
<Show when={body().hiddenLines > 0 && !props.part.omittedNote}>
|
||||
<text>
|
||||
<text selectable={false}>
|
||||
<span style={{ fg: theme().color.accent }}>{`… +${body().hiddenLines} more lines`}</span>
|
||||
</text>
|
||||
</Show>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue