refactor(web): unify form controls, one notification rule, dedupe i18n copy (#54)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yaowei ZhengandClaude Opus 4.8 authored and GitHub committed 2026-07-24 18:43:18 +08:00
1 parent c7465625a4
commit 3f868ac385
50 files changed
+2391 -1190

No files matched your search

+10 -6
View File
@@ -26,6 +26,7 @@ import { S } from "../../lib/strings";
import { apiErrorText } from "../../lib/api-error";
import { useDocumentTitle } from "../../lib/use-document-title";
import { formatDateTime, formatMoney, humanizeDuration, humanizeTokens } from "../../lib/format";
import { latestConversation } from "../../lib/session-grouping";
import { approvalKey } from "../../lib/omni/stream-model";
import { useTheme } from "../../state/theme";
import { useProject } from "../../state/project";
@@ -203,14 +204,17 @@ export function ChatPage() {
};
}, [draft, routeSessionId, sessionsLoading, sessions, addSession]);
// Auto-select the most recent Session when the route doesn't select one; if there are none at all, fall back to draft state (instead of auto-creating one).
// Auto-select the most recent conversation when the route doesn't select one (newest loaded
// active/schedule Session — archived rows are hidden by choice and subagent Sessions belong
// to their parent, so neither is auto-opened); if there is none, fall back to draft state
// (instead of auto-creating one).
useEffect(() => {
if (sessionsLoading || draft) return;
if (routeSessionId && sessions.some((s) => s.sessionId === routeSessionId)) return;
// A routed id missing from the paged list isn't gone until the direct lookup fails.
if (routeSessionId && probeFailedId !== routeSessionId) return;
const first = sessions[0];
navigate(first ? `/chat/${first.sessionId}` : `/chat/${DRAFT_SESSION_ID}`, { replace: true });
const last = latestConversation(sessions);
navigate(last ? `/chat/${last.sessionId}` : `/chat/${DRAFT_SESSION_ID}`, { replace: true });
}, [sessionsLoading, draft, routeSessionId, probeFailedId, sessions, navigate]);
// Sync task_state to the sidebar list badge.
@@ -574,7 +578,7 @@ export function ChatPage() {
<StatChip
icon={STAT_ICONS.cost}
value={`${formatMoney(sessionCost, currency)}${costUncosted ? " *" : ""}`}
label={`${S.chat.statCost}(${currency})${costUncosted ? ` · ${S.usage.uncostedNote}` : ""}`}
label={`${S.common.cost}(${currency})${costUncosted ? ` · ${S.usage.uncostedNote}` : ""}`}
/>
)}
<StatChip
@@ -660,7 +664,7 @@ export function ChatPage() {
</div>
<div>
<p className="text-xs font-medium text-gray-500 dark:text-gray-400">
{S.models.createdAt}
{S.common.created}
</p>
<p className="font-mono text-xs">{formatDateTime(selected.createdAt)}</p>
</div>
@@ -672,7 +676,7 @@ export function ChatPage() {
<p className="font-mono text-xs">
{S.chat.statTokens} {humanizeTokens(totalTokens)}
{sessionCost != null &&
` · ${S.chat.statCost} ${formatMoney(sessionCost, currency)}`}{" "}
` · ${S.common.cost} ${formatMoney(sessionCost, currency)}`}{" "}
· {S.chat.statElapsed} {humanizeDuration(stream.model.stats.sessionElapsedMs)}
</p>
</div>