opentui(v6): background-agents tray — down-arrow focus + enter to dashboard

This commit is contained in:
alt-glitch 2026-06-10 21:00:59 +05:30
parent 6438acec60
commit c9c6cfc0ee
6 changed files with 546 additions and 8 deletions

View file

@ -0,0 +1,265 @@
/**
* Background-agents tray tests (Epic 2.7). Headless frames through the real
* App + Composer + AgentsTray with a simulated keyboard:
*
* - visibility: nothing rendered with 0 running agents; a one-line muted
* indicator with the count otherwise; completed/failed agents drop out.
* - focus-routing table: Down on an EMPTY composer with running agents
* focuses/expands the tray; Down with text keeps its meaning; Down with
* the slash menu open stays menu navigation (routeMenuKey integration
* pin); Down with 0 agents keeps prompt history; Esc from the tray
* returns focus to the composer; a printable key from the tray bounces
* focus back AND inserts the char (the composer's reclaim rule).
* - Enter on a tray row opens the agents dashboard preselected on that row.
*
* The onType wiring mirrors slashMenu.test.tsx (planCompletion fake catalog)
* so the menu-precedence pin runs against entry-parity completions.
*/
import { describe, expect, test } from 'vitest'
import { createPromptHistory } from '../logic/history.ts'
import { planCompletion } from '../logic/slash.ts'
import { createSessionStore, type CompletionItem, type SessionStore } from '../logic/store.ts'
import { App } from '../view/App.tsx'
import { isTrayAgent } from '../view/agentsTray.tsx'
import { ThemeProvider } from '../view/theme.tsx'
import { renderProbe, type RenderProbe } from './lib/render.ts'
const INDICATOR = 'running — ↓ to inspect'
const EXPANDED_HINT = 'Enter inspect'
/** Fake gateway catalog (what `complete.slash` would return for a `/` prefix). */
const CATALOG: CompletionItem[] = [
{ display: '/clear', meta: 'clear the transcript', text: '/clear' },
{ display: '/copy', meta: 'copy the last response', text: '/copy' }
]
interface Harness {
probe: RenderProbe
store: SessionStore
submitted: string[]
typed: string[]
}
/** Mount the real App (entry-parity onType, like slashMenu.test.tsx). */
async function mountApp(historyEntries: string[] = []): Promise<Harness> {
const store = createSessionStore()
store.apply({ type: 'gateway.ready' })
const submitted: string[] = []
const typed: string[] = []
const history = createPromptHistory({ initial: historyEntries })
const onType = (text: string) => {
typed.push(text)
const plan = planCompletion(text)
if (!plan || plan.method !== 'complete.slash') {
store.clearCompletions()
return
}
const q = String(plan.params.text).toLowerCase()
const items = CATALOG.filter(c => c.text.startsWith(q) && c.text !== q)
if (items.length) store.setCompletions(items, plan.from)
else store.clearCompletions()
}
const probe = await renderProbe(
() => (
<ThemeProvider theme={() => store.state.theme}>
<App store={store} onSubmit={t => submitted.push(t)} onType={onType} history={history} />
</ThemeProvider>
),
// kitty keyboard: a SIMULATED lone ESC never parses under legacy input, and
// the Esc-from-tray test needs it.
{ height: 26, kittyKeyboard: true, width: 70 }
)
return { probe, store, submitted, typed }
}
const spawn = (store: SessionStore, id: string, goal: string) =>
store.apply({ type: 'subagent.start', payload: { depth: 0, goal, subagent_id: id } })
const complete = (store: SessionStore, id: string) =>
store.apply({ type: 'subagent.complete', payload: { subagent_id: id, summary: 'done' } })
describe('agents tray — visibility', () => {
test('isTrayAgent: running-ish statuses are in; ALL terminal statuses are out', () => {
for (const status of ['running', 'thinking', 'tool', 'working']) {
expect(isTrayAgent({ depth: 0, goal: 'g', id: 'x', status })).toBe(true)
}
// `complete` is the store fallback; the LIVE gateway sends delegate_tool's
// payload status verbatim — `completed`/`failed`/`error`/`timeout`/`interrupted`
// (verified live: the success path emits status="completed").
for (const status of ['complete', 'completed', 'failed', 'error', 'timeout', 'interrupted']) {
expect(isTrayAgent({ depth: 0, goal: 'g', id: 'x', status })).toBe(false)
}
})
test('0 running agents → the tray renders nothing', async () => {
const h = await mountApp()
try {
expect(h.probe.frame()).not.toContain(INDICATOR)
} finally {
h.probe.destroy()
}
})
test('2 running agents → a one-line indicator with the count', async () => {
const h = await mountApp()
try {
spawn(h.store, 'a1', 'research X')
spawn(h.store, 'a2', 'compile Y')
const frame = await h.probe.waitForFrame(f => f.includes(INDICATOR))
expect(frame).toContain(`⚡ 2 agents ${INDICATOR}`)
expect(frame).not.toContain(EXPANDED_HINT) // collapsed until focused
} finally {
h.probe.destroy()
}
})
test('completed agents drop out; the tray empties when all finish', async () => {
const h = await mountApp()
try {
spawn(h.store, 'a1', 'research X')
spawn(h.store, 'a2', 'compile Y')
await h.probe.waitForFrame(f => f.includes('⚡ 2 agents'))
complete(h.store, 'a1')
const one = await h.probe.waitForFrame(f => f.includes('⚡ 1 agent '))
expect(one).toContain(`⚡ 1 agent ${INDICATOR}`)
complete(h.store, 'a2')
const none = await h.probe.waitForFrame(f => !f.includes(INDICATOR))
expect(none).not.toContain('⚡')
} finally {
h.probe.destroy()
}
})
})
describe('agents tray — Down-arrow focus routing', () => {
test('Down on an EMPTY composer with running agents focuses + expands the tray', async () => {
const h = await mountApp()
try {
spawn(h.store, 'a1', 'research X')
spawn(h.store, 'a2', 'compile Y')
await h.probe.waitForFrame(f => f.includes(INDICATOR))
h.probe.keys.pressArrow('down')
const frame = await h.probe.waitForFrame(f => f.includes(EXPANDED_HINT))
// rows show goal + status, with the first row selected
expect(frame).toContain('research X')
expect(frame).toContain('compile Y')
expect(frame).toContain('● running')
expect(frame).toMatch(/▸ ● running\s+research X/)
expect(frame).not.toContain(INDICATOR) // collapsed line replaced by rows
} finally {
h.probe.destroy()
}
})
test('Down with TEXT in the composer keeps its meaning (no tray focus)', async () => {
const h = await mountApp()
try {
spawn(h.store, 'a1', 'research X')
await h.probe.waitForFrame(f => f.includes(INDICATOR))
await h.probe.keys.typeText('hello')
await h.probe.settle()
h.probe.keys.pressArrow('down')
await h.probe.settle()
const frame = h.probe.frame()
expect(frame).toContain('hello') // text untouched
expect(frame).not.toContain(EXPANDED_HINT)
expect(frame).toContain(INDICATOR) // still just the indicator
} finally {
h.probe.destroy()
}
})
test('Down with the slash menu open stays MENU navigation (routeMenuKey pin)', async () => {
const h = await mountApp()
try {
spawn(h.store, 'a1', 'research X')
await h.probe.waitForFrame(f => f.includes(INDICATOR))
await h.probe.keys.typeText('/')
await h.probe.settle()
await h.probe.waitForFrame(f => f.includes('/copy'))
h.probe.keys.pressArrow('down') // menu: /clear → /copy (NOT the tray)
await h.probe.settle()
expect(h.probe.frame()).not.toContain(EXPANDED_HINT)
h.probe.keys.pressEnter() // accepts the highlighted command
await h.probe.settle()
expect(h.typed.at(-1)).toBe('/copy ')
expect(h.submitted).toEqual([])
} finally {
h.probe.destroy()
}
})
test('Down with 0 running agents keeps prompt history as today', async () => {
const h = await mountApp(['older prompt'])
try {
h.probe.keys.pressArrow('up') // recall
await h.probe.settle()
expect(h.probe.frame()).toContain('older prompt')
h.probe.keys.pressArrow('down') // back to the (empty) draft — not a tray focus
await h.probe.settle()
const frame = h.probe.frame()
expect(frame).not.toContain('older prompt')
expect(frame).not.toContain(EXPANDED_HINT)
} finally {
h.probe.destroy()
}
})
test('Esc from the focused tray collapses it and refocuses the composer', async () => {
const h = await mountApp()
try {
spawn(h.store, 'a1', 'research X')
await h.probe.waitForFrame(f => f.includes(INDICATOR))
h.probe.keys.pressArrow('down')
await h.probe.waitForFrame(f => f.includes(EXPANDED_HINT))
h.probe.keys.pressEscape()
const frame = await h.probe.waitForFrame(f => !f.includes(EXPANDED_HINT))
expect(frame).toContain(INDICATOR) // back to the collapsed line
await h.probe.keys.typeText('hi') // composer has focus again
await h.probe.settle()
expect(h.probe.frame()).toContain('hi')
} finally {
h.probe.destroy()
}
})
test('a printable key from the focused tray bounces to the composer AND inserts', async () => {
const h = await mountApp()
try {
spawn(h.store, 'a1', 'research X')
await h.probe.waitForFrame(f => f.includes(INDICATOR))
h.probe.keys.pressArrow('down')
await h.probe.waitForFrame(f => f.includes(EXPANDED_HINT))
await h.probe.keys.typeText('x')
const frame = await h.probe.waitForFrame(f => !f.includes(EXPANDED_HINT))
expect(frame).toContain(INDICATOR) // tray collapsed (textarea reclaimed focus)
expect(frame).toContain('x') // …and the char landed in the composer
} finally {
h.probe.destroy()
}
})
})
describe('agents tray — Enter opens the dashboard preselected', () => {
test('Down to the second row + Enter → dashboard open on THAT agent', async () => {
const h = await mountApp()
try {
spawn(h.store, 'a1', 'research X')
spawn(h.store, 'a2', 'compile Y')
await h.probe.waitForFrame(f => f.includes(INDICATOR))
h.probe.keys.pressArrow('down') // focus the tray (row 0)
await h.probe.waitForFrame(f => f.includes(EXPANDED_HINT))
h.probe.keys.pressArrow('down') // select row 1 (compile Y)
await h.probe.settle()
h.probe.keys.pressEnter()
const frame = await h.probe.waitForFrame(f => f.includes('⛓ Agents'))
expect(h.store.state.dashboard).toBe(true)
expect(h.store.state.dashboardAgent).toBe('a2')
expect(frame).toMatch(/▸ ● running\s+compile Y/) // master list preselected
expect(h.submitted).toEqual([]) // Enter opened the dashboard, no submit
} finally {
h.probe.destroy()
}
})
})

View file

@ -19,6 +19,7 @@ import { deferClose } from '../logic/defer.ts'
import type { PromptHistory } from '../logic/history.ts'
import type { PasteStore } from '../logic/pastes.ts'
import type { SessionStore } from '../logic/store.ts'
import { AgentsTray, type AgentsTrayApi } from './agentsTray.tsx'
import { Composer } from './composer.tsx'
import { DimensionsProvider } from './dimensions.tsx'
import { Header } from './header.tsx'
@ -52,6 +53,11 @@ const NO_SESSION = () => undefined
export function App(props: AppProps) {
const theme = useTheme()
// Background-agents tray focus plumbing (Epic 2.7): Down on an empty composer
// hands focus to the tray; Esc from the tray hands it back. Plain refs — the
// tray persists across composer remounts (overlay close re-registers focus).
let trayApi: AgentsTrayApi | undefined
let focusComposer: (() => void) | undefined
const blocked = () => props.store.state.prompt !== undefined
const pager = () => props.store.state.pager
const dashboard = () => props.store.state.dashboard
@ -105,6 +111,8 @@ export function App(props: AppProps) {
history={props.history}
onImagePaste={props.onImagePaste}
pasteStore={props.pasteStore}
onFocusDown={() => trayApi?.focusTray() ?? false}
registerFocus={fn => (focusComposer = fn)}
/>
}
>
@ -132,13 +140,26 @@ export function App(props: AppProps) {
)}
</Match>
</Switch>
{/* background-agents tray (Epic 2.7): renders nothing with no
running agents; a one-line indicator otherwise; expands while
focused (composer Down). Enter opens the dashboard on that row. */}
<AgentsTray
subagents={props.store.state.subagents}
onOpen={id => props.store.openDashboard(id)}
onExit={() => focusComposer?.()}
bind={api => (trayApi = api)}
/>
</box>
</>
}
>
<Match when={pager()}>{p => <Pager title={p().title} text={p().text} onClose={closePager} />}</Match>
<Match when={dashboard()}>
<AgentsDashboard subagents={props.store.state.subagents} onClose={closeDashboard} />
<AgentsDashboard
subagents={props.store.state.subagents}
onClose={closeDashboard}
preselect={props.store.state.dashboardAgent}
/>
</Match>
</Switch>
</box>

View file

@ -0,0 +1,203 @@
/**
* AgentsTray the background-agents tray docked below the composer (Epic 2.7).
*
* Collapsed (unfocused): one muted, always-honest line `⚡ N agents running —
* to inspect`. Nothing at all when no agent is running (no stolen transcript
* height). Expanded (focused): one row per RUNNING subagent (status not
* complete/failed) showing status · goal · elapsed-ish · last activity line,
* with a themed highlight on the selection.
*
* Focus routing (the hard part): the tray takes NATIVE focus (its root box is
* focusable) `focusRenderable` blurs the composer textarea for us, and
* focusing the textarea back blurs the box, whose BLURRED event is the single
* collapse trigger. The composer hands focus over via `onFocusDown` (Down on an
* EMPTY composer, no dropdown see composer.tsx); while the tray is focused:
* - Up/Down move the selection (composer's history handler is gated on the
* textarea being focused, so it stays out of the way);
* - Enter opens the agents dashboard preselected on the row (the dashboard
* replaces the input zone, unmounting the tray destroy blur collapse);
* - Esc returns focus to the composer (`onExit` the composer's focus());
* - a printable key is NOT handled here the composer's reclaim rule focuses
* the textarea and inserts the char, and the resulting blur collapses us.
* The `defaultPrevented` guard keeps the very Down that focused the tray (the
* composer preventDefaults it) from also moving the selection.
*/
import { RenderableEvents, type BoxRenderable } from '@opentui/core'
import { useKeyboard } from '@opentui/solid'
import { createEffect, createMemo, createSignal, For, Show } from 'solid-js'
import type { SubagentInfo } from '../logic/store.ts'
import { elapsedSeconds, useElapsedTick } from './elapsed.ts'
import { useTheme } from './theme.tsx'
/** What the App binds to hand the tray keyboard focus (composer Down). */
export interface AgentsTrayApi {
/** Try to take focus; false when ineligible (no running agents / not mounted). */
focusTray: () => boolean
}
/** Terminal subagent statuses everything the wire can end a branch with.
* `complete` is the store's fallback mapping for a status-less
* `subagent.complete`; the LIVE gateway sends the payload status verbatim from
* delegate_tool: `completed` / `failed` / `error` / `timeout` / `interrupted`. */
const TERMINAL_STATUSES = new Set(['complete', 'completed', 'failed', 'error', 'timeout', 'interrupted'])
/** Tray membership: a subagent still doing work. */
export function isTrayAgent(sa: SubagentInfo): boolean {
return !TERMINAL_STATUSES.has(sa.status)
}
/** `m:ss` for the row's elapsed-ish counter. */
function fmtElapsed(secs: number): string {
return `${Math.floor(secs / 60)}:${String(secs % 60).padStart(2, '0')}`
}
/** Keep a row's activity tail to one line's worth. */
function truncate(s: string, max = 48): string {
const flat = s.replace(/\s+/g, ' ').trim()
return flat.length > max ? `${flat.slice(0, max - 1)}` : flat
}
function statusColor(status: string, theme: ReturnType<typeof useTheme>): string {
const c = theme().color
if (status === 'tool' || status === 'working') return c.accent
if (status.includes('error')) return c.error
return c.warn
}
export function AgentsTray(props: {
subagents: SubagentInfo[]
/** Enter on a row — open that agent in the dashboard. */
onOpen: (id: string) => void
/** Esc (or the tray emptying while focused) — give focus back to the composer. */
onExit?: (() => void) | undefined
/** Receives the focus-handoff API once (the App wires it to the composer's Down). */
bind?: ((api: AgentsTrayApi) => void) | undefined
}) {
const theme = useTheme()
const running = createMemo(() => props.subagents.filter(isTrayAgent))
const [focused, setFocused] = createSignal(false)
const [sel, setSel] = createSignal(0)
// Clamp against a shrinking list (an agent above the selection completing).
const selected = () => Math.min(sel(), Math.max(0, running().length - 1))
let boxRef: BoxRenderable | undefined
// First-seen wall clock per agent id — the subagent stream carries no start
// timestamp, so "elapsed-ish" is time since the tray first saw the agent.
// Non-reactive Map; rows repaint via the shared 1s tick while expanded.
const firstSeen = new Map<string, number>()
createEffect(() => {
for (const sa of running()) if (!firstSeen.has(sa.id)) firstSeen.set(sa.id, Date.now())
})
const attach = (el: BoxRenderable) => {
boxRef = el
// The single collapse trigger: native focus left the box (printable-key
// reclaim by the composer, an overlay opening, or unmount-destroy).
el.on(RenderableEvents.BLURRED, () => setFocused(false))
}
props.bind?.({
focusTray: () => {
if (running().length === 0 || !boxRef) return false
setSel(0)
setFocused(true)
boxRef.focus()
return true
}
})
// The last running agent finished while the tray was focused: the box is about
// to unmount — hand focus back to the composer instead of leaving it nowhere.
createEffect(() => {
if (focused() && running().length === 0) {
setFocused(false)
props.onExit?.()
}
})
useKeyboard(key => {
// defaultPrevented: the Down that HANDED us focus was consumed by the composer.
if (!focused() || key.defaultPrevented) return
if (key.name === 'up') {
setSel(Math.max(0, selected() - 1))
key.preventDefault()
} else if (key.name === 'down') {
setSel(Math.min(running().length - 1, selected() + 1))
key.preventDefault()
} else if (key.name === 'return') {
const sa = running()[selected()]
if (sa) props.onOpen(sa.id) // dashboard replaces the input zone → unmount → blur → collapse
key.preventDefault()
} else if (key.name === 'escape') {
boxRef?.blur()
setFocused(false)
props.onExit?.()
}
})
return (
<Show when={running().length > 0}>
<box ref={attach} focusable style={{ flexDirection: 'column', flexShrink: 0 }}>
<Show
when={focused()}
fallback={
<text selectable={false} fg={theme().color.muted}>
{`${running().length} agent${running().length === 1 ? '' : 's'} running — ↓ to inspect`}
</text>
}
>
<TrayRows agents={running()} selected={selected()} firstSeen={firstSeen} />
</Show>
</box>
</Show>
)
}
/** The expanded rows split out so the 1s elapsed tick is only subscribed while
* the tray is focused (the `<Show>` scope owns the subscription's onCleanup). */
function TrayRows(props: { agents: SubagentInfo[]; selected: number; firstSeen: Map<string, number> }) {
const theme = useTheme()
const tick = useElapsedTick()
return (
<box
style={{
backgroundColor: theme().color.completionBg,
flexDirection: 'column',
paddingLeft: 1,
paddingRight: 1
}}
>
<For each={props.agents}>
{(sa, i) => {
const active = () => i() === props.selected
const last = () => sa.trace?.at(-1) ?? sa.thought
const secs = () => (tick(), elapsedSeconds(props.firstSeen.get(sa.id) ?? Date.now()))
return (
<box
style={{
backgroundColor: active() ? theme().color.completionCurrentBg : theme().color.completionBg
}}
>
<text selectable={false}>
<span style={{ fg: active() ? theme().color.accent : theme().color.muted }}>
{active() ? '▸ ' : ' '}
</span>
<span style={{ fg: statusColor(sa.status, theme) }}>{`${sa.status}`}</span>
<span style={{ fg: active() ? theme().color.text : theme().color.label }}>{` ${truncate(
sa.goal || sa.id,
72
)}`}</span>
<span style={{ fg: theme().color.muted }}>{` · ${fmtElapsed(secs())}`}</span>
<span style={{ fg: theme().color.muted }}>{last() ? ` ${truncate(last() ?? '')}` : ''}</span>
</text>
</box>
)
}}
</For>
<text selectable={false} fg={theme().color.muted}>
/ select · Enter inspect · Esc back
</text>
</box>
)
}

