mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-18 13:23:33 +08:00
Desktop repository launches now defer isolated worktree creation until the first user turn so the CLI owns worktree setup, cwd initialization, and session metadata. The chat UI surfaces the pre-startup Git phase so users see when a session is creating a worktree or switching a branch before model output begins. Constraint: Desktop must preserve the selected source checkout until a user actually sends a message Constraint: CLI setup is the canonical owner for worktree creation and cwd initialization Rejected: Create the worktree eagerly in the desktop session picker | it diverges from CLI session startup and creates worktrees before a conversation exists Confidence: high Scope-risk: moderate Directive: Keep repository session startup routed through CLI worktree flags; do not reintroduce eager desktop worktree creation without testing transcript cwd and cleanup behavior Tested: bun test src/server/__tests__/sessions.test.ts src/server/__tests__/conversation-service.test.ts Tested: bun test src/server/__tests__/conversations.test.ts Tested: bun test src/server/__tests__/conversations.test.ts --test-name-pattern "worktree startup status" Tested: cd desktop && bun run test -- src/pages/EmptySession.test.tsx Tested: cd desktop && bun run lint Tested: cd desktop && bun run build Tested: git diff --check Tested: CLI init-only native worktree smoke from feature/rail Tested: agent-browser UI flow with MiniMax-M2.7-highspeed in a /tmp repository
57 lines
2.0 KiB
TypeScript
57 lines
2.0 KiB
TypeScript
import { useChatStore } from '../../stores/chatStore'
|
|
import { useTabStore } from '../../stores/tabStore'
|
|
import { useTranslation, type TranslationKey } from '../../i18n'
|
|
|
|
function formatElapsed(seconds: number): string {
|
|
if (seconds < 60) return `${seconds}s`
|
|
const m = Math.floor(seconds / 60)
|
|
const s = seconds % 60
|
|
return `${m}m ${s}s`
|
|
}
|
|
|
|
function translateServerVerb(
|
|
t: (key: TranslationKey) => string,
|
|
verb: string,
|
|
): string {
|
|
const key = `serverVerb.${verb}` as TranslationKey
|
|
const translated = t(key)
|
|
return translated === key ? verb : translated
|
|
}
|
|
|
|
export function StreamingIndicator() {
|
|
const t = useTranslation()
|
|
const activeTabId = useTabStore((s) => s.activeTabId)
|
|
const sessionState = useChatStore((s) => activeTabId ? s.sessions[activeTabId] : undefined)
|
|
const chatState = sessionState?.chatState ?? 'idle'
|
|
const statusVerb = sessionState?.statusVerb ?? ''
|
|
const elapsedSeconds = sessionState?.elapsedSeconds ?? 0
|
|
const tokenUsage = sessionState?.tokenUsage ?? { input_tokens: 0, output_tokens: 0 }
|
|
let verb: string
|
|
if (statusVerb) {
|
|
verb = translateServerVerb(t, statusVerb)
|
|
} else {
|
|
verb = chatState === 'thinking'
|
|
? t('serverVerb.Thinking')
|
|
: chatState === 'tool_executing'
|
|
? t('serverVerb.Running')
|
|
: t('serverVerb.Working')
|
|
}
|
|
|
|
return (
|
|
<div className="mb-2 flex w-fit items-center gap-2 rounded-full border border-[var(--color-border)]/40 bg-[var(--color-surface-container-low)] px-3 py-1">
|
|
<span className="text-[var(--color-brand)] animate-shimmer text-xs">✦</span>
|
|
<span className="text-xs font-medium text-[var(--color-text-secondary)]">{verb}...</span>
|
|
{elapsedSeconds > 0 && (
|
|
<span className="text-[10px] text-[var(--color-text-tertiary)]">
|
|
{formatElapsed(elapsedSeconds)}
|
|
</span>
|
|
)}
|
|
{tokenUsage.output_tokens > 0 && (
|
|
<span className="text-[10px] text-[var(--color-text-tertiary)]">
|
|
· ↓ {tokenUsage.output_tokens}
|
|
</span>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|