opentui(ts): shared envFlag parser

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).
This commit is contained in:
alt-glitch 2026-06-09 08:26:43 +00:00
parent da07e67efd
commit 20865a2653
4 changed files with 52 additions and 7 deletions

View file

@ -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

View file

@ -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
}

View file

@ -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<string>(['Apple_Terminal'])
const LUMA_LIGHT_THRESHOLD = 0.6

View file

@ -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)
})
})