diff --git a/desktop/src/components/shared/UpdateChecker.test.tsx b/desktop/src/components/shared/UpdateChecker.test.tsx
index e29e2785..a5799871 100644
--- a/desktop/src/components/shared/UpdateChecker.test.tsx
+++ b/desktop/src/components/shared/UpdateChecker.test.tsx
@@ -32,9 +32,12 @@ describe('UpdateChecker', () => {
})
it('renders markdown release notes in the update prompt', () => {
+ useUpdateStore.setState({ status: 'downloaded' })
+
render()
- expect(screen.getByText('v0.1.5 available')).toBeInTheDocument()
+ expect(screen.getByText('Update ready')).toBeInTheDocument()
+ expect(screen.getByText('v0.1.5 has been downloaded. Restart when you are ready to use it.')).toBeInTheDocument()
expect(screen.getByRole('heading', { name: 'Claude Code Haha v0.1.5' })).toBeInTheDocument()
const link = screen.getByRole('link', { name: 'Release notes' })
@@ -61,7 +64,35 @@ describe('UpdateChecker', () => {
render()
- expect(screen.getByText('Downloading update... 1.5 KB downloaded')).toBeInTheDocument()
+ expect(screen.queryByText('Downloading update... 1.5 KB downloaded')).not.toBeInTheDocument()
+ expect(screen.queryByText('Update ready')).not.toBeInTheDocument()
expect(screen.queryByText(/0%/)).not.toBeInTheDocument()
})
+
+ it.each(['installing', 'restarting'] as const)('does not keep a forced prompt during %s', (status) => {
+ useUpdateStore.setState({
+ status,
+ availableVersion: '0.1.5',
+ shouldPrompt: true,
+ })
+
+ render()
+
+ expect(screen.queryByText('Update ready')).not.toBeInTheDocument()
+ expect(screen.queryByText('Install and restart')).not.toBeInTheDocument()
+ })
+
+ it('keeps the ready prompt retryable when install fails after download', () => {
+ useUpdateStore.setState({
+ status: 'downloaded',
+ error: 'installer failed',
+ shouldPrompt: true,
+ })
+
+ render()
+
+ expect(screen.getByText('Update ready')).toBeInTheDocument()
+ expect(screen.getByText('Update failed: installer failed')).toBeInTheDocument()
+ expect(screen.getByText('Install and restart')).toBeInTheDocument()
+ })
})
diff --git a/desktop/src/components/shared/UpdateChecker.tsx b/desktop/src/components/shared/UpdateChecker.tsx
index d9bec6a5..3d915881 100644
--- a/desktop/src/components/shared/UpdateChecker.tsx
+++ b/desktop/src/components/shared/UpdateChecker.tsx
@@ -3,16 +3,12 @@ import { useTranslation } from '../../i18n'
import { MarkdownRenderer } from '../markdown/MarkdownRenderer'
import { isTauriRuntime } from '../../lib/desktopRuntime'
import { useUpdateStore } from '../../stores/updateStore'
-import { formatBytes } from '../../lib/formatBytes'
export function UpdateChecker() {
const t = useTranslation()
const status = useUpdateStore((s) => s.status)
const availableVersion = useUpdateStore((s) => s.availableVersion)
const releaseNotes = useUpdateStore((s) => s.releaseNotes)
- const progressPercent = useUpdateStore((s) => s.progressPercent)
- const downloadedBytes = useUpdateStore((s) => s.downloadedBytes)
- const totalBytes = useUpdateStore((s) => s.totalBytes)
const error = useUpdateStore((s) => s.error)
const shouldPrompt = useUpdateStore((s) => s.shouldPrompt)
const initialize = useUpdateStore((s) => s.initialize)
@@ -25,31 +21,24 @@ export function UpdateChecker() {
if (!isTauriRuntime()) return null
- const showPopup =
- shouldPrompt && !!availableVersion && ['available', 'downloading', 'restarting'].includes(status)
+ const showPopup = shouldPrompt && !!availableVersion && status === 'downloaded'
if (!showPopup) return null
- const hasKnownProgress = typeof totalBytes === 'number' && totalBytes > 0
- const downloadedText = formatBytes(downloadedBytes)
- const statusText =
- status === 'restarting'
- ? t('update.restarting')
- : status === 'downloading'
- ? hasKnownProgress
- ? t('update.downloading')
- : t('update.progressBytes', { downloaded: downloadedText })
- : null
+ const statusText = t('update.readyBody', { version: availableVersion })
return (
-
-
+
+
- {t('update.available', { version: availableVersion })}
+ {t('update.readyTitle')}
+
+
+ {statusText}
{releaseNotes && (
-
+
)}
- {(status === 'downloading' || status === 'restarting') && (
-
-
- {hasKnownProgress || status === 'restarting' ? (
-
- ) : (
-
- )}
-
- {statusText && (
-
- {statusText}
- {status === 'downloading' && hasKnownProgress ? ` ${progressPercent}%` : ''}
-
- )}
-
- )}
-
{error && (
{t('update.failed', { error })}
)}
- {status === 'available' && (
+ {status === 'downloaded' && (