a transcript with nothing in it can say nothing
"Send a message to start" is right on the chat screen and wrong under a setup card that already says what to do — it contradicts the Start button above it and takes 200px of height doing so, which is what squeezed the new-commander pane into a scroller a third of the panel tall. `emptyState` is optional and three-valued on purpose: undefined keeps the default, a node replaces it, null shows nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import type { UseChatType } from '../../../hooks/useChat';
|
||||
import { useEmbeddableChat } from './useEmbeddableChat';
|
||||
import { MessageList } from '../components/MessageList';
|
||||
@@ -5,6 +6,14 @@ import { InputArea } from '../components/InputArea';
|
||||
|
||||
type EmbeddableChatProps = {
|
||||
sessionId?: string;
|
||||
/**
|
||||
* What the transcript shows before there is anything in it. `null` shows nothing.
|
||||
*
|
||||
* The default, "Send a message to start", is true on the chat screen and wrong wherever something else
|
||||
* already explains what to do — a new commander has a setup card and a Start button above it, so the
|
||||
* message contradicts them and takes 200px of height while doing it.
|
||||
*/
|
||||
emptyState?: ReactNode;
|
||||
/** Which panel this is — see `queue-store`. Absent on the screen, where there is only one chat. */
|
||||
panelId?: string;
|
||||
initialModel?: string | null;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import type { KeyboardEvent } from 'react';
|
||||
import { useState, useRef, useEffect, useCallback } from 'react';
|
||||
import { useUserVisibleModels } from 'state/useModels';
|
||||
@@ -51,6 +52,7 @@ type UseEmbeddableChatParams = {
|
||||
contextId?: string;
|
||||
/** This session is a commander, per `GET /chat/sessions/:id`. See the note where it is surfaced. */
|
||||
isCommander?: boolean;
|
||||
emptyState?: ReactNode;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -622,6 +624,7 @@ export function useEmbeddableChat(params: UseEmbeddableChatParams, onMessageComp
|
||||
would lose the brand-new composer, which has no session yet.
|
||||
*/
|
||||
isCommander: params.context === 'command-hq' || params.isCommander === true,
|
||||
emptyState: params.emptyState,
|
||||
/*
|
||||
Claude's transcript id, which is what a commander is keyed on everywhere — the Command HQ row, the
|
||||
record of what it spawned, the delete cascade. `sessionId` above is OFFICER's handle for the same
|
||||
|
||||
@@ -107,11 +107,17 @@ export const MessageList = ({ manager }: MessageListProps) => {
|
||||
</div>
|
||||
)}
|
||||
<div ref={scrollViewportRef} className="h-full overflow-y-auto">
|
||||
{rows.length === 0 && !isGenerating && (
|
||||
{rows.length === 0 &&
|
||||
!isGenerating &&
|
||||
// `undefined` means nobody said, so keep the default. `null` means "show nothing", which is what
|
||||
// a screen that already explains itself above the transcript passes.
|
||||
(manager.emptyState !== undefined ? (
|
||||
manager.emptyState
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-full min-h-[200px] text-muted-foreground text-sm">
|
||||
Send a message to start
|
||||
</div>
|
||||
)}
|
||||
))}
|
||||
{rows.length > 0 && (
|
||||
<ToolOpenProvider>
|
||||
<div className="relative w-full" style={{ height: virtualizer.getTotalSize() }}>
|
||||
|
||||
@@ -365,6 +365,7 @@ function NewCommander({
|
||||
!started && !ready ? { reason: !name.trim() ? 'Name it first…' : 'Choose a working directory first…' } : false
|
||||
}
|
||||
className={!started ? 'shrink-0' : 'min-h-0 flex-1'}
|
||||
{...(!started ? { emptyState: null } : {})}
|
||||
/>
|
||||
<DirPickerModal open={pickerOpen} onClose={() => setPickerOpen(false)} onSelect={setCwd} />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user