centralized ask logic
This commit is contained in:
parent
8402e7d296
commit
c948d4ed08
@ -22,11 +22,6 @@ async function sendMessage(userPrompt) {
|
||||
console.warn('[AskService] Cannot process empty message');
|
||||
return { success: false, error: 'Empty message' };
|
||||
}
|
||||
|
||||
const askWindow = windowPool.get('ask');
|
||||
if (askWindow && !askWindow.isDestroyed()) {
|
||||
askWindow.webContents.send('hide-text-input');
|
||||
}
|
||||
|
||||
let sessionId;
|
||||
|
||||
|
@ -193,8 +193,6 @@ class ListenService {
|
||||
this.currentSessionId = null;
|
||||
this.summaryService.resetConversationHistory();
|
||||
|
||||
this.sendToRenderer('session-did-close');
|
||||
|
||||
console.log('Listen service session closed.');
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
|
@ -729,17 +729,13 @@ export class AskView extends LitElement {
|
||||
this.handleStreamChunk = this.handleStreamChunk.bind(this);
|
||||
this.handleStreamEnd = this.handleStreamEnd.bind(this);
|
||||
this.handleSendText = this.handleSendText.bind(this);
|
||||
this.handleGlobalSendRequest = this.handleGlobalSendRequest.bind(this);
|
||||
this.handleTextKeydown = this.handleTextKeydown.bind(this);
|
||||
this.closeResponsePanel = this.closeResponsePanel.bind(this);
|
||||
this.handleCopy = this.handleCopy.bind(this);
|
||||
this.clearResponseContent = this.clearResponseContent.bind(this);
|
||||
this.processAssistantQuestion = this.processAssistantQuestion.bind(this);
|
||||
this.handleEscKey = this.handleEscKey.bind(this);
|
||||
this.handleDocumentClick = this.handleDocumentClick.bind(this);
|
||||
this.handleWindowBlur = this.handleWindowBlur.bind(this);
|
||||
|
||||
this.handleScroll = this.handleScroll.bind(this);
|
||||
this.handleCloseAskWindow = this.handleCloseAskWindow.bind(this);
|
||||
this.handleCloseIfNoContent = this.handleCloseIfNoContent.bind(this);
|
||||
|
||||
this.loadLibraries();
|
||||
|
||||
@ -747,6 +743,94 @@ export class AskView extends LitElement {
|
||||
this.isThrottled = false;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
console.log('📱 AskView connectedCallback - IPC 이벤트 리스너 설정');
|
||||
|
||||
document.addEventListener('keydown', this.handleEscKey);
|
||||
|
||||
this.resizeObserver = new ResizeObserver(entries => {
|
||||
for (const entry of entries) {
|
||||
const needed = entry.contentRect.height;
|
||||
const current = window.innerHeight;
|
||||
|
||||
if (needed > current - 4) {
|
||||
this.requestWindowResize(Math.ceil(needed));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const container = this.shadowRoot?.querySelector('.ask-container');
|
||||
if (container) this.resizeObserver.observe(container);
|
||||
|
||||
this.handleQuestionFromAssistant = (event, question) => {
|
||||
console.log('📨 AskView: Received question from ListenView:', question);
|
||||
this.handleSendText(null, question);
|
||||
};
|
||||
|
||||
if (window.require) {
|
||||
const { ipcRenderer } = window.require('electron');
|
||||
ipcRenderer.on('ask:sendQuestionToRenderer', this.handleQuestionFromAssistant);
|
||||
ipcRenderer.on('hide-text-input', () => {
|
||||
console.log('📤 Hide text input signal received');
|
||||
this.showTextInput = false;
|
||||
this.requestUpdate();
|
||||
});
|
||||
ipcRenderer.on('ask:showTextInput', () => {
|
||||
console.log('📤 Show text input signal received');
|
||||
if (!this.showTextInput) {
|
||||
this.showTextInput = true;
|
||||
this.requestUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
ipcRenderer.on('ask-response-chunk', this.handleStreamChunk);
|
||||
ipcRenderer.on('ask-response-stream-end', this.handleStreamEnd);
|
||||
|
||||
ipcRenderer.on('scroll-response-up', () => this.handleScroll('up'));
|
||||
ipcRenderer.on('scroll-response-down', () => this.handleScroll('down'));
|
||||
console.log('✅ AskView: IPC 이벤트 리스너 등록 완료');
|
||||
}
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.resizeObserver?.disconnect();
|
||||
|
||||
console.log('📱 AskView disconnectedCallback - IPC 이벤트 리스너 제거');
|
||||
|
||||
document.removeEventListener('keydown', this.handleEscKey);
|
||||
|
||||
if (this.copyTimeout) {
|
||||
clearTimeout(this.copyTimeout);
|
||||
}
|
||||
|
||||
if (this.headerAnimationTimeout) {
|
||||
clearTimeout(this.headerAnimationTimeout);
|
||||
}
|
||||
|
||||
if (this.streamingTimeout) {
|
||||
clearTimeout(this.streamingTimeout);
|
||||
}
|
||||
|
||||
Object.values(this.lineCopyTimeouts).forEach(timeout => clearTimeout(timeout));
|
||||
|
||||
if (window.require) {
|
||||
const { ipcRenderer } = window.require('electron');
|
||||
ipcRenderer.removeListener('hide-text-input', () => { });
|
||||
ipcRenderer.removeListener('ask:showTextInput', () => { });
|
||||
|
||||
ipcRenderer.removeListener('ask-response-chunk', this.handleStreamChunk);
|
||||
ipcRenderer.removeListener('ask-response-stream-end', this.handleStreamEnd);
|
||||
|
||||
ipcRenderer.removeListener('scroll-response-up', () => this.handleScroll('up'));
|
||||
ipcRenderer.removeListener('scroll-response-down', () => this.handleScroll('down'));
|
||||
console.log('✅ AskView: IPC 이벤트 리스너 제거 완료');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async loadLibraries() {
|
||||
try {
|
||||
if (!window.marked) {
|
||||
@ -803,38 +887,49 @@ export class AskView extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
handleDocumentClick(e) {
|
||||
handleCloseAskWindow() {
|
||||
this.clearResponseContent();
|
||||
ipcRenderer.invoke('ask:closeAskWindow');
|
||||
}
|
||||
|
||||
handleCloseIfNoContent() {
|
||||
if (!this.currentResponse && !this.isLoading && !this.isStreaming) {
|
||||
const askContainer = this.shadowRoot?.querySelector('.ask-container');
|
||||
if (askContainer && !e.composedPath().includes(askContainer)) {
|
||||
this.closeIfNoContent();
|
||||
}
|
||||
this.handleCloseAskWindow();
|
||||
}
|
||||
}
|
||||
|
||||
handleEscKey(e) {
|
||||
if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
this.closeResponsePanel();
|
||||
this.handleCloseIfNoContent();
|
||||
}
|
||||
}
|
||||
|
||||
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');
|
||||
clearResponseContent() {
|
||||
this.currentResponse = '';
|
||||
this.currentQuestion = '';
|
||||
this.isLoading = false;
|
||||
this.isStreaming = false;
|
||||
this.headerText = 'AI Response';
|
||||
this.showTextInput = true;
|
||||
this.accumulatedResponse = '';
|
||||
this.requestUpdate();
|
||||
this.renderContent();
|
||||
}
|
||||
|
||||
handleInputFocus() {
|
||||
this.isInputFocused = true;
|
||||
}
|
||||
|
||||
focusTextInput() {
|
||||
requestAnimationFrame(() => {
|
||||
const textInput = this.shadowRoot?.getElementById('textInput');
|
||||
if (textInput) {
|
||||
textInput.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
closeIfNoContent() {
|
||||
if (window.require) {
|
||||
const { ipcRenderer } = window.require('electron');
|
||||
ipcRenderer.invoke('force-close-window', 'ask');
|
||||
}
|
||||
}
|
||||
|
||||
loadScript(src) {
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -874,114 +969,6 @@ export class AskView extends LitElement {
|
||||
return text;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
console.log('📱 AskView connectedCallback - IPC 이벤트 리스너 설정');
|
||||
|
||||
document.addEventListener('click', this.handleDocumentClick, true);
|
||||
document.addEventListener('keydown', this.handleEscKey);
|
||||
|
||||
this.resizeObserver = new ResizeObserver(entries => {
|
||||
for (const entry of entries) {
|
||||
const needed = entry.contentRect.height;
|
||||
const current = window.innerHeight;
|
||||
|
||||
if (needed > current - 4) {
|
||||
this.requestWindowResize(Math.ceil(needed));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const container = this.shadowRoot?.querySelector('.ask-container');
|
||||
if (container) this.resizeObserver.observe(container);
|
||||
|
||||
this.handleQuestionFromAssistant = (event, question) => {
|
||||
console.log('📨 AskView: Received question from ListenView:', question);
|
||||
this.currentResponse = '';
|
||||
this.isStreaming = false;
|
||||
this.requestUpdate();
|
||||
|
||||
this.currentQuestion = question;
|
||||
this.isLoading = true;
|
||||
this.showTextInput = false;
|
||||
this.headerText = 'analyzing screen...';
|
||||
this.startHeaderAnimation();
|
||||
this.requestUpdate();
|
||||
|
||||
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', () => {
|
||||
console.log('📤 Hide text input signal received');
|
||||
this.showTextInput = false;
|
||||
this.requestUpdate();
|
||||
});
|
||||
ipcRenderer.on('window-hide-animation', () => {
|
||||
console.log('📤 Ask window hiding - clearing response content');
|
||||
setTimeout(() => {
|
||||
this.clearResponseContent();
|
||||
}, 250);
|
||||
});
|
||||
ipcRenderer.on('window-blur', this.handleWindowBlur);
|
||||
ipcRenderer.on('window-did-show', () => {
|
||||
if (!this.currentResponse && !this.isLoading && !this.isStreaming) {
|
||||
this.focusTextInput();
|
||||
}
|
||||
});
|
||||
|
||||
ipcRenderer.on('ask-response-chunk', this.handleStreamChunk);
|
||||
ipcRenderer.on('ask-response-stream-end', this.handleStreamEnd);
|
||||
|
||||
ipcRenderer.on('scroll-response-up', () => this.handleScroll('up'));
|
||||
ipcRenderer.on('scroll-response-down', () => this.handleScroll('down'));
|
||||
console.log('✅ AskView: IPC 이벤트 리스너 등록 완료');
|
||||
}
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.resizeObserver?.disconnect();
|
||||
|
||||
console.log('📱 AskView disconnectedCallback - IPC 이벤트 리스너 제거');
|
||||
|
||||
document.removeEventListener('click', this.handleDocumentClick, true);
|
||||
document.removeEventListener('keydown', this.handleEscKey);
|
||||
|
||||
if (this.copyTimeout) {
|
||||
clearTimeout(this.copyTimeout);
|
||||
}
|
||||
|
||||
if (this.headerAnimationTimeout) {
|
||||
clearTimeout(this.headerAnimationTimeout);
|
||||
}
|
||||
|
||||
if (this.streamingTimeout) {
|
||||
clearTimeout(this.streamingTimeout);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
ipcRenderer.removeListener('ask-response-chunk', this.handleStreamChunk);
|
||||
ipcRenderer.removeListener('ask-response-stream-end', this.handleStreamEnd);
|
||||
|
||||
ipcRenderer.removeListener('scroll-response-up', () => this.handleScroll('up'));
|
||||
ipcRenderer.removeListener('scroll-response-down', () => this.handleScroll('down'));
|
||||
console.log('✅ AskView: IPC 이벤트 리스너 제거 완료');
|
||||
}
|
||||
}
|
||||
|
||||
handleScroll(direction) {
|
||||
const scrollableElement = this.shadowRoot.querySelector('#responseContainer');
|
||||
if (scrollableElement) {
|
||||
@ -1124,18 +1111,6 @@ export class AskView extends LitElement {
|
||||
this.adjustWindowHeightThrottled();
|
||||
}
|
||||
|
||||
clearResponseContent() {
|
||||
this.currentResponse = '';
|
||||
this.currentQuestion = '';
|
||||
this.isLoading = false;
|
||||
this.isStreaming = false;
|
||||
this.headerText = 'AI Response';
|
||||
this.showTextInput = true;
|
||||
this.accumulatedResponse = '';
|
||||
this.requestUpdate();
|
||||
this.renderContent(); // 👈 updateResponseContent() 대신 renderContent() 호출
|
||||
}
|
||||
|
||||
|
||||
requestWindowResize(targetHeight) {
|
||||
if (window.require) {
|
||||
@ -1180,13 +1155,6 @@ export class AskView extends LitElement {
|
||||
.replace(/`(.*?)`/g, '<code>$1</code>');
|
||||
}
|
||||
|
||||
closeResponsePanel() {
|
||||
if (window.require) {
|
||||
const { ipcRenderer } = window.require('electron');
|
||||
ipcRenderer.invoke('force-close-window', 'ask');
|
||||
}
|
||||
}
|
||||
|
||||
fixIncompleteMarkdown(text) {
|
||||
if (!text) return text;
|
||||
|
||||
@ -1224,29 +1192,6 @@ export class AskView extends LitElement {
|
||||
return text;
|
||||
}
|
||||
|
||||
// ✨ processAssistantQuestion 수정
|
||||
async processAssistantQuestion(question) {
|
||||
this.currentQuestion = question;
|
||||
this.showTextInput = false;
|
||||
this.isLoading = true;
|
||||
this.isStreaming = false;
|
||||
this.currentResponse = '';
|
||||
this.accumulatedResponse = '';
|
||||
this.startHeaderAnimation();
|
||||
this.requestUpdate();
|
||||
this.renderContent();
|
||||
|
||||
if (window.require) {
|
||||
const { ipcRenderer } = window.require('electron');
|
||||
ipcRenderer.invoke('ask:sendMessage', question).catch(error => {
|
||||
console.error('Error processing assistant question:', error);
|
||||
this.isLoading = false;
|
||||
this.isStreaming = false;
|
||||
this.currentResponse = `Error: ${error.message}`;
|
||||
this.renderContent();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async handleCopy() {
|
||||
if (this.copyState === 'copied') return;
|
||||
@ -1316,10 +1261,9 @@ export class AskView extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
async handleSendText() {
|
||||
async handleSendText(e, overridingText = '') {
|
||||
const textInput = this.shadowRoot?.getElementById('textInput');
|
||||
if (!textInput) return;
|
||||
const text = textInput.value.trim();
|
||||
const text = (overridingText || textInput?.value || '').trim();
|
||||
if (!text) return;
|
||||
|
||||
textInput.value = '';
|
||||
@ -1377,37 +1321,10 @@ export class AskView extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
focusTextInput() {
|
||||
requestAnimationFrame(() => {
|
||||
const textInput = this.shadowRoot?.getElementById('textInput');
|
||||
if (textInput) {
|
||||
textInput.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
setTimeout(() => this.adjustWindowHeight(), 200);
|
||||
}
|
||||
|
||||
handleGlobalSendRequest() {
|
||||
const textInput = this.shadowRoot?.getElementById('textInput');
|
||||
|
||||
if (!this.showTextInput) {
|
||||
this.showTextInput = true;
|
||||
this.requestUpdate();
|
||||
this.focusTextInput();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!textInput) return;
|
||||
|
||||
textInput.focus();
|
||||
|
||||
if (!textInput.value.trim()) return;
|
||||
|
||||
this.handleSendText();
|
||||
}
|
||||
|
||||
getTruncatedQuestion(question, maxLength = 30) {
|
||||
if (!question) return '';
|
||||
@ -1415,24 +1332,7 @@ export class AskView extends LitElement {
|
||||
return question.substring(0, maxLength) + '...';
|
||||
}
|
||||
|
||||
handleInputFocus() {
|
||||
this.isInputFocused = true;
|
||||
}
|
||||
|
||||
handleInputBlur(e) {
|
||||
this.isInputFocused = false;
|
||||
|
||||
// 잠시 후 포커스가 다른 곳으로 갔는지 확인
|
||||
setTimeout(() => {
|
||||
const activeElement = this.shadowRoot?.activeElement || document.activeElement;
|
||||
const textInput = this.shadowRoot?.getElementById('textInput');
|
||||
|
||||
// 포커스가 AskView 내부가 아니고, 응답이 없는 경우
|
||||
if (!this.currentResponse && !this.isLoading && !this.isStreaming && activeElement !== textInput && !this.isInputFocused) {
|
||||
this.closeIfNoContent();
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
render() {
|
||||
const hasResponse = this.isLoading || this.currentResponse || this.isStreaming;
|
||||
@ -1470,7 +1370,7 @@ export class AskView extends LitElement {
|
||||
<path d="M20 6L9 17l-5-5" />
|
||||
</svg>
|
||||
</button>
|
||||
<button class="close-button" @click=${this.closeResponsePanel}>
|
||||
<button class="close-button" @click=${this.handleCloseAskWindow}>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18" />
|
||||
<line x1="6" y1="6" x2="18" y2="18" />
|
||||
@ -1493,7 +1393,6 @@ export class AskView extends LitElement {
|
||||
placeholder="Ask about your screen or audio"
|
||||
@keydown=${this.handleTextKeydown}
|
||||
@focus=${this.handleInputFocus}
|
||||
@blur=${this.handleInputBlur}
|
||||
/>
|
||||
<button
|
||||
class="submit-btn"
|
||||
|
@ -412,14 +412,7 @@ export class SummaryView extends LitElement {
|
||||
const { ipcRenderer } = window.require('electron');
|
||||
|
||||
try {
|
||||
const isAskViewVisible = await ipcRenderer.invoke('is-ask-window-visible', 'ask');
|
||||
|
||||
if (!isAskViewVisible) {
|
||||
await ipcRenderer.invoke('toggle-feature', 'ask');
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
}
|
||||
|
||||
const result = await ipcRenderer.invoke('send-question-to-ask', requestText);
|
||||
const result = await ipcRenderer.invoke('ask:sendQuestionToMain', requestText);
|
||||
|
||||
if (result.success) {
|
||||
console.log('✅ Question sent to AskView successfully');
|
||||
|
@ -76,8 +76,16 @@ function updateLayout() {
|
||||
let movementManager = null;
|
||||
const windowBridge = require('../bridge/windowBridge');
|
||||
|
||||
|
||||
async function toggleFeature(featureName) {
|
||||
/**
|
||||
*
|
||||
* @param {'listen'|'ask'|'settings'} featureName
|
||||
* @param {{
|
||||
* listen?: { targetVisibility?: 'show'|'hide' },
|
||||
* ask?: { targetVisibility?: 'show'|'hide', questionText?: string },
|
||||
* settings?: { targetVisibility?: 'show'|'hide' }
|
||||
* }} [options={}]
|
||||
*/
|
||||
async function toggleFeature(featureName, options = {}) {
|
||||
if (!windowPool.get(featureName) && currentHeaderState === 'main') {
|
||||
createFeatureWindows(windowPool.get('header'));
|
||||
}
|
||||
@ -117,14 +125,27 @@ async function toggleFeature(featureName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const questionText = options?.ask?.questionText ?? null;
|
||||
const targetVisibility = options?.ask?.targetVisibility ?? null;
|
||||
if (askWindow.isVisible()) {
|
||||
askWindow.webContents.send('ask-global-send');
|
||||
if (questionText) {
|
||||
askWindow.webContents.send('ask:sendQuestionToRenderer', questionText);
|
||||
} else {
|
||||
updateLayout();
|
||||
if (targetVisibility === 'show') {
|
||||
askWindow.webContents.send('ask:showTextInput');
|
||||
} else {
|
||||
askWindow.webContents.send('window-hide-animation');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log('[WindowManager] Showing hidden Ask window');
|
||||
askWindow.show();
|
||||
updateLayout();
|
||||
if (questionText) {
|
||||
askWindow.webContents.send('ask:sendQuestionToRenderer', questionText);
|
||||
}
|
||||
askWindow.webContents.send('window-show-animation');
|
||||
askWindow.webContents.send('window-did-show');
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,8 +259,6 @@ function createFeatureWindows(header, namesToCreate) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ask.on('blur',()=>ask.webContents.send('window-blur'));
|
||||
|
||||
// Open DevTools in development
|
||||
if (!app.isPackaged) {
|
||||
@ -532,61 +551,6 @@ function createWindows() {
|
||||
updateLayout();
|
||||
});
|
||||
|
||||
ipcMain.handle('toggle-all-windows-visibility', () => toggleAllWindowsVisibility());
|
||||
|
||||
ipcMain.handle('toggle-feature', async (event, featureName) => {
|
||||
return toggleFeature(featureName);
|
||||
});
|
||||
|
||||
ipcMain.handle('send-question-to-ask', (event, question) => {
|
||||
const askWindow = windowPool.get('ask');
|
||||
if (askWindow && !askWindow.isDestroyed()) {
|
||||
console.log('📨 Main process: Sending question to AskView', question);
|
||||
askWindow.webContents.send('receive-question-from-assistant', question);
|
||||
return { success: true };
|
||||
} else {
|
||||
console.error('❌ Cannot find AskView window');
|
||||
return { success: false, error: 'AskView window not found' };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('adjust-window-height', (event, targetHeight) => {
|
||||
const senderWindow = BrowserWindow.fromWebContents(event.sender);
|
||||
if (senderWindow) {
|
||||
const wasResizable = senderWindow.isResizable();
|
||||
if (!wasResizable) {
|
||||
senderWindow.setResizable(true);
|
||||
}
|
||||
|
||||
const currentBounds = senderWindow.getBounds();
|
||||
const minHeight = senderWindow.getMinimumSize()[1];
|
||||
const maxHeight = senderWindow.getMaximumSize()[1];
|
||||
|
||||
let adjustedHeight;
|
||||
if (maxHeight === 0) {
|
||||
adjustedHeight = Math.max(minHeight, targetHeight);
|
||||
} else {
|
||||
adjustedHeight = Math.max(minHeight, Math.min(maxHeight, targetHeight));
|
||||
}
|
||||
|
||||
senderWindow.setSize(currentBounds.width, adjustedHeight, false);
|
||||
|
||||
if (!wasResizable) {
|
||||
senderWindow.setResizable(false);
|
||||
}
|
||||
|
||||
updateLayout();
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('session-did-close', () => {
|
||||
const listenWindow = windowPool.get('listen');
|
||||
if (listenWindow && listenWindow.isVisible()) {
|
||||
console.log('[WindowManager] Session closed, hiding listen window.');
|
||||
listenWindow.hide();
|
||||
}
|
||||
});
|
||||
|
||||
return windowPool;
|
||||
}
|
||||
|
||||
@ -617,6 +581,12 @@ function loadAndRegisterShortcuts(movementManager) {
|
||||
|
||||
|
||||
function setupIpcHandlers(movementManager) {
|
||||
setupApiKeyIPC();
|
||||
|
||||
ipcMain.handle('quit-application', () => {
|
||||
app.quit();
|
||||
});
|
||||
|
||||
screen.on('display-added', (event, newDisplay) => {
|
||||
console.log('[Display] New display added:', newDisplay.id);
|
||||
});
|
||||
@ -631,107 +601,10 @@ function setupIpcHandlers(movementManager) {
|
||||
});
|
||||
|
||||
screen.on('display-metrics-changed', (event, display, changedMetrics) => {
|
||||
console.log('[Display] Display metrics changed:', display.id, changedMetrics);
|
||||
// console.log('[Display] Display metrics changed:', display.id, changedMetrics);
|
||||
updateLayout();
|
||||
});
|
||||
|
||||
// 1. 스트리밍 데이터 조각(chunk)을 받아서 ask 창으로 전달
|
||||
ipcMain.on('ask-response-chunk', (event, { token }) => {
|
||||
const askWindow = windowPool.get('ask');
|
||||
if (askWindow && !askWindow.isDestroyed()) {
|
||||
// renderer.js가 보낸 토큰을 AskView.js로 그대로 전달합니다.
|
||||
askWindow.webContents.send('ask-response-chunk', { token });
|
||||
}
|
||||
});
|
||||
|
||||
// 2. 스트리밍 종료 신호를 받아서 ask 창으로 전달
|
||||
ipcMain.on('ask-response-stream-end', () => {
|
||||
const askWindow = windowPool.get('ask');
|
||||
if (askWindow && !askWindow.isDestroyed()) {
|
||||
askWindow.webContents.send('ask-response-stream-end');
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('animation-finished', (event) => {
|
||||
const win = BrowserWindow.fromWebContents(event.sender);
|
||||
if (win && !win.isDestroyed()) {
|
||||
console.log(`[WindowManager] Hiding window after animation.`);
|
||||
win.hide();
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('show-settings-window', (event, bounds) => {
|
||||
if (!bounds) return;
|
||||
const win = windowPool.get('settings');
|
||||
|
||||
if (win && !win.isDestroyed()) {
|
||||
if (settingsHideTimer) {
|
||||
clearTimeout(settingsHideTimer);
|
||||
settingsHideTimer = null;
|
||||
}
|
||||
|
||||
// Adjust position based on button bounds
|
||||
const header = windowPool.get('header');
|
||||
const headerBounds = header?.getBounds() ?? { x: 0, y: 0 };
|
||||
const settingsBounds = win.getBounds();
|
||||
|
||||
const disp = getCurrentDisplay(header);
|
||||
const { x: waX, y: waY, width: waW, height: waH } = disp.workArea;
|
||||
|
||||
let x = Math.round(headerBounds.x + (bounds?.x ?? 0) + (bounds?.width ?? 0) / 2 - settingsBounds.width / 2);
|
||||
let y = Math.round(headerBounds.y + (bounds?.y ?? 0) + (bounds?.height ?? 0) + 31);
|
||||
|
||||
x = Math.max(waX + 10, Math.min(waX + waW - settingsBounds.width - 10, x));
|
||||
y = Math.max(waY + 10, Math.min(waY + waH - settingsBounds.height - 10, y));
|
||||
|
||||
win.setBounds({ x, y });
|
||||
win.__lockedByButton = true;
|
||||
console.log(`[WindowManager] Positioning settings window at (${x}, ${y}) based on button bounds.`);
|
||||
|
||||
win.show();
|
||||
win.moveTop();
|
||||
win.setAlwaysOnTop(true);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('hide-settings-window', (event) => {
|
||||
const window = windowPool.get("settings");
|
||||
if (window && !window.isDestroyed()) {
|
||||
if (settingsHideTimer) {
|
||||
clearTimeout(settingsHideTimer);
|
||||
}
|
||||
settingsHideTimer = setTimeout(() => {
|
||||
if (window && !window.isDestroyed()) {
|
||||
window.setAlwaysOnTop(false);
|
||||
window.hide();
|
||||
}
|
||||
settingsHideTimer = null;
|
||||
}, 200);
|
||||
|
||||
window.__lockedByButton = false;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('cancel-hide-settings-window', (event) => {
|
||||
if (settingsHideTimer) {
|
||||
clearTimeout(settingsHideTimer);
|
||||
settingsHideTimer = null;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('quit-application', () => {
|
||||
app.quit();
|
||||
});
|
||||
|
||||
ipcMain.handle('is-ask-window-visible', (event, windowName) => {
|
||||
const window = windowPool.get(windowName);
|
||||
if (window && !window.isDestroyed()) {
|
||||
return window.isVisible();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
ipcMain.handle('toggle-content-protection', () => {
|
||||
isContentProtectionOn = !isContentProtectionOn;
|
||||
console.log(`[Protection] Content protection toggled to: ${isContentProtectionOn}`);
|
||||
@ -827,8 +700,6 @@ function setupIpcHandlers(movementManager) {
|
||||
console.log('Opening personalization page:', personalizeUrl);
|
||||
});
|
||||
|
||||
setupApiKeyIPC();
|
||||
|
||||
|
||||
ipcMain.handle('resize-header-window', (event, { width, height }) => {
|
||||
const header = windowPool.get('header');
|
||||
@ -952,12 +823,32 @@ function setupIpcHandlers(movementManager) {
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('force-close-window', (event, windowName) => {
|
||||
const window = windowPool.get(windowName);
|
||||
if (window && !window.isDestroyed()) {
|
||||
console.log(`[WindowManager] Force closing window: ${windowName}`);
|
||||
ipcMain.handle('adjust-window-height', (event, targetHeight) => {
|
||||
const senderWindow = BrowserWindow.fromWebContents(event.sender);
|
||||
if (senderWindow) {
|
||||
const wasResizable = senderWindow.isResizable();
|
||||
if (!wasResizable) {
|
||||
senderWindow.setResizable(true);
|
||||
}
|
||||
|
||||
window.webContents.send('window-hide-animation');
|
||||
const currentBounds = senderWindow.getBounds();
|
||||
const minHeight = senderWindow.getMinimumSize()[1];
|
||||
const maxHeight = senderWindow.getMaximumSize()[1];
|
||||
|
||||
let adjustedHeight;
|
||||
if (maxHeight === 0) {
|
||||
adjustedHeight = Math.max(minHeight, targetHeight);
|
||||
} else {
|
||||
adjustedHeight = Math.max(minHeight, Math.min(maxHeight, targetHeight));
|
||||
}
|
||||
|
||||
senderWindow.setSize(currentBounds.width, adjustedHeight, false);
|
||||
|
||||
if (!wasResizable) {
|
||||
senderWindow.setResizable(false);
|
||||
}
|
||||
|
||||
updateLayout();
|
||||
}
|
||||
});
|
||||
|
||||
@ -1140,13 +1031,95 @@ function setupIpcHandlers(movementManager) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('toggle-all-windows-visibility', () => toggleAllWindowsVisibility());
|
||||
|
||||
ipcMain.handle('close-ask-window-if-empty', async () => {
|
||||
const askWindow = windowPool.get('ask');
|
||||
if (askWindow && !askWindow.isFocused()) {
|
||||
askWindow.hide();
|
||||
ipcMain.handle('toggle-feature', async (event, featureName) => {
|
||||
return toggleFeature(featureName);
|
||||
});
|
||||
|
||||
ipcMain.on('animation-finished', (event) => {
|
||||
const win = BrowserWindow.fromWebContents(event.sender);
|
||||
if (win && !win.isDestroyed()) {
|
||||
console.log(`[WindowManager] Hiding window after animation.`);
|
||||
win.hide();
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('show-settings-window', (event, bounds) => {
|
||||
if (!bounds) return;
|
||||
const win = windowPool.get('settings');
|
||||
|
||||
if (win && !win.isDestroyed()) {
|
||||
if (settingsHideTimer) {
|
||||
clearTimeout(settingsHideTimer);
|
||||
settingsHideTimer = null;
|
||||
}
|
||||
|
||||
// Adjust position based on button bounds
|
||||
const header = windowPool.get('header');
|
||||
const headerBounds = header?.getBounds() ?? { x: 0, y: 0 };
|
||||
const settingsBounds = win.getBounds();
|
||||
|
||||
const disp = getCurrentDisplay(header);
|
||||
const { x: waX, y: waY, width: waW, height: waH } = disp.workArea;
|
||||
|
||||
let x = Math.round(headerBounds.x + (bounds?.x ?? 0) + (bounds?.width ?? 0) / 2 - settingsBounds.width / 2);
|
||||
let y = Math.round(headerBounds.y + (bounds?.y ?? 0) + (bounds?.height ?? 0) + 31);
|
||||
|
||||
x = Math.max(waX + 10, Math.min(waX + waW - settingsBounds.width - 10, x));
|
||||
y = Math.max(waY + 10, Math.min(waY + waH - settingsBounds.height - 10, y));
|
||||
|
||||
win.setBounds({ x, y });
|
||||
win.__lockedByButton = true;
|
||||
console.log(`[WindowManager] Positioning settings window at (${x}, ${y}) based on button bounds.`);
|
||||
|
||||
win.show();
|
||||
win.moveTop();
|
||||
win.setAlwaysOnTop(true);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('hide-settings-window', (event) => {
|
||||
const window = windowPool.get("settings");
|
||||
if (window && !window.isDestroyed()) {
|
||||
if (settingsHideTimer) {
|
||||
clearTimeout(settingsHideTimer);
|
||||
}
|
||||
settingsHideTimer = setTimeout(() => {
|
||||
if (window && !window.isDestroyed()) {
|
||||
window.setAlwaysOnTop(false);
|
||||
window.hide();
|
||||
}
|
||||
settingsHideTimer = null;
|
||||
}, 200);
|
||||
|
||||
window.__lockedByButton = false;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('cancel-hide-settings-window', (event) => {
|
||||
if (settingsHideTimer) {
|
||||
clearTimeout(settingsHideTimer);
|
||||
settingsHideTimer = null;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
ipcMain.handle('ask:closeAskWindow', async () => {
|
||||
const askWindow = windowPool.get('ask');
|
||||
if (askWindow) {
|
||||
askWindow.webContents.send('window-hide-animation');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
ipcMain.handle('ask:sendQuestionToMain', (event, question) => {
|
||||
console.log('📨 Main process: Sending question to AskView', question);
|
||||
toggleFeature('ask', {ask: { questionText: question }});
|
||||
return { success: true };
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -1290,7 +1263,7 @@ function updateGlobalShortcuts(keybinds, mainWindow, sendToRenderer, movementMan
|
||||
callback = () => toggleAllWindowsVisibility();
|
||||
break;
|
||||
case 'nextStep':
|
||||
callback = () => toggleFeature('ask');
|
||||
callback = () => toggleFeature('ask', {ask: { targetVisibility: 'show' }});
|
||||
break;
|
||||
case 'scrollUp':
|
||||
callback = () => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user