hyperhive#4008, mara: 'add a setting to not display verbose output... like the grey colored debug stuff.' Same shape as the existing expand-tool-output preference (ExpandDetailsSetting.tsx): a new shared/src/prefs.ts key pair (getHideDebugPref/setHideDebugPref), a new settings-menu-row component owning its own state (HideDebugSetting.tsx, not a prop threaded through the shared SettingsMenu component — per mara's earlier review on the first one, more per-page options as props there is how that component accumulates cruft), mounted next to ExpandDetailsSetting in Root.tsx. Row.tsx skips (returns null for, not CSS display:none) any TermMsg whose level is 'debug' when the pref is set — matches the muted 'debug' row this issue is about (see docs/web-ui/terminal-rendering.md's Levels table). Read live per-row, same as expand-details, so toggling applies to newly streamed rows in an already-open tab without a reload; already -rendered rows are unaffected either way, same non-retroactive precedent the existing preference already sets.
101 lines
3.4 KiB
TypeScript
101 lines
3.4 KiB
TypeScript
// Renders one `TermRow` — flat `<div class="row …">` or expandable
|
|
// `<details class="row …">`, driven straight off the wire shape
|
|
// (`lib/termMsg.ts`'s `TermMsg`, mirroring hive-agent's `term_msg.rs`):
|
|
// `level` picks the colour class, an empty `summary` + markdown `body`
|
|
// is a flat row with just the body (assistant text), anything else with
|
|
// a body is an expandable details row gated by the operator's
|
|
// expand-tool-output preference. No separate classification step —
|
|
// mara: "StreamRow should now match what the server sends in TermMsg."
|
|
import { useEffect, useRef } from "preact/hooks";
|
|
import type { TermRow } from "../lib/termMsg.js";
|
|
import { linkifyToNodes } from "../lib/linkify.js";
|
|
import { renderMarkdown } from "../lib/markdown.js";
|
|
import { getExpandDetailsPref, getHideDebugPref } from "@hive/shared/prefs.js";
|
|
|
|
function MarkdownBody({ text }: { text: string }) {
|
|
const ref = useRef<HTMLDivElement>(null);
|
|
const html = renderMarkdown(text);
|
|
useEffect(() => {
|
|
// marked autolinks URLs but leaves them same-tab — open externally
|
|
// so a click never navigates the terminal away.
|
|
ref.current?.querySelectorAll("a[href]").forEach((a) => {
|
|
a.setAttribute("target", "_blank");
|
|
a.setAttribute("rel", "noopener noreferrer");
|
|
});
|
|
}, [html]);
|
|
// eslint-disable-next-line react/no-danger -- sanitized by DOMPurify in renderMarkdown
|
|
return (
|
|
<div className="md" ref={ref} dangerouslySetInnerHTML={{ __html: html }} />
|
|
);
|
|
}
|
|
|
|
function DiffBody({ text }: { text: string }) {
|
|
const lines = String(text).split("\n");
|
|
return (
|
|
<pre className="tool-body diff-body">
|
|
{lines.map((line, i) => {
|
|
const cls = line.startsWith("+ ")
|
|
? "diff-add"
|
|
: line.startsWith("- ")
|
|
? "diff-del"
|
|
: "diff-ctx";
|
|
return (
|
|
<span key={i} className={cls}>
|
|
{line}
|
|
{"\n"}
|
|
</span>
|
|
);
|
|
})}
|
|
</pre>
|
|
);
|
|
}
|
|
|
|
export function Row({ row }: { row: TermRow }) {
|
|
// "hide debug output" is a skip, not a dimmer — a hidden debug row
|
|
// still occupies no DOM node at all, same as it never streamed, rather
|
|
// than a CSS `display: none` that would keep it in the layout/DOM for
|
|
// no benefit.
|
|
if (row.level === "debug" && getHideDebugPref()) return null;
|
|
|
|
const cssClass = "level-" + row.level;
|
|
const icon = row.icon != null && row.icon !== "" && (
|
|
<span className="row-glyph">{row.icon}</span>
|
|
);
|
|
|
|
if (row.body == null) {
|
|
return (
|
|
<div className={`row ${cssClass}`}>
|
|
{icon}
|
|
{linkifyToNodes(row.summary)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Empty summary + markdown body → the body itself is the whole row
|
|
// (assistant text), no summary prefix line, never collapsible.
|
|
if (row.body_format === "markdown" && row.summary === "") {
|
|
return (
|
|
<div className={`row ${cssClass}`}>
|
|
{icon}
|
|
<MarkdownBody text={row.body} />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<details
|
|
className={`row ${cssClass}`}
|
|
open={getExpandDetailsPref() || undefined}
|
|
>
|
|
<summary>
|
|
{icon}
|
|
<span className="summary-text">{row.summary}</span>
|
|
</summary>
|
|
{row.body_format === "diff" && <DiffBody text={row.body} />}
|
|
{row.body_format === "markdown" && <MarkdownBody text={row.body} />}
|
|
{row.body_format == null && (
|
|
<pre className="tool-body">{linkifyToNodes(row.body)}</pre>
|
|
)}
|
|
</details>
|
|
);
|
|
}
|