mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-19 15:18:03 +00:00
Bring apps/desktop and ui-tui to a clean state for typecheck, eslint,
and prettier:
- Run prettier across both trees (printWidth/wrap drift; prettier is not
CI-enforced for these JS projects, so main had accumulated drift).
- Apply eslint --fix for padding-line-between-statements and perfectionist
import/export sorting.
- Manual fixes for non-auto-fixable rules:
- remove unused node:net import in electron/main.cjs (uses Electron net)
- replace inline `typeof import(...)` annotations with top-level
`import type * as EnvModule` in two ui-tui test files
- scoped eslint-disable no-control-regex on intentional sentinel/ANSI
regexes (mathUnicode.ts, text.ts)
- resolve react-hooks/exhaustive-deps per-case: correct swapped/missing
deps, collapse redundant session.* members, and justified disables on
settings mount-only data-load effects to preserve run-once behavior
No behavior changes; test pass/fail counts are unchanged from the main
baseline.
127 lines
3.1 KiB
TypeScript
127 lines
3.1 KiB
TypeScript
import { atom } from 'nanostores'
|
|
|
|
import type { SessionInfo } from '@/types/hermes'
|
|
|
|
import { $selectedStoredSessionId, $sessions } from './session'
|
|
|
|
// Mac-style session switcher (^Tab). Quick tap jumps on keydown; the HUD opens
|
|
// only when Tab is held past REVEAL_MS or tapped again while Ctrl is down.
|
|
|
|
export const SWITCHER_REVEAL_MS = 220
|
|
|
|
export const $switcherOpen = atom(false)
|
|
export const $switcherSessions = atom<SessionInfo[]>([])
|
|
export const $switcherIndex = atom(0)
|
|
|
|
const wrap = (index: number, length: number): number => ((index % length) + length) % length
|
|
|
|
let pendingBrowse = false
|
|
let revealTimer: ReturnType<typeof setTimeout> | null = null
|
|
let tabHeld = false
|
|
let closedAt = 0
|
|
|
|
function clearRevealTimer(): void {
|
|
if (revealTimer) {
|
|
clearTimeout(revealTimer)
|
|
revealTimer = null
|
|
}
|
|
}
|
|
|
|
function revealOverlay(): void {
|
|
pendingBrowse = false
|
|
$switcherOpen.set(true)
|
|
}
|
|
|
|
function scheduleReveal(): void {
|
|
clearRevealTimer()
|
|
revealTimer = setTimeout(() => {
|
|
revealTimer = null
|
|
|
|
if (pendingBrowse && tabHeld) {
|
|
revealOverlay()
|
|
}
|
|
}, SWITCHER_REVEAL_MS)
|
|
}
|
|
|
|
export function onSwitcherTabDown(): void {
|
|
tabHeld = true
|
|
}
|
|
|
|
export function onSwitcherTabUp(): void {
|
|
tabHeld = false
|
|
|
|
if (!$switcherOpen.get()) {
|
|
clearRevealTimer()
|
|
}
|
|
}
|
|
|
|
// First Tab returns a session id to jump to immediately; later Tabs move the
|
|
// highlight (Ctrl↑ commits when the HUD is open).
|
|
export function openOrAdvanceSwitcher(direction: 1 | -1): string | null {
|
|
const sessions = $sessions.get()
|
|
|
|
if (sessions.length < 2) {
|
|
return null
|
|
}
|
|
|
|
if ($switcherOpen.get()) {
|
|
const { length } = $switcherSessions.get()
|
|
|
|
if (length) {
|
|
$switcherIndex.set(wrap($switcherIndex.get() + direction, length))
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
const current = sessions.findIndex(session => session.id === $selectedStoredSessionId.get())
|
|
const start = current === -1 ? (direction === 1 ? -1 : 0) : current
|
|
const nextIndex = wrap(start + direction, sessions.length)
|
|
|
|
$switcherSessions.set(sessions)
|
|
$switcherIndex.set(nextIndex)
|
|
|
|
if (pendingBrowse) {
|
|
clearRevealTimer()
|
|
$switcherIndex.set(wrap($switcherIndex.get() + direction, sessions.length))
|
|
revealOverlay()
|
|
|
|
return null
|
|
}
|
|
|
|
pendingBrowse = true
|
|
scheduleReveal()
|
|
|
|
return sessions[nextIndex]?.id ?? null
|
|
}
|
|
|
|
export const highlightedSessionId = (): string | null => $switcherSessions.get()[$switcherIndex.get()]?.id ?? null
|
|
|
|
export const slotSessionId = (slot: number): string | null =>
|
|
($switcherOpen.get() || pendingBrowse ? $switcherSessions.get() : $sessions.get())[slot - 1]?.id ?? null
|
|
|
|
export function closeSwitcher(): void {
|
|
closedAt = Date.now()
|
|
clearRevealTimer()
|
|
pendingBrowse = false
|
|
tabHeld = false
|
|
$switcherOpen.set(false)
|
|
}
|
|
|
|
export function commitOnCtrlUp(): string | null {
|
|
clearRevealTimer()
|
|
pendingBrowse = false
|
|
|
|
if (!$switcherOpen.get()) {
|
|
return null
|
|
}
|
|
|
|
const target = highlightedSessionId()
|
|
closeSwitcher()
|
|
|
|
return target
|
|
}
|
|
|
|
export const switcherJustClosed = (): boolean => Date.now() - closedAt < 400
|
|
|
|
export const switcherActive = (): boolean => $switcherOpen.get() || pendingBrowse
|