toggle listen button logic improved
This commit is contained in:
parent
3031d0d288
commit
e86c2db464
35
package-lock.json
generated
35
package-lock.json
generated
@ -4172,28 +4172,6 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fs-temp": {
|
|
||||||
"version": "1.2.1",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"random-path": "^0.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/fs-xattr": {
|
|
||||||
"version": "0.3.1",
|
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"!win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8.6.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/fs.realpath": {
|
"node_modules/fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -5158,19 +5136,6 @@
|
|||||||
"lru-cache": "6.0.0"
|
"lru-cache": "6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/macos-alias": {
|
|
||||||
"version": "0.2.12",
|
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"nan": "^2.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/make-fetch-happen": {
|
"node_modules/make-fetch-happen": {
|
||||||
"version": "10.2.1",
|
"version": "10.2.1",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
@ -68,12 +68,7 @@ export class PickleGlassApp extends LitElement {
|
|||||||
this.selectedScreenshotInterval = localStorage.getItem('selectedScreenshotInterval') || '5';
|
this.selectedScreenshotInterval = localStorage.getItem('selectedScreenshotInterval') || '5';
|
||||||
this.selectedImageQuality = localStorage.getItem('selectedImageQuality') || 'medium';
|
this.selectedImageQuality = localStorage.getItem('selectedImageQuality') || 'medium';
|
||||||
this._isClickThrough = false;
|
this._isClickThrough = false;
|
||||||
this.outlines = [];
|
|
||||||
this.analysisRequests = [];
|
|
||||||
|
|
||||||
window.pickleGlass.setStructuredData = data => {
|
|
||||||
this.updateStructuredData(data);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
@ -82,14 +77,13 @@ export class PickleGlassApp extends LitElement {
|
|||||||
if (window.require) {
|
if (window.require) {
|
||||||
const { ipcRenderer } = window.require('electron');
|
const { ipcRenderer } = window.require('electron');
|
||||||
|
|
||||||
ipcRenderer.on('update-status', (_, status) => this.setStatus(status));
|
|
||||||
ipcRenderer.on('click-through-toggled', (_, isEnabled) => {
|
ipcRenderer.on('click-through-toggled', (_, isEnabled) => {
|
||||||
this._isClickThrough = isEnabled;
|
this._isClickThrough = isEnabled;
|
||||||
});
|
});
|
||||||
ipcRenderer.on('start-listening-session', () => {
|
// ipcRenderer.on('start-listening-session', () => {
|
||||||
console.log('Received start-listening-session command, calling handleListenClick.');
|
// console.log('Received start-listening-session command, calling handleListenClick.');
|
||||||
this.handleListenClick();
|
// this.handleListenClick();
|
||||||
});
|
// });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,16 +91,15 @@ export class PickleGlassApp extends LitElement {
|
|||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
if (window.require) {
|
if (window.require) {
|
||||||
const { ipcRenderer } = window.require('electron');
|
const { ipcRenderer } = window.require('electron');
|
||||||
ipcRenderer.removeAllListeners('update-status');
|
|
||||||
ipcRenderer.removeAllListeners('click-through-toggled');
|
ipcRenderer.removeAllListeners('click-through-toggled');
|
||||||
ipcRenderer.removeAllListeners('start-listening-session');
|
// ipcRenderer.removeAllListeners('start-listening-session');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updated(changedProperties) {
|
updated(changedProperties) {
|
||||||
if (changedProperties.has('isMainViewVisible') || changedProperties.has('currentView')) {
|
// if (changedProperties.has('isMainViewVisible') || changedProperties.has('currentView')) {
|
||||||
this.requestWindowResize();
|
// this.requestWindowResize();
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (changedProperties.has('currentView')) {
|
if (changedProperties.has('currentView')) {
|
||||||
const viewContainer = this.shadowRoot?.querySelector('.view-container');
|
const viewContainer = this.shadowRoot?.querySelector('.view-container');
|
||||||
@ -136,57 +129,35 @@ export class PickleGlassApp extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatus(text) {
|
|
||||||
this.statusText = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleListenClick() {
|
// async handleListenClick() {
|
||||||
if (window.require) {
|
// if (window.require) {
|
||||||
const { ipcRenderer } = window.require('electron');
|
// const { ipcRenderer } = window.require('electron');
|
||||||
const isActive = await ipcRenderer.invoke('is-session-active');
|
// const isActive = await ipcRenderer.invoke('is-session-active');
|
||||||
if (isActive) {
|
// // if (isActive) {
|
||||||
console.log('Session is already active. No action needed.');
|
// // console.log('Session is already active. No action needed.');
|
||||||
return;
|
// // return;
|
||||||
}
|
// // }
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (window.pickleGlass) {
|
// if (window.pickleGlass) {
|
||||||
await window.pickleGlass.initializeopenai(this.selectedProfile, this.selectedLanguage);
|
// // await window.pickleGlass.initializeopenai(this.selectedProfile, this.selectedLanguage);
|
||||||
window.pickleGlass.startCapture(this.selectedScreenshotInterval, this.selectedImageQuality);
|
// window.pickleGlass.startCapture(this.selectedScreenshotInterval, this.selectedImageQuality);
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 🔄 Clear previous summary/analysis when a new listening session begins
|
// // 🔄 Clear previous summary/analysis when a new listening session begins
|
||||||
this.structuredData = {
|
// this.structuredData = {
|
||||||
summary: [],
|
// summary: [],
|
||||||
topic: { header: '', bullets: [] },
|
// topic: { header: '', bullets: [] },
|
||||||
actions: [],
|
// actions: [],
|
||||||
followUps: [],
|
// followUps: [],
|
||||||
};
|
// };
|
||||||
|
|
||||||
this.currentResponseIndex = -1;
|
// this.currentResponseIndex = -1;
|
||||||
this.startTime = Date.now();
|
// this.startTime = Date.now();
|
||||||
this.currentView = 'listen';
|
// this.currentView = 'listen';
|
||||||
this.isMainViewVisible = true;
|
// this.isMainViewVisible = true;
|
||||||
}
|
// }
|
||||||
|
|
||||||
handleShowHideClick() {
|
|
||||||
this.isMainViewVisible = !this.isMainViewVisible;
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSettingsClick() {
|
|
||||||
this.currentView = 'settings';
|
|
||||||
this.isMainViewVisible = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
handleHelpClick() {
|
|
||||||
this.currentView = 'help';
|
|
||||||
this.isMainViewVisible = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
handleHistoryClick() {
|
|
||||||
this.currentView = 'history';
|
|
||||||
this.isMainViewVisible = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleClose() {
|
async handleClose() {
|
||||||
if (window.require) {
|
if (window.require) {
|
||||||
@ -195,50 +166,8 @@ export class PickleGlassApp extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBackClick() {
|
|
||||||
this.currentView = 'listen';
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleSendText(message) {
|
|
||||||
if (window.pickleGlass) {
|
|
||||||
const result = await window.pickleGlass.sendTextMessage(message);
|
|
||||||
|
|
||||||
if (!result.success) {
|
|
||||||
console.error('Failed to send message:', result.error);
|
|
||||||
this.setStatus('Error sending message: ' + result.error);
|
|
||||||
} else {
|
|
||||||
this.setStatus('Message sent...');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// updateOutline(outline) {
|
|
||||||
// console.log('📝 PickleGlassApp updateOutline:', outline);
|
|
||||||
// this.outlines = [...outline];
|
|
||||||
// this.requestUpdate();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// updateAnalysisRequests(requests) {
|
|
||||||
// console.log('📝 PickleGlassApp updateAnalysisRequests:', requests);
|
|
||||||
// this.analysisRequests = [...requests];
|
|
||||||
// this.requestUpdate();
|
|
||||||
// }
|
|
||||||
|
|
||||||
updateStructuredData(data) {
|
|
||||||
console.log('📝 PickleGlassApp updateStructuredData:', data);
|
|
||||||
this.structuredData = data;
|
|
||||||
this.requestUpdate();
|
|
||||||
|
|
||||||
const assistantView = this.shadowRoot?.querySelector('assistant-view');
|
|
||||||
if (assistantView) {
|
|
||||||
assistantView.structuredData = data;
|
|
||||||
console.log('✅ Structured data passed to AssistantView');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleResponseIndexChanged(e) {
|
|
||||||
this.currentResponseIndex = e.detail.index;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
switch (this.currentView) {
|
switch (this.currentView) {
|
||||||
@ -247,7 +176,6 @@ export class PickleGlassApp extends LitElement {
|
|||||||
.currentResponseIndex=${this.currentResponseIndex}
|
.currentResponseIndex=${this.currentResponseIndex}
|
||||||
.selectedProfile=${this.selectedProfile}
|
.selectedProfile=${this.selectedProfile}
|
||||||
.structuredData=${this.structuredData}
|
.structuredData=${this.structuredData}
|
||||||
.onSendText=${message => this.handleSendText(message)}
|
|
||||||
@response-index-changed=${e => (this.currentResponseIndex = e.detail.index)}
|
@response-index-changed=${e => (this.currentResponseIndex = e.detail.index)}
|
||||||
></assistant-view>`;
|
></assistant-view>`;
|
||||||
case 'ask':
|
case 'ask':
|
||||||
|
@ -90,7 +90,7 @@ function createFeatureWindows(header, namesToCreate) {
|
|||||||
hasShadow: false,
|
hasShadow: false,
|
||||||
skipTaskbar: true,
|
skipTaskbar: true,
|
||||||
hiddenInMissionControl: true,
|
hiddenInMissionControl: true,
|
||||||
resizable: false,
|
resizable: true,
|
||||||
webPreferences: { nodeIntegration: true, contextIsolation: false },
|
webPreferences: { nodeIntegration: true, contextIsolation: false },
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -100,8 +100,8 @@ function createFeatureWindows(header, namesToCreate) {
|
|||||||
switch (name) {
|
switch (name) {
|
||||||
case 'listen': {
|
case 'listen': {
|
||||||
const listen = new BrowserWindow({
|
const listen = new BrowserWindow({
|
||||||
...commonChildOptions, width:400,minWidth:400,maxWidth:400,
|
...commonChildOptions, width:400,minWidth:400,maxWidth:900,
|
||||||
maxHeight:700,
|
maxHeight:900,
|
||||||
});
|
});
|
||||||
listen.setContentProtection(isContentProtectionOn);
|
listen.setContentProtection(isContentProtectionOn);
|
||||||
listen.setVisibleOnAllWorkspaces(true,{visibleOnFullScreen:true});
|
listen.setVisibleOnAllWorkspaces(true,{visibleOnFullScreen:true});
|
||||||
@ -472,18 +472,27 @@ function createWindows() {
|
|||||||
createFeatureWindows(windowPool.get('header'));
|
createFeatureWindows(windowPool.get('header'));
|
||||||
}
|
}
|
||||||
|
|
||||||
const windowToToggle = windowPool.get(featureName);
|
|
||||||
|
|
||||||
if (windowToToggle) {
|
if (featureName === 'listen') {
|
||||||
if (featureName === 'listen') {
|
|
||||||
const listenService = global.listenService;
|
|
||||||
if (listenService && listenService.isSessionActive()) {
|
|
||||||
console.log('[WindowManager] Listen session is active, closing it via toggle.');
|
|
||||||
await listenService.closeSession();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log(`[WindowManager] Toggling feature: ${featureName}`);
|
console.log(`[WindowManager] Toggling feature: ${featureName}`);
|
||||||
|
const listenWindow = windowPool.get(featureName);
|
||||||
|
const listenService = global.listenService;
|
||||||
|
if (listenService && listenService.isSessionActive()) {
|
||||||
|
console.log('[WindowManager] Listen session is active, closing it via toggle.');
|
||||||
|
await listenService.closeSession();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listenWindow.isVisible()) {
|
||||||
|
listenWindow.webContents.send('window-hide-animation');
|
||||||
|
} else {
|
||||||
|
listenWindow.show();
|
||||||
|
updateLayout();
|
||||||
|
// listenWindow.webContents.send('start-listening-session');
|
||||||
|
listenWindow.webContents.send('window-show-animation');
|
||||||
|
await listenService.initializeSession();
|
||||||
|
// listenWindow.webContents.send('start-listening-session');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (featureName === 'ask') {
|
if (featureName === 'ask') {
|
||||||
@ -561,31 +570,29 @@ function createWindows() {
|
|||||||
askWindow.webContents.send('window-show-animation');
|
askWindow.webContents.send('window-show-animation');
|
||||||
askWindow.webContents.send('window-did-show');
|
askWindow.webContents.send('window-did-show');
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
const windowToToggle = windowPool.get(featureName);
|
|
||||||
|
|
||||||
if (windowToToggle) {
|
if (featureName === 'settings') {
|
||||||
if (windowToToggle.isDestroyed()) {
|
const settingsWindow = windowPool.get(featureName);
|
||||||
|
|
||||||
|
if (settingsWindow) {
|
||||||
|
if (settingsWindow.isDestroyed()) {
|
||||||
console.error(`Window ${featureName} is destroyed, cannot toggle`);
|
console.error(`Window ${featureName} is destroyed, cannot toggle`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (windowToToggle.isVisible()) {
|
if (settingsWindow.isVisible()) {
|
||||||
if (featureName === 'settings') {
|
if (featureName === 'settings') {
|
||||||
windowToToggle.webContents.send('settings-window-hide-animation');
|
settingsWindow.webContents.send('settings-window-hide-animation');
|
||||||
} else {
|
} else {
|
||||||
windowToToggle.webContents.send('window-hide-animation');
|
settingsWindow.webContents.send('window-hide-animation');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
windowToToggle.show();
|
settingsWindow.show();
|
||||||
updateLayout();
|
updateLayout();
|
||||||
|
|
||||||
if (featureName === 'listen') {
|
settingsWindow.webContents.send('window-show-animation');
|
||||||
windowToToggle.webContents.send('start-listening-session');
|
|
||||||
}
|
|
||||||
|
|
||||||
windowToToggle.webContents.send('window-show-animation');
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error showing window:', e);
|
console.error('Error showing window:', e);
|
||||||
}
|
}
|
||||||
|
@ -146,6 +146,7 @@ class ListenService {
|
|||||||
|
|
||||||
this.sendToRenderer('session-state-changed', { isActive: true });
|
this.sendToRenderer('session-state-changed', { isActive: true });
|
||||||
this.sendToRenderer('update-status', 'Connected. Ready to listen.');
|
this.sendToRenderer('update-status', 'Connected. Ready to listen.');
|
||||||
|
// this.sendToRenderer('change-listen-capture-state', { status: "start" });
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -155,6 +156,7 @@ class ListenService {
|
|||||||
} finally {
|
} finally {
|
||||||
this.isInitializingSession = false;
|
this.isInitializingSession = false;
|
||||||
this.sendToRenderer('session-initializing', false);
|
this.sendToRenderer('session-initializing', false);
|
||||||
|
this.sendToRenderer('change-listen-capture-state', { status: "start" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,6 +196,7 @@ class ListenService {
|
|||||||
|
|
||||||
this.sendToRenderer('session-state-changed', { isActive: false });
|
this.sendToRenderer('session-state-changed', { isActive: false });
|
||||||
this.sendToRenderer('session-did-close');
|
this.sendToRenderer('session-did-close');
|
||||||
|
this.sendToRenderer('change-listen-capture-state', { status: "stop" });
|
||||||
|
|
||||||
console.log('Listen service session closed.');
|
console.log('Listen service session closed.');
|
||||||
return { success: true };
|
return { success: true };
|
||||||
|
@ -7,9 +7,15 @@ let aecPtr = 0; // Rust Aec* 1개만 재사용
|
|||||||
|
|
||||||
/** WASM 모듈 가져오고 1회 초기화 */
|
/** WASM 모듈 가져오고 1회 초기화 */
|
||||||
async function getAec () {
|
async function getAec () {
|
||||||
if (aecModPromise) return aecModPromise; // 캐시
|
if (aecModPromise) {
|
||||||
|
console.log('[AEC] getAec: 캐시=있음(재사용)');
|
||||||
|
return aecModPromise; // 캐시
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[AEC] getAec: 캐시=없음 → 모듈 로드 시작');
|
||||||
|
|
||||||
aecModPromise = createAecModule().then((M) => {
|
aecModPromise = createAecModule().then((M) => {
|
||||||
|
console.log('[AEC] WASM 모듈 로드 완료');
|
||||||
aecMod = M;
|
aecMod = M;
|
||||||
// C 심볼 → JS 래퍼 바인딩 (딱 1번)
|
// C 심볼 → JS 래퍼 바인딩 (딱 1번)
|
||||||
M.newPtr = M.cwrap('AecNew', 'number',
|
M.newPtr = M.cwrap('AecNew', 'number',
|
||||||
@ -18,7 +24,12 @@ async function getAec () {
|
|||||||
['number','number','number','number','number']);
|
['number','number','number','number','number']);
|
||||||
M.destroy = M.cwrap('AecDestroy', null, ['number']);
|
M.destroy = M.cwrap('AecDestroy', null, ['number']);
|
||||||
return M;
|
return M;
|
||||||
});
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('[AEC] WASM 모듈 로드 실패:', err);
|
||||||
|
throw err; // 상위에서도 잡을 수 있게
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
return aecModPromise;
|
return aecModPromise;
|
||||||
}
|
}
|
||||||
@ -132,6 +143,10 @@ function disposeAec () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function runAecSync (micF32, sysF32) {
|
function runAecSync (micF32, sysF32) {
|
||||||
|
const modStat = aecMod?.HEAPU8 ? '있음' : '없음'; // aecMod가 초기화되었고 HEAP 접근 가능?
|
||||||
|
const ptrStat = aecPtr ? '있음' : '없음'; // newPtr 호출 여부
|
||||||
|
const heapStat = aecMod?.HEAPU8 ? '있음' : '없음'; // HEAPU8 생성 여부
|
||||||
|
console.log(`[AEC] mod:${modStat} ptr:${ptrStat} heap:${heapStat}`);
|
||||||
if (!aecMod || !aecPtr || !aecMod.HEAPU8) return micF32; // 아직 모듈 안 뜸 → 패스
|
if (!aecMod || !aecPtr || !aecMod.HEAPU8) return micF32; // 아직 모듈 안 뜸 → 패스
|
||||||
|
|
||||||
const len = micF32.length;
|
const len = micF32.length;
|
||||||
@ -145,6 +160,7 @@ function runAecSync (micF32, sysF32) {
|
|||||||
const outF32 = float32FromInt16View(new Int16Array(heapBuf, out, len));
|
const outF32 = float32FromInt16View(new Int16Array(heapBuf, out, len));
|
||||||
|
|
||||||
aecMod._free(mic.ptr); aecMod._free(echo.ptr); aecMod._free(out);
|
aecMod._free(mic.ptr); aecMod._free(echo.ptr); aecMod._free(out);
|
||||||
|
console.log(`[AEC] 적용 완료`);
|
||||||
return outF32;
|
return outF32;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,7 +282,7 @@ async function setupMicProcessing(micStream) {
|
|||||||
micProcessor.onaudioprocess = (e) => {
|
micProcessor.onaudioprocess = (e) => {
|
||||||
const inputData = e.inputBuffer.getChannelData(0);
|
const inputData = e.inputBuffer.getChannelData(0);
|
||||||
audioBuffer.push(...inputData);
|
audioBuffer.push(...inputData);
|
||||||
console.log('🎤 micProcessor.onaudioprocess');
|
// console.log('🎤 micProcessor.onaudioprocess');
|
||||||
|
|
||||||
// samplesPerChunk(=2400) 만큼 모이면 전송
|
// samplesPerChunk(=2400) 만큼 모이면 전송
|
||||||
while (audioBuffer.length >= samplesPerChunk) {
|
while (audioBuffer.length >= samplesPerChunk) {
|
||||||
@ -280,7 +296,7 @@ async function setupMicProcessing(micStream) {
|
|||||||
|
|
||||||
// **음성 구간일 때만 런**
|
// **음성 구간일 때만 런**
|
||||||
processedChunk = runAecSync(new Float32Array(chunk), sysF32);
|
processedChunk = runAecSync(new Float32Array(chunk), sysF32);
|
||||||
console.log('🔊 Applied WASM-AEC (speex)');
|
// console.log('🔊 Applied WASM-AEC (speex)');
|
||||||
} else {
|
} else {
|
||||||
console.log('🔊 No system audio for AEC reference');
|
console.log('🔊 No system audio for AEC reference');
|
||||||
}
|
}
|
||||||
|
@ -1,138 +1,30 @@
|
|||||||
// renderer.js
|
// renderer.js
|
||||||
const { ipcRenderer } = require('electron');
|
const { ipcRenderer } = require('electron');
|
||||||
const listenCapture = require('./listenCapture.js');
|
const listenCapture = require('./listenCapture.js');
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
const isListenView = params.get('view') === 'listen';
|
||||||
|
|
||||||
let realtimeConversationHistory = [];
|
|
||||||
|
|
||||||
async function queryLoginState() {
|
|
||||||
const userState = await ipcRenderer.invoke('get-current-user');
|
|
||||||
return userState;
|
|
||||||
}
|
|
||||||
|
|
||||||
function pickleGlassElement() {
|
|
||||||
return document.getElementById('pickle-glass');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function initializeopenai(profile = 'interview', language = 'en') {
|
|
||||||
// The API key is now handled in the main process from .env file.
|
|
||||||
// We just need to trigger the initialization.
|
|
||||||
try {
|
|
||||||
console.log(`Requesting OpenAI initialization with profile: ${profile}, language: ${language}`);
|
|
||||||
const success = await ipcRenderer.invoke('initialize-openai', profile, language);
|
|
||||||
if (success) {
|
|
||||||
// The status will be updated via 'update-status' event from the main process.
|
|
||||||
console.log('OpenAI initialization successful.');
|
|
||||||
} else {
|
|
||||||
console.error('OpenAI initialization failed.');
|
|
||||||
const appElement = pickleGlassElement();
|
|
||||||
if (appElement && typeof appElement.setStatus === 'function') {
|
|
||||||
appElement.setStatus('Initialization Failed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error during OpenAI initialization IPC call:', error);
|
|
||||||
const appElement = pickleGlassElement();
|
|
||||||
if (appElement && typeof appElement.setStatus === 'function') {
|
|
||||||
appElement.setStatus('Error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listen for status updates
|
|
||||||
ipcRenderer.on('update-status', (event, status) => {
|
|
||||||
console.log('Status update:', status);
|
|
||||||
pickleGlass.e().setStatus(status);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Listen for real-time STT updates
|
|
||||||
ipcRenderer.on('stt-update', (event, data) => {
|
|
||||||
console.log('Renderer.js stt-update', data);
|
|
||||||
const { speaker, text, isFinal, isPartial, timestamp } = data;
|
|
||||||
|
|
||||||
if (isPartial) {
|
|
||||||
console.log(`🔄 [${speaker} - partial]: ${text}`);
|
|
||||||
} else if (isFinal) {
|
|
||||||
console.log(`✅ [${speaker} - final]: ${text}`);
|
|
||||||
|
|
||||||
const speakerText = speaker.toLowerCase();
|
|
||||||
const conversationText = `${speakerText}: ${text.trim()}`;
|
|
||||||
|
|
||||||
realtimeConversationHistory.push(conversationText);
|
|
||||||
|
|
||||||
if (realtimeConversationHistory.length > 30) {
|
|
||||||
realtimeConversationHistory = realtimeConversationHistory.slice(-30);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`📝 Updated realtime conversation history: ${realtimeConversationHistory.length} texts`);
|
|
||||||
console.log(`📋 Latest text: ${conversationText}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pickleGlass.e() && typeof pickleGlass.e().updateRealtimeTranscription === 'function') {
|
|
||||||
pickleGlass.e().updateRealtimeTranscription({
|
|
||||||
speaker,
|
|
||||||
text,
|
|
||||||
isFinal,
|
|
||||||
isPartial,
|
|
||||||
timestamp,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcRenderer.on('update-structured-data', (_, structuredData) => {
|
|
||||||
console.log('📥 Received structured data update:', structuredData);
|
|
||||||
window.pickleGlass.structuredData = structuredData;
|
|
||||||
window.pickleGlass.setStructuredData(structuredData);
|
|
||||||
});
|
|
||||||
|
|
||||||
window.pickleGlass.structuredData = {
|
|
||||||
summary: [],
|
|
||||||
topic: { header: '', bullets: [] },
|
|
||||||
actions: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
window.pickleGlass.setStructuredData = data => {
|
|
||||||
window.pickleGlass.structuredData = data;
|
|
||||||
pickleGlass.e()?.updateStructuredData?.(data);
|
|
||||||
};
|
|
||||||
|
|
||||||
function formatRealtimeConversationHistory() {
|
|
||||||
if (realtimeConversationHistory.length === 0) return 'No conversation history available.';
|
|
||||||
|
|
||||||
return realtimeConversationHistory.slice(-30).join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
window.pickleGlass = {
|
window.pickleGlass = {
|
||||||
initializeopenai,
|
|
||||||
startCapture: listenCapture.startCapture,
|
startCapture: listenCapture.startCapture,
|
||||||
stopCapture: listenCapture.stopCapture,
|
stopCapture: listenCapture.stopCapture,
|
||||||
isLinux: listenCapture.isLinux,
|
isLinux: listenCapture.isLinux,
|
||||||
isMacOS: listenCapture.isMacOS,
|
isMacOS: listenCapture.isMacOS,
|
||||||
captureManualScreenshot: listenCapture.captureManualScreenshot,
|
captureManualScreenshot: listenCapture.captureManualScreenshot,
|
||||||
getCurrentScreenshot: listenCapture.getCurrentScreenshot,
|
getCurrentScreenshot: listenCapture.getCurrentScreenshot,
|
||||||
e: pickleGlassElement,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------------------------
|
|
||||||
// 🔔 React to session state changes from the main process
|
ipcRenderer.on('change-listen-capture-state', (_event, { status }) => {
|
||||||
// When the session ends (isActive === false), ensure we stop
|
if (!isListenView) {
|
||||||
// all local capture pipelines (mic, screen, etc.).
|
console.log('[Renderer] Non-listen view: ignoring capture-state change');
|
||||||
// -------------------------------------------------------
|
return;
|
||||||
ipcRenderer.on('session-state-changed', (_event, { isActive }) => {
|
}
|
||||||
if (!isActive) {
|
if (status === "stop") {
|
||||||
console.log('[Renderer] Session ended – stopping local capture');
|
console.log('[Renderer] Session ended – stopping local capture');
|
||||||
listenCapture.stopCapture();
|
listenCapture.stopCapture();
|
||||||
} else {
|
} else {
|
||||||
console.log('[Renderer] New session started – clearing in-memory history and summaries');
|
console.log('[Renderer] Session initialized – starting local capture');
|
||||||
|
listenCapture.startCapture();
|
||||||
// Reset live conversation & analysis caches
|
|
||||||
realtimeConversationHistory = [];
|
|
||||||
|
|
||||||
const blankData = {
|
|
||||||
summary: [],
|
|
||||||
topic: { header: '', bullets: [] },
|
|
||||||
actions: [],
|
|
||||||
followUps: [],
|
|
||||||
};
|
|
||||||
window.pickleGlass.setStructuredData(blankData);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -264,7 +264,7 @@ export class SummaryView extends LitElement {
|
|||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
if (window.require) {
|
if (window.require) {
|
||||||
const { ipcRenderer } = window.require('electron');
|
const { ipcRenderer } = window.require('electron');
|
||||||
ipcRenderer.on('update-structured-data', (event, data) => {
|
ipcRenderer.on('summary-update', (event, data) => {
|
||||||
this.structuredData = data;
|
this.structuredData = data;
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
});
|
});
|
||||||
@ -275,7 +275,7 @@ export class SummaryView extends LitElement {
|
|||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
if (window.require) {
|
if (window.require) {
|
||||||
const { ipcRenderer } = window.require('electron');
|
const { ipcRenderer } = window.require('electron');
|
||||||
ipcRenderer.removeAllListeners('update-structured-data');
|
ipcRenderer.removeAllListeners('summary-update');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,23 +27,6 @@ class SummaryService {
|
|||||||
this.currentSessionId = sessionId;
|
this.currentSessionId = sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// async getApiKey() {
|
|
||||||
// const storedKey = await getStoredApiKey();
|
|
||||||
// if (storedKey) {
|
|
||||||
// console.log('[SummaryService] Using stored API key');
|
|
||||||
// return storedKey;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const envKey = process.env.OPENAI_API_KEY;
|
|
||||||
// if (envKey) {
|
|
||||||
// console.log('[SummaryService] Using environment API key');
|
|
||||||
// return envKey;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// console.error('[SummaryService] No API key found in storage or environment');
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
|
|
||||||
sendToRenderer(channel, data) {
|
sendToRenderer(channel, data) {
|
||||||
BrowserWindow.getAllWindows().forEach(win => {
|
BrowserWindow.getAllWindows().forEach(win => {
|
||||||
if (!win.isDestroyed()) {
|
if (!win.isDestroyed()) {
|
||||||
@ -327,7 +310,7 @@ Keep all points concise and build upon previous analysis if provided.`,
|
|||||||
.then(data => {
|
.then(data => {
|
||||||
if (data) {
|
if (data) {
|
||||||
console.log('📤 Sending structured data to renderer');
|
console.log('📤 Sending structured data to renderer');
|
||||||
this.sendToRenderer('update-structured-data', data);
|
this.sendToRenderer('summary-update', data);
|
||||||
|
|
||||||
// Notify callback
|
// Notify callback
|
||||||
if (this.onAnalysisComplete) {
|
if (this.onAnalysisComplete) {
|
||||||
|
15
src/index.js
15
src/index.js
@ -396,21 +396,6 @@ function setupGeneralIpcHandlers() {
|
|||||||
const userRepository = require('./common/repositories/user');
|
const userRepository = require('./common/repositories/user');
|
||||||
const presetRepository = require('./common/repositories/preset');
|
const presetRepository = require('./common/repositories/preset');
|
||||||
|
|
||||||
ipcMain.handle('save-api-key', (event, apiKey) => {
|
|
||||||
try {
|
|
||||||
// The adapter injects the UID and handles local/firebase logic.
|
|
||||||
// Assuming a default provider if not specified.
|
|
||||||
userRepository.saveApiKey(apiKey, 'openai');
|
|
||||||
BrowserWindow.getAllWindows().forEach(win => {
|
|
||||||
win.webContents.send('api-key-updated');
|
|
||||||
});
|
|
||||||
return { success: true };
|
|
||||||
} catch (error) {
|
|
||||||
console.error('IPC: Failed to save API key:', error);
|
|
||||||
return { success: false, error: error.message };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.handle('get-user-presets', () => {
|
ipcMain.handle('get-user-presets', () => {
|
||||||
// The adapter injects the UID.
|
// The adapter injects the UID.
|
||||||
return presetRepository.getPresets();
|
return presetRepository.getPresets();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user