fix: address reviewbot lint issues (#39)

* fix: address reviewbot lint issues

Resolve lint findings to satisfy ObsidianReviewBot checks.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: address reviewbot typecheck feedback

Restore required typing, narrow DOM queries, and move deepgram SDK to devDependencies.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

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:
asyouplz 2025-12-26 11:21:47 +09:00 committed by GitHub
parent 78380adc1a
commit c455f7ffa2
46 changed files with 165 additions and 137 deletions

2
.gitignore vendored
View file

@ -146,3 +146,5 @@ docs/
# Requirement documents
requirment*.md
# Local review notes
REVIEW_NOTES_PR8004.md

View file

@ -44,11 +44,10 @@
],
"author": "Taesun Lee",
"license": "MIT",
"dependencies": {
"@deepgram/sdk": "^3.9.0"
},
"dependencies": {},
"devDependencies": {
"@testing-library/jest-dom": "^6.9.1",
"@deepgram/sdk": "^3.9.0",
"@types/jest": "^29.5.12",
"@types/node": "^16.18.126",
"@typescript-eslint/eslint-plugin": "^5.29.0",

View file

@ -104,10 +104,10 @@ export class EventManager extends EventEmitter<AppEventMap> {
emit(event: string, data?: unknown): void;
emit(event: string, data?: unknown): void {
// 통계 업데이트
this.updateStats(event as keyof AppEventMap);
this.updateStats(event);
// 히스토리 기록
this.recordHistory(event as keyof AppEventMap, data as AppEventMap[keyof AppEventMap]);
this.recordHistory(event, data as AppEventMap[keyof AppEventMap]);
// 디버그 로깅
if (this.isDebugMode && this.logger) {
@ -115,7 +115,7 @@ export class EventManager extends EventEmitter<AppEventMap> {
}
// 부모 클래스의 emit 호출
super.emit(event as keyof AppEventMap, data as AppEventMap[keyof AppEventMap]);
super.emit(event, data as AppEventMap[keyof AppEventMap]);
}
/**
@ -125,10 +125,10 @@ export class EventManager extends EventEmitter<AppEventMap> {
async emitAsync(event: string, data?: unknown): Promise<void>;
async emitAsync(event: string, data?: unknown): Promise<void> {
// 통계 업데이트
this.updateStats(event as keyof AppEventMap);
this.updateStats(event);
// 히스토리 기록
this.recordHistory(event as keyof AppEventMap, data as AppEventMap[keyof AppEventMap]);
this.recordHistory(event, data as AppEventMap[keyof AppEventMap]);
// 디버그 로깅
if (this.isDebugMode && this.logger) {
@ -136,7 +136,7 @@ export class EventManager extends EventEmitter<AppEventMap> {
}
// 부모 클래스의 emitAsync 호출
await super.emitAsync(event as keyof AppEventMap, data as AppEventMap[keyof AppEventMap]);
await super.emitAsync(event, data as AppEventMap[keyof AppEventMap]);
}
/**
@ -203,7 +203,7 @@ export class EventManager extends EventEmitter<AppEventMap> {
): Unsubscribe {
return this.on(event, (data) => {
if (predicate(data)) {
listener(data);
void listener(data);
}
});
}
@ -224,7 +224,7 @@ export class EventManager extends EventEmitter<AppEventMap> {
}
timeoutId = setTimeout(() => {
listener(data);
void listener(data);
timeoutId = null;
}, wait);
});
@ -242,7 +242,7 @@ export class EventManager extends EventEmitter<AppEventMap> {
return this.on(event, (data) => {
if (!inThrottle) {
listener(data);
void listener(data);
inThrottle = true;
setTimeout(() => {
inThrottle = false;
@ -330,7 +330,7 @@ export class EventManager extends EventEmitter<AppEventMap> {
return {
totalEvents: eventNames.length,
totalListeners: eventNames.reduce((sum: number, event) =>
sum + this.listenerCount(event as keyof AppEventMap), 0
sum + this.listenerCount(event), 0
),
totalEmitted: Array.from(this.eventStats.values()).reduce((sum, count) =>
sum + count, 0

View file

@ -367,19 +367,21 @@ export class TextInsertionHandler {
case 'prepend':
return await this.editorService.prependToDocument(text, true);
case 'line-end':
case 'line-end': {
const lineNumber = this.editorService.getCurrentLineNumber();
if (lineNumber !== null) {
return await this.editorService.appendToLine(lineNumber, text);
}
return false;
}
case 'new-line':
case 'new-line': {
const cursor = this.editorService.getCursorPosition();
if (cursor) {
return await this.editorService.insertAtPosition(`\n${text}`, cursor);
}
return false;
}
default:
return await this.editorService.insertAtCursor(text);

View file

@ -99,7 +99,7 @@ export class DependencyContainer {
return this.resolveScoped<T>(token, registration);
default:
throw new Error(`Unknown lifetime: ${registration.lifetime}`);
throw new Error(`Unknown lifetime: ${String(registration.lifetime)}`);
}
}

View file

@ -107,7 +107,7 @@ export class ErrorBoundary {
// 처리되지 않은 Promise rejection 처리
if (typeof window !== 'undefined') {
window.addEventListener('unhandledrejection', (event) => {
this.handleError(
void this.handleError(
new Error(event.reason?.message || 'Unhandled Promise rejection'),
{ component: 'Global', operation: 'Promise' }
);
@ -116,7 +116,7 @@ export class ErrorBoundary {
// 전역 에러 처리
window.addEventListener('error', (event) => {
this.handleError(
void this.handleError(
event.error || new Error(event.message),
{ component: 'Global', operation: 'Runtime' }
);
@ -158,7 +158,7 @@ export class ErrorBoundary {
try {
return fn();
} catch (error) {
this.handleError(error as Error, context);
void this.handleError(error as Error, context);
return undefined;
}
}

View file

@ -87,7 +87,7 @@ export class BatchRequestManager {
} = {}
): Promise<T> {
return new Promise((resolve, reject) => {
const request: BatchRequest<T> = {
const request: AnyBatch = {
id: this.generateRequestId(),
endpoint,
method,
@ -95,13 +95,13 @@ export class BatchRequestManager {
body: options.body,
headers: options.headers,
priority: options.priority || 'normal',
resolve,
resolve: (value: unknown) => resolve(value as T),
reject,
timestamp: Date.now(),
retries: 0
};
this.enqueueRequest(request as AnyBatch);
this.enqueueRequest(request);
this.stats.totalRequests++;
});
}
@ -117,7 +117,7 @@ export class BatchRequestManager {
}
const queue = this.queues.get(batchKey)!;
queue.push(request as AnyBatch);
queue.push(request);
// 우선순위 정렬
if (this.priorityQueuing) {
@ -141,7 +141,7 @@ export class BatchRequestManager {
if (this.timers.has(batchKey)) return;
const timer = window.setTimeout(() => {
this.processBatch(batchKey);
void this.processBatch(batchKey);
this.timers.delete(batchKey);
}, this.batchDelay);
@ -157,7 +157,7 @@ export class BatchRequestManager {
clearTimeout(timer);
this.timers.delete(batchKey);
}
this.processBatch(batchKey);
void this.processBatch(batchKey);
}
/**

View file

@ -516,7 +516,7 @@ export class FileUploadManager {
*/
cleanup(): void {
if (this.audioContext) {
this.audioContext.close();
void this.audioContext.close();
this.audioContext = undefined;
}
this.abortController = undefined;

View file

@ -302,11 +302,11 @@ export class SettingsAPI implements ISettingsAPI {
await this.apiKeyManager.clearApiKey();
} else if (Array.isArray(scope)) {
scope.forEach(key => {
const typedKey = key as keyof SettingsSchema;
const typedKey = key;
this.settings[typedKey] = this.defaultSettings[typedKey];
});
} else {
const typedKey = scope as keyof SettingsSchema;
const typedKey = scope;
this.settings[typedKey] = this.defaultSettings[typedKey];
}
@ -408,8 +408,8 @@ export class SettingsAPI implements ISettingsAPI {
readable: ReadableStream<Uint8Array>;
};
const writer = cs.writable.getWriter();
writer.write(data);
writer.close();
await writer.write(data);
await writer.close();
const chunks: Uint8Array[] = [];
const reader = cs.readable.getReader();
@ -451,8 +451,8 @@ export class SettingsAPI implements ISettingsAPI {
readable: ReadableStream<Uint8Array>;
};
const writer = ds.writable.getWriter();
writer.write(data);
writer.close();
await writer.write(data);
await writer.close();
const chunks: Uint8Array[] = [];
const reader = ds.readable.getReader();

View file

@ -43,7 +43,7 @@ export class SettingsMigrator {
if (path.length === 0) {
// 마이그레이션 경로가 없으면 그대로 반환
return currentSettings as SettingsSchema;
return currentSettings;
}
let settings = currentSettings;
@ -62,7 +62,7 @@ export class SettingsMigrator {
// 버전 업데이트
settings.version = toVersion;
return settings as SettingsSchema;
return settings;
}
/**
@ -383,7 +383,7 @@ export class SettingsMigrator {
}
const backup = JSON.parse(backupData);
return backup.settings as SettingsSchema;
return backup.settings;
}
/**

View file

@ -253,7 +253,7 @@ export class TranscriberFactory {
return {
defaultProvider: settings.defaultProvider || 'whisper',
autoSelect: settings.autoSelect || false,
selectionStrategy: (settings.selectionStrategy as SelectionStrategy) || SelectionStrategy.MANUAL,
selectionStrategy: settings.selectionStrategy || SelectionStrategy.MANUAL,
fallbackEnabled: settings.fallbackEnabled !== false,
whisper: {
@ -424,7 +424,7 @@ export class TranscriberFactory {
// 모니터링 엔드포인트로 전송 (설정된 경우)
if (this.config.monitoring?.enabled && this.config.monitoring.metricsEndpoint) {
this.sendMetricsToEndpoint(provider);
void this.sendMetricsToEndpoint(provider);
}
}

View file

@ -85,7 +85,7 @@ export class TranscriberFactoryRefactored {
this.metricsTracker.recordRequest(provider, success, latency, cost, error);
if (this.shouldSendMetrics()) {
this.sendMetricsToEndpoint(provider);
void this.sendMetricsToEndpoint(provider);
}
}
@ -542,7 +542,7 @@ class PluginSettingsManagerAdapter implements ISettingsManager {
enabled: Boolean(deepgramApiKey),
apiKey: deepgramApiKey,
model:
this.pickString((deepgramConfigRaw as Partial<ProviderConfig>).model) ??
this.pickString(deepgramConfigRaw?.model) ??
this.pickString(deepgramSettings?.model) ??
this.settings.deepgramModel ??
'nova-3',

View file

@ -424,9 +424,10 @@ export class WhisperService implements IWhisperService {
);
case 401:
throw new AuthenticationError();
case 429:
case 429: {
const retryAfter = response.headers?.['retry-after'];
throw new RateLimitError(retryAfter ? parseInt(retryAfter) : undefined);
}
case 413:
throw new FileTooLargeError();
case 500:

View file

@ -70,7 +70,7 @@ export class RateLimiter {
}
this.queue.push(resolve);
this.processQueue();
void this.processQueue();
});
}
@ -165,4 +165,4 @@ export class RateLimiter {
private sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
}

View file

@ -380,8 +380,8 @@ export class DeepgramAdapter implements ITranscriber {
const transcriptionSettings = this.settingsManager?.get(
'transcription'
) as SettingsStoreTranscriptionSettings | undefined;
const deepgramSettings = transcriptionSettings?.deepgram as DeepgramSettings | undefined;
const deepgramFeatures = deepgramSettings?.features as DeepgramFeatures | undefined;
const deepgramSettings = transcriptionSettings?.deepgram;
const deepgramFeatures = deepgramSettings?.features;
if (deepgramFeatures) {
this.logger.debug(

View file

@ -233,7 +233,7 @@ class RateLimiter {
async acquire(): Promise<void> {
return new Promise<void>(resolve => {
this.queue.push(resolve);
this.processQueue();
void this.processQueue();
});
}
@ -785,17 +785,18 @@ export class DeepgramService {
false,
402
);
case 429:
case 429: {
const retryAfter = response.headers?.['retry-after'];
throw new ProviderRateLimitError(
'deepgram',
retryAfter ? parseInt(retryAfter) : undefined
);
}
case 500:
case 502:
case 503:
throw new ProviderUnavailableError('deepgram');
case 504:
case 504: {
// Extract file size info if available
const sizeInfo = this.lastAudioSize ? ` (${Math.round(this.lastAudioSize / (1024 * 1024))}MB)` : '';
throw new TranscriptionError(
@ -805,6 +806,7 @@ export class DeepgramService {
true, // retryable
504
);
}
default:
throw new TranscriptionError(
`API error: ${errorMessage}`,

View file

@ -375,9 +375,10 @@ export class DiarizationFormatter {
}
switch (numbering) {
case 'alphabetic':
case 'alphabetic': {
const letter = String.fromCharCode(65 + (speakerNumber % 26)); // A, B, C...
return `${prefix} ${letter}`;
}
case 'numeric':
default:

View file

@ -451,15 +451,17 @@ export class ModelMigrationService {
// 실제 구현에서는 UI를 통해 사용자 동의를 받아야 함
return false; // 기본적으로 사용자 동의 필요
case 'cost_threshold':
case 'cost_threshold': {
const _maxIncrease = condition.parameters.maxIncrease || 20;
// 비용 증가율 계산 로직
return true; // 임시 구현
}
case 'feature_compatible':
case 'feature_compatible': {
const _requiredFeatures = condition.parameters.requiredFeatures || [];
// 기능 호환성 체크
return true; // 임시 구현
}
default:
return true;
@ -562,7 +564,7 @@ export class ModelMigrationService {
break;
default:
throw new Error(`Unknown migration action: ${step.action}`);
throw new Error(`Unknown migration action: ${String(step.action)}`);
}
}
@ -619,7 +621,7 @@ export class ModelMigrationService {
}
const backupId = `migration_backup_${Date.now()}`;
this.settingsManager.set(`backup.${backupId}`, backup);
await this.settingsManager.set(`backup.${backupId}`, backup);
this.logger.debug('Settings backup created', { backupId, settingsCount: settingsToBackup.length });
}

View file

@ -142,12 +142,13 @@ export class DeepgramErrorHandler {
false,
402
);
case 429:
case 429: {
const retryAfter = response.headers?.['retry-after'];
throw new ProviderRateLimitError(
'deepgram',
retryAfter ? parseInt(retryAfter) : undefined
);
}
case 500:
case 502:
case 503:

View file

@ -30,7 +30,7 @@ export class RateLimiter {
async acquire(): Promise<void> {
return new Promise<void>(resolve => {
this.queue.push(resolve);
this.processQueue();
void this.processQueue();
});
}
@ -296,4 +296,4 @@ export class ReliabilityManager {
circuitBreakerState: this.circuitBreaker.getState()
};
}
}
}

View file

@ -135,7 +135,7 @@ export default class SpeechToTextPlugin extends Plugin {
id: 'transcribe-audio',
name: 'Transcribe audio file',
callback: () => {
this.showAudioFilePicker();
void this.showAudioFilePicker();
}
});

View file

@ -287,7 +287,7 @@ export default class SpeechToTextPlugin extends Plugin {
id: 'transcribe-audio',
name: 'Transcribe audio file',
callback: () => {
this.errorBoundary.wrap(
void this.errorBoundary.wrap(
() => this.showAudioFilePicker(),
{ component: 'Command', operation: 'transcribe-audio' }
);
@ -313,7 +313,7 @@ export default class SpeechToTextPlugin extends Plugin {
id: 'show-format-options',
name: 'Show text formatting options',
callback: () => {
this.errorBoundary.wrap(
void this.errorBoundary.wrap(
() => this.showFormatOptions(),
{ component: 'Command', operation: 'show-format-options' }
);
@ -413,7 +413,7 @@ export default class SpeechToTextPlugin extends Plugin {
await this.insertTranscription(result.text);
}
} catch (error) {
this.errorBoundary.handleError(
void this.errorBoundary.handleError(
error as Error,
{ component: 'TranscriptionService', operation: 'transcribe' }
);
@ -472,15 +472,17 @@ export default class SpeechToTextPlugin extends Plugin {
case 'cursor':
editor.replaceSelection(text);
break;
case 'end':
case 'end': {
const lastLine = editor.lastLine();
const currentText = editor.getLine(lastLine);
editor.setLine(lastLine, currentText + '\n\n' + text);
break;
case 'beginning':
}
case 'beginning': {
const firstLineText = editor.getLine(0);
editor.setLine(0, text + '\n\n' + firstLineText);
break;
}
}
}

View file

@ -278,7 +278,7 @@ export default class SpeechToTextPlugin extends Plugin {
id: 'transcribe-audio-file',
name: 'Transcribe audio file',
callback: () => {
this.showAudioFilePicker();
void this.showAudioFilePicker();
}
});
@ -635,15 +635,17 @@ export default class SpeechToTextPlugin extends Plugin {
case 'cursor':
editor.replaceSelection(text);
break;
case 'end':
case 'end': {
const lastLine = editor.lastLine();
const lastLineText = editor.getLine(lastLine);
editor.setLine(lastLine, lastLineText + '\n\n' + text);
break;
case 'beginning':
}
case 'beginning': {
const firstLineText = editor.getLine(0);
editor.setLine(0, text + '\n\n' + firstLineText);
break;
}
}
}

View file

@ -78,7 +78,7 @@ export class EventEmitter<T extends EventMap = EventMap> {
// 일반 리스너 실행
(this.events.get(event) as Set<EventListener<T[K]>> | undefined)?.forEach(listener => {
try {
listener(data);
void listener(data);
} catch (error) {
console.error(`Error in event listener for ${String(event)}:`, error);
}
@ -89,7 +89,7 @@ export class EventEmitter<T extends EventMap = EventMap> {
if (onceListeners) {
onceListeners.forEach(listener => {
try {
listener(data);
void listener(data);
} catch (error) {
console.error(`Error in once listener for ${String(event)}:`, error);
}
@ -181,7 +181,7 @@ export class Subject<T> implements IObservable<T> {
notify(data: T): void {
this.observers.forEach(observer => {
try {
observer.update(data);
void observer.update(data);
} catch (error) {
console.error('Error notifying observer:', error);
}
@ -225,7 +225,7 @@ export class BehaviorSubject<T> extends Subject<T> {
subscribe(observer: IObserver<T>): Unsubscribe {
// 구독 시 현재 값을 즉시 전달
observer.update(this.value);
void observer.update(this.value);
return super.subscribe(observer);
}
}

View file

@ -154,7 +154,7 @@ export class ResourcePool<T extends IDisposable> {
private maxSize = 10,
private minSize = 2
) {
this.initialize();
void this.initialize();
}
private async initialize(): Promise<void> {
@ -194,4 +194,4 @@ export class ResourcePool<T extends IDisposable> {
this.available = [];
this.inUse.clear();
}
}
}

View file

@ -213,7 +213,7 @@ export class StatusBarManager implements IDisposable {
*/
public show(): void {
if (this.statusBarItem) {
(this.statusBarItem as HTMLElement).classList.remove('sn-hidden');
this.statusBarItem.classList.remove('sn-hidden');
}
}
@ -222,7 +222,7 @@ export class StatusBarManager implements IDisposable {
*/
public hide(): void {
if (this.statusBarItem) {
(this.statusBarItem as HTMLElement).classList.add('sn-hidden');
this.statusBarItem.classList.add('sn-hidden');
}
}

View file

@ -183,7 +183,7 @@ export class FilePickerModal extends Modal {
// 파일 선택 이벤트
this.fileBrowser.onFileSelected((file) => {
this.handleFileSelection(file);
void this.handleFileSelection(file);
});
return browseContent;
@ -197,7 +197,7 @@ export class FilePickerModal extends Modal {
// 최근 파일 선택 이벤트
this.recentFiles.onFileSelected((file) => {
this.handleFileSelection(file);
void this.handleFileSelection(file);
});
}
@ -433,7 +433,7 @@ export class FilePickerModal extends Modal {
}
if (e.key === 'Enter' && this.selectedFiles.length > 0) {
this.processSelectedFiles();
void this.processSelectedFiles();
}
});
}
@ -479,4 +479,4 @@ export class FilePickerModal extends Modal {
this.recentFiles?.unmount();
this.progressIndicator.unmount();
}
}
}

View file

@ -168,7 +168,9 @@ export class FilePickerModalRefactored extends Modal {
uiBuilder.buildProgressSection();
uiBuilder.buildFooter(
() => this.handleCancel(),
() => this.handleSubmit(),
() => {
void this.handleSubmit();
},
this.state.selectedFiles.length
);
@ -261,7 +263,7 @@ export class FilePickerModalRefactored extends Modal {
this.handleCancel();
} else if (e.key === 'Enter' && !this.state.isProcessing) {
if (this.state.selectedFiles.length > 0) {
this.handleSubmit();
void this.handleSubmit();
}
}
};

View file

@ -684,7 +684,7 @@ class ProgressNotification implements IProgressNotification {
progress: 0
};
this.init();
void this.init();
}
private async init(): Promise<void> {
@ -857,7 +857,7 @@ export class NotificationManager implements INotificationAPI {
// 채널 선택 및 전송
const channels = this.selectChannels(notification);
Promise.all(
void Promise.all(
channels.map(channel =>
channel.send(notification).catch(error => {
console.error('Notification channel error:', error);
@ -977,7 +977,7 @@ export class NotificationManager implements INotificationAPI {
const modal = new ModalChannel();
const id = this.generateNotificationId();
modal.send({
void modal.send({
type: 'info',
title: options?.title || '확인',
message,
@ -1022,7 +1022,7 @@ export class NotificationManager implements INotificationAPI {
const modal = new ModalChannel();
const id = this.generateNotificationId();
modal.send({
void modal.send({
type: 'info',
title: title || '알림',
message,

View file

@ -30,7 +30,7 @@ export class EnhancedSettingsTab extends PluginSettingTab {
this.memoryManager = new ResourceManager();
// 초기화
this.initialize();
void this.initialize();
}
/**
@ -179,16 +179,16 @@ export class EnhancedSettingsTab extends PluginSettingTab {
this.showGeneralSettings(container);
break;
case 'api':
this.showApiSettings(container);
void this.showApiSettings(container);
break;
case 'audio':
this.showAudioSettings(container);
void this.showAudioSettings(container);
break;
case 'advanced':
this.showAdvancedSettings(container);
void this.showAdvancedSettings(container);
break;
case 'shortcuts':
this.showShortcutSettings(container);
void this.showShortcutSettings(container);
break;
case 'about':
this.showAbout(container);
@ -224,7 +224,7 @@ export class EnhancedSettingsTab extends PluginSettingTab {
dropdown.addOption(code, name);
});
this.settingsAPI.get('general').then(general => {
void this.settingsAPI.get('general').then(general => {
dropdown.setValue(general.language);
});
@ -245,7 +245,7 @@ export class EnhancedSettingsTab extends PluginSettingTab {
.addOption('light', 'Light')
.addOption('dark', 'Dark');
this.settingsAPI.get('general').then(general => {
void this.settingsAPI.get('general').then(general => {
dropdown.setValue(general.theme);
});
@ -262,7 +262,7 @@ export class EnhancedSettingsTab extends PluginSettingTab {
.setName('Auto Save')
.setDesc('Automatically save transcriptions')
.addToggle(toggle => {
this.settingsAPI.get('general').then(general => {
void this.settingsAPI.get('general').then(general => {
toggle.setValue(general.autoSave);
});
@ -282,7 +282,7 @@ export class EnhancedSettingsTab extends PluginSettingTab {
.setLimits(10, 300, 10)
.setDynamicTooltip();
this.settingsAPI.get('general').then(general => {
void this.settingsAPI.get('general').then(general => {
slider.setValue(general.saveInterval / 1000);
});
@ -301,7 +301,7 @@ export class EnhancedSettingsTab extends PluginSettingTab {
.setName('Enable Notifications')
.setDesc('Show notifications for events')
.addToggle(toggle => {
this.settingsAPI.get('general').then(general => {
void this.settingsAPI.get('general').then(general => {
toggle.setValue(general.notifications.enabled);
});
@ -316,7 +316,7 @@ export class EnhancedSettingsTab extends PluginSettingTab {
.setName('Sound')
.setDesc('Play sound with notifications')
.addToggle(toggle => {
this.settingsAPI.get('general').then(general => {
void this.settingsAPI.get('general').then(general => {
toggle.setValue(general.notifications.sound);
});
@ -346,7 +346,7 @@ export class EnhancedSettingsTab extends PluginSettingTab {
.addOption('azure', 'Azure Speech Services')
.addOption('custom', 'Custom Endpoint');
this.settingsAPI.get('api').then(api => {
void this.settingsAPI.get('api').then(api => {
dropdown.setValue(api.provider);
});
@ -356,7 +356,7 @@ export class EnhancedSettingsTab extends PluginSettingTab {
await this.settingsAPI.set('api', api);
// UI 업데이트
this.showApiSettings(container);
void this.showApiSettings(container);
});
});
@ -831,7 +831,7 @@ export class EnhancedSettingsTab extends PluginSettingTab {
.onClick(async () => {
const defaults = this.settingsAPI.getDefault('shortcuts');
await this.settingsAPI.set('shortcuts', defaults);
this.showShortcutSettings(container); // 화면 새로고침
await this.showShortcutSettings(container); // 화면 새로고침
new Notice('Shortcuts reset to defaults');
});
}

View file

@ -216,7 +216,7 @@ export class SettingsTab extends PluginSettingTab {
default:
console.warn('Unknown provider:', provider);
containerEl.createEl('p', {
text: `Unknown provider: ${provider}`,
text: `Unknown provider: ${String(provider)}`,
cls: 'mod-warning'
});
}

View file

@ -215,7 +215,7 @@ export class SettingsTabOptimized extends PluginSettingTab {
section.render();
section.onSettingsChange(() => {
this.state.isDirty = true;
this.saveSettings();
void this.saveSettings();
});
this.components.sectionRenderers.set('api', section);
@ -310,7 +310,7 @@ export class SettingsTabOptimized extends PluginSettingTab {
inputs.forEach(input => {
this.eventManager.add(input as HTMLElement, 'change', () => {
this.state.isDirty = true;
this.saveSettings();
void this.saveSettings();
});
});
}
@ -580,7 +580,7 @@ class SecureApiKeyInput {
// Validate on button click
this.eventManager.add(this.validateBtn, 'click', () => {
this.validate();
void this.validate();
});
// Track changes
@ -659,17 +659,23 @@ class SettingsFooter extends SectionRenderer {
new Setting(footer)
.addButton(btn => btn
.setButtonText('설정 내보내기')
.onClick(() => this.exportSettings()))
.onClick(() => {
void this.exportSettings();
}))
.addButton(btn => btn
.setButtonText('설정 가져오기')
.onClick(() => this.importSettings()));
.onClick(() => {
void this.importSettings();
}));
// Reset button
new Setting(footer)
.addButton(btn => btn
.setButtonText('기본값으로 재설정')
.setWarning()
.onClick(() => this.resetSettings()));
.onClick(() => {
void this.resetSettings();
}));
}
private async exportSettings(): Promise<void> {

View file

@ -68,7 +68,7 @@ export class SettingsTabRefactored extends PluginSettingTab {
containerEl.empty();
containerEl.addClass('speech-to-text-settings');
tryCatchAsync(async () => {
void tryCatchAsync(async () => {
await this.renderContent(containerEl);
}, {
onError: (error) => {
@ -704,4 +704,4 @@ class ShortcutSettingsWrapper extends AutoDisposable {
protected onDispose(): void {
// 추가 정리 로직
}
}
}

View file

@ -154,7 +154,7 @@ export class ScreenReaderAnnouncer {
this.liveRegion!.setAttribute('aria-live', 'assertive');
}
this.processQueue();
void this.processQueue();
}
/**

View file

@ -117,7 +117,7 @@ export class ShortcutSettings {
.onClick(() => {
this.openShortcutModal(shortcut.id, shortcut.name, (newKey) => {
if (newKey) {
this.setShortcut(shortcut.id, newKey);
void this.setShortcut(shortcut.id, newKey);
keyDisplay.textContent = newKey;
keyDisplay.className = 'shortcut-set';
}
@ -130,7 +130,7 @@ export class ShortcutSettings {
.setButtonText('삭제')
.setWarning()
.onClick(async () => {
this.removeShortcut(shortcut.id);
await this.removeShortcut(shortcut.id);
keyDisplay.textContent = '설정 안 됨';
keyDisplay.className = 'shortcut-unset';
}));
@ -181,7 +181,7 @@ export class ShortcutSettings {
return;
}
// 기존 단축키 제거
this.removeShortcut(existingCommand);
void this.removeShortcut(existingCommand);
}
}
onSubmit(key);

View file

@ -412,7 +412,7 @@ class StorageErrorHandler implements ErrorHandler {
// 저장소 용량 확인
if ('storage' in navigator && 'estimate' in navigator.storage) {
navigator.storage.estimate().then(estimate => {
void navigator.storage.estimate().then(estimate => {
const percentUsed = ((estimate.usage || 0) / (estimate.quota || 1)) * 100;
if (percentUsed > 90) {
new Notice('저장 공간이 부족합니다. 일부 데이터를 정리해주세요.');

View file

@ -36,7 +36,7 @@ export class ProviderSettingsContainer {
this.apiKeyManager = new APIKeyManager(plugin);
this.advancedPanel = new AdvancedSettingsPanel(plugin);
this.initialize();
void this.initialize();
}
/**

View file

@ -59,7 +59,7 @@ export class ProviderSettingsContainerRefactored extends BaseSettingsComponent {
this.apiKeyManager = new APIKeyManager(plugin);
this.advancedPanel = new AdvancedSettingsPanel(plugin);
this.initialize();
void this.initialize();
}
/**
@ -488,7 +488,7 @@ export class ProviderSettingsContainerRefactored extends BaseSettingsComponent {
// 5분마다 상태 업데이트
this.statusUpdateInterval = window.setInterval(() => {
this.checkAllConnectionsOptimized();
void this.checkAllConnectionsOptimized();
}, 5 * 60 * 1000);
this.disposables.push(() => this.stopStatusMonitoring());

View file

@ -29,7 +29,7 @@ export class APIKeyManager {
constructor(private plugin: SpeechToTextPlugin) {
this.encryptor = new Encryptor();
this.loadApiKeys();
void this.loadApiKeys();
}
/**
@ -131,7 +131,7 @@ export class APIKeyManager {
'autocomplete': 'off',
'spellcheck': 'false'
}
}) as HTMLInputElement;
});
// 현재 값 설정
const currentKey = this.apiKeys.get(provider);
@ -293,7 +293,7 @@ export class APIKeyManager {
'aria-label': 'Verify API key',
'title': 'Test API key validity'
}
}) as HTMLButtonElement;
});
btn.onclick = async () => {
const value = inputEl.value;
@ -809,7 +809,7 @@ export class APIKeyManager {
// UI 업데이트
const container = document.querySelector(`.api-key-input-container.${provider}`);
if (container) {
const statusEl = container.querySelector('.key-status-indicator') as HTMLElement;
const statusEl = container.querySelector<HTMLElement>('.key-status-indicator');
if (statusEl) {
this.updateStatusIndicator(statusEl, provider);
}

View file

@ -81,7 +81,7 @@ export class APIKeyManagerRefactored extends BaseSettingsComponent {
constructor(plugin: SpeechToTextPlugin) {
super(plugin);
this.encryptor = new Encryptor();
this.initialize();
void this.initialize();
}
/**
@ -227,7 +227,7 @@ export class APIKeyManagerRefactored extends BaseSettingsComponent {
'aria-label': `${config.name} API 키`,
'aria-describedby': `${provider}-validation-message`
}
}) as HTMLInputElement;
});
// 기존 값 설정
if (keyData?.key) {
@ -363,7 +363,7 @@ export class APIKeyManagerRefactored extends BaseSettingsComponent {
'aria-label': 'API 키 검증',
'title': 'API 키 유효성 검사'
}
}) as HTMLButtonElement;
});
btn.onclick = async () => {
await this.validateApiKeyWithUI(inputEl, provider, config, btn);
@ -775,11 +775,11 @@ export class APIKeyManagerRefactored extends BaseSettingsComponent {
await this.withErrorHandling(async () => {
const content = await file.text();
const keys = JSON.parse(content);
const keys = JSON.parse(content) as Record<string, string>;
for (const [provider, key] of Object.entries(keys)) {
if (this.providerConfigs.has(provider as TranscriptionProvider)) {
await this.saveApiKey(provider as TranscriptionProvider, key as string);
if (this.isTranscriptionProvider(provider)) {
await this.saveApiKey(provider, key);
}
}
@ -821,6 +821,10 @@ export class APIKeyManagerRefactored extends BaseSettingsComponent {
}, 'API 키 내보내기 실패');
}
private isTranscriptionProvider(value: string): value is TranscriptionProvider {
return value === 'whisper' || value === 'deepgram';
}
/**
*
*/

View file

@ -427,8 +427,8 @@ export function deepEqual(a: unknown, b: unknown): boolean {
if (typeof a !== 'object') return false;
const keysA = Object.keys(a as object);
const keysB = Object.keys(b as object);
const keysA = Object.keys(a);
const keysB = Object.keys(b);
if (keysA.length !== keysB.length) return false;

View file

@ -418,7 +418,7 @@ export class AsyncQueue<T> {
});
if (!this.running) {
this.run();
void this.run();
}
});
}
@ -433,10 +433,10 @@ export class AsyncQueue<T> {
const task = this.queue.shift();
if (task) {
this.active++;
task().finally(() => {
void task().finally(() => {
this.active--;
if (this.queue.length > 0) {
this.run();
void this.run();
} else if (this.active === 0) {
this.running = false;
}

View file

@ -368,7 +368,7 @@ export class ConcurrencyManager {
async acquire(priority = 0): Promise<void> {
return new Promise((resolve) => {
this.priorityQueue.enqueue(resolve, priority);
this.processQueue();
void this.processQueue();
});
}
@ -378,7 +378,7 @@ export class ConcurrencyManager {
release(): void {
this.semaphore.release();
this.activeCount--;
this.processQueue();
void this.processQueue();
}
/**

View file

@ -86,7 +86,7 @@ export class GlobalErrorManager {
private setupGlobalHandlers(): void {
// Unhandled rejection 처리
window.addEventListener('unhandledrejection', (event) => {
this.handleError(new Error(event.reason), {
void this.handleError(new Error(event.reason), {
type: ErrorType.UNKNOWN,
severity: ErrorSeverity.HIGH,
context: { promise: true }
@ -96,7 +96,7 @@ export class GlobalErrorManager {
// 일반 에러 처리
window.addEventListener('error', (event) => {
this.handleError(event.error || new Error(event.message), {
void this.handleError(event.error || new Error(event.message), {
type: ErrorType.UNKNOWN,
severity: ErrorSeverity.HIGH,
context: {
@ -419,7 +419,7 @@ export class ErrorBoundary {
this.errorHandler(error);
// 전역 에러 매니저에 보고
GlobalErrorManager.getInstance().handleError(error, {
void GlobalErrorManager.getInstance().handleError(error, {
type: ErrorType.UNKNOWN,
severity: ErrorSeverity.MEDIUM,
context: { boundary: true }
@ -486,7 +486,7 @@ export function tryCatch<T>(
options.onError(err);
}
GlobalErrorManager.getInstance().handleError(err, {
void GlobalErrorManager.getInstance().handleError(err, {
type: ErrorType.UNKNOWN,
severity: ErrorSeverity.LOW
});

View file

@ -261,7 +261,7 @@ export class MemoryMonitor {
this.monitoring = true;
this.interval = window.setInterval(() => {
this.check();
void this.check();
}, intervalMs);
}

View file

@ -69,7 +69,7 @@ export class MemoryProfiler {
if (this.isMonitoring || !this.isSupported()) return;
this.isMonitoring = true;
this.profileLoop();
void this.profileLoop();
if (process.env.NODE_ENV === 'development') {
console.debug('Memory profiling started');
@ -120,7 +120,9 @@ export class MemoryProfiler {
// 다음 스냅샷 스케줄
this.monitoringInterval = window.setTimeout(
() => this.profileLoop(),
() => {
void this.profileLoop();
},
this.snapshotInterval
);
}