diff --git a/app/renderer/components/Splash.test.tsx b/app/renderer/components/Splash.test.tsx index 31b2310..00f253b 100644 --- a/app/renderer/components/Splash.test.tsx +++ b/app/renderer/components/Splash.test.tsx @@ -80,7 +80,7 @@ describe('Splash', () => { expect(splashEl()).not.toBeInTheDocument() }) - it('reveals the per-provider indexing list on real cold-scan progress', () => { + it('reveals the compact indexing status on real cold-scan progress', () => { render() expect(splashEl()).toBeInTheDocument() // No detail before any progress arrives. @@ -95,15 +95,17 @@ describe('Splash', () => { const status = document.querySelector('.splash-status') expect(status).toBeInTheDocument() - expect(status?.textContent).toContain('First run: indexing your usage history') - expect(status?.textContent).toContain('Ingesting Claude…') - expect(status?.textContent).toContain('120/480') - // Both detected providers render a row; claude is active, codex pending. + // One compact line: active provider + live counter. No per-provider text rows. + expect(status?.querySelector('.splash-status-line')?.textContent).toBe('Indexing Claude · 120/480') + expect(status?.textContent).toContain('One-time scan') + // Both detected providers render as small logos in the strip; claude active. expect(document.querySelectorAll('.splash-prov').length).toBe(2) - expect(document.querySelector('.splash-prov.active')?.textContent).toContain('Claude') + expect(document.querySelector('.splash-prov.active')?.getAttribute('title')).toBe('Claude') act(() => { progressCb?.({ kind: 'provider', provider: 'claude', state: 'done' }) }) - expect(document.querySelector('.splash-prov.done')?.textContent).toContain('Claude') + expect(document.querySelector('.splash-prov.done')?.getAttribute('title')).toBe('Claude') + // No active provider left: the line falls back to the generic copy. + expect(document.querySelector('.splash-status-line')?.textContent).toBe('Indexing your usage history…') }) it('swaps instantly under reduced motion (no fade, no min-time)', () => { diff --git a/app/renderer/components/Splash.tsx b/app/renderer/components/Splash.tsx index a27dfb9..5af7f7e 100644 --- a/app/renderer/components/Splash.tsx +++ b/app/renderer/components/Splash.tsx @@ -58,6 +58,35 @@ function providerLabel(id: string): string { return id.split(/[-\s]+/).filter(Boolean).map(part => part.charAt(0).toUpperCase() + part.slice(1)).join(' ') } +/** + * Compact first-run indexing status: one line of copy (with a live counter when + * Claude is parsing), a small horizontal strip of provider logos (dim = pending, + * lit = active, settled = done), and one muted note. Deliberately no per-provider + * text rows -- a machine with 12 providers must not turn the splash into a list. + */ +function SplashStatus({ progress }: { progress: Progress }) { + const active = progress.order.find(id => progress.status[id] === 'active') ?? null + const counter = active === 'claude' && progress.claudeTotal > 0 + ? ` · ${progress.claudeDone.toLocaleString('en-US')}/${progress.claudeTotal.toLocaleString('en-US')}` + : '' + const line = active ? `Indexing ${providerLabel(active)}${counter}` : 'Indexing your usage history…' + return ( +
+
{line}
+ {progress.order.length > 0 && ( +
+ {progress.order.map(id => ( + + + + ))} +
+ )} +
One-time scan · future launches are instant
+
+ ) +} + /** * Full-window branded startup loader -- the same scanning moment as the menubar * app's ignite-while-loading flame. Mounts once with the app and stays up while @@ -70,9 +99,9 @@ function providerLabel(id: string): string { * * On a genuinely cold first run the overview warmup streams per-provider scan * progress (main.ts forwards the CLI's stderr). Once real parse work is detected - * (or the scan simply outlasts REVEAL_FALLBACK_MS), the splash reveals a "first - * run: indexing" line and a per-provider ingest list. A warm launch resolves - * before that threshold and never shows it. + * (or the scan simply outlasts REVEAL_FALLBACK_MS), the splash reveals a compact + * status block (see SplashStatus). A warm launch resolves before that threshold + * and never shows it. */ export function Splash({ hasData, hasError }: { hasData: boolean; hasError: boolean }) { const [phase, setPhase] = useState('lit') @@ -140,31 +169,7 @@ export function Splash({ hasData, hasError }: { hasData: boolean; hasError: bool )}
CodeBurn
v{version}
- {showDetail && ( -
-
- First run: indexing your usage history. This one-time scan can take a few minutes; future launches are instant. -
- {progress.order.length > 0 && ( -
    - {progress.order.map(id => { - const status = progress.status[id] ?? 'pending' - const count = id === 'claude' && status === 'active' && progress.claudeTotal > 0 - ? ` ${progress.claudeDone.toLocaleString('en-US')}/${progress.claudeTotal.toLocaleString('en-US')}` - : '' - const text = status === 'active' ? `Ingesting ${providerLabel(id)}…${count}` : providerLabel(id) - return ( -
  • - - {text} - {status === 'done' && } -
  • - ) - })} -
- )} -
- )} + {showDetail && } , document.body, ) diff --git a/app/renderer/styles/plain.css b/app/renderer/styles/plain.css index 6f58111..23cf360 100644 --- a/app/renderer/styles/plain.css +++ b/app/renderer/styles/plain.css @@ -900,35 +900,30 @@ td:first-child { font-size: var(--fs-body); font-weight: var(--fw-body); } to { opacity: 1; transform: none; } } -/* First-run indexing detail: framing line + per-provider ingest list. Colors are - fixed to the dark splash canvas (like .splash-word/.splash-version), not the theme. */ +/* First-run indexing status: one line + a small provider-logo strip + one note. + Colors are fixed to the dark splash canvas (like .splash-word/.splash-version), + not the theme. Tabular numerals keep the live counter from jittering. */ .splash-status { - margin-top: 8px; display: flex; flex-direction: column; align-items: center; gap: 16px; - max-width: 420px; text-align: center; + margin-top: 4px; display: flex; flex-direction: column; align-items: center; gap: 10px; + max-width: 340px; text-align: center; animation: splash-word-in 400ms ease-out both; } -.splash-status-line { font-size: var(--fs-label); line-height: 1.5; color: #b3a595; } -.splash-providers { - list-style: none; margin: 0; padding: 0; width: 100%; - display: flex; flex-direction: column; gap: 7px; +.splash-status-line { + font-size: var(--fs-label); color: #f4f1ec; font-variant-numeric: tabular-nums; } -.splash-prov { - display: flex; align-items: center; gap: 9px; - font-size: var(--fs-label); color: #6f665c; padding: 0 6px; +.splash-prov-strip { + display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 9px; } -.splash-prov .provider-logo { opacity: 0.4; flex: none; } -.splash-prov-name { flex: 1; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -.splash-prov.active { color: #f4f1ec; } -.splash-prov.active .provider-logo { opacity: 1; animation: splash-prov-pulse 1.4s ease-in-out infinite; } -.splash-prov.done { color: #b3a595; } -.splash-prov.done .provider-logo { opacity: 0.85; } -.splash-prov-check { color: #7ec98f; font-size: 0.85em; flex: none; } -@keyframes splash-prov-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } } +.splash-prov { display: inline-flex; opacity: 0.28; transition: opacity 200ms ease; } +.splash-prov.done { opacity: 0.8; } +.splash-prov.active { opacity: 1; animation: splash-prov-pulse 1.4s ease-in-out infinite; } +.splash-status-note { font-size: var(--fs-label); color: #6f665c; } +@keyframes splash-prov-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } @media (prefers-reduced-motion: reduce) { .fm-flicker, .splash-lit .splash-mark, .splash-lit .splash-mark::before, .splash-lit .splash-word { animation: none; } .splash { transition: none; } .splash-status { animation: none; } - .splash-prov.active .provider-logo { animation: none; } + .splash-prov.active { animation: none; } }