perf improvements + /login fix

This commit is contained in:
x1xhlol
2026-04-01 17:12:45 +02:00
Unverified
parent 7282a40549
commit c5912b625e
21 changed files with 942 additions and 198 deletions

View File

@@ -377,7 +377,13 @@ const MessagesImpl = ({
columns
} = useTerminalSize();
const toggleShowAllShortcut = useShortcutDisplay('transcript:toggleShowAll', 'Transcript', 'Ctrl+E');
const normalizedMessages = useMemo(() => normalizeMessages(messages).filter(isNotEmptyMessage), [messages]);
// In the main-screen renderer, pre-compact history already lives in native
// terminal scrollback, so normalizing it again just adds O(n) work on every
// render. Fullscreen/transcript modes still need the full in-memory history.
const normalizationSourceMessages = useMemo(() => verbose || isFullscreenEnvEnabled() ? messages : getMessagesAfterCompactBoundary(messages, {
includeSnipped: true
}), [messages, verbose]);
const normalizedMessages = useMemo(() => normalizeMessages(normalizationSourceMessages).filter(isNotEmptyMessage), [normalizationSourceMessages]);
// Check if streaming thinking should be visible (streaming or within 30s timeout)
const isStreamingThinkingVisible = useMemo(() => {
@@ -485,18 +491,7 @@ const MessagesImpl = ({
hasTruncatedMessages: hasTruncatedMessages_0,
hiddenMessageCount: hiddenMessageCount_0
} = useMemo(() => {
// In fullscreen mode the alt buffer has no native scrollback, so the
// compact-boundary filter just hides history the ScrollBox could
// otherwise scroll to. Main-screen mode keeps the filter — pre-compact
// rows live above the viewport in native scrollback there, and
// re-rendering them triggers full resets.
// includeSnipped: UI rendering keeps snipped messages for scrollback
// (this PR's core goal — full history in UI, filter only for the model).
// Also avoids a UUID mismatch: normalizeMessages derives new UUIDs, so
// projectSnippedView's check against original removedUuids would fail.
const compactAwareMessages = verbose || isFullscreenEnvEnabled() ? normalizedMessages : getMessagesAfterCompactBoundary(normalizedMessages, {
includeSnipped: true
});
const compactAwareMessages = normalizedMessages;
const messagesToShowNotTruncated = reorderMessagesInUI(compactAwareMessages.filter((msg_2): msg_2 is Exclude<NormalizedMessage, ProgressMessageType> => msg_2.type !== 'progress')
// CC-724: drop attachment messages that AttachmentMessage renders as
// null (hook_success, hook_additional_context, hook_cancelled, etc.)

View File

@@ -0,0 +1,88 @@
import * as React from 'react'
import { useState } from 'react'
import { Box, Text } from '../ink.js'
import { saveOpenRouterApiKey } from '../utils/auth.js'
import { Spinner } from './Spinner.js'
import TextInput from './TextInput.js'
type OpenRouterLoginFlowProps = {
onDone: () => void
startingMessage?: string
}
export function OpenRouterLoginFlow({
onDone,
startingMessage,
}: OpenRouterLoginFlowProps): React.ReactNode {
const [isBusy, setIsBusy] = useState(false)
const [status, setStatus] = useState<string | null>(null)
const [inputValue, setInputValue] = useState('')
const [cursorOffset, setCursorOffset] = useState(0)
async function handleSubmit(value: string): Promise<void> {
const trimmed = value.trim()
if (!trimmed) {
return
}
setIsBusy(true)
setStatus(null)
try {
await saveOpenRouterApiKey(trimmed)
onDone()
} catch (error) {
setStatus(error instanceof Error ? error.message : String(error))
} finally {
setIsBusy(false)
}
}
if (isBusy) {
return (
<Box flexDirection="column" gap={1}>
<Box>
<Spinner />
<Text>Configuring OpenRouter login for Better-Clawd...</Text>
</Box>
<Text dimColor={true}>
OpenRouter support uses your OpenRouter API key with the Responses API
endpoint.
</Text>
</Box>
)
}
return (
<Box flexDirection="column" gap={1}>
<Text>
{startingMessage ??
'Better-Clawd can use OpenRouter with your OpenRouter API key.'}
</Text>
<Text dimColor={true}>
Paste your OpenRouter key to use `https://openrouter.ai/api/v1` and the
Responses API compatibility layer.
</Text>
<Box>
<Text>Paste your OpenRouter API key:</Text>
<TextInput
value={inputValue}
onChange={setInputValue}
onSubmit={handleSubmit}
onExit={() => {
setInputValue('')
setCursorOffset(0)
}}
cursorOffset={cursorOffset}
onChangeCursorOffset={setCursorOffset}
columns={72}
mask="*"
/>
</Box>
{status ? <Text color="error">{status}</Text> : null}
<Text dimColor={true}>
Press <Text bold={true}>Enter</Text> to save, or <Text bold={true}>Esc</Text>{' '}
to cancel.
</Text>
</Box>
)
}