mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-01 01:51:44 +00:00
refactor(tui): /clean pass on per-section visibility plumbing
- domain/details: extract `norm()`, fold parseDetailsMode + resolveSections into terser functional form, reject array values for resolveSections - slash /details: destructure tokens, factor reset/mode into one dispatch, drop DETAIL_MODES set + DetailsMode/SectionName imports (parseDetailsMode + isSectionName narrow + return), centralize usage strings - ToolTrail: collapse 4 separate xxxSection vars into one memoized `visible` map; effect deps stabilize on the memo identity instead of 4 primitives
This commit is contained in:
parent
728767e910
commit
005cc29e98
3 changed files with 88 additions and 124 deletions
|
|
@ -1,7 +1,15 @@
|
|||
import type { DetailsMode, SectionName, SectionVisibility } from '../types.js'
|
||||
|
||||
const MODES = ['hidden', 'collapsed', 'expanded'] as const
|
||||
export const SECTION_NAMES: readonly SectionName[] = ['thinking', 'tools', 'subagents', 'activity']
|
||||
|
||||
export const SECTION_NAMES = ['thinking', 'tools', 'subagents', 'activity'] as const
|
||||
|
||||
// Activity panel = ambient meta (gateway hints, terminal-parity nudges,
|
||||
// background-process notifications). Hidden out of the box because tool
|
||||
// failures already render inline on the failing tool row — the panel itself
|
||||
// is noise for typical use. Opt back in via `display.sections.activity` or
|
||||
// `/details activity collapsed`.
|
||||
const SECTION_DEFAULTS: SectionVisibility = { activity: 'hidden' }
|
||||
|
||||
const THINKING_FALLBACK: Record<string, DetailsMode> = {
|
||||
collapsed: 'collapsed',
|
||||
|
|
@ -9,66 +17,36 @@ const THINKING_FALLBACK: Record<string, DetailsMode> = {
|
|||
truncated: 'collapsed'
|
||||
}
|
||||
|
||||
export const parseDetailsMode = (v: unknown): DetailsMode | null => {
|
||||
const s = typeof v === 'string' ? v.trim().toLowerCase() : ''
|
||||
const norm = (v: unknown) => String(v ?? '').trim().toLowerCase()
|
||||
|
||||
return MODES.find(m => m === s) ?? null
|
||||
}
|
||||
export const parseDetailsMode = (v: unknown): DetailsMode | null =>
|
||||
MODES.find(m => m === norm(v)) ?? null
|
||||
|
||||
export const isSectionName = (v: unknown): v is SectionName =>
|
||||
typeof v === 'string' && (SECTION_NAMES as readonly string[]).includes(v)
|
||||
|
||||
export const resolveDetailsMode = (d?: { details_mode?: unknown; thinking_mode?: unknown } | null): DetailsMode =>
|
||||
parseDetailsMode(d?.details_mode) ??
|
||||
THINKING_FALLBACK[
|
||||
String(d?.thinking_mode ?? '')
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
] ??
|
||||
'collapsed'
|
||||
parseDetailsMode(d?.details_mode) ?? THINKING_FALLBACK[norm(d?.thinking_mode)] ?? 'collapsed'
|
||||
|
||||
// Build a SectionVisibility from a free-form `display.sections` config blob.
|
||||
// Skips keys that aren't recognized section names or don't parse to a valid
|
||||
// mode — partial overrides are intentional, missing keys fall through to the
|
||||
// global details_mode at render time.
|
||||
export const resolveSections = (raw: unknown): SectionVisibility => {
|
||||
const out: SectionVisibility = {}
|
||||
// Build SectionVisibility from a free-form blob. Unknown section names and
|
||||
// invalid modes are dropped silently — partial overrides are intentional, so
|
||||
// missing keys fall through to SECTION_DEFAULTS / global at lookup time.
|
||||
export const resolveSections = (raw: unknown): SectionVisibility =>
|
||||
raw && typeof raw === 'object' && !Array.isArray(raw)
|
||||
? (Object.fromEntries(
|
||||
Object.entries(raw as Record<string, unknown>)
|
||||
.map(([k, v]) => [k, parseDetailsMode(v)] as const)
|
||||
.filter(([k, m]) => !!m && isSectionName(k))
|
||||
) as SectionVisibility)
|
||||
: {}
|
||||
|
||||
if (!raw || typeof raw !== 'object') {
|
||||
return out
|
||||
}
|
||||
|
||||
for (const [k, v] of Object.entries(raw as Record<string, unknown>)) {
|
||||
const mode = parseDetailsMode(v)
|
||||
|
||||
if (mode && isSectionName(k)) {
|
||||
out[k] = mode
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// Built-in per-section defaults applied when the user has no explicit
|
||||
// override. The activity panel (gateway hints, terminal-parity nudges,
|
||||
// background-process notifications) is hidden out of the box — it's noise
|
||||
// for the typical day-to-day user, who only cares about thinking + tools +
|
||||
// streamed content. Tool failures still surface inline on the failing tool
|
||||
// row; this default only suppresses the ambient meta feed.
|
||||
//
|
||||
// Opt back in with `display.sections.activity: collapsed` (under chevron)
|
||||
// or `expanded` (always open) in `~/.hermes/config.yaml`, or live with
|
||||
// `/details activity collapsed`.
|
||||
const SECTION_DEFAULTS: SectionVisibility = { activity: 'hidden' }
|
||||
|
||||
// Resolve the effective mode for one section: explicit override wins,
|
||||
// then the SECTION_DEFAULTS fallback, then the global details_mode.
|
||||
// Single source of truth — every render site that needs to know "is this
|
||||
// section open by default" calls this.
|
||||
// Effective mode for one section: explicit override → SECTION_DEFAULTS → global.
|
||||
// Single source of truth for "is this section open by default / rendered at all".
|
||||
export const sectionMode = (
|
||||
name: SectionName,
|
||||
global: DetailsMode,
|
||||
sections?: SectionVisibility
|
||||
): DetailsMode => sections?.[name] ?? SECTION_DEFAULTS[name] ?? global
|
||||
|
||||
export const nextDetailsMode = (m: DetailsMode): DetailsMode => MODES[(MODES.indexOf(m) + 1) % MODES.length]!
|
||||
export const nextDetailsMode = (m: DetailsMode): DetailsMode =>
|
||||
MODES[(MODES.indexOf(m) + 1) % MODES.length]!
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue