Add AI provider and model info support across components

This commit is contained in:
Илья Глазунов 2026-01-15 04:23:27 +03:00
parent 0011bacfa3
commit 501dadd265
3 changed files with 153 additions and 13 deletions

View File

@ -186,6 +186,82 @@ export class AppHeader extends LitElement {
.status-tooltip .tooltip-content {
color: #f14c4c;
}
.model-info {
display: flex;
gap: 6px;
align-items: center;
}
.model-badge {
font-size: 10px;
color: var(--text-muted);
background: var(--key-background);
padding: 2px 6px;
border-radius: 3px;
font-family: 'SF Mono', Monaco, monospace;
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.model-badge-wrapper {
position: relative;
display: inline-flex;
}
.model-badge-wrapper .model-tooltip {
position: absolute;
top: 100%;
right: 0;
margin-top: 8px;
background: var(--tooltip-bg, #1a1a1a);
color: var(--tooltip-text, #ffffff);
padding: 10px 14px;
border-radius: 6px;
font-size: 12px;
white-space: nowrap;
opacity: 0;
visibility: hidden;
transition: opacity 0.15s ease, visibility 0.15s ease;
pointer-events: none;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
z-index: 1000;
}
.model-badge-wrapper .model-tooltip::before {
content: '';
position: absolute;
bottom: 100%;
right: 16px;
border: 6px solid transparent;
border-bottom-color: var(--tooltip-bg, #1a1a1a);
}
.model-badge-wrapper:hover .model-tooltip {
opacity: 1;
visibility: visible;
}
.model-tooltip-row {
display: flex;
justify-content: space-between;
gap: 16px;
margin-bottom: 4px;
}
.model-tooltip-row:last-child {
margin-bottom: 0;
}
.model-tooltip-label {
opacity: 0.7;
}
.model-tooltip-value {
font-family: 'SF Mono', Monaco, monospace;
}
`;
static properties = {
@ -200,6 +276,8 @@ export class AppHeader extends LitElement {
onHideToggleClick: { type: Function },
isClickThrough: { type: Boolean, reflect: true },
updateAvailable: { type: Boolean },
aiProvider: { type: String },
modelInfo: { type: Object },
};
constructor() {
@ -216,6 +294,8 @@ export class AppHeader extends LitElement {
this.isClickThrough = false;
this.updateAvailable = false;
this._timerInterval = null;
this.aiProvider = 'gemini';
this.modelInfo = { model: '', visionModel: '', whisperModel: '' };
}
connectedCallback() {
@ -337,6 +417,45 @@ export class AppHeader extends LitElement {
return navigationViews.includes(this.currentView);
}
getProviderDisplayName() {
const names = {
'gemini': 'Gemini',
'openai-realtime': 'OpenAI Realtime',
'openai-sdk': 'OpenAI SDK'
};
return names[this.aiProvider] || this.aiProvider;
}
renderModelInfo() {
// Only show model info for OpenAI SDK provider
if (this.aiProvider !== 'openai-sdk' || !this.modelInfo) {
return '';
}
const { model, visionModel, whisperModel } = this.modelInfo;
// Show a compact badge with tooltip for model details
return html`
<div class="model-badge-wrapper">
<span class="model-badge" title="Models">${model || 'gpt-4o'}</span>
<div class="model-tooltip">
<div class="model-tooltip-row">
<span class="model-tooltip-label">Text</span>
<span class="model-tooltip-value">${model || 'gpt-4o'}</span>
</div>
<div class="model-tooltip-row">
<span class="model-tooltip-label">Vision</span>
<span class="model-tooltip-value">${visionModel || 'gpt-4o'}</span>
</div>
<div class="model-tooltip-row">
<span class="model-tooltip-label">Speech</span>
<span class="model-tooltip-value">${whisperModel || 'whisper-1'}</span>
</div>
</div>
</div>
`;
}
render() {
const elapsedTime = this.getElapsedTime();
const isError = this.statusText && (this.statusText.toLowerCase().includes('error') || this.statusText.toLowerCase().includes('failed'));
@ -348,6 +467,7 @@ export class AppHeader extends LitElement {
<div class="header-actions">
${this.currentView === 'assistant'
? html`
${this.renderModelInfo()}
<span>${elapsedTime}</span>
<div class="status-wrapper">
<span class="status-text ${isError ? 'error' : ''}">${shortStatus}</span>

View File

@ -110,6 +110,8 @@ export class CheatingDaddyApp extends LitElement {
_awaitingNewResponse: { state: true },
shouldAnimateResponse: { type: Boolean },
_storageLoaded: { state: true },
aiProvider: { type: String },
modelInfo: { type: Object },
};
constructor() {
@ -133,6 +135,8 @@ export class CheatingDaddyApp extends LitElement {
this._currentResponseIsComplete = true;
this.shouldAnimateResponse = false;
this._storageLoaded = false;
this.aiProvider = 'gemini';
this.modelInfo = { model: '', visionModel: '', whisperModel: '' };
// Load from storage
this._loadFromStorage();
@ -140,9 +144,10 @@ export class CheatingDaddyApp extends LitElement {
async _loadFromStorage() {
try {
const [config, prefs] = await Promise.all([
const [config, prefs, openaiSdkCreds] = await Promise.all([
cheatingDaddy.storage.getConfig(),
cheatingDaddy.storage.getPreferences()
cheatingDaddy.storage.getPreferences(),
cheatingDaddy.storage.getOpenAISDKCredentials()
]);
// Check onboarding status
@ -161,6 +166,14 @@ export class CheatingDaddyApp extends LitElement {
this.selectedImageQuality = prefs.selectedImageQuality || 'medium';
this.layoutMode = config.layout || 'normal';
// Load AI provider and model info
this.aiProvider = prefs.aiProvider || 'gemini';
this.modelInfo = {
model: openaiSdkCreds.model || 'gpt-4o',
visionModel: openaiSdkCreds.visionModel || 'gpt-4o',
whisperModel: openaiSdkCreds.whisperModel || 'whisper-1'
};
this._storageLoaded = true;
this.updateLayoutMode();
this.requestUpdate();
@ -487,6 +500,7 @@ export class CheatingDaddyApp extends LitElement {
.responses=${this.responses}
.currentResponseIndex=${this.currentResponseIndex}
.selectedProfile=${this.selectedProfile}
.aiProvider=${this.aiProvider}
.onSendText=${message => this.handleSendText(message)}
.shouldAnimateResponse=${this.shouldAnimateResponse}
@response-index-changed=${this.handleResponseIndexChanged}
@ -521,6 +535,8 @@ export class CheatingDaddyApp extends LitElement {
.currentView=${this.currentView}
.statusText=${this.statusText}
.startTime=${this.startTime}
.aiProvider=${this.aiProvider}
.modelInfo=${this.modelInfo}
.onCustomizeClick=${() => this.handleCustomizeClick()}
.onHelpClick=${() => this.handleHelpClick()}
.onHistoryClick=${() => this.handleHistoryClick()}

View File

@ -358,6 +358,7 @@ export class AssistantView extends LitElement {
shouldAnimateResponse: { type: Boolean },
flashCount: { type: Number },
flashLiteCount: { type: Number },
aiProvider: { type: String },
};
constructor() {
@ -368,6 +369,7 @@ export class AssistantView extends LitElement {
this.onSendText = () => {};
this.flashCount = 0;
this.flashLiteCount = 0;
this.aiProvider = 'gemini';
}
getProfileNames() {
@ -658,23 +660,25 @@ export class AssistantView extends LitElement {
<span>Select region</span>
</button>
<div class="screen-answer-btn-wrapper">
<div class="tooltip">
<div class="tooltip-row">
<span class="tooltip-label">Flash</span>
<span class="tooltip-value">${this.flashCount}/20</span>
${this.aiProvider === 'gemini' ? html`
<div class="tooltip">
<div class="tooltip-row">
<span class="tooltip-label">Flash</span>
<span class="tooltip-value">${this.flashCount}/20</span>
</div>
<div class="tooltip-row">
<span class="tooltip-label">Flash Lite</span>
<span class="tooltip-value">${this.flashLiteCount}/20</span>
</div>
<div class="tooltip-note">Resets every 24 hours</div>
</div>
<div class="tooltip-row">
<span class="tooltip-label">Flash Lite</span>
<span class="tooltip-value">${this.flashLiteCount}/20</span>
</div>
<div class="tooltip-note">Resets every 24 hours</div>
</div>
` : ''}
<button class="screen-answer-btn" @click=${this.handleScreenAnswer}>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path d="M15.98 1.804a1 1 0 0 0-1.96 0l-.24 1.192a1 1 0 0 1-.784.785l-1.192.238a1 1 0 0 0 0 1.962l1.192.238a1 1 0 0 1 .785.785l.238 1.192a1 1 0 0 0 1.962 0l.238-1.192a1 1 0 0 1 .785-.785l1.192-.238a1 1 0 0 0 0-1.962l-1.192-.238a1 1 0 0 1-.785-.785l-.238-1.192ZM6.949 5.684a1 1 0 0 0-1.898 0l-.683 2.051a1 1 0 0 1-.633.633l-2.051.683a1 1 0 0 0 0 1.898l2.051.684a1 1 0 0 1 .633.632l.683 2.051a1 1 0 0 0 1.898 0l.683-2.051a1 1 0 0 1 .633-.633l2.051-.683a1 1 0 0 0 0-1.898l-2.051-.683a1 1 0 0 1-.633-.633L6.95 5.684ZM13.949 13.684a1 1 0 0 0-1.898 0l-.184.551a1 1 0 0 1-.632.633l-.551.183a1 1 0 0 0 0 1.898l.551.183a1 1 0 0 1 .633.633l.183.551a1 1 0 0 0 1.898 0l.184-.551a1 1 0 0 1 .632-.633l.551-.183a1 1 0 0 0 0-1.898l-.551-.184a1 1 0 0 1-.633-.632l-.183-.551Z" />
</svg>
<span>Full screen</span>
<span class="usage-count">(${this.getTotalUsed()}/${this.getTotalAvailable()})</span>
${this.aiProvider === 'gemini' ? html`<span class="usage-count">(${this.getTotalUsed()}/${this.getTotalAvailable()})</span>` : ''}
</button>
</div>
</div>