fix(desktop): resolve session usage footer by stored id with lineage match
CI / Check no case-colliding filenames (push) Has been cancelled
CI / package-lock.json diff (push) Has been cancelled
CI / Lint Docker scripts (push) Has been cancelled
CI / Supply-chain scan (push) Has been cancelled
CI / Review label gate (push) Has been cancelled
CI / All required checks pass (push) Has been cancelled
CI / Check contributors (push) Has been cancelled
CI / Check uv.lock (push) Has been cancelled
CI / Check no committed infographics (push) Has been cancelled
CI / Profile artifact check (push) Has been cancelled
CI / Detect affected areas (push) Has been cancelled
CI / Python tests (push) Has been cancelled
CI / OS-specific tests (push) Has been cancelled
CI / Python lints (push) Has been cancelled
CI / JS & TS checks (push) Has been cancelled
CI / Installer tests (push) Has been cancelled
CI / Rust tests (push) Has been cancelled
CI / Desktop E2E (push) Has been cancelled
CI / Docs Site (push) Has been cancelled
CI / Deny unrelated histories (push) Has been cancelled
CI / CI timing report (push) Has been cancelled
Docker Build, Test, and Publish / Detect affected areas (push) Has been cancelled
Docker Build, Test, and Publish / build (amd64, type=gha,scope=docker-amd64, type=gha,mode=max,scope=docker-amd64, linux/amd64, ubuntu-latest-32-core) (push) Has been cancelled
Docker Build, Test, and Publish / build (arm64, type=gha,scope=docker-arm64, type=gha,mode=max,scope=docker-arm64, linux/arm64, ubuntu-latest-32-arm-core) (push) Has been cancelled
Docker Build, Test, and Publish / publish (amd64, type=gha,scope=docker-amd64, type=gha,mode=max,scope=docker-amd64, linux/amd64, ubuntu-latest-32-core) (push) Has been cancelled
Docker Build, Test, and Publish / publish (arm64, type=gha,scope=docker-arm64, type=gha,mode=max,scope=docker-arm64, linux/arm64, ubuntu-latest-32-arm-core) (push) Has been cancelled
Docker Build, Test, and Publish / merge (push) Has been cancelled
auto-fix lint issues & formatting / Generate eslint --fix patch (push) Has been cancelled
auto-fix lint issues & formatting / Apply patch (push) Has been cancelled
Nix flake check / Detect affected areas (push) Has been cancelled
Nix flake check / nix flake check (push) Has been cancelled
Deploy Site / deploy-vercel (push) Has been cancelled
Deploy Site / deploy-docs (push) Has been cancelled
Build Skills Index / build-index (push) Has been cancelled
Build Skills Index / trigger-deploy (push) Has been cancelled
CI / Check no case-colliding filenames (push) Has been cancelled
CI / package-lock.json diff (push) Has been cancelled
CI / Lint Docker scripts (push) Has been cancelled
CI / Supply-chain scan (push) Has been cancelled
CI / Review label gate (push) Has been cancelled
CI / All required checks pass (push) Has been cancelled
CI / Check contributors (push) Has been cancelled
CI / Check uv.lock (push) Has been cancelled
CI / Check no committed infographics (push) Has been cancelled
CI / Profile artifact check (push) Has been cancelled
CI / Detect affected areas (push) Has been cancelled
CI / Python tests (push) Has been cancelled
CI / OS-specific tests (push) Has been cancelled
CI / Python lints (push) Has been cancelled
CI / JS & TS checks (push) Has been cancelled
CI / Installer tests (push) Has been cancelled
CI / Rust tests (push) Has been cancelled
CI / Desktop E2E (push) Has been cancelled
CI / Docs Site (push) Has been cancelled
CI / Deny unrelated histories (push) Has been cancelled
CI / CI timing report (push) Has been cancelled
Docker Build, Test, and Publish / Detect affected areas (push) Has been cancelled
Docker Build, Test, and Publish / build (amd64, type=gha,scope=docker-amd64, type=gha,mode=max,scope=docker-amd64, linux/amd64, ubuntu-latest-32-core) (push) Has been cancelled
Docker Build, Test, and Publish / build (arm64, type=gha,scope=docker-arm64, type=gha,mode=max,scope=docker-arm64, linux/arm64, ubuntu-latest-32-arm-core) (push) Has been cancelled
Docker Build, Test, and Publish / publish (amd64, type=gha,scope=docker-amd64, type=gha,mode=max,scope=docker-amd64, linux/amd64, ubuntu-latest-32-core) (push) Has been cancelled
Docker Build, Test, and Publish / publish (arm64, type=gha,scope=docker-arm64, type=gha,mode=max,scope=docker-arm64, linux/arm64, ubuntu-latest-32-arm-core) (push) Has been cancelled
Docker Build, Test, and Publish / merge (push) Has been cancelled
auto-fix lint issues & formatting / Generate eslint --fix patch (push) Has been cancelled
auto-fix lint issues & formatting / Apply patch (push) Has been cancelled
Nix flake check / Detect affected areas (push) Has been cancelled
Nix flake check / nix flake check (push) Has been cancelled
Deploy Site / deploy-vercel (push) Has been cancelled
Deploy Site / deploy-docs (push) Has been cancelled
Build Skills Index / build-index (push) Has been cancelled
Build Skills Index / trigger-deploy (push) Has been cancelled
CDP verification on the live app showed the footer never rendered: ChatView passed the runtime id (view.$runtimeId) while $sessions rows key off stored ids, and compression chains hand the view a lineage id rather than the row's own id. Look up with selectedSessionId || routedSessionId and sessionMatchesStoredId, same as ChatHeader. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -697,9 +697,12 @@ const ChatViewContent = memo(function ChatViewContent({
|
||||
// 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.
|
||||
// The footer needs a STORED id ($sessions rows key off it): activeSessionId
|
||||
// is the runtime id, so fall back through the routed id for fresh resumes.
|
||||
const footerSessionId = selectedSessionId || routedSessionId
|
||||
const threadFooter = useMemo(
|
||||
() => (activeSessionId ? <SessionUsageFooter sessionId={activeSessionId} /> : undefined),
|
||||
[activeSessionId]
|
||||
() => (footerSessionId ? <SessionUsageFooter sessionId={footerSessionId} /> : undefined),
|
||||
[footerSessionId]
|
||||
)
|
||||
|
||||
return (
|
||||
|
||||
@@ -58,6 +58,16 @@ describe('SessionUsageFooter', () => {
|
||||
expect(container.querySelector('[data-slot="session-usage-footer"]')).toBeNull()
|
||||
})
|
||||
|
||||
it('matches a live lineage id to the stored row, like the header does', () => {
|
||||
// Auto-compression rotates the session id: the view holds a chain tip while
|
||||
// the list row still carries its own id plus the lineage ids.
|
||||
setSessions([makeSessionInfo({ _lineage_ids: ['root', 'tip'], id: 'root', input_tokens: 2_000, output_tokens: 400 })])
|
||||
|
||||
const { container } = render(<SessionUsageFooter sessionId="tip" />)
|
||||
|
||||
expect(container.querySelector('[data-slot="session-usage-footer"]')?.textContent).toContain('2.4k tok')
|
||||
})
|
||||
|
||||
it('re-renders when the sessions atom refreshes after a turn', () => {
|
||||
setSessions([makeSessionInfo({ id: 's1', input_tokens: 1_000, output_tokens: 200 })])
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useStore } from '@nanostores/react'
|
||||
|
||||
import { useI18n } from '@/i18n'
|
||||
import { compactNumber } from '@/lib/format'
|
||||
import { $sessions } from '@/store/session'
|
||||
import { $sessions, sessionMatchesStoredId } from '@/store/session'
|
||||
import { sessionCostUsd } from '@/store/sidebar-archive'
|
||||
|
||||
/** Cumulative token/cost totals for one session, rendered at the end of the
|
||||
@@ -13,7 +13,9 @@ import { sessionCostUsd } from '@/store/sidebar-archive'
|
||||
export function SessionUsageFooter({ sessionId }: { sessionId: string }) {
|
||||
const { t } = useI18n()
|
||||
const sessions = useStore($sessions)
|
||||
const session = sessions.find(candidate => candidate.id === sessionId)
|
||||
// Match like ChatHeader does: a compression chain hands the live view a
|
||||
// lineage id, not necessarily the row's own id.
|
||||
const session = sessions.find(candidate => sessionMatchesStoredId(candidate, sessionId))
|
||||
|
||||
if (!session) {
|
||||
return null
|
||||
|
||||
Reference in New Issue
Block a user