Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/web-core/src/i18n/locales/en/common.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"cliMode": {
"title": "Claude CLI",
"titleShort": "CLI",
"persistentHint": "persistent tmux session",
"backToChat": "Back to chat",
"agentRunningTitle": "Claude is working on your prompt",
Expand Down
1 change: 1 addition & 0 deletions packages/web-core/src/i18n/locales/es/common.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"cliMode": {
"title": "CLI de Claude",
"titleShort": "CLI",
"persistentHint": "sesión tmux persistente",
"backToChat": "Volver al chat",
"agentRunningTitle": "Claude está trabajando en tu prompt",
Expand Down
1 change: 1 addition & 0 deletions packages/web-core/src/i18n/locales/fr/common.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"cliMode": {
"title": "CLI Claude",
"titleShort": "CLI",
"persistentHint": "session tmux persistante",
"backToChat": "Retour au chat",
"agentRunningTitle": "Claude travaille sur votre prompt",
Expand Down
1 change: 1 addition & 0 deletions packages/web-core/src/i18n/locales/ja/common.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"cliMode": {
"title": "Claude CLI",
"titleShort": "CLI",
"persistentHint": "永続的な tmux セッション",
"backToChat": "チャットに戻る",
"agentRunningTitle": "Claude がプロンプトを処理しています",
Expand Down
1 change: 1 addition & 0 deletions packages/web-core/src/i18n/locales/ko/common.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"cliMode": {
"title": "Claude CLI",
"titleShort": "CLI",
"persistentHint": "지속되는 tmux 세션",
"backToChat": "채팅으로 돌아가기",
"agentRunningTitle": "Claude가 프롬프트를 처리하고 있습니다",
Expand Down
1 change: 1 addition & 0 deletions packages/web-core/src/i18n/locales/zh-Hans/common.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"cliMode": {
"title": "Claude CLI",
"titleShort": "CLI",
"persistentHint": "持久 tmux 会话",
"backToChat": "返回聊天",
"agentRunningTitle": "Claude 正在处理你的提示",
Expand Down
1 change: 1 addition & 0 deletions packages/web-core/src/i18n/locales/zh-Hant/common.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"cliMode": {
"title": "Claude CLI",
"titleShort": "CLI",
"persistentHint": "持久 tmux 工作階段",
"backToChat": "返回聊天",
"agentRunningTitle": "Claude 正在處理你的提示",
Expand Down
20 changes: 13 additions & 7 deletions packages/web-core/src/pages/workspaces/CliMainPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,23 +65,29 @@ export function CliMainPane({

return (
<div className="h-full bg-secondary flex flex-col">
<div className="px-4 py-1 flex items-center justify-between shrink-0 h-8">
<span className="text-sm font-medium text-normal">
{t('cliMode.title')}
<span className="ml-2 text-xs text-low">
{/* md: (>=768px) pairs with useIsMobile's 767px max-width — keep in
sync with MOBILE_BREAKPOINT in useIsMobile.ts. */}
<div className="px-4 py-1 flex items-center justify-between gap-2 shrink-0 h-8 min-w-0">
<span className="text-sm font-medium text-normal min-w-0 truncate">
<span className="hidden md:inline">{t('cliMode.title')}</span>
<span className="md:hidden">{t('cliMode.titleShort')}</span>
<span className="ml-2 text-xs text-low hidden md:inline">
{t('cliMode.persistentHint')}
</span>
</span>
<div className="flex items-center gap-3">
<div className="flex items-center gap-2 md:gap-3 shrink-0">
<LoopAutomationControl workspaceId={workspaceId} />
<button
type="button"
onClick={onBackToChat}
className="flex items-center gap-1 text-low hover:text-normal transition-colors"
className="flex items-center gap-1 text-low hover:text-normal transition-colors shrink-0 p-1.5 -m-1.5 md:p-0 md:m-0"
title={t('cliMode.backToChat')}
aria-label={t('cliMode.backToChat')}
>
<ChatsTeardropIcon className="size-icon-sm" weight="bold" />
<span className="text-xs">{t('cliMode.backToChat')}</span>
<span className="text-xs hidden md:inline">
{t('cliMode.backToChat')}
</span>
</button>
</div>
</div>
Expand Down
46 changes: 30 additions & 16 deletions packages/web-core/src/shared/components/LoopAutomationControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,26 +129,40 @@ export function LoopAutomationControl({
const attemptsUsed = policy ? Number(policy.attempts_used) : 0;
const showAttempts = enabled && maxAttemptsCount > 0;

// Mobile (<768px) shows only the bare time next to the moon/clock icon; the
// full "Waking/Retrying at …" string stays in the accessibility tree via
// sr-only (the attempts counter is hidden on mobile for AT and sighted
// users alike). md: pairs with useIsMobile's 767px max-width — keep in
// sync with MOBILE_BREAKPOINT in useIsMobile.ts.
const isUsageLimitWake = nextWakeup?.kind === 'usage_limit_wake';
const wakeupTimeText = nextWakeup
? isUsageLimitWake
? formatUtcTime(nextWakeup.fire_at)
: formatLocalTime(nextWakeup.fire_at)
: null;
const wakeupStatusText = nextWakeup
? isUsageLimitWake
? t('loopAutomation.wakingAt', { time: wakeupTimeText })
: t('loopAutomation.retryingAt', { time: wakeupTimeText })
: null;

return (
<div className="flex items-center gap-2">
<div className="flex items-center gap-1.5 md:gap-2 min-w-0">
{enabled && nextWakeup && (
<span className="flex items-center gap-1 text-xs text-low">
{nextWakeup.kind === 'usage_limit_wake' ? (
<span className="flex items-center gap-1 text-xs text-low whitespace-nowrap shrink-0">
{isUsageLimitWake ? (
<MoonIcon className="size-icon-sm" weight="bold" aria-hidden />
) : (
<ClockIcon className="size-icon-sm" weight="bold" aria-hidden />
)}
<span>
{nextWakeup.kind === 'usage_limit_wake'
? t('loopAutomation.wakingAt', {
time: formatUtcTime(nextWakeup.fire_at),
})
: t('loopAutomation.retryingAt', {
time: formatLocalTime(nextWakeup.fire_at),
})}
<span className="sr-only md:not-sr-only md:whitespace-nowrap">
{wakeupStatusText}
</span>
<span className="md:hidden" aria-hidden>
{wakeupTimeText}
</span>
{showAttempts && (
<span>
<span className="hidden md:inline">
{'· '}
{t('loopAutomation.attempts', {
used: attemptsUsed,
Expand All @@ -167,7 +181,7 @@ export function LoopAutomationControl({
)}

{showAttempts && !nextWakeup && (
<span className="text-xs text-low">
<span className="text-xs text-low hidden md:inline">
{t('loopAutomation.attempts', {
used: attemptsUsed,
max: maxAttemptsCount,
Expand All @@ -176,13 +190,13 @@ export function LoopAutomationControl({
)}

<Tooltip content={t('loopAutomation.tooltip')} side="bottom">
<span className="flex items-center gap-1.5">
<span className="flex items-center gap-1.5 shrink-0">
<ArrowsClockwiseIcon
className="size-icon-sm text-low"
weight="bold"
aria-hidden
/>
<span className="text-xs text-normal select-none">
<span className="text-xs text-normal select-none hidden md:inline">
{t('loopAutomation.label')}
</span>
<Switch
Expand All @@ -201,7 +215,7 @@ export function LoopAutomationControl({
type="button"
aria-label={t('loopAutomation.settingsLabel')}
title={t('loopAutomation.settingsLabel')}
className="flex items-center justify-center p-half rounded-sm text-low hover:text-normal hover:bg-secondary/50 transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-brand"
className="flex items-center justify-center p-half rounded-sm text-low hover:text-normal hover:bg-secondary/50 transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-brand shrink-0"
>
<GearSixIcon className="size-icon-sm" weight="bold" aria-hidden />
</button>
Expand Down
Loading