mirror of
https://github.com/asyouplz/SpeechNote.git
synced 2026-07-22 06:43:33 +00:00
fix: address reviewbot required issues (#62)
* fix: address reviewbot feedback and update tests Align request handling with Obsidian requestUrl and clean up UI text/lint directives while updating tests for the new mocks. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * chore: ignore release_log.txt Add release_log.txt to gitignore to keep local release logs out of version control. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix: align requesturl handling with review feedback centralize requesturl availability errors, bind provider defaults once, and stabilize timeout test. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix: address reviewbot required issues Normalize UI sentence case and tighten error/string handling to satisfy Obsidian reviewbot checks. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix(ui): restore brand casing in settings Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
e7e0e6b2cc
commit
a87dc8f9d9
18 changed files with 99 additions and 77 deletions
|
|
@ -282,10 +282,22 @@ export class SettingsValidator {
|
|||
typeof audio.sampleRate !== 'number' ||
|
||||
!validRates.includes(audio.sampleRate)
|
||||
) {
|
||||
const sampleRateLabel =
|
||||
typeof audio.sampleRate === 'number'
|
||||
? audio.sampleRate
|
||||
: JSON.stringify(audio.sampleRate) ?? String(audio.sampleRate);
|
||||
const sampleRateLabel = (() => {
|
||||
if (typeof audio.sampleRate === 'number') {
|
||||
return String(audio.sampleRate);
|
||||
}
|
||||
if (typeof audio.sampleRate === 'string') {
|
||||
return audio.sampleRate;
|
||||
}
|
||||
if (audio.sampleRate !== null && typeof audio.sampleRate === 'object') {
|
||||
try {
|
||||
return JSON.stringify(audio.sampleRate);
|
||||
} catch {
|
||||
return '[unserializable]';
|
||||
}
|
||||
}
|
||||
return String(audio.sampleRate);
|
||||
})();
|
||||
errors.push({
|
||||
field: 'audio.sampleRate',
|
||||
message: `Invalid sample rate: ${sampleRateLabel}`,
|
||||
|
|
|
|||
|
|
@ -774,7 +774,7 @@ export class DeepgramService {
|
|||
json?: unknown;
|
||||
text?: unknown;
|
||||
headers?: Record<string, string>;
|
||||
}): never {
|
||||
}): Error {
|
||||
const errorBody = isPlainRecord(response.json) ? response.json : undefined;
|
||||
const rawText = typeof response.text === 'string' ? response.text : undefined;
|
||||
const messageFromBody =
|
||||
|
|
@ -792,7 +792,7 @@ export class DeepgramService {
|
|||
|
||||
switch (response.status) {
|
||||
case 400:
|
||||
throw new TranscriptionError(
|
||||
return new TranscriptionError(
|
||||
errorMessage || 'Invalid request',
|
||||
'BAD_REQUEST',
|
||||
'deepgram',
|
||||
|
|
@ -800,9 +800,9 @@ export class DeepgramService {
|
|||
400
|
||||
);
|
||||
case 401:
|
||||
throw new ProviderAuthenticationError('deepgram');
|
||||
return new ProviderAuthenticationError('deepgram');
|
||||
case 402:
|
||||
throw new TranscriptionError(
|
||||
return new TranscriptionError(
|
||||
'Insufficient credits',
|
||||
'INSUFFICIENT_CREDITS',
|
||||
'deepgram',
|
||||
|
|
@ -811,7 +811,7 @@ export class DeepgramService {
|
|||
);
|
||||
case 429: {
|
||||
const retryAfter = response.headers?.['retry-after'];
|
||||
throw new ProviderRateLimitError(
|
||||
return new ProviderRateLimitError(
|
||||
'deepgram',
|
||||
retryAfter ? parseInt(retryAfter) : undefined
|
||||
);
|
||||
|
|
@ -819,13 +819,13 @@ export class DeepgramService {
|
|||
case 500:
|
||||
case 502:
|
||||
case 503:
|
||||
throw new ProviderUnavailableError('deepgram');
|
||||
return new ProviderUnavailableError('deepgram');
|
||||
case 504: {
|
||||
// Extract file size info if available
|
||||
const sizeInfo = this.lastAudioSize
|
||||
? ` (${Math.round(this.lastAudioSize / (1024 * 1024))}MB)`
|
||||
: '';
|
||||
throw new TranscriptionError(
|
||||
return new TranscriptionError(
|
||||
`Server timeout processing large audio file${sizeInfo}. This often happens with very large files (>50MB). Try: 1) Breaking the file into smaller chunks (recommended: <50MB per chunk), 2) Reducing audio quality/bitrate to 64-128 kbps, 3) Using the 'enhanced' model which may be faster, or 4) Converting to a more efficient format like MP3 or OGG.`,
|
||||
'SERVER_TIMEOUT',
|
||||
'deepgram',
|
||||
|
|
@ -834,7 +834,7 @@ export class DeepgramService {
|
|||
);
|
||||
}
|
||||
default:
|
||||
throw new TranscriptionError(
|
||||
return new TranscriptionError(
|
||||
`API error: ${errorMessage}`,
|
||||
'UNKNOWN_ERROR',
|
||||
'deepgram',
|
||||
|
|
|
|||
|
|
@ -341,7 +341,7 @@ export class DeepgramServiceRefactored {
|
|||
status: number;
|
||||
json?: unknown;
|
||||
headers?: Record<string, string>;
|
||||
}): never {
|
||||
}): Error {
|
||||
const errorBody = isPlainRecord(response.json) ? response.json : undefined;
|
||||
const errorMessage =
|
||||
(typeof errorBody?.message === 'string' ? errorBody.message : undefined) ??
|
||||
|
|
@ -378,10 +378,10 @@ export class DeepgramServiceRefactored {
|
|||
|
||||
const errorFactory = errorMap[response.status];
|
||||
if (errorFactory) {
|
||||
throw errorFactory();
|
||||
return errorFactory();
|
||||
}
|
||||
|
||||
throw new TranscriptionError(
|
||||
return new TranscriptionError(
|
||||
`API error: ${errorMessage}`,
|
||||
'UNKNOWN_ERROR',
|
||||
'deepgram',
|
||||
|
|
|
|||
|
|
@ -65,24 +65,22 @@ export function createSingleton<T>(factory: () => T): () => T {
|
|||
/**
|
||||
* 클래스 데코레이터를 사용한 Singleton 패턴
|
||||
*/
|
||||
export function SingletonDecorator<
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- required by TS mixin constructor typing
|
||||
T extends new (...args: any[]) => object
|
||||
>(constructor: T): T {
|
||||
let instance: InstanceType<T> | undefined;
|
||||
export const SingletonDecorator: ClassDecorator = (constructor) => {
|
||||
let instance: object | undefined;
|
||||
|
||||
return class extends constructor {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- required by TS mixin constructor typing
|
||||
constructor(...args: any[]) {
|
||||
const proxy = new Proxy(constructor, {
|
||||
construct(target, args, newTarget) {
|
||||
if (instance) {
|
||||
return instance;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument -- required by TS mixin constructor typing
|
||||
super(...args);
|
||||
instance = this as InstanceType<T>;
|
||||
}
|
||||
} as T;
|
||||
}
|
||||
const constructed = Reflect.construct(target, args, newTarget) as object;
|
||||
instance = constructed;
|
||||
return constructed;
|
||||
},
|
||||
});
|
||||
|
||||
return proxy;
|
||||
};
|
||||
|
||||
/**
|
||||
* 비동기 Singleton 팩토리
|
||||
|
|
|
|||
|
|
@ -268,7 +268,7 @@ export class FormatOptionsModal extends Modal {
|
|||
.setName('Language')
|
||||
.setDesc('Programming language for syntax highlighting')
|
||||
.addText((text) => {
|
||||
text.setPlaceholder('JavaScript, Python, etc.')
|
||||
text.setPlaceholder('JavaScript, python, etc.')
|
||||
.setValue(this.options.codeLanguage || '')
|
||||
.onChange((value) => {
|
||||
this.options.codeLanguage = value;
|
||||
|
|
|
|||
|
|
@ -261,6 +261,18 @@ export class MessageStore {
|
|||
this.messages[key] = translations;
|
||||
}
|
||||
|
||||
private static formatParamValue(value: unknown): string {
|
||||
if (value === null) return 'null';
|
||||
if (typeof value === 'object') {
|
||||
try {
|
||||
return JSON.stringify(value);
|
||||
} catch {
|
||||
return '[unserializable]';
|
||||
}
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 메시지 가져오기
|
||||
*/
|
||||
|
|
@ -272,7 +284,7 @@ export class MessageStore {
|
|||
// 파라미터 치환
|
||||
return message.replace(/\{(\w+)\}/g, (match: string, param: string) => {
|
||||
const value = params[param];
|
||||
return value !== undefined ? String(value) : match;
|
||||
return value !== undefined ? this.formatParamValue(value) : match;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -495,7 +495,7 @@ export class EnhancedSettingsTab extends PluginSettingTab {
|
|||
new Notice(`❌ ${error}`);
|
||||
}
|
||||
} catch (error) {
|
||||
new Notice('❌ Failed to validate API key.');
|
||||
new Notice('❌ failed to validate API key.');
|
||||
console.error(error);
|
||||
} finally {
|
||||
validateBtn.disabled = false;
|
||||
|
|
@ -509,7 +509,7 @@ export class EnhancedSettingsTab extends PluginSettingTab {
|
|||
if (api.provider === 'openai') {
|
||||
new Setting(section)
|
||||
.setName('Model')
|
||||
.setDesc('Select the openai model')
|
||||
.setDesc('Select the OpenAI model')
|
||||
.addDropdown((dropdown) => {
|
||||
dropdown.addOption('whisper-1', 'Whisper v1');
|
||||
dropdown.setValue(api.model);
|
||||
|
|
@ -586,7 +586,7 @@ export class EnhancedSettingsTab extends PluginSettingTab {
|
|||
.addOption('webm', 'WebM (recommended)')
|
||||
.addOption('mp3', 'MP3')
|
||||
.addOption('m4a', 'M4A')
|
||||
.addOption('wav', 'WAV (lossless)');
|
||||
.addOption('wav', 'Wav (lossless)');
|
||||
|
||||
dropdown.setValue(audio.format);
|
||||
dropdown.onChange(async (value) => {
|
||||
|
|
@ -721,7 +721,7 @@ export class EnhancedSettingsTab extends PluginSettingTab {
|
|||
});
|
||||
|
||||
new Setting(cacheSection)
|
||||
.setName('Cache TTL (days)')
|
||||
.setName('Cache ttl (days)')
|
||||
.setDesc('Cache time-to-live in days')
|
||||
.addSlider((slider) => {
|
||||
slider
|
||||
|
|
@ -919,7 +919,7 @@ export class EnhancedSettingsTab extends PluginSettingTab {
|
|||
const links = section.createDiv({ cls: 'about-links' });
|
||||
|
||||
const githubLink = links.createEl('a', {
|
||||
text: '📖 Documentation and guides',
|
||||
text: '📖 documentation and guides',
|
||||
href: 'https://github.com/yourusername/obsidian-speech-to-text',
|
||||
});
|
||||
githubLink.setAttribute('target', '_blank');
|
||||
|
|
@ -927,7 +927,7 @@ export class EnhancedSettingsTab extends PluginSettingTab {
|
|||
links.createEl('br');
|
||||
|
||||
const issueLink = links.createEl('a', {
|
||||
text: '🐛 Report an issue',
|
||||
text: '🐛 report an issue',
|
||||
href: 'https://github.com/yourusername/obsidian-speech-to-text/issues',
|
||||
});
|
||||
issueLink.setAttribute('target', '_blank');
|
||||
|
|
|
|||
|
|
@ -262,7 +262,7 @@ export class SettingsTab extends PluginSettingTab {
|
|||
.addOption('performance_optimized', 'Performance optimized')
|
||||
.addOption('quality_optimized', 'Quality optimized')
|
||||
.addOption('round_robin', 'Round robin')
|
||||
.addOption('ab_test', 'A/B testing')
|
||||
.addOption('ab_test', 'A/b testing')
|
||||
.setValue(
|
||||
this.plugin.settings.selectionStrategy ||
|
||||
SelectionStrategy.PERFORMANCE_OPTIMIZED
|
||||
|
|
@ -368,7 +368,7 @@ export class SettingsTab extends PluginSettingTab {
|
|||
.setName('OpenAI API key')
|
||||
.setDesc('Enter your OpenAI API key for Whisper transcription')
|
||||
.addText((text) => {
|
||||
text.setPlaceholder('sk-...')
|
||||
text.setPlaceholder('Sk-...')
|
||||
.setValue(this.maskApiKey(this.plugin.settings.apiKey || ''))
|
||||
.onChange(async (value) => {
|
||||
if (value && !value.includes('*')) {
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export class SimpleSettingsTab extends PluginSettingTab {
|
|||
.setDesc('Enter your OpenAI API key for Whisper')
|
||||
.addText((text) =>
|
||||
text
|
||||
.setPlaceholder('sk-...')
|
||||
.setPlaceholder('Sk-...')
|
||||
.setValue(this.plugin.settings.apiKey || '')
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.apiKey = value;
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ export class ApiKeyValidator {
|
|||
);
|
||||
|
||||
if (!hasWhisper) {
|
||||
new Notice('⚠ API 키는 유효하지만 Whisper 모델 접근 권한이 없을 수 있습니다');
|
||||
new Notice('⚠ API 키는 유효하지만 whisper 모델 접근 권한이 없을 수 있습니다');
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export class AudioSettings {
|
|||
// Whisper 모델 선택
|
||||
new Setting(containerEl)
|
||||
.setName('Whisper 모델')
|
||||
.setDesc('사용할 Whisper 모델을 선택하세요')
|
||||
.setDesc('사용할 whisper 모델을 선택하세요')
|
||||
.addDropdown((dropdown) =>
|
||||
dropdown
|
||||
.addOption('whisper-1', 'Whisper-1 (기본)')
|
||||
|
|
@ -61,8 +61,8 @@ export class AudioSettings {
|
|||
.addOption('json', 'JSON (구조화된 데이터)')
|
||||
.addOption('text', 'Text (일반 텍스트)')
|
||||
.addOption('verbose_json', 'Verbose JSON (상세 정보)')
|
||||
.addOption('srt', 'SRT (자막 형식)')
|
||||
.addOption('vtt', 'VTT (WebVTT 형식)')
|
||||
.addOption('srt', 'Srt (자막 형식)')
|
||||
.addOption('vtt', 'Vtt (webvtt 형식)')
|
||||
.setValue(
|
||||
typeof this.plugin.settings['responseFormat'] === 'string'
|
||||
? this.plugin.settings['responseFormat']
|
||||
|
|
@ -111,7 +111,7 @@ export class AudioSettings {
|
|||
// 파일 크기 제한
|
||||
const fileSizeSetting = new Setting(containerEl)
|
||||
.setName('최대 파일 크기')
|
||||
.setDesc('업로드 가능한 최대 파일 크기 (MB)');
|
||||
.setDesc('업로드 가능한 최대 파일 크기 (mb)');
|
||||
|
||||
const sizeValue = containerEl.createDiv({ cls: 'filesize-value' });
|
||||
const currentSize = Math.round(this.plugin.settings.maxFileSize / (1024 * 1024));
|
||||
|
|
|
|||
|
|
@ -614,7 +614,7 @@ export class DeepgramSettings {
|
|||
});
|
||||
|
||||
noteEl.createEl('p', {
|
||||
text: 'For best results with very large files (>100MB), consider:',
|
||||
text: 'For best results with very large files (>100mb), consider:',
|
||||
});
|
||||
const secondaryList = noteEl.createEl('ul');
|
||||
[
|
||||
|
|
|
|||
|
|
@ -219,11 +219,11 @@ export class ProviderSettings {
|
|||
.setDesc('How should the system choose between providers?')
|
||||
.addDropdown((dropdown) => {
|
||||
dropdown
|
||||
.addOption(SelectionStrategy.COST_OPTIMIZED, '💰 Cost optimized')
|
||||
.addOption(SelectionStrategy.PERFORMANCE_OPTIMIZED, '⚡ Performance optimized')
|
||||
.addOption(SelectionStrategy.QUALITY_OPTIMIZED, '✨ Quality optimized')
|
||||
.addOption(SelectionStrategy.ROUND_ROBIN, '🔄 Round robin')
|
||||
.addOption(SelectionStrategy.AB_TEST, '🧪 A/B testing')
|
||||
.addOption(SelectionStrategy.COST_OPTIMIZED, '💰 cost optimized')
|
||||
.addOption(SelectionStrategy.PERFORMANCE_OPTIMIZED, '⚡ performance optimized')
|
||||
.addOption(SelectionStrategy.QUALITY_OPTIMIZED, '✨ quality optimized')
|
||||
.addOption(SelectionStrategy.ROUND_ROBIN, '🔄 round robin')
|
||||
.addOption(SelectionStrategy.AB_TEST, '🧪 a/b testing')
|
||||
.setValue(
|
||||
this.plugin.settings.selectionStrategy ||
|
||||
SelectionStrategy.PERFORMANCE_OPTIMIZED
|
||||
|
|
@ -349,7 +349,7 @@ export class ProviderSettings {
|
|||
private renderMetrics(containerEl: HTMLElement): void {
|
||||
const metricsEl = containerEl.createDiv({ cls: 'metrics-container' });
|
||||
|
||||
metricsEl.createEl('h4', { text: '📊 Performance metrics' });
|
||||
metricsEl.createEl('h4', { text: '📊 performance metrics' });
|
||||
|
||||
// 각 Provider별 메트릭
|
||||
this.renderProviderMetrics(metricsEl, 'whisper');
|
||||
|
|
@ -405,7 +405,7 @@ export class ProviderSettings {
|
|||
*/
|
||||
private renderComparisonChart(containerEl: HTMLElement): void {
|
||||
const chartEl = containerEl.createDiv({ cls: 'comparison-chart' });
|
||||
chartEl.createEl('h5', { text: '📈 Provider comparison' });
|
||||
chartEl.createEl('h5', { text: '📈 provider comparison' });
|
||||
|
||||
// 간단한 막대 차트 (실제로는 Chart.js 등 사용 권장)
|
||||
const chartContent = chartEl.createDiv({ cls: 'chart-content' });
|
||||
|
|
@ -432,10 +432,10 @@ export class ProviderSettings {
|
|||
|
||||
if (whisperConnected || deepgramConnected) {
|
||||
statusEl.addClass('connected');
|
||||
statusEl.setText('✅ Connected');
|
||||
statusEl.setText('✅ connected');
|
||||
} else {
|
||||
statusEl.addClass('disconnected');
|
||||
statusEl.setText('⚠️ No providers configured');
|
||||
statusEl.setText('⚠️ no providers configured');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -485,7 +485,7 @@ class ShortcutModal extends Modal {
|
|||
this.isRecording = true;
|
||||
this.recordedKeys.clear();
|
||||
|
||||
button.setButtonText('녹음 중... (ESC로 취소)');
|
||||
button.setButtonText('녹음 중... (esc로 취소)');
|
||||
button.buttonEl.addClass('is-recording');
|
||||
|
||||
// 키 이벤트 리스너
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ export class ProviderSettingsContainer {
|
|||
|
||||
// 타이틀
|
||||
const titleEl = headerEl.createDiv({ cls: 'provider-title' });
|
||||
new Setting(titleEl).setName('🎯 Transcription provider configuration').setHeading();
|
||||
new Setting(titleEl).setName('🎯 transcription provider configuration').setHeading();
|
||||
|
||||
// 확장/축소 토글
|
||||
const toggleBtn = headerEl.createEl('button', {
|
||||
|
|
@ -233,10 +233,10 @@ export class ProviderSettingsContainer {
|
|||
.setDesc('How should the system choose between providers?')
|
||||
.addDropdown((dropdown) => {
|
||||
dropdown
|
||||
.addOption(SelectionStrategy.PERFORMANCE_OPTIMIZED, '⚡ Performance first')
|
||||
.addOption(SelectionStrategy.COST_OPTIMIZED, '💰 Cost optimized')
|
||||
.addOption(SelectionStrategy.QUALITY_OPTIMIZED, '✨ Quality first')
|
||||
.addOption(SelectionStrategy.ROUND_ROBIN, '🔄 Round robin')
|
||||
.addOption(SelectionStrategy.PERFORMANCE_OPTIMIZED, '⚡ performance first')
|
||||
.addOption(SelectionStrategy.COST_OPTIMIZED, '💰 cost optimized')
|
||||
.addOption(SelectionStrategy.QUALITY_OPTIMIZED, '✨ quality first')
|
||||
.addOption(SelectionStrategy.ROUND_ROBIN, '🔄 round robin')
|
||||
.setValue(
|
||||
this.plugin.settings.selectionStrategy ||
|
||||
SelectionStrategy.PERFORMANCE_OPTIMIZED
|
||||
|
|
@ -579,10 +579,10 @@ export class ProviderSettingsContainer {
|
|||
await new Promise((resolve) => setTimeout(resolve, 2000)); // 시뮬레이션
|
||||
|
||||
notice.hide();
|
||||
new Notice('✅ Connection successful.', 3000);
|
||||
new Notice('✅ connection successful.', 3000);
|
||||
} catch (error) {
|
||||
notice.hide();
|
||||
new Notice('❌ Connection failed.', 3000);
|
||||
new Notice('❌ connection failed.', 3000);
|
||||
console.error('Connection test error:', error);
|
||||
}
|
||||
}
|
||||
|
|
@ -841,10 +841,10 @@ class ProviderDetailsModal extends Modal {
|
|||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
|
||||
notice.hide();
|
||||
new Notice('✅ Connection test successful.', 3000);
|
||||
new Notice('✅ connection test successful.', 3000);
|
||||
} catch {
|
||||
notice.hide();
|
||||
new Notice('❌ Connection test failed.', 3000);
|
||||
new Notice('❌ connection test failed.', 3000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -856,7 +856,7 @@ class ProviderMetricsDisplay {
|
|||
constructor(private plugin: SpeechToTextPlugin) {}
|
||||
|
||||
render(containerEl: HTMLElement): void {
|
||||
new Setting(containerEl).setName('📊 Performance metrics').setHeading();
|
||||
new Setting(containerEl).setName('📊 performance metrics').setHeading();
|
||||
|
||||
// TODO: 실제 메트릭 구현
|
||||
const metricsEl = containerEl.createDiv({ cls: 'metrics-display' });
|
||||
|
|
|
|||
|
|
@ -300,9 +300,9 @@ export class ProviderSettingsContainerRefactored extends BaseSettingsComponent {
|
|||
this.createSetting(section, '선택 전략', 'Provider 선택 방법').addDropdown(
|
||||
(dropdown) => {
|
||||
dropdown
|
||||
.addOption(SelectionStrategy.PERFORMANCE_OPTIMIZED, '⚡ Performance first')
|
||||
.addOption(SelectionStrategy.COST_OPTIMIZED, '💰 Cost optimized')
|
||||
.addOption(SelectionStrategy.QUALITY_OPTIMIZED, '✨ Quality first')
|
||||
.addOption(SelectionStrategy.PERFORMANCE_OPTIMIZED, '⚡ performance first')
|
||||
.addOption(SelectionStrategy.COST_OPTIMIZED, '💰 cost optimized')
|
||||
.addOption(SelectionStrategy.QUALITY_OPTIMIZED, '✨ quality first')
|
||||
.setValue(
|
||||
this.plugin.settings.selectionStrategy ||
|
||||
SelectionStrategy.PERFORMANCE_OPTIMIZED
|
||||
|
|
|
|||
|
|
@ -819,7 +819,7 @@ export class APIKeyManager {
|
|||
|
||||
instructions.createEl('p', {
|
||||
cls: 'warning',
|
||||
text: '⚠️ Environment variables take precedence over saved keys',
|
||||
text: '⚠️ environment variables take precedence over saved keys',
|
||||
});
|
||||
|
||||
modal.open();
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ export class AdvancedSettingsPanel {
|
|||
const headerEl = containerEl.createDiv({ cls: 'advanced-panel-header' });
|
||||
|
||||
headerEl.createEl('h4', {
|
||||
text: '⚙️ Advanced configuration',
|
||||
text: '⚙️ advanced configuration',
|
||||
cls: 'advanced-title',
|
||||
});
|
||||
|
||||
|
|
@ -96,11 +96,11 @@ export class AdvancedSettingsPanel {
|
|||
.setDesc('How the system selects providers when in auto mode')
|
||||
.addDropdown((dropdown) => {
|
||||
dropdown
|
||||
.addOption(SelectionStrategy.PERFORMANCE_OPTIMIZED, '⚡ Performance first')
|
||||
.addOption(SelectionStrategy.COST_OPTIMIZED, '💰 Cost optimized')
|
||||
.addOption(SelectionStrategy.QUALITY_OPTIMIZED, '✨ Quality first')
|
||||
.addOption(SelectionStrategy.ROUND_ROBIN, '🔄 Round robin')
|
||||
.addOption(SelectionStrategy.AB_TEST, '🧪 A/B testing')
|
||||
.addOption(SelectionStrategy.PERFORMANCE_OPTIMIZED, '⚡ performance first')
|
||||
.addOption(SelectionStrategy.COST_OPTIMIZED, '💰 cost optimized')
|
||||
.addOption(SelectionStrategy.QUALITY_OPTIMIZED, '✨ quality first')
|
||||
.addOption(SelectionStrategy.ROUND_ROBIN, '🔄 round robin')
|
||||
.addOption(SelectionStrategy.AB_TEST, '🧪 a/b testing')
|
||||
.setValue(
|
||||
this.plugin.settings.selectionStrategy ||
|
||||
SelectionStrategy.PERFORMANCE_OPTIMIZED
|
||||
|
|
@ -880,7 +880,7 @@ export class AdvancedSettingsPanel {
|
|||
*/
|
||||
private showABTestResults(): void {
|
||||
// TODO: Implement A/B test results modal
|
||||
new Notice('A/B test results coming soon.');
|
||||
new Notice('A/b test results coming soon.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue