Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion ui-tui/src/app/uiStore.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { atom } from 'nanostores'
import { atom, computed } from 'nanostores'

import { MOUSE_TRACKING } from '../config/env.js'
import { ZERO } from '../domain/usage.js'
Expand Down Expand Up @@ -30,6 +30,9 @@ const buildUiState = (): UiState => ({

export const $uiState = atom<UiState>(buildUiState())

export const $uiTheme = computed($uiState, state => state.theme)
export const $uiSessionId = computed($uiState, state => state.sid)

export const getUiState = () => $uiState.get()

export const patchUiState = (next: Partial<UiState> | ((state: UiState) => UiState)) =>
Expand Down
45 changes: 23 additions & 22 deletions ui-tui/src/components/appOverlays.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useStore } from '@nanostores/react'
import { useGateway } from '../app/gatewayContext.js'
import type { AppOverlaysProps } from '../app/interfaces.js'
import { $overlayState, patchOverlayState } from '../app/overlayStore.js'
import { $uiState } from '../app/uiStore.js'
import { $uiSessionId, $uiTheme } from '../app/uiStore.js'

import { FloatBox } from './appChrome.js'
import { MaskedPrompt } from './maskedPrompt.js'
Expand All @@ -24,12 +24,12 @@ export function PromptZone({
onSudoSubmit
}: Pick<AppOverlaysProps, 'cols' | 'onApprovalChoice' | 'onClarifyAnswer' | 'onSecretSubmit' | 'onSudoSubmit'>) {
const overlay = useStore($overlayState)
const ui = useStore($uiState)
const theme = useStore($uiTheme)

if (overlay.approval) {
return (
<Box flexDirection="column" flexShrink={0} paddingX={1} paddingY={1}>
<ApprovalPrompt onChoice={onApprovalChoice} req={overlay.approval} t={ui.theme} />
<ApprovalPrompt onChoice={onApprovalChoice} req={overlay.approval} t={theme} />
</Box>
)
}
Expand All @@ -46,7 +46,7 @@ export function PromptZone({

return (
<Box flexDirection="column" flexShrink={0} paddingX={1} paddingY={1}>
<ConfirmPrompt onCancel={onCancel} onConfirm={onConfirm} req={req} t={ui.theme} />
<ConfirmPrompt onCancel={onCancel} onConfirm={onConfirm} req={req} t={theme} />
</Box>
)
}
Expand All @@ -59,7 +59,7 @@ export function PromptZone({
onAnswer={onClarifyAnswer}
onCancel={() => onClarifyAnswer('')}
req={overlay.clarify}
t={ui.theme}
t={theme}
/>
</Box>
)
Expand All @@ -68,7 +68,7 @@ export function PromptZone({
if (overlay.sudo) {
return (
<Box flexDirection="column" flexShrink={0} paddingX={1} paddingY={1}>
<MaskedPrompt cols={cols} icon="🔐" label="sudo password required" onSubmit={onSudoSubmit} t={ui.theme} />
<MaskedPrompt cols={cols} icon="🔐" label="sudo password required" onSubmit={onSudoSubmit} t={theme} />
</Box>
)
}
Expand All @@ -82,7 +82,7 @@ export function PromptZone({
label={overlay.secret.prompt}
onSubmit={onSecretSubmit}
sub={`for ${overlay.secret.envVar}`}
t={ui.theme}
t={theme}
/>
</Box>
)
Expand All @@ -101,7 +101,8 @@ export function FloatingOverlays({
}: Pick<AppOverlaysProps, 'cols' | 'compIdx' | 'completions' | 'onModelSelect' | 'onPickerSelect' | 'pagerPageSize'>) {
const { gw } = useGateway()
const overlay = useStore($overlayState)
const ui = useStore($uiState)
const sid = useStore($uiSessionId)
const theme = useStore($uiTheme)

const hasAny = overlay.modelPicker || overlay.pager || overlay.picker || overlay.skillsHub || completions.length

Expand All @@ -119,40 +120,40 @@ export function FloatingOverlays({
return (
<Box alignItems="flex-start" bottom="100%" flexDirection="column" left={0} position="absolute" right={0}>
{overlay.picker && (
<FloatBox color={ui.theme.color.border}>
<FloatBox color={theme.color.border}>
<SessionPicker
gw={gw}
onCancel={() => patchOverlayState({ picker: false })}
onSelect={onPickerSelect}
t={ui.theme}
t={theme}
/>
</FloatBox>
)}

{overlay.modelPicker && (
<FloatBox color={ui.theme.color.border}>
<FloatBox color={theme.color.border}>
<ModelPicker
gw={gw}
onCancel={() => patchOverlayState({ modelPicker: false })}
onSelect={onModelSelect}
sessionId={ui.sid}
t={ui.theme}
sessionId={sid}
t={theme}
/>
</FloatBox>
)}

{overlay.skillsHub && (
<FloatBox color={ui.theme.color.border}>
<SkillsHub gw={gw} onClose={() => patchOverlayState({ skillsHub: false })} t={ui.theme} />
<FloatBox color={theme.color.border}>
<SkillsHub gw={gw} onClose={() => patchOverlayState({ skillsHub: false })} t={theme} />
</FloatBox>
)}

{overlay.pager && (
<FloatBox color={ui.theme.color.border}>
<FloatBox color={theme.color.border}>
<Box flexDirection="column" paddingX={1} paddingY={1}>
{overlay.pager.title && (
<Box justifyContent="center" marginBottom={1}>
<Text bold color={ui.theme.color.primary}>
<Text bold color={theme.color.primary}>
{overlay.pager.title}
</Text>
</Box>
Expand All @@ -163,7 +164,7 @@ export function FloatingOverlays({
))}

<Box marginTop={1}>
<OverlayHint t={ui.theme}>
<OverlayHint t={theme}>
{overlay.pager.offset + pagerPageSize < overlay.pager.lines.length
? `↑↓/jk line · Enter/Space/PgDn page · b/PgUp back · g/G top/bottom · Esc/q close (${Math.min(overlay.pager.offset + pagerPageSize, overlay.pager.lines.length)}/${overlay.pager.lines.length})`
: `end · ↑↓/jk · b/PgUp back · g top · Esc/q close (${overlay.pager.lines.length} lines)`}
Expand All @@ -174,23 +175,23 @@ export function FloatingOverlays({
)}

{!!completions.length && (
<FloatBox color={ui.theme.color.primary}>
<FloatBox color={theme.color.primary}>
<Box flexDirection="column" width={Math.max(28, cols - 6)}>
{completions.slice(start, start + viewportSize).map((item, i) => {
const active = start + i === compIdx

return (
<Box
backgroundColor={active ? ui.theme.color.completionCurrentBg : undefined}
backgroundColor={active ? theme.color.completionCurrentBg : undefined}
flexDirection="row"
key={`${start + i}:${item.text}:${item.display}:${item.meta ?? ''}`}
width="100%"
>
<Text bold color={ui.theme.color.label}>
<Text bold color={theme.color.label}>
{' '}
{item.display}
</Text>
{item.meta ? <Text color={ui.theme.color.muted}> {item.meta}</Text> : null}
{item.meta ? <Text color={theme.color.muted}> {item.meta}</Text> : null}
</Box>
)
})}
Expand Down
Loading