View file

@ -89,6 +89,11 @@ export function Composer(props: {
history?: PromptHistory | undefined
onImagePaste?: (() => void) | undefined
pasteStore?: PasteStore | undefined
/** Down on an EMPTY focused composer with no dropdown open (Epic 2.7 tray
* handoff): return true to consume the key (the tray took focus). */
onFocusDown?: (() => boolean) | undefined
/** Hands the parent a "focus the textarea" callback (Esc from the tray). */
registerFocus?: ((focus: () => void) => void) | undefined
}) {
const theme = useTheme()
const dims = useDimensions()
@ -178,10 +183,32 @@ export function Composer(props: {
return
}
}
// 2) prompt history (item 6): Up at the first line → older prompt; Down at the
// 2) background-agents tray handoff (Epic 2.7): Down on an EMPTY focused
// composer with NO dropdown open offers focus to the tray. The parent decides
// eligibility (≥1 running agent; overlays/prompts replace the composer
// entirely, so they can't get here) and returns true when it took focus —
// preventDefault keeps the consumed Down out of the textarea AND out of the
// tray's own selection handler (it skips defaultPrevented keys). Otherwise
// Down keeps every existing meaning (menu nav above, history below).
if (
key.name === 'down' &&
!key.ctrl &&
!key.meta &&
!key.option &&
menu.length === 0 &&
ta?.focused === true &&
ta.plainText === '' &&
props.onFocusDown?.() === true
) {
key.preventDefault()
return
}
// 3) prompt history (item 6): Up at the first line → older prompt; Down at the
// last line → newer/draft. At the boundary the textarea's own up/down is a
// no-op, so there's no conflict; mid-buffer it falls through to cursor moves.
if (ta && props.history) {
// Gated on the textarea being FOCUSED: while focus is elsewhere (the agents
// tray, the transcript scrollbox) arrows must not recall history into the buffer.
if (ta?.focused && props.history) {
if (key.name === 'up' && ta.logicalCursor.row === 0) {
const entry = props.history.prev(ta.plainText)
if (entry !== null) setBuffer(entry)
@ -197,7 +224,7 @@ export function Composer(props: {
props.history.reset()
}
}
// 3) always-active input (item 2): a printable key while the textarea lost
// 4) always-active input (item 2): a printable key while the textarea lost
// focus reclaims it. The renderer runs this GLOBAL handler BEFORE routing the
// key to the focused renderable, so after focus() the SAME keystroke is still
// delivered to the (now-focused) textarea — do NOT insert it here too, or the
@ -207,7 +234,10 @@ export function Composer(props: {
}
})
onMount(() => ta?.focus())
onMount(() => {
ta?.focus()
props.registerFocus?.(() => ta?.focus())
})
return (
<box style={{ flexDirection: 'column', flexShrink: 0 }}>

View file

@ -26,7 +26,12 @@ function statusColor(status: string, theme: ReturnType<typeof useTheme>): string
return c.warn
}
export function AgentsDashboard(props: { subagents: SubagentInfo[]; onClose: () => void }) {
export function AgentsDashboard(props: {
subagents: SubagentInfo[]
onClose: () => void
/** Subagent id to preselect on open (Enter from the agents tray — Epic 2.7). */
preselect?: string | undefined
}) {
const theme = useTheme()
const [sel, setSel] = createSignal(0)
let rootRef: BoxRenderable | undefined
@ -38,7 +43,14 @@ export function AgentsDashboard(props: { subagents: SubagentInfo[]; onClose: ()
// Close (Esc/Ctrl+C) is the native keymap; select + scroll stay in the raw global
// handler below. Focus the root box on mount so the focus-within close layer is active.
onMount(() => rootRef?.focus())
// A `preselect` id (tray Enter) lands the selection on that agent's row.
onMount(() => {
rootRef?.focus()
if (props.preselect) {
const idx = props.subagents.findIndex(sa => sa.id === props.preselect)
if (idx >= 0) setSel(idx)
}
})
useCloseLayer(
() => rootRef,
() => props.onClose()

View file

@ -63,7 +63,14 @@ export default defineConfig({
// runtime, so post-mount store updates would never repaint test frames
// (@opentui/solid itself deep-imports `solid-js/dist/solid.js`, which is
// exactly where the alias points — one shared runtime).
inline: [/solid-js[/\\]store/]
//
// Same story for @opentui/keymap: externalized, its bare `import "solid-js"`
// gets the SSR server build — a SECOND runtime whose `Owner` is null inside
// client-runtime computations, so `useKeymap()` threw "Keymap not found" for
// any overlay mounted AFTER the initial render (dashboard/pager opened by a
// store update). The production build is immune (esbuild bundles the keymap
// and force-resolves solid-js to the client build for every importer).
inline: [/solid-js[/\\]store/, /@opentui[/\\]keymap/]
}
}
}