From 20865a2653915bdcf4d30c7fd641fec03a046564 Mon Sep 17 00:00:00 2001 From: alt-glitch Date: Tue, 9 Jun 2026 08:26:43 +0000 Subject: [PATCH] opentui(ts): shared envFlag parser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract one boolean env-flag parser (src/logic/env.ts: envFlag + the shared TRUE_RE/FALSE_RE) instead of per-file regexes. Rewire entry/main.tsx (HERMES_TUI_FAKE → envFlag(…, false); HERMES_TUI_MOUSE → envFlag(…, true)) and logic/theme.ts (detectLightMode's HERMES_TUI_LIGHT tri-state now uses the shared regexes; the lowercased-input + /i regex is behaviorally identical to the prior lowercased-input + non-/i regex). Semantics are byte-identical. Adds src/test/env.test.ts (true/false/unset/garbage→fallback). --- ui-tui-opentui-v2/src/entry/main.tsx | 7 +++--- ui-tui-opentui-v2/src/logic/env.ts | 16 +++++++++++++ ui-tui-opentui-v2/src/logic/theme.ts | 5 ++--- ui-tui-opentui-v2/src/test/env.test.ts | 31 ++++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 ui-tui-opentui-v2/src/logic/env.ts create mode 100644 ui-tui-opentui-v2/src/test/env.test.ts diff --git a/ui-tui-opentui-v2/src/entry/main.tsx b/ui-tui-opentui-v2/src/entry/main.tsx index c1a30d53d7c..f0163fc3d97 100644 --- a/ui-tui-opentui-v2/src/entry/main.tsx +++ b/ui-tui-opentui-v2/src/entry/main.tsx @@ -32,6 +32,7 @@ import { getLog } from '../boundary/log.ts' import { acquireRenderer } from '../boundary/renderer.ts' import { makeAppLayer } from '../boundary/runtime.ts' import { nthAssistantResponse } from '../logic/copy.ts' +import { envFlag } from '../logic/env.ts' import { createPromptHistory, dirHistoryPersister, loadDirHistory } from '../logic/history.ts' import { createPasteStore } from '../logic/pastes.ts' import { mapResumeHistory, mapSessionList } from '../logic/resume.ts' @@ -461,16 +462,14 @@ function streamHello(controller: FakeGatewayController): void { controller.emit({ type: 'message.complete' }) } -const TRUE_RE = /^(?:1|true|yes|on)$/i - if (import.meta.main) { - const fake = TRUE_RE.test(process.env.HERMES_TUI_FAKE?.trim() ?? '') + const fake = envFlag(process.env.HERMES_TUI_FAKE, false) const cols = process.stdout.columns || 80 const initialPrompt = process.env.HERMES_TUI_PROMPT?.trim() || process.argv.slice(2).join(' ').trim() const resumeId = process.env.HERMES_TUI_RESUME?.trim() // Mouse on by default (opencode parity: wheel-scroll the transcript, drag the // scrollbar, click-to-expand tools, text-aware selection). HERMES_TUI_MOUSE=0 opts out. - const mouse = !/^(?:0|false|no|off)$/i.test(process.env.HERMES_TUI_MOUSE?.trim() ?? '') + const mouse = envFlag(process.env.HERMES_TUI_MOUSE, true) const base = { mouse, fake, cols } const withPrompt = initialPrompt ? { ...base, initialPrompt } : base const input: TuiInput = resumeId ? { ...withPrompt, resumeId } : withPrompt diff --git a/ui-tui-opentui-v2/src/logic/env.ts b/ui-tui-opentui-v2/src/logic/env.ts new file mode 100644 index 00000000000..294e5c69ef2 --- /dev/null +++ b/ui-tui-opentui-v2/src/logic/env.ts @@ -0,0 +1,16 @@ +/** + * env — shared boolean env-flag parsing (one source for the TRUE/FALSE regexes). + * + * Recognized truthy values: 1/true/yes/on; falsy: 0/false/no/off (case-insensitive, + * surrounding whitespace trimmed). Anything else (incl. unset) is "unrecognized". + */ +export const TRUE_RE = /^(?:1|true|yes|on)$/i +export const FALSE_RE = /^(?:0|false|no|off)$/i + +/** Parse a boolean env var; returns `fallback` when unset/unrecognized. */ +export function envFlag(value: string | undefined, fallback: boolean): boolean { + const v = value?.trim() ?? '' + if (TRUE_RE.test(v)) return true + if (FALSE_RE.test(v)) return false + return fallback +} diff --git a/ui-tui-opentui-v2/src/logic/theme.ts b/ui-tui-opentui-v2/src/logic/theme.ts index e273a98f345..7d20013b8e2 100644 --- a/ui-tui-opentui-v2/src/logic/theme.ts +++ b/ui-tui-opentui-v2/src/logic/theme.ts @@ -14,6 +14,8 @@ * ui-tui/src/gatewayTypes.ts). Keep this port in sync if that contract changes. */ +import { FALSE_RE, TRUE_RE } from './env.ts' + export interface ThemeColors { primary: string accent: string @@ -317,9 +319,6 @@ export const LIGHT_THEME: Theme = { bannerHero: '' } -const TRUE_RE = /^(?:1|true|yes|on)$/ -const FALSE_RE = /^(?:0|false|no|off)$/ - const LIGHT_DEFAULT_TERM_PROGRAMS = new Set(['Apple_Terminal']) const LUMA_LIGHT_THRESHOLD = 0.6 diff --git a/ui-tui-opentui-v2/src/test/env.test.ts b/ui-tui-opentui-v2/src/test/env.test.ts new file mode 100644 index 00000000000..cf510b8ae76 --- /dev/null +++ b/ui-tui-opentui-v2/src/test/env.test.ts @@ -0,0 +1,31 @@ +import { describe, expect, test } from 'bun:test' + +import { envFlag } from '../logic/env.ts' + +describe('envFlag', () => { + test('recognizes truthy values regardless of case/whitespace', () => { + for (const v of ['1', 'true', 'yes', 'on', 'TRUE', 'Yes', ' on ']) { + expect(envFlag(v, false)).toBe(true) + } + }) + + test('recognizes falsy values regardless of case/whitespace', () => { + for (const v of ['0', 'false', 'no', 'off', 'FALSE', 'No', ' off ']) { + expect(envFlag(v, true)).toBe(false) + } + }) + + test('returns fallback when unset', () => { + expect(envFlag(undefined, true)).toBe(true) + expect(envFlag(undefined, false)).toBe(false) + expect(envFlag('', true)).toBe(true) + expect(envFlag(' ', false)).toBe(false) + }) + + test('returns fallback for unrecognized garbage', () => { + expect(envFlag('maybe', true)).toBe(true) + expect(envFlag('maybe', false)).toBe(false) + expect(envFlag('2', true)).toBe(true) + expect(envFlag('enabled', false)).toBe(false) + }) +})