feat(app): real provider logos in the picker and Settings
- add Claude/OpenAI/Cursor/Grok brand SVGs under app/renderer/assets/providers - ProviderLogo component: brand-orange Claude in both themes; light/dark monochrome variants for OpenAI/Cursor/Grok swapped per active theme - show the logo in the provider dropdown (trigger + menu items) and in Settings > Providers, left of each provider name typecheck clean; 128/128 tests pass.
1
app/renderer/assets/providers/claude.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="257" preserveAspectRatio="xMidYMid" viewBox="0 0 256 257"><path fill="#D97757" d="m50.228 170.321 50.357-28.257.843-2.463-.843-1.361h-2.462l-8.426-.518-28.775-.778-24.952-1.037-24.175-1.296-6.092-1.297L0 125.796l.583-3.759 5.12-3.434 7.324.648 16.202 1.101 24.304 1.685 17.629 1.037 26.118 2.722h4.148l.583-1.685-1.426-1.037-1.101-1.037-25.147-17.045-27.22-18.017-14.258-10.37-7.713-5.25-3.888-4.925-1.685-10.758 7-7.713 9.397.649 2.398.648 9.527 7.323 20.35 15.75L94.817 91.9l3.889 3.24 1.555-1.102.195-.777-1.75-2.917-14.453-26.118-15.425-26.572-6.87-11.018-1.814-6.61c-.648-2.723-1.102-4.991-1.102-7.778l7.972-10.823L71.42 0 82.05 1.426l4.472 3.888 6.61 15.101 10.694 23.786 16.591 32.34 4.861 9.592 2.592 8.879.973 2.722h1.685v-1.556l1.36-18.211 2.528-22.36 2.463-28.776.843-8.1 4.018-9.722 7.971-5.25 6.222 2.981 5.12 7.324-.713 4.73-3.046 19.768-5.962 30.98-3.889 20.739h2.268l2.593-2.593 10.499-13.934 17.628-22.036 7.778-8.749 9.073-9.657 5.833-4.601h11.018l8.1 12.055-3.628 12.443-11.342 14.388-9.398 12.184-13.48 18.147-8.426 14.518.778 1.166 2.01-.194 30.46-6.481 16.462-2.982 19.637-3.37 8.88 4.148.971 4.213-3.5 8.62-20.998 5.184-24.628 4.926-36.682 8.685-.454.324.519.648 16.526 1.555 7.065.389h17.304l32.21 2.398 8.426 5.574 5.055 6.805-.843 5.184-12.962 6.611-17.498-4.148-40.83-9.721-14-3.5h-1.944v1.167l11.666 11.406 21.387 19.314 26.767 24.887 1.36 6.157-3.434 4.86-3.63-.518-23.526-17.693-9.073-7.972-20.545-17.304h-1.36v1.814l4.73 6.935 25.017 37.59 1.296 11.536-1.814 3.76-6.481 2.268-7.13-1.297-14.647-20.544-15.1-23.138-12.185-20.739-1.49.843-7.194 77.448-3.37 3.953-7.778 2.981-6.48-4.925-3.436-7.972 3.435-15.749 4.148-20.544 3.37-16.333 3.046-20.285 1.815-6.74-.13-.454-1.49.194-15.295 20.999-23.267 31.433-18.406 19.702-4.407 1.75-7.648-3.954.713-7.064 4.277-6.286 25.47-32.405 15.36-20.092 9.917-11.6-.065-1.686h-.583L44.07 198.125l-12.055 1.555-5.185-4.86.648-7.972 2.463-2.593 20.35-13.999-.064.065Z"/></svg>
|
||||
|
After Width: | Height: | Size: 2 KiB |
12
app/renderer/assets/providers/cursor-dark.svg
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Ebene_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 466.73 532.09">
|
||||
<!-- Generator: Adobe Illustrator 29.6.1, SVG Export Plug-In . SVG Version: 2.1.1 Build 9) -->
|
||||
<defs>
|
||||
<style>
|
||||
.st0 {
|
||||
fill: #edecec;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path class="st0" d="M457.43,125.94L244.42,2.96c-6.84-3.95-15.28-3.95-22.12,0L9.3,125.94c-5.75,3.32-9.3,9.46-9.3,16.11v247.99c0,6.65,3.55,12.79,9.3,16.11l213.01,122.98c6.84,3.95,15.28,3.95,22.12,0l213.01-122.98c5.75-3.32,9.3-9.46,9.3-16.11v-247.99c0-6.65-3.55-12.79-9.3-16.11h-.01ZM444.05,151.99l-205.63,356.16c-1.39,2.4-5.06,1.42-5.06-1.36v-233.21c0-4.66-2.49-8.97-6.53-11.31L24.87,145.67c-2.4-1.39-1.42-5.06,1.36-5.06h411.26c5.84,0,9.49,6.33,6.57,11.39h-.01Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 793 B |
12
app/renderer/assets/providers/cursor-light.svg
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Ebene_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 466.73 532.09">
|
||||
<!-- Generator: Adobe Illustrator 29.6.1, SVG Export Plug-In . SVG Version: 2.1.1 Build 9) -->
|
||||
<defs>
|
||||
<style>
|
||||
.st0 {
|
||||
fill: #26251e;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path class="st0" d="M457.43,125.94L244.42,2.96c-6.84-3.95-15.28-3.95-22.12,0L9.3,125.94c-5.75,3.32-9.3,9.46-9.3,16.11v247.99c0,6.65,3.55,12.79,9.3,16.11l213.01,122.98c6.84,3.95,15.28,3.95,22.12,0l213.01-122.98c5.75-3.32,9.3-9.46,9.3-16.11v-247.99c0-6.65-3.55-12.79-9.3-16.11h-.01ZM444.05,151.99l-205.63,356.16c-1.39,2.4-5.06,1.42-5.06-1.36v-233.21c0-4.66-2.49-8.97-6.53-11.31L24.87,145.67c-2.4-1.39-1.42-5.06,1.36-5.06h411.26c5.84,0,9.49,6.33,6.57,11.39h-.01Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 793 B |
4
app/renderer/assets/providers/grok-dark.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M395.479 633.828L735.91 381.105C752.599 368.715 776.454 373.548 784.406 392.792C826.26 494.285 807.561 616.253 724.288 699.996C641.016 783.739 525.151 802.104 419.247 760.277L303.556 814.143C469.49 928.202 670.987 899.995 796.901 773.282C896.776 672.843 927.708 535.937 898.785 412.476L899.047 412.739C857.105 231.37 909.358 158.874 1016.4 10.6326C1018.93 7.11771 1021.47 3.60279 1024 0L883.144 141.651V141.212L395.392 633.916" fill="white"/>
|
||||
<path d="M325.226 695.251C206.128 580.84 226.662 403.776 328.285 301.668C403.431 226.097 526.549 195.254 634.026 240.596L749.454 186.994C728.657 171.88 702.007 155.623 671.424 144.2C533.19 86.9942 367.693 115.465 255.323 228.382C147.234 337.081 113.244 504.215 171.613 646.833C215.216 753.423 143.739 828.818 71.7385 904.916C46.2237 931.893 20.6216 958.87 0 987.429L325.139 695.339" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 961 B |
4
app/renderer/assets/providers/grok-light.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M395.479 633.828L735.91 381.105C752.599 368.715 776.454 373.548 784.406 392.792C826.26 494.285 807.561 616.253 724.288 699.996C641.016 783.739 525.151 802.104 419.247 760.277L303.556 814.143C469.49 928.202 670.987 899.995 796.901 773.282C896.776 672.843 927.708 535.937 898.785 412.476L899.047 412.739C857.105 231.37 909.358 158.874 1016.4 10.6326C1018.93 7.11771 1021.47 3.60279 1024 0L883.144 141.651V141.212L395.392 633.916" fill="#0A0A0A"/>
|
||||
<path d="M325.226 695.251C206.128 580.84 226.662 403.776 328.285 301.668C403.431 226.097 526.549 195.254 634.026 240.596L749.454 186.994C728.657 171.88 702.007 155.623 671.424 144.2C533.19 86.9942 367.693 115.465 255.323 228.382C147.234 337.081 113.244 504.215 171.613 646.833C215.216 753.423 143.739 828.818 71.7385 904.916C46.2237 931.893 20.6216 958.87 0 987.429L325.139 695.339" fill="#0A0A0A"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 965 B |
1
app/renderer/assets/providers/openai-dark.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="260" preserveAspectRatio="xMidYMid" viewBox="0 0 256 260"><path fill="#fff" d="M239.184 106.203a64.716 64.716 0 0 0-5.576-53.103C219.452 28.459 191 15.784 163.213 21.74A65.586 65.586 0 0 0 52.096 45.22a64.716 64.716 0 0 0-43.23 31.36c-14.31 24.602-11.061 55.634 8.033 76.74a64.665 64.665 0 0 0 5.525 53.102c14.174 24.65 42.644 37.324 70.446 31.36a64.72 64.72 0 0 0 48.754 21.744c28.481.025 53.714-18.361 62.414-45.481a64.767 64.767 0 0 0 43.229-31.36c14.137-24.558 10.875-55.423-8.083-76.483Zm-97.56 136.338a48.397 48.397 0 0 1-31.105-11.255l1.535-.87 51.67-29.825a8.595 8.595 0 0 0 4.247-7.367v-72.85l21.845 12.636c.218.111.37.32.409.563v60.367c-.056 26.818-21.783 48.545-48.601 48.601Zm-104.466-44.61a48.345 48.345 0 0 1-5.781-32.589l1.534.921 51.722 29.826a8.339 8.339 0 0 0 8.441 0l63.181-36.425v25.221a.87.87 0 0 1-.358.665l-52.335 30.184c-23.257 13.398-52.97 5.431-66.404-17.803ZM23.549 85.38a48.499 48.499 0 0 1 25.58-21.333v61.39a8.288 8.288 0 0 0 4.195 7.316l62.874 36.272-21.845 12.636a.819.819 0 0 1-.767 0L41.353 151.53c-23.211-13.454-31.171-43.144-17.804-66.405v.256Zm179.466 41.695-63.08-36.63L161.73 77.86a.819.819 0 0 1 .768 0l52.233 30.184a48.6 48.6 0 0 1-7.316 87.635v-61.391a8.544 8.544 0 0 0-4.4-7.213Zm21.742-32.69-1.535-.922-51.619-30.081a8.39 8.39 0 0 0-8.492 0L99.98 99.808V74.587a.716.716 0 0 1 .307-.665l52.233-30.133a48.652 48.652 0 0 1 72.236 50.391v.205ZM88.061 139.097l-21.845-12.585a.87.87 0 0 1-.41-.614V65.685a48.652 48.652 0 0 1 79.757-37.346l-1.535.87-51.67 29.825a8.595 8.595 0 0 0-4.246 7.367l-.051 72.697Zm11.868-25.58 28.138-16.217 28.188 16.218v32.434l-28.086 16.218-28.188-16.218-.052-32.434Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
1
app/renderer/assets/providers/openai-light.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="260" preserveAspectRatio="xMidYMid" viewBox="0 0 256 260"><path d="M239.184 106.203a64.716 64.716 0 0 0-5.576-53.103C219.452 28.459 191 15.784 163.213 21.74A65.586 65.586 0 0 0 52.096 45.22a64.716 64.716 0 0 0-43.23 31.36c-14.31 24.602-11.061 55.634 8.033 76.74a64.665 64.665 0 0 0 5.525 53.102c14.174 24.65 42.644 37.324 70.446 31.36a64.72 64.72 0 0 0 48.754 21.744c28.481.025 53.714-18.361 62.414-45.481a64.767 64.767 0 0 0 43.229-31.36c14.137-24.558 10.875-55.423-8.083-76.483Zm-97.56 136.338a48.397 48.397 0 0 1-31.105-11.255l1.535-.87 51.67-29.825a8.595 8.595 0 0 0 4.247-7.367v-72.85l21.845 12.636c.218.111.37.32.409.563v60.367c-.056 26.818-21.783 48.545-48.601 48.601Zm-104.466-44.61a48.345 48.345 0 0 1-5.781-32.589l1.534.921 51.722 29.826a8.339 8.339 0 0 0 8.441 0l63.181-36.425v25.221a.87.87 0 0 1-.358.665l-52.335 30.184c-23.257 13.398-52.97 5.431-66.404-17.803ZM23.549 85.38a48.499 48.499 0 0 1 25.58-21.333v61.39a8.288 8.288 0 0 0 4.195 7.316l62.874 36.272-21.845 12.636a.819.819 0 0 1-.767 0L41.353 151.53c-23.211-13.454-31.171-43.144-17.804-66.405v.256Zm179.466 41.695-63.08-36.63L161.73 77.86a.819.819 0 0 1 .768 0l52.233 30.184a48.6 48.6 0 0 1-7.316 87.635v-61.391a8.544 8.544 0 0 0-4.4-7.213Zm21.742-32.69-1.535-.922-51.619-30.081a8.39 8.39 0 0 0-8.492 0L99.98 99.808V74.587a.716.716 0 0 1 .307-.665l52.233-30.133a48.652 48.652 0 0 1 72.236 50.391v.205ZM88.061 139.097l-21.845-12.585a.87.87 0 0 1-.41-.614V65.685a48.652 48.652 0 0 1 79.757-37.346l-1.535.87-51.67 29.825a8.595 8.595 0 0 0-4.246 7.367l-.051 72.697Zm11.868-25.58 28.138-16.217 28.188 16.218v32.434l-28.086 16.218-28.188-16.218-.052-32.434Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
27
app/renderer/components/ProviderLogo.tsx
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import claude from '../assets/providers/claude.svg'
|
||||
import cursorDark from '../assets/providers/cursor-dark.svg'
|
||||
import cursorLight from '../assets/providers/cursor-light.svg'
|
||||
import grokDark from '../assets/providers/grok-dark.svg'
|
||||
import grokLight from '../assets/providers/grok-light.svg'
|
||||
import openaiDark from '../assets/providers/openai-dark.svg'
|
||||
import openaiLight from '../assets/providers/openai-light.svg'
|
||||
|
||||
const THEMED_LOGOS: Record<string, { light: string; dark: string }> = {
|
||||
codex: { light: openaiLight, dark: openaiDark },
|
||||
cursor: { light: cursorLight, dark: cursorDark },
|
||||
grok: { light: grokLight, dark: grokDark },
|
||||
}
|
||||
|
||||
export function ProviderLogo({ provider, size = 16 }: { provider: string; size?: number }) {
|
||||
if (provider === 'claude') {
|
||||
return <img src={claude} width={size} height={size} alt="" aria-hidden className="provider-logo" />
|
||||
}
|
||||
|
||||
const logos = THEMED_LOGOS[provider]
|
||||
if (!logos) return null
|
||||
|
||||
return <>
|
||||
<img src={logos.light} width={size} height={size} alt="" aria-hidden className="provider-logo pl-light" />
|
||||
<img src={logos.dark} width={size} height={size} alt="" aria-hidden className="provider-logo pl-dark" />
|
||||
</>
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
import { useEffect, useRef, useState } from 'react'
|
||||
|
||||
import { ProviderLogo } from './ProviderLogo'
|
||||
|
||||
export type ProviderOption = { value: string; label: string }
|
||||
|
||||
/** Provider selector populated from providers detected in the CLI payload. */
|
||||
|
|
@ -49,7 +51,7 @@ export function ProviderPop({
|
|||
}
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
<ProviderLogo provider={value} />{label}
|
||||
</div>
|
||||
{open && (
|
||||
<div className="pop-menu" role="listbox" aria-label="Providers">
|
||||
|
|
@ -72,7 +74,7 @@ export function ProviderPop({
|
|||
}
|
||||
}}
|
||||
>
|
||||
{option.label}
|
||||
<ProviderLogo provider={option.value} />{option.label}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { useEffect, useState } from 'react'
|
|||
import { Hint } from '../components/Hint'
|
||||
import { CliErrorText, cliErrorDisplay } from '../components/CliErrorPanel'
|
||||
import { Panel } from '../components/Panel'
|
||||
import { ProviderLogo } from '../components/ProviderLogo'
|
||||
import type { Section } from '../components/Sidebar'
|
||||
import { usePolled } from '../hooks/usePolled'
|
||||
import { formatUsd } from '../lib/format'
|
||||
|
|
@ -142,7 +143,7 @@ function ProvidersPane({ period, refreshToken }: { period: Period; refreshToken:
|
|||
const providers = Object.entries(overview.data?.current.providers ?? {})
|
||||
return <section className="set-p on">
|
||||
<div><h3 className="set-h">Providers</h3><p className="set-sub">codeburn auto-detects coding tools from local session files — no setup needed.</p></div>
|
||||
{overview.error ? <SettingsErrorText error={overview.error} /> : !overview.data ? <p className="set-cap">Loading detected providers…</p> : providers.length === 0 ? <p className="set-cap">No providers detected.</p> : providers.map(([name, cost]) => <div className="card" key={name}><div className="set-prov-head"><span className="set-prov-name">{name.charAt(0).toUpperCase() + name.slice(1)}</span><span className="set-status"><span className="set-dot ok" />Detected · {formatUsd(cost)}</span></div></div>)}
|
||||
{overview.error ? <SettingsErrorText error={overview.error} /> : !overview.data ? <p className="set-cap">Loading detected providers…</p> : providers.length === 0 ? <p className="set-cap">No providers detected.</p> : providers.map(([name, cost]) => <div className="card" key={name}><div className="set-prov-head"><ProviderLogo provider={name} /><span className="set-prov-name">{name.charAt(0).toUpperCase() + name.slice(1)}</span><span className="set-status"><span className="set-dot ok" />Detected · {formatUsd(cost)}</span></div></div>)}
|
||||
</section>
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,9 @@
|
|||
--hair2: var(--line2);
|
||||
}
|
||||
|
||||
.provider-logo { display: inline-flex; object-fit: contain; flex: 0 0 auto; border-radius: 4px; }
|
||||
.pl-dark { display: none; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--canvas: #0e1013;
|
||||
|
|
@ -90,6 +93,8 @@
|
|||
--tip-bg: #eef0f3;
|
||||
--tip-ink: #15181d;
|
||||
}
|
||||
.pl-light { display: none; }
|
||||
.pl-dark { display: inline-flex; }
|
||||
}
|
||||
|
||||
:root[data-theme='dark'] {
|
||||
|
|
@ -106,6 +111,8 @@
|
|||
--fill: #20242b; --hover: #20242b;
|
||||
--tip-bg: #eef0f3; --tip-ink: #15181d;
|
||||
}
|
||||
:root[data-theme='dark'] .pl-light { display: none; }
|
||||
:root[data-theme='dark'] .pl-dark { display: inline-flex; }
|
||||
|
||||
:root[data-theme='light'] {
|
||||
color-scheme: light;
|
||||
|
|
@ -121,6 +128,8 @@
|
|||
--fill: #f1f2f4; --hover: #f2f3f5;
|
||||
--tip-bg: #1a1d23; --tip-ink: #ffffff;
|
||||
}
|
||||
:root[data-theme='light'] .pl-light { display: inline-flex; }
|
||||
:root[data-theme='light'] .pl-dark { display: none; }
|
||||
|
||||
html, body, #root { height: 100%; margin: 0; }
|
||||
body { overflow: hidden; background: var(--bg); color: var(--ink); }
|
||||
|
|
@ -174,7 +183,7 @@ body { overflow: hidden; background: var(--bg); color: var(--ink); }
|
|||
background: var(--panel); border: 1px solid var(--line); border-radius: 8px;
|
||||
box-shadow: 0 8px 24px rgba(0,0,0,.14); z-index: 20;
|
||||
}
|
||||
.pop-item { padding: 6px 11px; font-size: 12px; border-radius: 6px; cursor: pointer; color: var(--t1); white-space: nowrap; }
|
||||
.pop-item { display: flex; align-items: center; gap: 6px; padding: 6px 11px; font-size: 12px; border-radius: 6px; cursor: pointer; color: var(--t1); white-space: nowrap; }
|
||||
.pop-item:hover, .pop-item:focus-visible { background: var(--hover); outline: none; }
|
||||
.pop-item.on { color: var(--accent); font-weight: 560; }
|
||||
.calendar-trigger {
|
||||
|
|
|
|||