backup: current state before creating backup branch
This commit is contained in:
		
							parent
							
								
									8402e7d296
								
							
						
					
					
						commit
						39b75da818
					
				@ -1,10 +1,16 @@
 | 
			
		||||
// src/bridge/featureBridge.js
 | 
			
		||||
const { ipcMain } = require('electron');
 | 
			
		||||
const settingsService = require('../features/settings/settingsService');
 | 
			
		||||
const askService = require('../features/ask/askService');
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
  // Renderer로부터의 요청을 수신
 | 
			
		||||
  initialize() {
 | 
			
		||||
    // Ask 관련 핸들러 추가
 | 
			
		||||
    ipcMain.handle('ask:sendMessage', async (event, userPrompt) => {
 | 
			
		||||
      return askService.sendMessage(userPrompt);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // 기존 ask 핸들러 유지
 | 
			
		||||
    ipcMain.handle('feature:ask', (e, query) => {
 | 
			
		||||
      // 실제로는 여기서 Controller -> Service 로직 수행
 | 
			
		||||
@ -61,7 +67,7 @@ module.exports = {
 | 
			
		||||
      return await settingsService.setAutoUpdateSetting(isEnabled);
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    console.log('[FeatureBridge] Initialized with settings handlers.');
 | 
			
		||||
    console.log('[FeatureBridge] Initialized with ask and settings handlers.');
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // Renderer로 상태를 전송
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ async function sendMessage(userPrompt) {
 | 
			
		||||
    let sessionId; 
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        console.log(`[AskService] 🤖 Processing message: ${userPrompt.substring(0, 50)}...`);
 | 
			
		||||
        console.log(`[AskService] Processing message: ${userPrompt.substring(0, 50)}...`);
 | 
			
		||||
 | 
			
		||||
        // --- Save user's message immediately ---
 | 
			
		||||
        // This ensures the user message is always timestamped before the assistant's response.
 | 
			
		||||
@ -139,12 +139,11 @@ async function sendMessage(userPrompt) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function initialize() {
 | 
			
		||||
    ipcMain.handle('ask:sendMessage', async (event, userPrompt) => {
 | 
			
		||||
        return sendMessage(userPrompt);
 | 
			
		||||
    });
 | 
			
		||||
    // IPC 핸들러는 featureBridge.js로 이동됨
 | 
			
		||||
    console.log('[AskService] Initialized and ready.');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
    initialize,
 | 
			
		||||
    sendMessage,  // sendMessage 함수 export 추가
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										258
									
								
								src/preload.js
									
									
									
									
									
								
							
							
						
						
									
										258
									
								
								src/preload.js
									
									
									
									
									
								
							@ -2,8 +2,254 @@
 | 
			
		||||
const { contextBridge, ipcRenderer } = require('electron');
 | 
			
		||||
 | 
			
		||||
contextBridge.exposeInMainWorld('api', {
 | 
			
		||||
  // Ask
 | 
			
		||||
  ask: {
 | 
			
		||||
    // sendMessage
 | 
			
		||||
    sendMessage: (message) => ipcRenderer.invoke('ask:sendMessage', message),
 | 
			
		||||
    
 | 
			
		||||
    // window
 | 
			
		||||
    adjustWindowHeight: (height) => ipcRenderer.invoke('adjust-window-height', height),
 | 
			
		||||
    forceCloseWindow: (windowName) => ipcRenderer.invoke('force-close-window', windowName),
 | 
			
		||||
    closeWindowIfEmpty: () => ipcRenderer.invoke('close-ask-window-if-empty'),
 | 
			
		||||
    
 | 
			
		||||
    // event listener
 | 
			
		||||
    onGlobalSend: (callback) => ipcRenderer.on('ask-global-send', callback),
 | 
			
		||||
    onReceiveQuestionFromAssistant: (callback) => ipcRenderer.on('receive-question-from-assistant', callback),
 | 
			
		||||
    onHideTextInput: (callback) => ipcRenderer.on('hide-text-input', callback),
 | 
			
		||||
    onWindowHideAnimation: (callback) => ipcRenderer.on('window-hide-animation', callback),
 | 
			
		||||
    onWindowBlur: (callback) => ipcRenderer.on('window-blur', callback),
 | 
			
		||||
    onWindowDidShow: (callback) => ipcRenderer.on('window-did-show', callback),
 | 
			
		||||
    onResponseChunk: (callback) => ipcRenderer.on('ask-response-chunk', callback),
 | 
			
		||||
    onResponseStreamEnd: (callback) => ipcRenderer.on('ask-response-stream-end', callback),
 | 
			
		||||
    onScrollResponseUp: (callback) => ipcRenderer.on('scroll-response-up', callback),
 | 
			
		||||
    onScrollResponseDown: (callback) => ipcRenderer.on('scroll-response-down', callback),
 | 
			
		||||
    
 | 
			
		||||
    // event listener remove
 | 
			
		||||
    removeOnGlobalSend: (callback) => ipcRenderer.removeListener('ask-global-send', callback),
 | 
			
		||||
    removeOnReceiveQuestionFromAssistant: (callback) => ipcRenderer.removeListener('receive-question-from-assistant', callback),
 | 
			
		||||
    removeOnHideTextInput: (callback) => ipcRenderer.removeListener('hide-text-input', callback),
 | 
			
		||||
    removeOnWindowHideAnimation: (callback) => ipcRenderer.removeListener('window-hide-animation', callback),
 | 
			
		||||
    removeOnWindowBlur: (callback) => ipcRenderer.removeListener('window-blur', callback),
 | 
			
		||||
    removeOnWindowDidShow: (callback) => ipcRenderer.removeListener('window-did-show', callback),
 | 
			
		||||
    removeOnResponseChunk: (callback) => ipcRenderer.removeListener('ask-response-chunk', callback),
 | 
			
		||||
    removeOnResponseStreamEnd: (callback) => ipcRenderer.removeListener('ask-response-stream-end', callback),
 | 
			
		||||
    removeOnScrollResponseUp: (callback) => ipcRenderer.removeListener('scroll-response-up', callback),
 | 
			
		||||
    removeOnScrollResponseDown: (callback) => ipcRenderer.removeListener('scroll-response-down', callback)
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // Listen
 | 
			
		||||
  listen: {
 | 
			
		||||
    // window
 | 
			
		||||
    adjustWindowHeight: (height) => ipcRenderer.invoke('adjust-window-height', height),
 | 
			
		||||
    
 | 
			
		||||
    // event listener
 | 
			
		||||
    onSessionStateChanged: (callback) => ipcRenderer.on('session-state-changed', callback),
 | 
			
		||||
    onSttUpdate: (callback) => ipcRenderer.on('stt-update', callback),
 | 
			
		||||
    onSummaryUpdate: (callback) => ipcRenderer.on('summary-update', callback),
 | 
			
		||||
    
 | 
			
		||||
    // remove event listener
 | 
			
		||||
    removeOnSessionStateChanged: (callback) => ipcRenderer.removeListener('session-state-changed', callback),
 | 
			
		||||
    removeOnSttUpdate: (callback) => ipcRenderer.removeListener('stt-update', callback),
 | 
			
		||||
    removeOnSummaryUpdate: (callback) => ipcRenderer.removeListener('summary-update', callback),
 | 
			
		||||
    
 | 
			
		||||
    // Ask window
 | 
			
		||||
    isAskWindowVisible: (windowName) => ipcRenderer.invoke('is-ask-window-visible', windowName),
 | 
			
		||||
    toggleFeature: (featureName) => ipcRenderer.invoke('toggle-feature', featureName),
 | 
			
		||||
    sendQuestionToAsk: (question) => ipcRenderer.invoke('send-question-to-ask', question)
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // Audio
 | 
			
		||||
  audio: {
 | 
			
		||||
    // audio capture
 | 
			
		||||
    sendAudioContent: (options) => ipcRenderer.invoke('send-audio-content', options),
 | 
			
		||||
    sendSystemAudioContent: (options) => ipcRenderer.invoke('send-system-audio-content', options),
 | 
			
		||||
    
 | 
			
		||||
    // macOS audio
 | 
			
		||||
    startMacosAudio: () => ipcRenderer.invoke('start-macos-audio'),
 | 
			
		||||
    stopMacosAudio: () => ipcRenderer.invoke('stop-macos-audio'),
 | 
			
		||||
    
 | 
			
		||||
    // screen capture
 | 
			
		||||
    startScreenCapture: () => ipcRenderer.invoke('start-screen-capture'),
 | 
			
		||||
    stopScreenCapture: () => ipcRenderer.invoke('stop-screen-capture'),
 | 
			
		||||
    captureScreenshot: (options) => ipcRenderer.invoke('capture-screenshot', options),
 | 
			
		||||
    getCurrentScreenshot: () => ipcRenderer.invoke('get-current-screenshot'),
 | 
			
		||||
    
 | 
			
		||||
    // session
 | 
			
		||||
    isSessionActive: () => ipcRenderer.invoke('is-session-active'),
 | 
			
		||||
    
 | 
			
		||||
    // event listener
 | 
			
		||||
    onChangeListenCaptureState: (callback) => ipcRenderer.on('change-listen-capture-state', callback),
 | 
			
		||||
    onSystemAudioData: (callback) => ipcRenderer.on('system-audio-data', callback),
 | 
			
		||||
    
 | 
			
		||||
    // remove event listener
 | 
			
		||||
    removeOnChangeListenCaptureState: (callback) => ipcRenderer.removeListener('change-listen-capture-state', callback),
 | 
			
		||||
    removeOnSystemAudioData: (callback) => ipcRenderer.removeListener('system-audio-data', callback)
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // Settings
 | 
			
		||||
  settings: {
 | 
			
		||||
    // shortcut
 | 
			
		||||
    saveShortcuts: (shortcuts) => ipcRenderer.invoke('save-shortcuts', shortcuts),
 | 
			
		||||
    getDefaultShortcuts: () => ipcRenderer.invoke('get-default-shortcuts'),
 | 
			
		||||
    
 | 
			
		||||
    // shortcut editor
 | 
			
		||||
    closeShortcutEditor: () => ipcRenderer.send('close-shortcut-editor'),
 | 
			
		||||
    
 | 
			
		||||
    // event listener
 | 
			
		||||
    onLoadShortcuts: (callback) => ipcRenderer.on('load-shortcuts', callback),
 | 
			
		||||
    
 | 
			
		||||
    // remove event listener
 | 
			
		||||
    removeOnLoadShortcuts: (callback) => ipcRenderer.removeListener('load-shortcuts', callback)
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // App
 | 
			
		||||
  app: {
 | 
			
		||||
    // quit application
 | 
			
		||||
    quitApplication: () => ipcRenderer.invoke('quit-application'),
 | 
			
		||||
    
 | 
			
		||||
    // session
 | 
			
		||||
    isSessionActive: () => ipcRenderer.invoke('is-session-active'),
 | 
			
		||||
    
 | 
			
		||||
    // event listener
 | 
			
		||||
    onClickThroughToggled: (callback) => ipcRenderer.on('click-through-toggled', callback),
 | 
			
		||||
    
 | 
			
		||||
    // remove event listener
 | 
			
		||||
    removeOnClickThroughToggled: (callback) => ipcRenderer.removeListener('click-through-toggled', callback),
 | 
			
		||||
    
 | 
			
		||||
    // remove all listeners
 | 
			
		||||
    removeAllListeners: (eventName) => ipcRenderer.removeAllListeners(eventName)
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // API Key Header
 | 
			
		||||
  apikey: {
 | 
			
		||||
    // model
 | 
			
		||||
    getProviderConfig: () => ipcRenderer.invoke('model:get-provider-config'),
 | 
			
		||||
    validateKey: (options) => ipcRenderer.invoke('model:validate-key', options),
 | 
			
		||||
    setSelectedModel: (options) => ipcRenderer.invoke('model:set-selected-model', options),
 | 
			
		||||
    areProvidersConfigured: () => ipcRenderer.invoke('model:are-providers-configured'),
 | 
			
		||||
    
 | 
			
		||||
    // Ollama
 | 
			
		||||
    getOllamaStatus: () => ipcRenderer.invoke('ollama:get-status'),
 | 
			
		||||
    getModelSuggestions: () => ipcRenderer.invoke('ollama:get-model-suggestions'),
 | 
			
		||||
    ensureReady: () => ipcRenderer.invoke('ollama:ensure-ready'),
 | 
			
		||||
    installOllama: () => ipcRenderer.invoke('ollama:install'),
 | 
			
		||||
    startService: () => ipcRenderer.invoke('ollama:start-service'),
 | 
			
		||||
    pullModel: (modelName) => ipcRenderer.invoke('ollama:pull-model', modelName),
 | 
			
		||||
    
 | 
			
		||||
    // Whisper
 | 
			
		||||
    downloadModel: (modelId) => ipcRenderer.invoke('whisper:download-model', modelId),
 | 
			
		||||
    
 | 
			
		||||
    // position
 | 
			
		||||
    getHeaderPosition: () => ipcRenderer.invoke('get-header-position'),
 | 
			
		||||
    moveHeaderTo: (x, y) => ipcRenderer.invoke('move-header-to', x, y),
 | 
			
		||||
    
 | 
			
		||||
    // authentication
 | 
			
		||||
    startFirebaseAuth: () => ipcRenderer.invoke('start-firebase-auth'),
 | 
			
		||||
    getCurrentUser: () => ipcRenderer.invoke('get-current-user'),
 | 
			
		||||
    quitApplication: () => ipcRenderer.invoke('quit-application'),
 | 
			
		||||
    
 | 
			
		||||
    // event listener
 | 
			
		||||
    onOllamaInstallProgress: (callback) => ipcRenderer.on('ollama:install-progress', callback),
 | 
			
		||||
    onOllamaInstallComplete: (callback) => ipcRenderer.on('ollama:install-complete', callback),
 | 
			
		||||
    onOllamaPullProgress: (callback) => ipcRenderer.on('ollama:pull-progress', callback),
 | 
			
		||||
    onWhisperDownloadProgress: (callback) => ipcRenderer.on('whisper:download-progress', callback),
 | 
			
		||||
    
 | 
			
		||||
    // remove event listener
 | 
			
		||||
    removeOnOllamaInstallProgress: (callback) => ipcRenderer.removeListener('ollama:install-progress', callback),
 | 
			
		||||
    removeOnOllamaInstallComplete: (callback) => ipcRenderer.removeListener('ollama:install-complete', callback),
 | 
			
		||||
    removeOnOllamaPullProgress: (callback) => ipcRenderer.removeListener('ollama:pull-progress', callback),
 | 
			
		||||
    removeOnWhisperDownloadProgress: (callback) => ipcRenderer.removeListener('whisper:download-progress', callback),
 | 
			
		||||
    
 | 
			
		||||
    // remove all listeners
 | 
			
		||||
    removeAllListeners: (eventName) => ipcRenderer.removeAllListeners(eventName)
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // Controller
 | 
			
		||||
  controller: {
 | 
			
		||||
    // user state
 | 
			
		||||
    getCurrentUser: () => ipcRenderer.invoke('get-current-user'),
 | 
			
		||||
    
 | 
			
		||||
    // model
 | 
			
		||||
    areProvidersConfigured: () => ipcRenderer.invoke('model:are-providers-configured'),
 | 
			
		||||
    
 | 
			
		||||
    // permission
 | 
			
		||||
    checkPermissionsCompleted: () => ipcRenderer.invoke('check-permissions-completed'),
 | 
			
		||||
    checkSystemPermissions: () => ipcRenderer.invoke('check-system-permissions'),
 | 
			
		||||
    
 | 
			
		||||
    // window
 | 
			
		||||
    resizeHeaderWindow: (options) => ipcRenderer.invoke('resize-header-window', options),
 | 
			
		||||
    
 | 
			
		||||
    // state change
 | 
			
		||||
    sendHeaderStateChanged: (state) => ipcRenderer.send('header-state-changed', state),
 | 
			
		||||
    
 | 
			
		||||
    // event listener
 | 
			
		||||
    onUserStateChanged: (callback) => ipcRenderer.on('user-state-changed', callback),
 | 
			
		||||
    onAuthFailed: (callback) => ipcRenderer.on('auth-failed', callback),
 | 
			
		||||
    onForceShowApiKeyHeader: (callback) => ipcRenderer.on('force-show-apikey-header', callback),
 | 
			
		||||
    
 | 
			
		||||
    // remove event listener
 | 
			
		||||
    removeOnUserStateChanged: (callback) => ipcRenderer.removeListener('user-state-changed', callback),
 | 
			
		||||
    removeOnAuthFailed: (callback) => ipcRenderer.removeListener('auth-failed', callback),
 | 
			
		||||
    removeOnForceShowApiKeyHeader: (callback) => ipcRenderer.removeListener('force-show-apikey-header', callback)
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // Header
 | 
			
		||||
  header: {
 | 
			
		||||
    // position
 | 
			
		||||
    getHeaderPosition: () => ipcRenderer.invoke('get-header-position'),
 | 
			
		||||
    moveHeaderTo: (x, y) => ipcRenderer.invoke('move-header-to', x, y),
 | 
			
		||||
    
 | 
			
		||||
    // event listener
 | 
			
		||||
    onSessionStateText: (callback) => ipcRenderer.on('session-state-text', callback),
 | 
			
		||||
    onShortcutsUpdated: (callback) => ipcRenderer.on('shortcuts-updated', callback),
 | 
			
		||||
    
 | 
			
		||||
    // remove event listener
 | 
			
		||||
    removeOnSessionStateText: (callback) => ipcRenderer.removeListener('session-state-text', callback),
 | 
			
		||||
    removeOnShortcutsUpdated: (callback) => ipcRenderer.removeListener('shortcuts-updated', callback),
 | 
			
		||||
    
 | 
			
		||||
    // animation
 | 
			
		||||
    sendAnimationFinished: (state) => ipcRenderer.send('header-animation-finished', state),
 | 
			
		||||
    
 | 
			
		||||
    // settings window
 | 
			
		||||
    cancelHideSettingsWindow: () => ipcRenderer.send('cancel-hide-settings-window'),
 | 
			
		||||
    showSettingsWindow: (options) => ipcRenderer.send('show-settings-window', options),
 | 
			
		||||
    hideSettingsWindow: () => ipcRenderer.send('hide-settings-window'),
 | 
			
		||||
    
 | 
			
		||||
    // invoke
 | 
			
		||||
    invoke: (channel, ...args) => ipcRenderer.invoke(channel, ...args)
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // Permissions
 | 
			
		||||
  permissions: {
 | 
			
		||||
    checkSystemPermissions: () => ipcRenderer.invoke('check-system-permissions'),
 | 
			
		||||
    requestMicrophonePermission: () => ipcRenderer.invoke('request-microphone-permission'),
 | 
			
		||||
    openSystemPreferences: (section) => ipcRenderer.invoke('open-system-preferences', section),
 | 
			
		||||
    markPermissionsCompleted: () => ipcRenderer.invoke('mark-permissions-completed'),
 | 
			
		||||
    quitApplication: () => ipcRenderer.invoke('quit-application')
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // Animation
 | 
			
		||||
  animation: {
 | 
			
		||||
    // send animation finished
 | 
			
		||||
    sendAnimationFinished: () => ipcRenderer.send('animation-finished'),
 | 
			
		||||
    
 | 
			
		||||
    // event listener
 | 
			
		||||
    onWindowShowAnimation: (callback) => ipcRenderer.on('window-show-animation', callback),
 | 
			
		||||
    onWindowHideAnimation: (callback) => ipcRenderer.on('window-hide-animation', callback),
 | 
			
		||||
    onSettingsWindowHideAnimation: (callback) => ipcRenderer.on('settings-window-hide-animation', callback),
 | 
			
		||||
    onListenWindowMoveToCenter: (callback) => ipcRenderer.on('listen-window-move-to-center', callback),
 | 
			
		||||
    onListenWindowMoveToLeft: (callback) => ipcRenderer.on('listen-window-move-to-left', callback),
 | 
			
		||||
    
 | 
			
		||||
    // remove event listener
 | 
			
		||||
    removeOnWindowShowAnimation: (callback) => ipcRenderer.removeListener('window-show-animation', callback),
 | 
			
		||||
    removeOnWindowHideAnimation: (callback) => ipcRenderer.removeListener('window-hide-animation', callback),
 | 
			
		||||
    removeOnSettingsWindowHideAnimation: (callback) => ipcRenderer.removeListener('settings-window-hide-animation', callback),
 | 
			
		||||
    removeOnListenWindowMoveToCenter: (callback) => ipcRenderer.removeListener('listen-window-move-to-center', callback),
 | 
			
		||||
    removeOnListenWindowMoveToLeft: (callback) => ipcRenderer.removeListener('listen-window-move-to-left', callback)
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  feature: {
 | 
			
		||||
    // 기존 ask 관련 유지
 | 
			
		||||
    // ask
 | 
			
		||||
    submitAsk: (query) => ipcRenderer.invoke('feature:ask', query),
 | 
			
		||||
    onAskProgress: (callback) => ipcRenderer.on('feature:ask:progress', (e, p) => callback(p)),
 | 
			
		||||
 | 
			
		||||
@ -51,13 +297,13 @@ contextBridge.exposeInMainWorld('api', {
 | 
			
		||||
      hideSettingsWindow: () => ipcRenderer.send('hide-settings-window')
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  // 기존 window 유지
 | 
			
		||||
  // window
 | 
			
		||||
  window: {
 | 
			
		||||
    // 기존
 | 
			
		||||
    // window
 | 
			
		||||
    hide: () => ipcRenderer.send('window:hide'),
 | 
			
		||||
    onFocusChange: (callback) => ipcRenderer.on('window:focus-change', (e, f) => callback(f)),
 | 
			
		||||
 | 
			
		||||
    // 추가
 | 
			
		||||
    // settings window
 | 
			
		||||
    showSettingsWindow: (bounds) => ipcRenderer.send('show-settings-window', bounds),
 | 
			
		||||
    hideSettingsWindow: () => ipcRenderer.send('hide-settings-window'),
 | 
			
		||||
    cancelHideSettingsWindow: () => ipcRenderer.send('cancel-hide-settings-window'),
 | 
			
		||||
@ -67,7 +313,7 @@ contextBridge.exposeInMainWorld('api', {
 | 
			
		||||
    ollamaShutdown: (graceful) => ipcRenderer.invoke('ollama:shutdown', graceful),
 | 
			
		||||
    startFirebaseAuth: () => ipcRenderer.invoke('start-firebase-auth'),
 | 
			
		||||
 | 
			
		||||
    // on methods (listeners)
 | 
			
		||||
    // event listener
 | 
			
		||||
    onUserStateChanged: (callback) => ipcRenderer.on('user-state-changed', callback),
 | 
			
		||||
    removeOnUserStateChanged: (callback) => ipcRenderer.removeListener('user-state-changed', callback),
 | 
			
		||||
    onSettingsUpdated: (callback) => ipcRenderer.on('settings-updated', callback),
 | 
			
		||||
@ -79,7 +325,7 @@ contextBridge.exposeInMainWorld('api', {
 | 
			
		||||
    onWhisperDownloadProgress: (callback) => ipcRenderer.on('whisper:download-progress', callback),
 | 
			
		||||
    removeOnWhisperDownloadProgress: (callback) => ipcRenderer.removeListener('whisper:download-progress', callback),
 | 
			
		||||
 | 
			
		||||
    // send methods
 | 
			
		||||
    // send
 | 
			
		||||
    cancelHideSettingsWindow: () => ipcRenderer.send('cancel-hide-settings-window'),
 | 
			
		||||
    hideSettingsWindow: () => ipcRenderer.send('hide-settings-window')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -370,13 +370,12 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async loadProviderConfig() {
 | 
			
		||||
    if (!window.require) return;
 | 
			
		||||
    const { ipcRenderer } = window.require('electron');
 | 
			
		||||
            if (!window.api || !window.api.apikey) return;
 | 
			
		||||
    
 | 
			
		||||
    try {
 | 
			
		||||
        const [config, ollamaStatus] = await Promise.all([
 | 
			
		||||
            ipcRenderer.invoke('model:get-provider-config'),
 | 
			
		||||
            ipcRenderer.invoke('ollama:get-status')
 | 
			
		||||
            window.api.apikey.getProviderConfig(),
 | 
			
		||||
            window.api.apikey.getOllamaStatus()
 | 
			
		||||
        ]);
 | 
			
		||||
        
 | 
			
		||||
        const llmProviders = [];
 | 
			
		||||
@ -428,8 +427,8 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
 | 
			
		||||
    e.preventDefault()
 | 
			
		||||
 | 
			
		||||
    const { ipcRenderer } = window.require("electron")
 | 
			
		||||
    const initialPosition = await ipcRenderer.invoke("get-header-position")
 | 
			
		||||
    if (!window.api || !window.api.apikey) return;
 | 
			
		||||
    const initialPosition = await window.api.apikey.getHeaderPosition()
 | 
			
		||||
 | 
			
		||||
    this.dragState = {
 | 
			
		||||
      initialMouseX: e.screenX,
 | 
			
		||||
@ -456,8 +455,9 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
    const newWindowX = this.dragState.initialWindowX + (e.screenX - this.dragState.initialMouseX)
 | 
			
		||||
    const newWindowY = this.dragState.initialWindowY + (e.screenY - this.dragState.initialMouseY)
 | 
			
		||||
 | 
			
		||||
    const { ipcRenderer } = window.require("electron")
 | 
			
		||||
    ipcRenderer.invoke("move-header-to", newWindowX, newWindowY)
 | 
			
		||||
    if (window.api && window.api.apikey) {
 | 
			
		||||
      window.api.apikey.moveHeaderTo(newWindowX, newWindowY)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleMouseUp(e) {
 | 
			
		||||
@ -652,9 +652,8 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
    try {
 | 
			
		||||
      // Lightweight health check - just ping the service
 | 
			
		||||
      const isHealthy = await this._executeOperation('health_check', async () => {
 | 
			
		||||
        if (!window.require) return false;
 | 
			
		||||
        const { ipcRenderer } = window.require('electron');
 | 
			
		||||
        const result = await ipcRenderer.invoke('ollama:get-status');
 | 
			
		||||
        if (!window.api || !window.api.apikey) return false;
 | 
			
		||||
                    const result = await window.api.apikey.getOllamaStatus();
 | 
			
		||||
        return result?.success && result?.running;
 | 
			
		||||
      }, { timeout: 5000, priority: 'low' });
 | 
			
		||||
      
 | 
			
		||||
@ -928,14 +927,13 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  async refreshOllamaStatus() {
 | 
			
		||||
    if (!window.require) return;
 | 
			
		||||
    if (!window.api || !window.api.apikey) return;
 | 
			
		||||
    
 | 
			
		||||
    try {
 | 
			
		||||
      this._updateConnectionState('connecting', 'Checking Ollama status');
 | 
			
		||||
      
 | 
			
		||||
      const result = await this._executeOperation('ollama_status', async () => {
 | 
			
		||||
        const { ipcRenderer } = window.require('electron');
 | 
			
		||||
        return await ipcRenderer.invoke('ollama:get-status');
 | 
			
		||||
        return await window.api.apikey.getOllamaStatus();
 | 
			
		||||
      });
 | 
			
		||||
      
 | 
			
		||||
      if (result?.success) {
 | 
			
		||||
@ -960,12 +958,11 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  async loadModelSuggestions() {
 | 
			
		||||
    if (!window.require) return;
 | 
			
		||||
    if (!window.api || !window.api.apikey) return;
 | 
			
		||||
    
 | 
			
		||||
    try {
 | 
			
		||||
      const result = await this._executeOperation('model_suggestions', async () => {
 | 
			
		||||
        const { ipcRenderer } = window.require('electron');
 | 
			
		||||
        return await ipcRenderer.invoke('ollama:get-model-suggestions');
 | 
			
		||||
        return await window.api.apikey.getModelSuggestions();
 | 
			
		||||
      });
 | 
			
		||||
      
 | 
			
		||||
      if (result?.success) {
 | 
			
		||||
@ -988,14 +985,13 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async ensureOllamaReady() {
 | 
			
		||||
    if (!window.require) return false;
 | 
			
		||||
    if (!window.api || !window.api.apikey) return false;
 | 
			
		||||
    
 | 
			
		||||
    try {
 | 
			
		||||
      this._updateConnectionState('connecting', 'Ensuring Ollama is ready');
 | 
			
		||||
      
 | 
			
		||||
      const result = await this._executeOperation('ollama_ensure_ready', async () => {
 | 
			
		||||
        const { ipcRenderer } = window.require('electron');
 | 
			
		||||
        return await ipcRenderer.invoke('ollama:ensure-ready');
 | 
			
		||||
        return await window.api.apikey.ensureReady();
 | 
			
		||||
      }, { timeout: this.operationTimeout });
 | 
			
		||||
      
 | 
			
		||||
      if (result?.success) {
 | 
			
		||||
@ -1015,8 +1011,7 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async ensureOllamaReadyWithUI() {
 | 
			
		||||
    if (!window.require) return false;
 | 
			
		||||
    const { ipcRenderer } = window.require("electron");
 | 
			
		||||
    if (!window.api || !window.api.apikey) return false;
 | 
			
		||||
 | 
			
		||||
    this.installingModel = "Setting up Ollama";
 | 
			
		||||
    this.installProgress = 0;
 | 
			
		||||
@ -1074,21 +1069,21 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
      operationCompleted = true;
 | 
			
		||||
      clearTimeout(completionTimeout);
 | 
			
		||||
      
 | 
			
		||||
      ipcRenderer.removeListener("ollama:install-progress", progressHandler);
 | 
			
		||||
      window.api.apikey.removeOnOllamaInstallProgress(progressHandler);
 | 
			
		||||
      await this._handleOllamaSetupCompletion(result.success, result.error);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    ipcRenderer.once("ollama:install-complete", completionHandler);
 | 
			
		||||
    ipcRenderer.on("ollama:install-progress", progressHandler);
 | 
			
		||||
    window.api.apikey.onOllamaInstallComplete(completionHandler);
 | 
			
		||||
    window.api.apikey.onOllamaInstallProgress(progressHandler);
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      let result;
 | 
			
		||||
      if (!this.ollamaStatus.installed) {
 | 
			
		||||
        console.log("[ApiKeyHeader] Ollama not installed. Starting installation.");
 | 
			
		||||
        result = await ipcRenderer.invoke("ollama:install");
 | 
			
		||||
        result = await window.api.apikey.installOllama();
 | 
			
		||||
      } else {
 | 
			
		||||
        console.log("[ApiKeyHeader] Ollama installed. Starting service.");
 | 
			
		||||
        result = await ipcRenderer.invoke("ollama:start-service");
 | 
			
		||||
        result = await window.api.apikey.startService();
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      // If IPC call succeeds but no event received, handle completion manually
 | 
			
		||||
@ -1106,8 +1101,8 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
      operationCompleted = true;
 | 
			
		||||
      clearTimeout(completionTimeout);
 | 
			
		||||
      console.error("[ApiKeyHeader] Ollama setup failed:", error);
 | 
			
		||||
      ipcRenderer.removeListener("ollama:install-progress", progressHandler);
 | 
			
		||||
      ipcRenderer.removeListener("ollama:install-complete", completionHandler);
 | 
			
		||||
      window.api.apikey.removeOnOllamaInstallProgress(progressHandler);
 | 
			
		||||
      window.api.apikey.removeOnOllamaInstallComplete(completionHandler);
 | 
			
		||||
      await this._handleOllamaSetupCompletion(false, error.message);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@ -1229,7 +1224,7 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
    this.clearMessages();
 | 
			
		||||
    this.requestUpdate();
 | 
			
		||||
    
 | 
			
		||||
    const { ipcRenderer } = window.require('electron');
 | 
			
		||||
    if (!window.api || !window.api.apikey) return;
 | 
			
		||||
    let progressHandler = null;
 | 
			
		||||
    
 | 
			
		||||
    try {
 | 
			
		||||
@ -1249,10 +1244,10 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
      };
 | 
			
		||||
      
 | 
			
		||||
      // Set up progress tracking
 | 
			
		||||
      ipcRenderer.on('ollama:pull-progress', progressHandler);
 | 
			
		||||
      window.api.apikey.onOllamaPullProgress(progressHandler);
 | 
			
		||||
      
 | 
			
		||||
      // Execute the model pull with timeout
 | 
			
		||||
      const installPromise = ipcRenderer.invoke('ollama:pull-model', modelName);
 | 
			
		||||
      const installPromise = window.api.apikey.pullModel(modelName);
 | 
			
		||||
      const timeoutPromise = new Promise((_, reject) => 
 | 
			
		||||
        setTimeout(() => reject(new Error('Installation timeout after 10 minutes')), 600000)
 | 
			
		||||
      );
 | 
			
		||||
@ -1281,7 +1276,7 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
    } finally {
 | 
			
		||||
      // Comprehensive cleanup
 | 
			
		||||
      if (progressHandler) {
 | 
			
		||||
        ipcRenderer.removeListener('ollama:pull-progress', progressHandler);
 | 
			
		||||
        window.api.apikey.removeOnOllamaPullProgress(progressHandler);
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      this.installingModel = null;
 | 
			
		||||
@ -1307,7 +1302,7 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
    this.clearMessages();
 | 
			
		||||
    this.requestUpdate();
 | 
			
		||||
    
 | 
			
		||||
    const { ipcRenderer } = window.require('electron');
 | 
			
		||||
    if (!window.api || !window.api.apikey) return;
 | 
			
		||||
    let progressHandler = null;
 | 
			
		||||
    
 | 
			
		||||
    try {
 | 
			
		||||
@ -1321,10 +1316,10 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
        }
 | 
			
		||||
      };
 | 
			
		||||
      
 | 
			
		||||
      ipcRenderer.on('whisper:download-progress', progressHandler);
 | 
			
		||||
      window.api.apikey.onWhisperDownloadProgress(progressHandler);
 | 
			
		||||
      
 | 
			
		||||
      // Start download with timeout protection
 | 
			
		||||
      const downloadPromise = ipcRenderer.invoke('whisper:download-model', modelId);
 | 
			
		||||
      const downloadPromise = window.api.apikey.downloadModel(modelId);
 | 
			
		||||
      const timeoutPromise = new Promise((_, reject) => 
 | 
			
		||||
        setTimeout(() => reject(new Error('Download timeout after 10 minutes')), 600000)
 | 
			
		||||
      );
 | 
			
		||||
@ -1351,7 +1346,7 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
    } finally {
 | 
			
		||||
      // Cleanup
 | 
			
		||||
      if (progressHandler) {
 | 
			
		||||
        ipcRenderer.removeListener('whisper:download-progress', progressHandler);
 | 
			
		||||
        window.api.apikey.removeOnWhisperDownloadProgress(progressHandler);
 | 
			
		||||
      }
 | 
			
		||||
      delete this.whisperInstallingModels[modelId];
 | 
			
		||||
      this.requestUpdate();
 | 
			
		||||
@ -1412,7 +1407,7 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
    this.clearMessages();
 | 
			
		||||
    this.requestUpdate();
 | 
			
		||||
 | 
			
		||||
    const { ipcRenderer } = window.require('electron');
 | 
			
		||||
    if (!window.api || !window.api.apikey) return;
 | 
			
		||||
    
 | 
			
		||||
    try {
 | 
			
		||||
        // Handle LLM provider
 | 
			
		||||
@ -1436,14 +1431,14 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            // Validate Ollama is working
 | 
			
		||||
            llmResult = await ipcRenderer.invoke('model:validate-key', { 
 | 
			
		||||
            llmResult = await window.api.apikey.validateKey({ 
 | 
			
		||||
                provider: 'ollama', 
 | 
			
		||||
                key: 'local' 
 | 
			
		||||
            });
 | 
			
		||||
            
 | 
			
		||||
            if (llmResult.success) {
 | 
			
		||||
                // Set the selected model
 | 
			
		||||
                await ipcRenderer.invoke('model:set-selected-model', { 
 | 
			
		||||
                await window.api.apikey.setSelectedModel({ 
 | 
			
		||||
                    type: 'llm', 
 | 
			
		||||
                    modelId: this.selectedLlmModel 
 | 
			
		||||
                });
 | 
			
		||||
@ -1454,7 +1449,7 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
                throw new Error('Please enter LLM API key');
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            llmResult = await ipcRenderer.invoke('model:validate-key', { 
 | 
			
		||||
            llmResult = await window.api.apikey.validateKey({ 
 | 
			
		||||
                provider: this.llmProvider, 
 | 
			
		||||
                key: this.llmApiKey.trim() 
 | 
			
		||||
            });
 | 
			
		||||
@ -1467,14 +1462,14 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
            sttResult = { success: true };
 | 
			
		||||
        } else if (this.sttProvider === 'whisper') {
 | 
			
		||||
            // For Whisper, just validate it's enabled (model download already handled in handleSttModelChange)
 | 
			
		||||
            sttResult = await ipcRenderer.invoke('model:validate-key', { 
 | 
			
		||||
            sttResult = await window.api.apikey.validateKey({ 
 | 
			
		||||
                provider: 'whisper', 
 | 
			
		||||
                key: 'local' 
 | 
			
		||||
            });
 | 
			
		||||
            
 | 
			
		||||
            if (sttResult.success && this.selectedSttModel) {
 | 
			
		||||
                // Set the selected model
 | 
			
		||||
                await ipcRenderer.invoke('model:set-selected-model', { 
 | 
			
		||||
                await window.api.apikey.setSelectedModel({ 
 | 
			
		||||
                    type: 'stt', 
 | 
			
		||||
                    modelId: this.selectedSttModel 
 | 
			
		||||
                });
 | 
			
		||||
@ -1485,7 +1480,7 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
                throw new Error('Please enter STT API key');
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            sttResult = await ipcRenderer.invoke('model:validate-key', { 
 | 
			
		||||
            sttResult = await window.api.apikey.validateKey({ 
 | 
			
		||||
                provider: this.sttProvider, 
 | 
			
		||||
                key: this.sttApiKey.trim() 
 | 
			
		||||
            });
 | 
			
		||||
@ -1522,15 +1517,15 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
    e.preventDefault()
 | 
			
		||||
 | 
			
		||||
    console.log("Requesting Firebase authentication from main process...")
 | 
			
		||||
    if (window.require) {
 | 
			
		||||
      window.require("electron").ipcRenderer.invoke("start-firebase-auth")
 | 
			
		||||
    if (window.api && window.api.apikey) {
 | 
			
		||||
      window.api.apikey.startFirebaseAuth()
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleClose() {
 | 
			
		||||
    console.log("Close button clicked")
 | 
			
		||||
    if (window.require) {
 | 
			
		||||
      window.require("electron").ipcRenderer.invoke("quit-application")
 | 
			
		||||
    if (window.api && window.api.apikey) {
 | 
			
		||||
      window.api.apikey.quitApplication()
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -1543,8 +1538,8 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
    
 | 
			
		||||
    console.log('[ApiKeyHeader] handleAnimationEnd: Animation completed, transitioning to next state...');
 | 
			
		||||
    
 | 
			
		||||
    if (!window.require) {
 | 
			
		||||
      console.error('[ApiKeyHeader] handleAnimationEnd: window.require not available');
 | 
			
		||||
    if (!window.api || !window.api.apikey) {
 | 
			
		||||
      console.error('[ApiKeyHeader] handleAnimationEnd: window.api.apikey not available');
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
@ -1553,14 +1548,12 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    const { ipcRenderer } = window.require('electron');
 | 
			
		||||
    
 | 
			
		||||
    ipcRenderer.invoke('get-current-user')
 | 
			
		||||
    window.api.apikey.getCurrentUser()
 | 
			
		||||
      .then(userState => {
 | 
			
		||||
        console.log('[ApiKeyHeader] handleAnimationEnd: User state retrieved:', userState);
 | 
			
		||||
        
 | 
			
		||||
        // Additional validation for local providers
 | 
			
		||||
        return ipcRenderer.invoke('model:are-providers-configured').then(isConfigured => {
 | 
			
		||||
        return window.api.apikey.areProvidersConfigured().then(isConfigured => {
 | 
			
		||||
          console.log('[ApiKeyHeader] handleAnimationEnd: Providers configured check:', isConfigured);
 | 
			
		||||
          
 | 
			
		||||
          if (!isConfigured) {
 | 
			
		||||
@ -1624,12 +1617,11 @@ export class ApiKeyHeader extends LitElement {
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Cleanup event listeners
 | 
			
		||||
    if (window.require) {
 | 
			
		||||
      const { ipcRenderer } = window.require('electron');
 | 
			
		||||
      ipcRenderer.removeAllListeners('whisper:download-progress');
 | 
			
		||||
      ipcRenderer.removeAllListeners('ollama:install-progress');
 | 
			
		||||
      ipcRenderer.removeAllListeners('ollama:pull-progress');
 | 
			
		||||
      ipcRenderer.removeAllListeners('ollama:install-complete');
 | 
			
		||||
    if (window.api && window.api.apikey) {
 | 
			
		||||
      window.api.apikey.removeAllListeners('whisper:download-progress');
 | 
			
		||||
      window.api.apikey.removeAllListeners('ollama:install-progress');
 | 
			
		||||
      window.api.apikey.removeAllListeners('ollama:pull-progress');
 | 
			
		||||
      window.api.apikey.removeAllListeners('ollama:install-complete');
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Cancel any ongoing downloads
 | 
			
		||||
 | 
			
		||||
@ -50,22 +50,20 @@ class HeaderTransitionManager {
 | 
			
		||||
 | 
			
		||||
        this._bootstrap();
 | 
			
		||||
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
 | 
			
		||||
            ipcRenderer.on('user-state-changed', (event, userState) => {
 | 
			
		||||
        if (window.api && window.api.controller) {
 | 
			
		||||
            window.api.controller.onUserStateChanged((event, userState) => {
 | 
			
		||||
                console.log('[HeaderController] Received user state change:', userState);
 | 
			
		||||
                this.handleStateUpdate(userState);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            ipcRenderer.on('auth-failed', (event, { message }) => {
 | 
			
		||||
            window.api.controller.onAuthFailed((event, { message }) => {
 | 
			
		||||
                console.error('[HeaderController] Received auth failure from main process:', message);
 | 
			
		||||
                if (this.apiKeyHeader) {
 | 
			
		||||
                    this.apiKeyHeader.errorMessage = 'Authentication failed. Please try again.';
 | 
			
		||||
                    this.apiKeyHeader.isLoading = false;
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            ipcRenderer.on('force-show-apikey-header', async () => {
 | 
			
		||||
            window.api.controller.onForceShowApiKeyHeader(async () => {
 | 
			
		||||
                console.log('[HeaderController] Received broadcast to show apikey header. Switching now.');
 | 
			
		||||
                await this._resizeForApiKey();
 | 
			
		||||
                this.ensureHeader('apikey');
 | 
			
		||||
@ -75,16 +73,16 @@ class HeaderTransitionManager {
 | 
			
		||||
 | 
			
		||||
    notifyHeaderState(stateOverride) {
 | 
			
		||||
        const state = stateOverride || this.currentHeaderType || 'apikey';
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            window.require('electron').ipcRenderer.send('header-state-changed', state);
 | 
			
		||||
        if (window.api && window.api.controller) {
 | 
			
		||||
            window.api.controller.sendHeaderStateChanged(state);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async _bootstrap() {
 | 
			
		||||
        // The initial state will be sent by the main process via 'user-state-changed'
 | 
			
		||||
        // We just need to request it.
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const userState = await window.require('electron').ipcRenderer.invoke('get-current-user');
 | 
			
		||||
        if (window.api && window.api.controller) {
 | 
			
		||||
            const userState = await window.api.controller.getCurrentUser();
 | 
			
		||||
            console.log('[HeaderController] Bootstrapping with initial user state:', userState);
 | 
			
		||||
            this.handleStateUpdate(userState);
 | 
			
		||||
        } else {
 | 
			
		||||
@ -96,8 +94,8 @@ class HeaderTransitionManager {
 | 
			
		||||
 | 
			
		||||
    //////// after_modelStateService ////////
 | 
			
		||||
    async handleStateUpdate(userState) {
 | 
			
		||||
        const { ipcRenderer } = window.require('electron');
 | 
			
		||||
        const isConfigured = await ipcRenderer.invoke('model:are-providers-configured');
 | 
			
		||||
        if (!window.api || !window.api.controller) return;
 | 
			
		||||
        const isConfigured = await window.api.controller.areProvidersConfigured();
 | 
			
		||||
 | 
			
		||||
        if (isConfigured) {
 | 
			
		||||
            const { isLoggedIn } = userState;
 | 
			
		||||
@ -126,10 +124,9 @@ class HeaderTransitionManager {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check if permissions were previously completed
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
        if (window.api && window.api.controller) {
 | 
			
		||||
            try {
 | 
			
		||||
                const permissionsCompleted = await ipcRenderer.invoke('check-permissions-completed');
 | 
			
		||||
                const permissionsCompleted = await window.api.controller.checkPermissionsCompleted();
 | 
			
		||||
                if (permissionsCompleted) {
 | 
			
		||||
                    console.log('[HeaderController] Permissions were previously completed, checking current status...');
 | 
			
		||||
                    
 | 
			
		||||
@ -162,38 +159,30 @@ class HeaderTransitionManager {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _resizeForMain() {
 | 
			
		||||
        if (!window.require) return;
 | 
			
		||||
        return window
 | 
			
		||||
            .require('electron')
 | 
			
		||||
            .ipcRenderer.invoke('resize-header-window', { width: 353, height: 47 })
 | 
			
		||||
        if (!window.api || !window.api.controller) return;
 | 
			
		||||
        return window.api.controller.resizeHeaderWindow({ width: 353, height: 47 })
 | 
			
		||||
            .catch(() => {});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async _resizeForApiKey() {
 | 
			
		||||
        if (!window.require) return;
 | 
			
		||||
        return window
 | 
			
		||||
            .require('electron')
 | 
			
		||||
            .ipcRenderer.invoke('resize-header-window', { width: 350, height: 300 })
 | 
			
		||||
        if (!window.api || !window.api.controller) return;
 | 
			
		||||
        return window.api.controller.resizeHeaderWindow({ width: 350, height: 300 })
 | 
			
		||||
            .catch(() => {});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async _resizeForPermissionHeader() {
 | 
			
		||||
        if (!window.require) return;
 | 
			
		||||
        return window
 | 
			
		||||
            .require('electron')
 | 
			
		||||
            .ipcRenderer.invoke('resize-header-window', { width: 285, height: 220 })
 | 
			
		||||
        if (!window.api || !window.api.controller) return;
 | 
			
		||||
        return window.api.controller.resizeHeaderWindow({ width: 285, height: 220 })
 | 
			
		||||
            .catch(() => {});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async checkPermissions() {
 | 
			
		||||
        if (!window.require) {
 | 
			
		||||
        if (!window.api || !window.api.controller) {
 | 
			
		||||
            return { success: true };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const { ipcRenderer } = window.require('electron');
 | 
			
		||||
        
 | 
			
		||||
        try {
 | 
			
		||||
            const permissions = await ipcRenderer.invoke('check-system-permissions');
 | 
			
		||||
            const permissions = await window.api.controller.checkSystemPermissions();
 | 
			
		||||
            console.log('[HeaderController] Current permissions:', permissions);
 | 
			
		||||
            
 | 
			
		||||
            if (!permissions.needsSetup) {
 | 
			
		||||
 | 
			
		||||
@ -362,8 +362,8 @@ export class MainHeader extends LitElement {
 | 
			
		||||
    async handleMouseDown(e) {
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
 | 
			
		||||
        const { ipcRenderer } = window.require('electron');
 | 
			
		||||
        const initialPosition = await ipcRenderer.invoke('get-header-position');
 | 
			
		||||
        if (!window.api || !window.api.header) return;
 | 
			
		||||
        const initialPosition = await window.api.header.getHeaderPosition();
 | 
			
		||||
 | 
			
		||||
        this.dragState = {
 | 
			
		||||
            initialMouseX: e.screenX,
 | 
			
		||||
@ -390,8 +390,9 @@ export class MainHeader extends LitElement {
 | 
			
		||||
        const newWindowX = this.dragState.initialWindowX + (e.screenX - this.dragState.initialMouseX);
 | 
			
		||||
        const newWindowY = this.dragState.initialWindowY + (e.screenY - this.dragState.initialMouseY);
 | 
			
		||||
 | 
			
		||||
        const { ipcRenderer } = window.require('electron');
 | 
			
		||||
        ipcRenderer.invoke('move-header-to', newWindowX, newWindowY);
 | 
			
		||||
        if (window.api && window.api.header) {
 | 
			
		||||
            window.api.header.moveHeaderTo(newWindowX, newWindowY);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    handleMouseUp(e) {
 | 
			
		||||
@ -447,12 +448,12 @@ export class MainHeader extends LitElement {
 | 
			
		||||
    
 | 
			
		||||
        if (this.classList.contains('hiding')) {
 | 
			
		||||
            this.classList.add('hidden');
 | 
			
		||||
            if (window.require) {
 | 
			
		||||
                window.require('electron').ipcRenderer.send('header-animation-finished', 'hidden');
 | 
			
		||||
            if (window.api && window.api.header) {
 | 
			
		||||
                window.api.header.sendAnimationFinished('hidden');
 | 
			
		||||
            }
 | 
			
		||||
        } else if (this.classList.contains('showing')) {
 | 
			
		||||
            if (window.require) {
 | 
			
		||||
                window.require('electron').ipcRenderer.send('header-animation-finished', 'visible');
 | 
			
		||||
            if (window.api && window.api.header) {
 | 
			
		||||
                window.api.header.sendAnimationFinished('visible');
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -466,14 +467,12 @@ export class MainHeader extends LitElement {
 | 
			
		||||
        super.connectedCallback();
 | 
			
		||||
        this.addEventListener('animationend', this.handleAnimationEnd);
 | 
			
		||||
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
 | 
			
		||||
        if (window.api && window.api.header) {
 | 
			
		||||
            this._sessionStateTextListener = (event, text) => {
 | 
			
		||||
                this.actionText = text;
 | 
			
		||||
                this.isTogglingSession = false;
 | 
			
		||||
            };
 | 
			
		||||
            ipcRenderer.on('session-state-text', this._sessionStateTextListener);
 | 
			
		||||
            window.api.header.onSessionStateText(this._sessionStateTextListener);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            // this._sessionStateListener = (event, { isActive }) => {
 | 
			
		||||
@ -485,7 +484,7 @@ export class MainHeader extends LitElement {
 | 
			
		||||
                console.log('[MainHeader] Received updated shortcuts:', keybinds);
 | 
			
		||||
                this.shortcuts = keybinds;
 | 
			
		||||
            };
 | 
			
		||||
            ipcRenderer.on('shortcuts-updated', this._shortcutListener);
 | 
			
		||||
            window.api.header.onShortcutsUpdated(this._shortcutListener);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -498,39 +497,37 @@ export class MainHeader extends LitElement {
 | 
			
		||||
            this.animationEndTimer = null;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
        if (window.api && window.api.header) {
 | 
			
		||||
            if (this._sessionStateTextListener) {
 | 
			
		||||
                ipcRenderer.removeListener('session-state-text', this._sessionStateTextListener);
 | 
			
		||||
                window.api.header.removeOnSessionStateText(this._sessionStateTextListener);
 | 
			
		||||
            }
 | 
			
		||||
            // if (this._sessionStateListener) {
 | 
			
		||||
            //     ipcRenderer.removeListener('session-state-changed', this._sessionStateListener);
 | 
			
		||||
            // }
 | 
			
		||||
            if (this._shortcutListener) {
 | 
			
		||||
                ipcRenderer.removeListener('shortcuts-updated', this._shortcutListener);
 | 
			
		||||
                window.api.header.removeOnShortcutsUpdated(this._shortcutListener);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    invoke(channel, ...args) {
 | 
			
		||||
        if (this.wasJustDragged) return;
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            window.require('electron').ipcRenderer.invoke(channel, ...args);
 | 
			
		||||
        if (window.api && window.api.header) {
 | 
			
		||||
            window.api.header.invoke(channel, ...args);
 | 
			
		||||
        }
 | 
			
		||||
        // return Promise.resolve();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    showSettingsWindow(element) {
 | 
			
		||||
        if (this.wasJustDragged) return;
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
        if (window.api && window.api.header) {
 | 
			
		||||
            console.log(`[MainHeader] showSettingsWindow called at ${Date.now()}`);
 | 
			
		||||
            
 | 
			
		||||
            ipcRenderer.send('cancel-hide-settings-window');
 | 
			
		||||
            window.api.header.cancelHideSettingsWindow();
 | 
			
		||||
 | 
			
		||||
            if (element) {
 | 
			
		||||
                const { left, top, width, height } = element.getBoundingClientRect();
 | 
			
		||||
                ipcRenderer.send('show-settings-window', {
 | 
			
		||||
                window.api.header.showSettingsWindow({
 | 
			
		||||
                    x: left,
 | 
			
		||||
                    y: top,
 | 
			
		||||
                    width,
 | 
			
		||||
@ -542,9 +539,9 @@ export class MainHeader extends LitElement {
 | 
			
		||||
 | 
			
		||||
    hideSettingsWindow() {
 | 
			
		||||
        if (this.wasJustDragged) return;
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
        if (window.api && window.api.header) {
 | 
			
		||||
            console.log(`[MainHeader] hideSettingsWindow called at ${Date.now()}`);
 | 
			
		||||
            window.require('electron').ipcRenderer.send('hide-settings-window');
 | 
			
		||||
            window.api.header.hideSettingsWindow();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -288,13 +288,12 @@ export class PermissionHeader extends LitElement {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async checkPermissions() {
 | 
			
		||||
        if (!window.require || this.isChecking) return;
 | 
			
		||||
        
 | 
			
		||||
                if (!window.api || !window.api.permissions || this.isChecking) return;
 | 
			
		||||
 | 
			
		||||
        this.isChecking = true;
 | 
			
		||||
        const { ipcRenderer } = window.require('electron');
 | 
			
		||||
        
 | 
			
		||||
        try {
 | 
			
		||||
            const permissions = await ipcRenderer.invoke('check-system-permissions');
 | 
			
		||||
            const permissions = await window.api.permissions.checkSystemPermissions();
 | 
			
		||||
            console.log('[PermissionHeader] Permission check result:', permissions);
 | 
			
		||||
            
 | 
			
		||||
            const prevMic = this.microphoneGranted;
 | 
			
		||||
@ -324,13 +323,12 @@ export class PermissionHeader extends LitElement {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async handleMicrophoneClick() {
 | 
			
		||||
        if (!window.require || this.microphoneGranted === 'granted') return;
 | 
			
		||||
        if (!window.api || !window.api.permissions || this.microphoneGranted === 'granted') return;
 | 
			
		||||
        
 | 
			
		||||
        console.log('[PermissionHeader] Requesting microphone permission...');
 | 
			
		||||
        const { ipcRenderer } = window.require('electron');
 | 
			
		||||
        
 | 
			
		||||
        try {
 | 
			
		||||
            const result = await ipcRenderer.invoke('check-system-permissions');
 | 
			
		||||
            const result = await window.api.permissions.checkSystemPermissions();
 | 
			
		||||
            console.log('[PermissionHeader] Microphone permission result:', result);
 | 
			
		||||
            
 | 
			
		||||
            if (result.microphone === 'granted') {
 | 
			
		||||
@ -340,7 +338,7 @@ export class PermissionHeader extends LitElement {
 | 
			
		||||
              }
 | 
			
		||||
            
 | 
			
		||||
              if (result.microphone === 'not-determined' || result.microphone === 'denied' || result.microphone === 'unknown' || result.microphone === 'restricted') {
 | 
			
		||||
                const res = await ipcRenderer.invoke('request-microphone-permission');
 | 
			
		||||
                const res = await window.api.permissions.requestMicrophonePermission();
 | 
			
		||||
                if (res.status === 'granted' || res.success === true) {
 | 
			
		||||
                    this.microphoneGranted = 'granted';
 | 
			
		||||
                    this.requestUpdate();
 | 
			
		||||
@ -357,13 +355,12 @@ export class PermissionHeader extends LitElement {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async handleScreenClick() {
 | 
			
		||||
        if (!window.require || this.screenGranted === 'granted') return;
 | 
			
		||||
        if (!window.api || !window.api.permissions || this.screenGranted === 'granted') return;
 | 
			
		||||
        
 | 
			
		||||
        console.log('[PermissionHeader] Checking screen recording permission...');
 | 
			
		||||
        const { ipcRenderer } = window.require('electron');
 | 
			
		||||
        
 | 
			
		||||
        try {
 | 
			
		||||
            const permissions = await ipcRenderer.invoke('check-system-permissions');
 | 
			
		||||
            const permissions = await window.api.permissions.checkSystemPermissions();
 | 
			
		||||
            console.log('[PermissionHeader] Screen permission check result:', permissions);
 | 
			
		||||
            
 | 
			
		||||
            if (permissions.screen === 'granted') {
 | 
			
		||||
@ -373,7 +370,7 @@ export class PermissionHeader extends LitElement {
 | 
			
		||||
            }
 | 
			
		||||
            if (permissions.screen === 'not-determined' || permissions.screen === 'denied' || permissions.screen === 'unknown' || permissions.screen === 'restricted') {
 | 
			
		||||
            console.log('[PermissionHeader] Opening screen recording preferences...');
 | 
			
		||||
            await ipcRenderer.invoke('open-system-preferences', 'screen-recording');
 | 
			
		||||
            await window.api.permissions.openSystemPreferences('screen-recording');
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            // Check permissions again after a delay
 | 
			
		||||
@ -389,10 +386,9 @@ export class PermissionHeader extends LitElement {
 | 
			
		||||
            this.microphoneGranted === 'granted' && 
 | 
			
		||||
            this.screenGranted === 'granted') {
 | 
			
		||||
            // Mark permissions as completed
 | 
			
		||||
            if (window.require) {
 | 
			
		||||
                const { ipcRenderer } = window.require('electron');
 | 
			
		||||
            if (window.api && window.api.permissions) {
 | 
			
		||||
                try {
 | 
			
		||||
                    await ipcRenderer.invoke('mark-permissions-completed');
 | 
			
		||||
                    await window.api.permissions.markPermissionsCompleted();
 | 
			
		||||
                    console.log('[PermissionHeader] Marked permissions as completed');
 | 
			
		||||
                } catch (error) {
 | 
			
		||||
                    console.error('[PermissionHeader] Error marking permissions as completed:', error);
 | 
			
		||||
@ -405,8 +401,8 @@ export class PermissionHeader extends LitElement {
 | 
			
		||||
 | 
			
		||||
    handleClose() {
 | 
			
		||||
        console.log('Close button clicked');
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            window.require('electron').ipcRenderer.invoke('quit-application');
 | 
			
		||||
        if (window.api && window.api.permissions) {
 | 
			
		||||
            window.api.permissions.quitApplication();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -74,13 +74,11 @@ export class PickleGlassApp extends LitElement {
 | 
			
		||||
    connectedCallback() {
 | 
			
		||||
        super.connectedCallback();
 | 
			
		||||
        
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
            
 | 
			
		||||
            ipcRenderer.on('click-through-toggled', (_, isEnabled) => {
 | 
			
		||||
        if (window.api && window.api.app) {
 | 
			
		||||
            window.api.app.onClickThroughToggled((_, isEnabled) => {
 | 
			
		||||
                this._isClickThrough = isEnabled;
 | 
			
		||||
            });
 | 
			
		||||
            // ipcRenderer.on('start-listening-session', () => {
 | 
			
		||||
            // window.api.app.onStartListeningSession(() => {
 | 
			
		||||
            //     console.log('Received start-listening-session command, calling handleListenClick.');
 | 
			
		||||
            //     this.handleListenClick();
 | 
			
		||||
            // });
 | 
			
		||||
@ -89,10 +87,9 @@ export class PickleGlassApp extends LitElement {
 | 
			
		||||
 | 
			
		||||
    disconnectedCallback() {
 | 
			
		||||
        super.disconnectedCallback();
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
            ipcRenderer.removeAllListeners('click-through-toggled');
 | 
			
		||||
            // ipcRenderer.removeAllListeners('start-listening-session');
 | 
			
		||||
        if (window.api && window.api.app) {
 | 
			
		||||
            window.api.app.removeAllListeners('click-through-toggled');
 | 
			
		||||
            // window.api.app.removeAllListeners('start-listening-session');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -160,9 +157,8 @@ export class PickleGlassApp extends LitElement {
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    async handleClose() {
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
            await ipcRenderer.invoke('quit-application');
 | 
			
		||||
        if (window.api && window.api.app) {
 | 
			
		||||
            await window.api.app.quitApplication();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -238,15 +238,13 @@
 | 
			
		||||
            window.addEventListener('DOMContentLoaded', () => {
 | 
			
		||||
                const app = document.getElementById('pickle-glass');
 | 
			
		||||
        
 | 
			
		||||
                if (window.require) {
 | 
			
		||||
                    const { ipcRenderer } = window.require('electron');
 | 
			
		||||
        
 | 
			
		||||
                if (window.api && window.api.animation) {
 | 
			
		||||
                    // --- REFACTORED: Event-driven animation handling ---
 | 
			
		||||
                    app.addEventListener('animationend', (event) => {
 | 
			
		||||
                        // 숨김 애니메이션이 끝나면 main 프로세스에 알려 창을 실제로 숨깁니다.
 | 
			
		||||
                        if (event.animationName === 'slideUpToHeader' || event.animationName === 'settingsCollapseToButton') {
 | 
			
		||||
                            console.log(`Animation finished: ${event.animationName}. Notifying main process.`);
 | 
			
		||||
                            ipcRenderer.send('animation-finished');
 | 
			
		||||
                            window.api.animation.sendAnimationFinished();
 | 
			
		||||
        
 | 
			
		||||
                            // 완료 후 애니메이션 클래스 정리
 | 
			
		||||
                            app.classList.remove('window-sliding-up', 'settings-window-hide');
 | 
			
		||||
@ -257,26 +255,26 @@
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
        
 | 
			
		||||
                    ipcRenderer.on('window-show-animation', () => {
 | 
			
		||||
                    window.api.animation.onWindowShowAnimation(() => {
 | 
			
		||||
                        console.log('Starting window show animation');
 | 
			
		||||
                        app.classList.remove('window-hidden', 'window-sliding-up', 'settings-window-hide');
 | 
			
		||||
                        app.classList.add('window-sliding-down');
 | 
			
		||||
                    });
 | 
			
		||||
        
 | 
			
		||||
                    ipcRenderer.on('window-hide-animation', () => {
 | 
			
		||||
                    window.api.animation.onWindowHideAnimation(() => {
 | 
			
		||||
                        console.log('Starting window hide animation');
 | 
			
		||||
                        app.classList.remove('window-sliding-down', 'settings-window-show');
 | 
			
		||||
                        app.classList.add('window-sliding-up');
 | 
			
		||||
                    });
 | 
			
		||||
        
 | 
			
		||||
                    ipcRenderer.on('settings-window-hide-animation', () => {
 | 
			
		||||
                    window.api.animation.onSettingsWindowHideAnimation(() => {
 | 
			
		||||
                        console.log('Starting settings window hide animation');
 | 
			
		||||
                        app.classList.remove('window-sliding-down', 'settings-window-show');
 | 
			
		||||
                        app.classList.add('settings-window-hide');
 | 
			
		||||
                    });
 | 
			
		||||
        
 | 
			
		||||
                    // --- UNCHANGED: Existing logic for listen window movement ---
 | 
			
		||||
                    ipcRenderer.on('listen-window-move-to-center', () => {
 | 
			
		||||
                    window.api.animation.onListenWindowMoveToCenter(() => {
 | 
			
		||||
                        console.log('Moving listen window to center');
 | 
			
		||||
                        app.classList.add('listen-window-moving');
 | 
			
		||||
                        app.classList.remove('listen-window-left');
 | 
			
		||||
@ -287,7 +285,7 @@
 | 
			
		||||
                        }, 350);
 | 
			
		||||
                    });
 | 
			
		||||
        
 | 
			
		||||
                    ipcRenderer.on('listen-window-move-to-left', () => {
 | 
			
		||||
                    window.api.animation.onListenWindowMoveToLeft(() => {
 | 
			
		||||
                        console.log('Moving listen window to left');
 | 
			
		||||
                        app.classList.add('listen-window-moving');
 | 
			
		||||
                        app.classList.remove('listen-window-center');
 | 
			
		||||
 | 
			
		||||
@ -822,17 +822,15 @@ export class AskView extends LitElement {
 | 
			
		||||
    handleWindowBlur() {
 | 
			
		||||
        if (!this.currentResponse && !this.isLoading && !this.isStreaming) {
 | 
			
		||||
            // If there's no active content, ask the main process to close this window.
 | 
			
		||||
            if (window.require) {
 | 
			
		||||
                const { ipcRenderer } = window.require('electron');
 | 
			
		||||
                ipcRenderer.invoke('close-ask-window-if-empty');
 | 
			
		||||
            if (window.api && window.api.ask) {
 | 
			
		||||
                window.api.ask.closeWindowIfEmpty();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    closeIfNoContent() {
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
            ipcRenderer.invoke('force-close-window', 'ask');
 | 
			
		||||
        if (window.api && window.api.ask) {
 | 
			
		||||
            window.api.ask.forceCloseWindow('ask');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -912,34 +910,34 @@ export class AskView extends LitElement {
 | 
			
		||||
            this.processAssistantQuestion(question);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
            ipcRenderer.on('ask-global-send', this.handleGlobalSendRequest);
 | 
			
		||||
            ipcRenderer.on('receive-question-from-assistant', this.handleQuestionFromAssistant);
 | 
			
		||||
            ipcRenderer.on('hide-text-input', () => {
 | 
			
		||||
        if (window.api && window.api.ask) {
 | 
			
		||||
            // preload API를 사용하여 이벤트 리스너 등록
 | 
			
		||||
            window.api.ask.onGlobalSend(this.handleGlobalSendRequest);
 | 
			
		||||
            window.api.ask.onReceiveQuestionFromAssistant(this.handleQuestionFromAssistant);
 | 
			
		||||
            window.api.ask.onHideTextInput(() => {
 | 
			
		||||
                console.log('📤 Hide text input signal received');
 | 
			
		||||
                this.showTextInput = false;
 | 
			
		||||
                this.requestUpdate();
 | 
			
		||||
            });
 | 
			
		||||
            ipcRenderer.on('window-hide-animation', () => {
 | 
			
		||||
            window.api.ask.onWindowHideAnimation(() => {
 | 
			
		||||
                console.log('📤 Ask window hiding - clearing response content');
 | 
			
		||||
                setTimeout(() => {
 | 
			
		||||
                    this.clearResponseContent();
 | 
			
		||||
                }, 250);
 | 
			
		||||
            });
 | 
			
		||||
            ipcRenderer.on('window-blur', this.handleWindowBlur);
 | 
			
		||||
            ipcRenderer.on('window-did-show', () => {
 | 
			
		||||
            window.api.ask.onWindowBlur(this.handleWindowBlur);
 | 
			
		||||
            window.api.ask.onWindowDidShow(() => {
 | 
			
		||||
                if (!this.currentResponse && !this.isLoading && !this.isStreaming) {
 | 
			
		||||
                    this.focusTextInput();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            ipcRenderer.on('ask-response-chunk', this.handleStreamChunk);
 | 
			
		||||
            ipcRenderer.on('ask-response-stream-end', this.handleStreamEnd);
 | 
			
		||||
            window.api.ask.onResponseChunk(this.handleStreamChunk);
 | 
			
		||||
            window.api.ask.onResponseStreamEnd(this.handleStreamEnd);
 | 
			
		||||
 | 
			
		||||
            ipcRenderer.on('scroll-response-up', () => this.handleScroll('up'));
 | 
			
		||||
            ipcRenderer.on('scroll-response-down', () => this.handleScroll('down'));
 | 
			
		||||
            console.log('✅ AskView: IPC 이벤트 리스너 등록 완료');
 | 
			
		||||
            window.api.ask.onScrollResponseUp(() => this.handleScroll('up'));
 | 
			
		||||
            window.api.ask.onScrollResponseDown(() => this.handleScroll('down'));
 | 
			
		||||
            console.log('✅ AskView: preload API 이벤트 리스너 등록 완료');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -966,19 +964,19 @@ export class AskView extends LitElement {
 | 
			
		||||
 | 
			
		||||
        Object.values(this.lineCopyTimeouts).forEach(timeout => clearTimeout(timeout));
 | 
			
		||||
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
            ipcRenderer.removeListener('ask-global-send', this.handleGlobalSendRequest);
 | 
			
		||||
            ipcRenderer.removeListener('hide-text-input', () => { });
 | 
			
		||||
            ipcRenderer.removeListener('window-hide-animation', () => { });
 | 
			
		||||
            ipcRenderer.removeListener('window-blur', this.handleWindowBlur);
 | 
			
		||||
        if (window.api && window.api.ask) {
 | 
			
		||||
            // preload API를 사용하여 이벤트 리스너 제거
 | 
			
		||||
            window.api.ask.removeOnGlobalSend(this.handleGlobalSendRequest);
 | 
			
		||||
            window.api.ask.removeOnHideTextInput(() => { });
 | 
			
		||||
            window.api.ask.removeOnWindowHideAnimation(() => { });
 | 
			
		||||
            window.api.ask.removeOnWindowBlur(this.handleWindowBlur);
 | 
			
		||||
 | 
			
		||||
            ipcRenderer.removeListener('ask-response-chunk', this.handleStreamChunk);
 | 
			
		||||
            ipcRenderer.removeListener('ask-response-stream-end', this.handleStreamEnd);
 | 
			
		||||
            window.api.ask.removeOnResponseChunk(this.handleStreamChunk);
 | 
			
		||||
            window.api.ask.removeOnResponseStreamEnd(this.handleStreamEnd);
 | 
			
		||||
 | 
			
		||||
            ipcRenderer.removeListener('scroll-response-up', () => this.handleScroll('up'));
 | 
			
		||||
            ipcRenderer.removeListener('scroll-response-down', () => this.handleScroll('down'));
 | 
			
		||||
            console.log('✅ AskView: IPC 이벤트 리스너 제거 완료');
 | 
			
		||||
            window.api.ask.removeOnScrollResponseUp(() => this.handleScroll('up'));
 | 
			
		||||
            window.api.ask.removeOnScrollResponseDown(() => this.handleScroll('down'));
 | 
			
		||||
            console.log('✅ AskView: preload API 이벤트 리스너 제거 완료');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1138,9 +1136,8 @@ export class AskView extends LitElement {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    requestWindowResize(targetHeight) {
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
            ipcRenderer.invoke('adjust-window-height', targetHeight);
 | 
			
		||||
        if (window.api && window.api.ask) {
 | 
			
		||||
            window.api.ask.adjustWindowHeight(targetHeight);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1181,9 +1178,8 @@ export class AskView extends LitElement {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    closeResponsePanel() {
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
            ipcRenderer.invoke('force-close-window', 'ask');
 | 
			
		||||
        if (window.api && window.api.ask) {
 | 
			
		||||
            window.api.ask.forceCloseWindow('ask');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1236,9 +1232,8 @@ export class AskView extends LitElement {
 | 
			
		||||
        this.requestUpdate();
 | 
			
		||||
        this.renderContent();
 | 
			
		||||
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
            ipcRenderer.invoke('ask:sendMessage', question).catch(error => {
 | 
			
		||||
        if (window.api && window.api.ask) {
 | 
			
		||||
            window.api.ask.sendMessage(question).catch(error => {
 | 
			
		||||
                console.error('Error processing assistant question:', error);
 | 
			
		||||
                this.isLoading = false;
 | 
			
		||||
                this.isStreaming = false;
 | 
			
		||||
@ -1335,9 +1330,8 @@ export class AskView extends LitElement {
 | 
			
		||||
        this.requestUpdate();
 | 
			
		||||
        this.renderContent();
 | 
			
		||||
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
            ipcRenderer.invoke('ask:sendMessage', text).catch(error => {
 | 
			
		||||
        if (window.api && window.api.ask) {
 | 
			
		||||
            window.api.ask.sendMessage(text).catch(error => {
 | 
			
		||||
                console.error('Error sending text:', error);
 | 
			
		||||
                this.isLoading = false;
 | 
			
		||||
                this.isStreaming = false;
 | 
			
		||||
@ -1511,7 +1505,7 @@ export class AskView extends LitElement {
 | 
			
		||||
 | 
			
		||||
    // Dynamically resize the BrowserWindow to fit current content
 | 
			
		||||
    adjustWindowHeight() {
 | 
			
		||||
        if (!window.require) return;
 | 
			
		||||
        if (!window.api || !window.api.ask) return;
 | 
			
		||||
 | 
			
		||||
        this.updateComplete.then(() => {
 | 
			
		||||
            const headerEl = this.shadowRoot.querySelector('.response-header');
 | 
			
		||||
@ -1528,8 +1522,7 @@ export class AskView extends LitElement {
 | 
			
		||||
 | 
			
		||||
            const targetHeight = Math.min(700, idealHeight);
 | 
			
		||||
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
            ipcRenderer.invoke('adjust-window-height', targetHeight);
 | 
			
		||||
            window.api.ask.adjustWindowHeight(targetHeight);
 | 
			
		||||
 | 
			
		||||
        }).catch(err => console.error('AskView adjustWindowHeight error:', err));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -453,9 +453,8 @@ export class ListenView extends LitElement {
 | 
			
		||||
        if (this.isSessionActive) {
 | 
			
		||||
            this.startTimer();
 | 
			
		||||
        }
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
            ipcRenderer.on('session-state-changed', (event, { isActive }) => {
 | 
			
		||||
        if (window.api && window.api.listen) {
 | 
			
		||||
            window.api.listen.onSessionStateChanged((event, { isActive }) => {
 | 
			
		||||
                const wasActive = this.isSessionActive;
 | 
			
		||||
                this.isSessionActive = isActive;
 | 
			
		||||
 | 
			
		||||
@ -514,7 +513,7 @@ export class ListenView extends LitElement {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    adjustWindowHeight() {
 | 
			
		||||
        if (!window.require) return;
 | 
			
		||||
        if (!window.api || !window.api.listen) return;
 | 
			
		||||
 | 
			
		||||
        this.updateComplete
 | 
			
		||||
            .then(() => {
 | 
			
		||||
@ -537,8 +536,7 @@ export class ListenView extends LitElement {
 | 
			
		||||
                    `[Height Adjusted] Mode: ${this.viewMode}, TopBar: ${topBarHeight}px, Content: ${contentHeight}px, Ideal: ${idealHeight}px, Target: ${targetHeight}px`
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                const { ipcRenderer } = window.require('electron');
 | 
			
		||||
                ipcRenderer.invoke('adjust-window-height', targetHeight);
 | 
			
		||||
                window.api.listen.adjustWindowHeight(targetHeight);
 | 
			
		||||
            })
 | 
			
		||||
            .catch(error => {
 | 
			
		||||
                console.error('Error in adjustWindowHeight:', error);
 | 
			
		||||
 | 
			
		||||
@ -198,7 +198,7 @@ function runAecSync(micF32, sysF32) {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// System audio data handler
 | 
			
		||||
ipcRenderer.on('system-audio-data', (event, { data }) => {
 | 
			
		||||
    window.api.audio.onSystemAudioData((event, { data }) => {
 | 
			
		||||
    systemAudioBuffer.push({
 | 
			
		||||
        data: data,
 | 
			
		||||
        timestamp: Date.now(),
 | 
			
		||||
@ -336,7 +336,7 @@ async function setupMicProcessing(micStream) {
 | 
			
		||||
            const pcm16 = convertFloat32ToInt16(processedChunk);
 | 
			
		||||
            const b64 = arrayBufferToBase64(pcm16.buffer);
 | 
			
		||||
 | 
			
		||||
            ipcRenderer.invoke('send-audio-content', {
 | 
			
		||||
            window.api.audio.sendAudioContent({
 | 
			
		||||
                data: b64,
 | 
			
		||||
                mimeType: 'audio/pcm;rate=24000',
 | 
			
		||||
            });
 | 
			
		||||
@ -369,7 +369,7 @@ function setupLinuxMicProcessing(micStream) {
 | 
			
		||||
            const pcmData16 = convertFloat32ToInt16(chunk);
 | 
			
		||||
            const base64Data = arrayBufferToBase64(pcmData16.buffer);
 | 
			
		||||
 | 
			
		||||
            await ipcRenderer.invoke('send-audio-content', {
 | 
			
		||||
            await window.api.audio.sendAudioContent({
 | 
			
		||||
                data: base64Data,
 | 
			
		||||
                mimeType: 'audio/pcm;rate=24000',
 | 
			
		||||
            });
 | 
			
		||||
@ -403,7 +403,7 @@ function setupSystemAudioProcessing(systemStream) {
 | 
			
		||||
            const base64Data = arrayBufferToBase64(pcmData16.buffer);
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                await ipcRenderer.invoke('send-system-audio-content', {
 | 
			
		||||
                await window.api.audio.sendSystemAudioContent({
 | 
			
		||||
                    data: base64Data,
 | 
			
		||||
                    mimeType: 'audio/pcm;rate=24000',
 | 
			
		||||
                });
 | 
			
		||||
@ -433,7 +433,7 @@ async function captureScreenshot(imageQuality = 'medium', isManual = false) {
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        // Request screenshot from main process
 | 
			
		||||
        const result = await ipcRenderer.invoke('capture-screenshot', {
 | 
			
		||||
        const result = await window.api.audio.captureScreenshot({
 | 
			
		||||
            quality: imageQuality,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
@ -470,7 +470,7 @@ async function captureManualScreenshot(imageQuality = null) {
 | 
			
		||||
async function getCurrentScreenshot() {
 | 
			
		||||
    try {
 | 
			
		||||
        // First try to get a fresh screenshot from main process
 | 
			
		||||
        const result = await ipcRenderer.invoke('get-current-screenshot');
 | 
			
		||||
        const result = await window.api.audio.getCurrentScreenshot();
 | 
			
		||||
 | 
			
		||||
        if (result.success && result.base64) {
 | 
			
		||||
            console.log('📸 Got fresh screenshot from main process');
 | 
			
		||||
@ -479,7 +479,7 @@ async function getCurrentScreenshot() {
 | 
			
		||||
 | 
			
		||||
        // If no screenshot available, capture one now
 | 
			
		||||
        console.log('📸 No screenshot available, capturing new one');
 | 
			
		||||
        const captureResult = await ipcRenderer.invoke('capture-screenshot', {
 | 
			
		||||
                    const captureResult = await window.api.audio.captureScreenshot({
 | 
			
		||||
            quality: currentImageQuality,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
@ -518,15 +518,15 @@ async function startCapture(screenshotIntervalSeconds = 5, imageQuality = 'mediu
 | 
			
		||||
            console.log('Starting macOS capture with SystemAudioDump...');
 | 
			
		||||
 | 
			
		||||
            // Start macOS audio capture
 | 
			
		||||
            const audioResult = await ipcRenderer.invoke('start-macos-audio');
 | 
			
		||||
            const audioResult = await window.api.audio.startMacosAudio();
 | 
			
		||||
            if (!audioResult.success) {
 | 
			
		||||
                console.warn('[listenCapture] macOS audio start failed:', audioResult.error);
 | 
			
		||||
 | 
			
		||||
                // 이미 실행 중 → stop 후 재시도
 | 
			
		||||
                if (audioResult.error === 'already_running') {
 | 
			
		||||
                    await ipcRenderer.invoke('stop-macos-audio');
 | 
			
		||||
                    await window.api.audio.stopMacosAudio();
 | 
			
		||||
                    await new Promise(r => setTimeout(r, 500));
 | 
			
		||||
                    const retry = await ipcRenderer.invoke('start-macos-audio');
 | 
			
		||||
                    const retry = await window.api.audio.startMacosAudio();
 | 
			
		||||
                    if (!retry.success) {
 | 
			
		||||
                        throw new Error('Retry failed: ' + retry.error);
 | 
			
		||||
                    }
 | 
			
		||||
@ -536,7 +536,7 @@ async function startCapture(screenshotIntervalSeconds = 5, imageQuality = 'mediu
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Initialize screen capture in main process
 | 
			
		||||
            const screenResult = await ipcRenderer.invoke('start-screen-capture');
 | 
			
		||||
            const screenResult = await window.api.audio.startScreenCapture();
 | 
			
		||||
            if (!screenResult.success) {
 | 
			
		||||
                throw new Error('Failed to start screen capture: ' + screenResult.error);
 | 
			
		||||
            }
 | 
			
		||||
@ -604,13 +604,13 @@ async function startCapture(screenshotIntervalSeconds = 5, imageQuality = 'mediu
 | 
			
		||||
            console.log('Starting Windows capture with native loopback audio...');
 | 
			
		||||
 | 
			
		||||
            // Start screen capture in main process for screenshots
 | 
			
		||||
            const screenResult = await ipcRenderer.invoke('start-screen-capture');
 | 
			
		||||
            const screenResult = await window.api.audio.startScreenCapture();
 | 
			
		||||
            if (!screenResult.success) {
 | 
			
		||||
                throw new Error('Failed to start screen capture: ' + screenResult.error);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Ensure STT sessions are initialized before starting audio capture
 | 
			
		||||
            const sessionActive = await ipcRenderer.invoke('is-session-active');
 | 
			
		||||
            const sessionActive = await window.api.audio.isSessionActive();
 | 
			
		||||
            if (!sessionActive) {
 | 
			
		||||
                throw new Error('STT sessions not initialized - please wait for initialization to complete');
 | 
			
		||||
            }
 | 
			
		||||
@ -715,13 +715,13 @@ function stopCapture() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Stop screen capture in main process
 | 
			
		||||
    ipcRenderer.invoke('stop-screen-capture').catch(err => {
 | 
			
		||||
    window.api.audio.stopScreenCapture().catch(err => {
 | 
			
		||||
        console.error('Error stopping screen capture:', err);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Stop macOS audio capture if running
 | 
			
		||||
    if (isMacOS) {
 | 
			
		||||
        ipcRenderer.invoke('stop-macos-audio').catch(err => {
 | 
			
		||||
        window.api.audio.stopMacosAudio().catch(err => {
 | 
			
		||||
            console.error('Error stopping macOS audio:', err);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@ window.pickleGlass = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ipcRenderer.on('change-listen-capture-state', (_event, { status }) => {
 | 
			
		||||
window.api.audio.onChangeListenCaptureState((_event, { status }) => {
 | 
			
		||||
    if (!isListenView) {
 | 
			
		||||
        console.log('[Renderer] Non-listen view: ignoring capture-state change');
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
@ -95,17 +95,15 @@ export class SttView extends LitElement {
 | 
			
		||||
 | 
			
		||||
    connectedCallback() {
 | 
			
		||||
        super.connectedCallback();
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
            ipcRenderer.on('stt-update', this.handleSttUpdate);
 | 
			
		||||
        if (window.api && window.api.listen) {
 | 
			
		||||
            window.api.listen.onSttUpdate(this.handleSttUpdate);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    disconnectedCallback() {
 | 
			
		||||
        super.disconnectedCallback();
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
            ipcRenderer.removeListener('stt-update', this.handleSttUpdate);
 | 
			
		||||
        if (window.api && window.api.listen) {
 | 
			
		||||
            window.api.listen.removeOnSttUpdate(this.handleSttUpdate);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -262,9 +262,8 @@ export class SummaryView extends LitElement {
 | 
			
		||||
 | 
			
		||||
    connectedCallback() {
 | 
			
		||||
        super.connectedCallback();
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
            ipcRenderer.on('summary-update', (event, data) => {
 | 
			
		||||
        if (window.api && window.api.listen) {
 | 
			
		||||
            window.api.listen.onSummaryUpdate((event, data) => {
 | 
			
		||||
                this.structuredData = data;
 | 
			
		||||
                this.requestUpdate();
 | 
			
		||||
            });
 | 
			
		||||
@ -273,9 +272,8 @@ export class SummaryView extends LitElement {
 | 
			
		||||
 | 
			
		||||
    disconnectedCallback() {
 | 
			
		||||
        super.disconnectedCallback();
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
            ipcRenderer.removeAllListeners('summary-update');
 | 
			
		||||
        if (window.api && window.api.listen) {
 | 
			
		||||
            window.api.listen.removeOnSummaryUpdate(() => {});
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -408,18 +406,16 @@ export class SummaryView extends LitElement {
 | 
			
		||||
    async handleRequestClick(requestText) {
 | 
			
		||||
        console.log('🔥 Analysis request clicked:', requestText);
 | 
			
		||||
 | 
			
		||||
        if (window.require) {
 | 
			
		||||
            const { ipcRenderer } = window.require('electron');
 | 
			
		||||
 | 
			
		||||
        if (window.api && window.api.listen) {
 | 
			
		||||
            try {
 | 
			
		||||
                const isAskViewVisible = await ipcRenderer.invoke('is-ask-window-visible', 'ask');
 | 
			
		||||
                const isAskViewVisible = await window.api.listen.isAskWindowVisible('ask');
 | 
			
		||||
 | 
			
		||||
                if (!isAskViewVisible) {
 | 
			
		||||
                    await ipcRenderer.invoke('toggle-feature', 'ask');
 | 
			
		||||
                    await window.api.listen.toggleFeature('ask');
 | 
			
		||||
                    await new Promise(resolve => setTimeout(resolve, 100));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                const result = await ipcRenderer.invoke('send-question-to-ask', requestText);
 | 
			
		||||
                const result = await window.api.listen.sendQuestionToAsk(requestText);
 | 
			
		||||
 | 
			
		||||
                if (result.success) {
 | 
			
		||||
                    console.log('✅ Question sent to AskView successfully');
 | 
			
		||||
 | 
			
		||||
@ -102,23 +102,23 @@ export class ShortcutSettingsView extends LitElement {
 | 
			
		||||
        this.feedback = {};
 | 
			
		||||
        this.isLoading = true;
 | 
			
		||||
        this.capturingKey = null;
 | 
			
		||||
        this.ipcRenderer = window.require ? window.require('electron').ipcRenderer : null;
 | 
			
		||||
        this.hasAPI = window.api && window.api.settings;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    connectedCallback() {
 | 
			
		||||
        super.connectedCallback();
 | 
			
		||||
        if (!this.ipcRenderer) return;
 | 
			
		||||
        if (!this.hasAPI) return;
 | 
			
		||||
        this.loadShortcutsHandler = (event, keybinds) => {
 | 
			
		||||
            this.shortcuts = keybinds;
 | 
			
		||||
            this.isLoading = false;
 | 
			
		||||
        };
 | 
			
		||||
        this.ipcRenderer.on('load-shortcuts', this.loadShortcutsHandler);
 | 
			
		||||
        window.api.settings.onLoadShortcuts(this.loadShortcutsHandler);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    disconnectedCallback() {
 | 
			
		||||
        super.disconnectedCallback();
 | 
			
		||||
        if (this.ipcRenderer && this.loadShortcutsHandler) {
 | 
			
		||||
            this.ipcRenderer.removeListener('load-shortcuts', this.loadShortcutsHandler);
 | 
			
		||||
        if (this.hasAPI && this.loadShortcutsHandler) {
 | 
			
		||||
            window.api.settings.removeOnLoadShortcuts(this.loadShortcutsHandler);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -171,25 +171,25 @@ export class ShortcutSettingsView extends LitElement {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async handleSave() {
 | 
			
		||||
        if (!this.ipcRenderer) return;
 | 
			
		||||
        const result = await this.ipcRenderer.invoke('save-shortcuts', this.shortcuts);
 | 
			
		||||
        if (!this.hasAPI) return;
 | 
			
		||||
        const result = await window.api.settings.saveShortcuts(this.shortcuts);
 | 
			
		||||
        if (!result.success) {
 | 
			
		||||
            alert('Failed to save shortcuts: ' + result.error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    handleClose() {
 | 
			
		||||
        if (!this.ipcRenderer) return;
 | 
			
		||||
        this.ipcRenderer.send('close-shortcut-editor');
 | 
			
		||||
        if (!this.hasAPI) return;
 | 
			
		||||
        window.api.settings.closeShortcutEditor();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async handleResetToDefault() {
 | 
			
		||||
        if (!this.ipcRenderer) return;
 | 
			
		||||
        if (!this.hasAPI) return;
 | 
			
		||||
        const confirmation = confirm("Are you sure you want to reset all shortcuts to their default values?");
 | 
			
		||||
        if (!confirmation) return;
 | 
			
		||||
    
 | 
			
		||||
        try {
 | 
			
		||||
            const defaultShortcuts = await this.ipcRenderer.invoke('get-default-shortcuts');
 | 
			
		||||
            const defaultShortcuts = await window.api.settings.getDefaultShortcuts();
 | 
			
		||||
            this.shortcuts = defaultShortcuts;
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            alert('Failed to load default settings.');
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user