mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-08-02 21:04:57 +00:00
Merge pull request #696 from getagentseal/fix/splash-compact
polish: compact first-run splash progress
This commit is contained in:
commit
7a91a86dcb
3 changed files with 57 additions and 55 deletions
|
|
@ -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(<Splash hasData={false} hasError={false} />)
|
||||
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)', () => {
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className="splash-status">
|
||||
<div className="splash-status-line">{line}</div>
|
||||
{progress.order.length > 0 && (
|
||||
<div className="splash-prov-strip">
|
||||
{progress.order.map(id => (
|
||||
<span key={id} className={`splash-prov ${progress.status[id] ?? 'pending'}`} title={providerLabel(id)}>
|
||||
<ProviderLogo provider={id} size={15} />
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="splash-status-note">One-time scan · future launches are instant</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<Phase>('lit')
|
||||
|
|
@ -140,31 +169,7 @@ export function Splash({ hasData, hasError }: { hasData: boolean; hasError: bool
|
|||
)}
|
||||
<div className="splash-word">CodeBurn</div>
|
||||
<div className="splash-version">v{version}</div>
|
||||
{showDetail && (
|
||||
<div className="splash-status">
|
||||
<div className="splash-status-line">
|
||||
First run: indexing your usage history. This one-time scan can take a few minutes; future launches are instant.
|
||||
</div>
|
||||
{progress.order.length > 0 && (
|
||||
<ul className="splash-providers">
|
||||
{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 (
|
||||
<li key={id} className={`splash-prov ${status}`}>
|
||||
<ProviderLogo provider={id} size={16} />
|
||||
<span className="splash-prov-name">{text}</span>
|
||||
{status === 'done' && <span className="splash-prov-check">✓</span>}
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{showDetail && <SplashStatus progress={progress} />}
|
||||
</div>,
|
||||
document.body,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue