feat(desktop): render session usage footer at transcript end
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -70,9 +70,9 @@ import { ProfileTag } from './profile-tag'
|
||||
import { isRouteSessionMismatch } from './route-session-state'
|
||||
import { useRuntimeMessageRepository } from './runtime-repository'
|
||||
import { ScrollToBottomButton } from './scroll-to-bottom-button'
|
||||
import { SessionUsageFooter } from './session-usage-footer'
|
||||
import { useSessionView } from './session-view'
|
||||
import { SessionActionsMenu } from './sidebar/session-actions-menu'
|
||||
import { WorkspaceBar } from './workspace-bar'
|
||||
import { routedSessionIsLoading, threadLoadingState } from './thread-loading'
|
||||
import {
|
||||
backfillOlderTranscriptPage,
|
||||
@@ -80,6 +80,7 @@ import {
|
||||
transcriptBackfillAvailable
|
||||
} from './transcript-backfill'
|
||||
import { advanceSessionTranscriptWindow, type SessionWindowMemo } from './transcript-window'
|
||||
import { WorkspaceBar } from './workspace-bar'
|
||||
|
||||
interface ChatViewProps extends Omit<React.ComponentProps<'div'>, 'onSubmit'> {
|
||||
gateway: HermesGateway | null
|
||||
@@ -692,6 +693,15 @@ const ChatViewContent = memo(function ChatViewContent({
|
||||
|
||||
const overlayKind: DragKind = dragKind === 'files' ? 'files' : sessionDragging && !sessionEdgeHover ? 'session' : null
|
||||
|
||||
// Identity must be stable across ChatView re-renders: this is a prop of the
|
||||
// memo'd Thread/ThreadMessageList, and a fresh element per render would
|
||||
// defeat their bail-out. Stats changes re-render inside SessionUsageFooter
|
||||
// via its own useStore($sessions) subscription, not through these props.
|
||||
const threadFooter = useMemo(
|
||||
() => (activeSessionId ? <SessionUsageFooter sessionId={activeSessionId} /> : undefined),
|
||||
[activeSessionId]
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
@@ -739,6 +749,7 @@ const ChatViewContent = memo(function ChatViewContent({
|
||||
<Thread
|
||||
clampToComposer={showChatBar}
|
||||
cwd={currentCwd}
|
||||
footer={threadFooter}
|
||||
gateway={gateway}
|
||||
intro={showIntro ? { personality: introPersonality, seed: introSeed } : undefined}
|
||||
loading={threadLoading}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createContext, memo, useCallback, useContext, useMemo, useRef, useState } from 'react'
|
||||
import { createContext, memo, type ReactNode, useCallback, useContext, useMemo, useRef, useState } from 'react'
|
||||
|
||||
import { ChatEmptySlot } from '@/components/assistant-ui/chat-empty-slot'
|
||||
import { AssistantMessage } from '@/components/assistant-ui/thread/assistant-message'
|
||||
@@ -36,6 +36,7 @@ const ThreadEditContext = createContext<ThreadEditContextValue>({ cwd: null, gat
|
||||
interface ThreadProps {
|
||||
clampToComposer?: boolean
|
||||
cwd?: string | null
|
||||
footer?: ReactNode
|
||||
gateway?: HermesGateway | null
|
||||
intro?: IntroProps
|
||||
loading?: ThreadLoadingState
|
||||
@@ -58,6 +59,7 @@ interface ThreadProps {
|
||||
export const Thread = memo(function Thread({
|
||||
clampToComposer = false,
|
||||
cwd = null,
|
||||
footer,
|
||||
gateway = null,
|
||||
intro,
|
||||
loading,
|
||||
@@ -176,6 +178,7 @@ export const Thread = memo(function Thread({
|
||||
clampToComposer={clampToComposer}
|
||||
components={messageComponents}
|
||||
emptyPlaceholder={emptyPlaceholder}
|
||||
footer={footer}
|
||||
loadingIndicator={loadingIndicator}
|
||||
scrollProfile={scrollProfile}
|
||||
sessionId={sessionId}
|
||||
|
||||
@@ -231,6 +231,7 @@ interface ThreadMessageListProps {
|
||||
clampToComposer: boolean
|
||||
components: ThreadMessageComponents
|
||||
emptyPlaceholder?: ReactNode
|
||||
footer?: ReactNode
|
||||
loadingIndicator?: ReactNode
|
||||
sessionId?: string | null
|
||||
sessionKey?: string | null
|
||||
@@ -421,6 +422,7 @@ const ThreadMessageListInner: FC<ThreadMessageListProps> = ({
|
||||
clampToComposer,
|
||||
components,
|
||||
emptyPlaceholder,
|
||||
footer,
|
||||
loadingIndicator,
|
||||
sessionId = null,
|
||||
scrollProfile,
|
||||
@@ -1064,6 +1066,7 @@ const ThreadMessageListInner: FC<ThreadMessageListProps> = ({
|
||||
if (contentRef.current) {
|
||||
resizeObserver.observe(contentRef.current)
|
||||
}
|
||||
|
||||
void scrollToBottom('instant')
|
||||
}
|
||||
|
||||
@@ -1365,6 +1368,7 @@ const ThreadMessageListInner: FC<ThreadMessageListProps> = ({
|
||||
)}
|
||||
{rows}
|
||||
{loadingIndicator}
|
||||
{footer}
|
||||
{clampToComposer && (
|
||||
<div
|
||||
aria-hidden="true"
|
||||
|
||||
Reference in New Issue
Block a user