rebrand better-clawd and ship initial npm-ready release

This commit is contained in:
x1xhlol
2026-04-01 16:51:18 +02:00
Unverified
parent 420d4155ec
commit 407fa14d6f
109 changed files with 4155 additions and 1690 deletions

View File

@@ -292,6 +292,8 @@ import { createAttachmentMessage, getQueuedCommandAttachments } from '../utils/a
// creating a new [] literal on every render in remote mode, which would
// cause useEffect dependency changes and infinite re-render loops.
const EMPTY_MCP_CLIENTS: MCPServerConnection[] = [];
const EMPTY_TOOL_USE_CONFIRM_QUEUE: ToolUseConfirm[] = [];
const EMPTY_IN_PROGRESS_TOOL_USE_IDS = new Set<string>();
// Stable stub for useAssistantHistory's non-KAIROS branch — avoids a new
// function identity each render, which would break composedOnScroll's memo.
@@ -980,6 +982,14 @@ export function REPL({
// True when user is actively typing — defers interrupt dialogs so keystrokes
// don't accidentally dismiss or answer a permission prompt the user hasn't read yet.
const [isPromptInputActive, setIsPromptInputActive] = React.useState(false);
const promptInputActiveRef = useRef(false);
const setPromptInputActive = useCallback((active: boolean) => {
if (promptInputActiveRef.current === active) {
return;
}
promptInputActiveRef.current = active;
setIsPromptInputActive(active);
}, []);
const [autoUpdaterResult, setAutoUpdaterResult] = useState<AutoUpdaterResult | null>(null);
useEffect(() => {
if (autoUpdaterResult?.notifications) {
@@ -1359,16 +1369,16 @@ export function REPL({
// block's `=== ''` guard — see the fresh value, not the stale render.
inputValueRef.current = value;
setInputValueRaw(value);
setIsPromptInputActive(value.trim().length > 0);
}, [setIsPromptInputActive, repinScroll, trySuggestBgPRIntercept]);
setPromptInputActive(value.trim().length > 0);
}, [setPromptInputActive, repinScroll, trySuggestBgPRIntercept]);
// Schedule a timeout to stop suppressing dialogs after the user stops typing.
// Only manages the timeout — the immediate activation is handled by setInputValue above.
useEffect(() => {
if (inputValue.trim().length === 0) return;
const timer = setTimeout(setIsPromptInputActive, PROMPT_SUPPRESSION_MS, false);
const timer = setTimeout(setPromptInputActive, PROMPT_SUPPRESSION_MS, false);
return () => clearTimeout(timer);
}, [inputValue]);
}, [inputValue, setPromptInputActive]);
const [inputMode, setInputMode] = useState<PromptInputMode>('prompt');
const [stashedPrompt, setStashedPrompt] = useState<{
text: string;
@@ -4399,7 +4409,7 @@ export function REPL({
// and transcript-mode are mutually exclusive (this early return), so
// only one ScrollBox is ever mounted at a time.
const transcriptScrollRef = isFullscreenEnvEnabled() && !disableVirtualScroll && !dumpMode ? scrollRef : undefined;
const transcriptMessagesElement = <Messages messages={transcriptMessages} tools={tools} commands={commands} verbose={true} toolJSX={null} toolUseConfirmQueue={[]} inProgressToolUseIDs={inProgressToolUseIDs} isMessageSelectorVisible={false} conversationId={conversationId} screen={screen} agentDefinitions={agentDefinitions} streamingToolUses={transcriptStreamingToolUses} showAllInTranscript={showAllInTranscript} onOpenRateLimitOptions={handleOpenRateLimitOptions} isLoading={isLoading} hidePastThinking={true} streamingThinking={streamingThinking} scrollRef={transcriptScrollRef} jumpRef={jumpRef} onSearchMatchesChange={onSearchMatchesChange} scanElement={scanElement} setPositions={setPositions} disableRenderCap={dumpMode} />;
const transcriptMessagesElement = <Messages messages={transcriptMessages} tools={tools} commands={commands} verbose={true} toolJSX={null} toolUseConfirmQueue={EMPTY_TOOL_USE_CONFIRM_QUEUE} inProgressToolUseIDs={inProgressToolUseIDs} isMessageSelectorVisible={false} conversationId={conversationId} screen={screen} agentDefinitions={agentDefinitions} streamingToolUses={transcriptStreamingToolUses} showAllInTranscript={showAllInTranscript} onOpenRateLimitOptions={handleOpenRateLimitOptions} isLoading={isLoading} hidePastThinking={true} streamingThinking={streamingThinking} scrollRef={transcriptScrollRef} jumpRef={jumpRef} onSearchMatchesChange={onSearchMatchesChange} scanElement={scanElement} setPositions={setPositions} disableRenderCap={dumpMode} />;
const transcriptToolJSX = toolJSX && <Box flexDirection="column" width="100%">
{toolJSX.jsx}
</Box>;
@@ -4507,6 +4517,7 @@ export function REPL({
// When viewing an agent, never fall through to leader — empty until
// bootstrap/stream fills. Closes the see-leader-type-agent footgun.
const displayedMessages = viewedAgentTask ? viewedAgentTask.messages ?? [] : usesSyncMessages ? messages : deferredMessages;
const activeInProgressToolUseIDs = viewedTeammateTask ? viewedTeammateTask.inProgressToolUseIDs ?? EMPTY_IN_PROGRESS_TOOL_USE_IDS : inProgressToolUseIDs;
// Show the placeholder until the real user message appears in
// displayedMessages. userInputOnProcessing stays set for the whole turn
// (cleared in resetLoadingState); this length check hides it once
@@ -4567,7 +4578,7 @@ export function REPL({
jumpToNew(scrollRef.current);
}} scrollable={<>
<TeammateViewHeader />
<Messages messages={displayedMessages} tools={tools} commands={commands} verbose={verbose} toolJSX={toolJSX} toolUseConfirmQueue={toolUseConfirmQueue} inProgressToolUseIDs={viewedTeammateTask ? viewedTeammateTask.inProgressToolUseIDs ?? new Set() : inProgressToolUseIDs} isMessageSelectorVisible={isMessageSelectorVisible} conversationId={conversationId} screen={screen} streamingToolUses={streamingToolUses} showAllInTranscript={showAllInTranscript} agentDefinitions={agentDefinitions} onOpenRateLimitOptions={handleOpenRateLimitOptions} isLoading={isLoading} streamingText={isLoading && !viewedAgentTask ? visibleStreamingText : null} isBriefOnly={viewedAgentTask ? false : isBriefOnly} unseenDivider={viewedAgentTask ? undefined : unseenDivider} scrollRef={isFullscreenEnvEnabled() ? scrollRef : undefined} trackStickyPrompt={isFullscreenEnvEnabled() ? true : undefined} cursor={cursor} setCursor={setCursor} cursorNavRef={cursorNavRef} />
<Messages messages={displayedMessages} tools={tools} commands={commands} verbose={verbose} toolJSX={toolJSX} toolUseConfirmQueue={toolUseConfirmQueue} inProgressToolUseIDs={activeInProgressToolUseIDs} isMessageSelectorVisible={isMessageSelectorVisible} conversationId={conversationId} screen={screen} streamingToolUses={streamingToolUses} showAllInTranscript={showAllInTranscript} agentDefinitions={agentDefinitions} onOpenRateLimitOptions={handleOpenRateLimitOptions} isLoading={isLoading} streamingText={isLoading && !viewedAgentTask ? visibleStreamingText : null} isBriefOnly={viewedAgentTask ? false : isBriefOnly} unseenDivider={viewedAgentTask ? undefined : unseenDivider} scrollRef={isFullscreenEnvEnabled() ? scrollRef : undefined} trackStickyPrompt={isFullscreenEnvEnabled() ? true : undefined} cursor={cursor} setCursor={setCursor} cursorNavRef={cursorNavRef} />
<AwsAuthStatusBox />
{/* Hide the processing placeholder while a modal is showing —
it would sit at the last visible transcript row right above