Files
claudecodeui/src/i18n/locales/en/chat.json
Haileyesus afc717e69e feat(chat): derive activity indicator from per-session state and unify provider lifecycle events
Replace the chat processing banner with a minimal activity indicator and
rebuild the state model underneath it. The old banner was driven by five
overlapping pieces of state (isLoading, canAbortSession, claudeStatus in the
chat, plus two app-level Sets updated in lockstep through four callbacks)
that had to be kept in sync imperatively. Because completion and status
events mutated the *viewed* session's flags regardless of which session they
belonged to, a background session finishing could hide the indicator for a
still-running session, returning to a finished session could briefly show a
stale banner, and a late status reply could override a newer request.

The fix is structural rather than patch-by-patch: a single
Map<sessionId, {statusText, canInterrupt, startedAt}> in useSessionProtection
is now the only source of truth for "this session is working". The indicator,
stop button, composer streaming state, and session protection are all derived
from the viewed session's entry on render, so there is no stale local copy to
restore or reset when switching sessions. A PENDING_SESSION_ID sentinel
covers the window before a new conversation receives its real session id.
Terminal events delete the entry atomically, which is why the indicator
disappears the instant the final chunk arrives. Stale check-session-status
replies are discarded via an ifStartedBefore guard (an idle reply older than
the entry's startedAt describes a previous request, not the current one).

The second half unifies the provider lifecycle contract, because the frontend
could not be made race-free while each provider terminated differently:

- cursor emitted complete twice per run (result line + process close), which
  double-played the completion sound and let a late close-complete clear a
  newer request's indicator
- aborts produced two completes (the abort-session reply plus the provider's
  own non-aborted one), so cancelling a run played the celebration sound
- codex omitted exitCode; others attached ad-hoc fields (resultText, isError,
  isNewSession) the client had to know about
- claude/codex failures ended with only an error event while gemini/cursor
  also emit kind:'error' for mid-run stderr noise, so 'error' was ambiguous
  between "the run died" and "a process wrote to stderr"

Every run now ends with exactly one complete built by createCompleteMessage()
({sessionId, actualSessionId, exitCode, success, aborted}); abort-session
sends it on behalf of cancelled runs and providers detect the abort and skip
their own. error is demoted to an informational row, so stderr noise no
longer kills the indicator mid-run, and the client celebrates only
success: true completes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 19:39:04 +03:00

243 lines
8.5 KiB
JSON

{
"codeBlock": {
"copy": "Copy",
"copied": "Copied",
"copyCode": "Copy code"
},
"copyMessage": {
"copy": "Copy message",
"copied": "Message copied",
"selectFormat": "Select copy format",
"copyAsMarkdown": "Copy as markdown",
"copyAsText": "Copy as text"
},
"messageTypes": {
"user": "U",
"error": "Error",
"tool": "Tool",
"claude": "Claude",
"cursor": "Cursor",
"codex": "Codex",
"gemini": "Gemini",
"opencode": "OpenCode"
},
"tools": {
"settings": "Tool Settings",
"error": "Tool Error",
"result": "Tool Result",
"viewParams": "View input parameters",
"viewRawParams": "View raw parameters",
"viewDiff": "View edit diff for",
"creatingFile": "Creating new file:",
"updatingTodo": "Updating Todo List",
"read": "Read",
"readFile": "Read file",
"updateTodo": "Update todo list",
"readTodo": "Read todo list",
"searchResults": "results"
},
"search": {
"found": "Found {{count}} {{type}}",
"file": "file",
"files": "files",
"pattern": "pattern:",
"in": "in:"
},
"fileOperations": {
"updated": "File updated successfully",
"created": "File created successfully",
"written": "File written successfully",
"diff": "Diff",
"newFile": "New File",
"viewContent": "View file content",
"viewFullOutput": "View full output ({{count}} chars)",
"contentDisplayed": "The file content is displayed in the diff view above"
},
"interactive": {
"title": "Interactive Prompt",
"waiting": "Waiting for your response in the CLI",
"instruction": "Please select an option in your terminal where Claude is running.",
"selectedOption": "✓ Claude selected option {{number}}",
"instructionDetail": "In the CLI, you would select this option interactively using arrow keys or by typing the number."
},
"thinking": {
"title": "Thinking...",
"emoji": "💭 Thinking..."
},
"json": {
"response": "JSON Response"
},
"permissions": {
"grant": "Grant permission for {{tool}}",
"added": "Permission added",
"addTo": "Adds {{entry}} to Allowed Tools.",
"retry": "Permission saved. Retry the request to use the tool.",
"error": "Unable to update permissions. Please try again.",
"openSettings": "Open settings"
},
"todo": {
"updated": "Todo list has been updated successfully",
"current": "Current Todo List"
},
"plan": {
"viewPlan": "📋 View implementation plan",
"title": "Implementation Plan"
},
"usageLimit": {
"resetAt": "Claude usage limit reached. Your limit will reset at **{{time}} {{timezone}}** - {{date}}"
},
"codex": {
"permissionMode": "Permission Mode",
"modes": {
"default": "Default Mode",
"auto": "Auto Mode",
"acceptEdits": "Accept Edits",
"bypassPermissions": "Bypass Permissions",
"plan": "Plan Mode"
},
"descriptions": {
"default": "Only trusted commands (ls, cat, grep, git status, etc.) run automatically. Other commands are skipped. Can write to workspace.",
"auto": "A model classifier decides per tool call whether to approve or deny. Hands-off, but safer than Bypass — denials still happen.",
"acceptEdits": "All commands run automatically within the workspace. Full auto mode with sandboxed execution.",
"bypassPermissions": "Full system access with no restrictions. All commands run automatically with full disk and network access. Use with caution.",
"plan": "Planning mode - no commands are executed"
},
"technicalDetails": "Technical details"
},
"gemini": {
"permissionMode": "Gemini Permission Mode",
"description": "Control how Gemini CLI handles operation approvals.",
"modes": {
"default": {
"title": "Standard (Ask for Approval)",
"description": "Gemini will prompt for approval before executing commands, writing files, and fetching web resources."
},
"autoEdit": {
"title": "Auto Edit (Skip File Approvals)",
"description": "Gemini will automatically approve file edits and web fetches, but will still prompt for shell commands."
},
"yolo": {
"title": "YOLO (Bypass All Permissions)",
"description": "Gemini will execute all operations without asking for approval. Exercise caution."
}
}
},
"input": {
"placeholder": "Type / for commands, @ for files, or ask {{provider}} anything...",
"placeholderDefault": "Type your message...",
"disabled": "Input disabled",
"attachFiles": "Attach files",
"attachImages": "Attach images",
"send": "Send",
"stop": "Stop",
"hintText": {
"ctrlEnter": "Ctrl+Enter to send • Shift+Enter for new line • Tab to change modes • / for slash commands",
"enter": "Enter to send • Shift+Enter for new line • Tab to change modes • / for slash commands"
},
"clickToChangeMode": "Click to change permission mode (or press Tab in input)",
"showAllCommands": "Show all commands",
"clearInput": "Clear input",
"scrollToBottom": "Scroll to bottom"
},
"providerSelection": {
"title": "Choose Your AI Assistant",
"description": "Select a provider to start a new conversation",
"selectModel": "Select Model",
"providerInfo": {
"anthropic": "by Anthropic",
"openai": "by OpenAI",
"cursorEditor": "AI Code Editor",
"google": "by Google"
},
"readyPrompt": {
"claude": "Ready to use Claude with {{model}}. Start typing your message below.",
"cursor": "Ready to use Cursor with {{model}}. Start typing your message below.",
"codex": "Ready to use Codex with {{model}}. Start typing your message below.",
"gemini": "Ready to use Gemini with {{model}}. Start typing your message below.",
"opencode": "Ready to use OpenCode with {{model}}. Start typing your message below.",
"default": "Select a provider above to begin"
},
"pressToSearch": "Press <kbd>{{shortcut}}</kbd> to search sessions, files, and commits"
},
"session": {
"continue": {
"title": "Continue your conversation",
"description": "Ask questions about your code, request changes, or get help with development tasks"
},
"loading": {
"olderMessages": "Loading older messages...",
"sessionMessages": "Loading session messages..."
},
"messages": {
"showingOf": "Showing {{shown}} of {{total}} messages",
"scrollToLoad": "Scroll up to load more",
"showingLast": "Showing last {{count}} messages ({{total}} total)",
"loadEarlier": "Load earlier messages",
"loadAll": "Load all messages",
"loadingAll": "Loading all messages...",
"allLoaded": "All messages loaded",
"perfWarning": "All messages loaded — scrolling may be slower. Click \"Scroll to bottom\" to restore performance."
}
},
"shell": {
"selectProject": {
"title": "Select a Project",
"description": "Choose a project to open an interactive shell in that directory"
},
"status": {
"newSession": "New Session",
"initializing": "Initializing...",
"restarting": "Restarting..."
},
"actions": {
"disconnect": "Disconnect",
"disconnectTitle": "Disconnect from shell",
"restart": "Restart",
"restartTitle": "Restart Shell",
"connect": "Continue in Shell",
"connectTitle": "Connect to shell"
},
"loading": "Loading terminal...",
"connecting": "Connecting to shell...",
"startSession": "Start a new Claude session",
"resumeSession": "Resume session: {{displayName}}...",
"runCommand": "Run {{command}} in {{projectName}}",
"startCli": "Starting Claude CLI in {{projectName}}",
"defaultCommand": "command"
},
"claudeStatus": {
"actions": {
"thinking": "Thinking",
"processing": "Processing",
"analyzing": "Analyzing",
"working": "Working",
"computing": "Computing",
"reasoning": "Reasoning"
},
"state": {
"live": "Live",
"paused": "Paused"
},
"elapsed": {
"seconds": "{{count}}s",
"minutesSeconds": "{{minutes}}m {{seconds}}s",
"label": "{{time}} elapsed",
"startingNow": "Starting now"
},
"stop": "Stop",
"controls": {
"stopGeneration": "Stop Generation",
"pressEscToStop": "Press Esc anytime to stop"
},
"providers": {
"assistant": "Assistant"
}
},
"projectSelection": {
"startChatWithProvider": "Select a project to start chatting with {{provider}}"
},
"tasks": {
"nextTaskPrompt": "Start the next task"
}
}