minor fix
This commit is contained in:
parent
4c51d5133c
commit
ba8401345b
@ -1,17 +0,0 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
gcc \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY requirements.txt .
|
||||
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY backend/ .
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
|
@ -1,15 +0,0 @@
|
||||
FROM node:18-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
|
||||
RUN npm ci --only=production
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN npm run build
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["npm", "start"]
|
@ -1,35 +0,0 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
backend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.backend
|
||||
container_name: pickleglass-backend
|
||||
restart: always
|
||||
ports:
|
||||
- "8000:8000"
|
||||
environment:
|
||||
- DATABASE_URL=/app/data/pickleglass.db
|
||||
volumes:
|
||||
- ./backend:/app
|
||||
- ./data:/app/data
|
||||
|
||||
frontend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.frontend
|
||||
container_name: pickleglass-frontend
|
||||
restart: always
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- NEXT_PUBLIC_API_URL=http://localhost:8000
|
||||
depends_on:
|
||||
- backend
|
||||
volumes:
|
||||
- .:/app
|
||||
- /app/node_modules
|
||||
|
||||
volumes:
|
||||
mongodb_data:
|
@ -346,9 +346,18 @@ export class ApiKeyHeader extends LitElement {
|
||||
const isValid = await this.validateApiKey(this.apiKey.trim());
|
||||
|
||||
if (isValid) {
|
||||
console.log('API key valid - starting slide out animation');
|
||||
this.startSlideOutAnimation();
|
||||
this.validatedApiKey = this.apiKey.trim();
|
||||
console.log('API key valid - checking system permissions...');
|
||||
|
||||
const permissionResult = await this.checkAndRequestPermissions();
|
||||
|
||||
if (permissionResult.success) {
|
||||
console.log('All permissions granted - starting slide out animation');
|
||||
this.startSlideOutAnimation();
|
||||
this.validatedApiKey = this.apiKey.trim();
|
||||
} else {
|
||||
this.errorMessage = permissionResult.error || 'Permission setup required';
|
||||
console.log('Permission setup incomplete:', permissionResult);
|
||||
}
|
||||
} else {
|
||||
this.errorMessage = 'Invalid API key - please check and try again';
|
||||
console.log('API key validation failed');
|
||||
@ -398,6 +407,58 @@ export class ApiKeyHeader extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
async checkAndRequestPermissions() {
|
||||
if (!window.require) {
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
const { ipcRenderer } = window.require('electron');
|
||||
|
||||
try {
|
||||
const permissions = await ipcRenderer.invoke('check-system-permissions');
|
||||
console.log('[Permissions] Current status:', permissions);
|
||||
|
||||
if (!permissions.needsSetup) {
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
if (!permissions.microphone) {
|
||||
console.log('[Permissions] Requesting microphone permission...');
|
||||
const micResult = await ipcRenderer.invoke('request-microphone-permission');
|
||||
|
||||
if (!micResult.success) {
|
||||
console.log('[Permissions] Microphone permission denied');
|
||||
await ipcRenderer.invoke('open-system-preferences', 'microphone');
|
||||
return {
|
||||
success: false,
|
||||
error: 'Please grant microphone access in System Preferences'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (!permissions.screen) {
|
||||
console.log('[Permissions] Screen recording permission needed');
|
||||
await ipcRenderer.invoke('open-system-preferences', 'screen-recording');
|
||||
|
||||
this.errorMessage = 'Please grant screen recording permission and try again';
|
||||
this.requestUpdate();
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: 'Please grant screen recording access in System Preferences'
|
||||
};
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error('[Permissions] Error checking/requesting permissions:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: 'Failed to check permissions'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
startSlideOutAnimation() {
|
||||
this.classList.add('sliding-out');
|
||||
}
|
||||
|
@ -10,25 +10,25 @@ export class AppHeader extends LitElement {
|
||||
display: block;
|
||||
transform: translate3d(0, 0, 0);
|
||||
backface-visibility: hidden;
|
||||
transition: transform 0.25s cubic-bezier(0.23, 1, 0.32, 1), opacity 0.25s ease-out;
|
||||
transition: transform 0.2s cubic-bezier(0.23, 1, 0.32, 1), opacity 0.2s ease-out;
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
|
||||
:host(.hiding) {
|
||||
animation: slideUp 0.45s cubic-bezier(0.55, 0.085, 0.68, 0.53) forwards;
|
||||
animation: slideUp 0.3s cubic-bezier(0.4, 0, 0.6, 1) forwards;
|
||||
}
|
||||
|
||||
:host(.showing) {
|
||||
animation: slideDown 0.5s cubic-bezier(0.25, 0.8, 0.25, 1) forwards;
|
||||
animation: slideDown 0.35s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
|
||||
}
|
||||
|
||||
:host(.sliding-in) {
|
||||
animation: fadeIn 0.25s ease-out forwards;
|
||||
will-change: opacity;
|
||||
animation: fadeIn 0.2s ease-out forwards;
|
||||
}
|
||||
|
||||
:host(.hidden) {
|
||||
opacity: 0;
|
||||
transform: translateY(-180%) scale(0.8);
|
||||
transform: translateY(-150%) scale(0.85);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@ -36,65 +36,50 @@ export class AppHeader extends LitElement {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
filter: blur(0px) brightness(1);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||
filter: blur(0px);
|
||||
}
|
||||
25% {
|
||||
opacity: 0.85;
|
||||
transform: translateY(-20%) scale(0.96);
|
||||
filter: blur(0px) brightness(0.95);
|
||||
box-shadow: 0 6px 28px rgba(0, 0, 0, 0.25);
|
||||
30% {
|
||||
opacity: 0.7;
|
||||
transform: translateY(-20%) scale(0.98);
|
||||
filter: blur(0.5px);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
transform: translateY(-60%) scale(0.9);
|
||||
filter: blur(1px) brightness(0.85);
|
||||
box-shadow: 0 3px 15px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
75% {
|
||||
opacity: 0.15;
|
||||
transform: translateY(-120%) scale(0.85);
|
||||
filter: blur(2px) brightness(0.75);
|
||||
box-shadow: 0 1px 8px rgba(0, 0, 0, 0.08);
|
||||
70% {
|
||||
opacity: 0.3;
|
||||
transform: translateY(-80%) scale(0.92);
|
||||
filter: blur(1.5px);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translateY(-180%) scale(0.8);
|
||||
filter: blur(3px) brightness(0.7);
|
||||
box-shadow: 0 0px 0px rgba(0, 0, 0, 0);
|
||||
transform: translateY(-150%) scale(0.85);
|
||||
filter: blur(2px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(-180%) scale(0.8);
|
||||
filter: blur(3px) brightness(0.7);
|
||||
box-shadow: 0 0px 0px rgba(0, 0, 0, 0);
|
||||
transform: translateY(-150%) scale(0.85);
|
||||
filter: blur(2px);
|
||||
}
|
||||
40% {
|
||||
opacity: 0.6;
|
||||
transform: translateY(-30%) scale(0.95);
|
||||
filter: blur(1px) brightness(0.9);
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
|
||||
30% {
|
||||
opacity: 0.5;
|
||||
transform: translateY(-50%) scale(0.92);
|
||||
filter: blur(1px);
|
||||
}
|
||||
70% {
|
||||
65% {
|
||||
opacity: 0.9;
|
||||
transform: translateY(-5%) scale(1.01);
|
||||
filter: blur(0.3px) brightness(1.02);
|
||||
box-shadow: 0 7px 28px rgba(0, 0, 0, 0.28);
|
||||
transform: translateY(-5%) scale(0.99);
|
||||
filter: blur(0.2px);
|
||||
}
|
||||
85% {
|
||||
opacity: 0.98;
|
||||
transform: translateY(1%) scale(0.995);
|
||||
filter: blur(0.1px) brightness(1.01);
|
||||
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.31);
|
||||
transform: translateY(2%) scale(1.005);
|
||||
filter: blur(0px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
filter: blur(0px) brightness(1);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||
filter: blur(0px);
|
||||
}
|
||||
}
|
||||
|
||||
@ -318,6 +303,7 @@ export class AppHeader extends LitElement {
|
||||
this.hasSlidIn = false;
|
||||
this.settingsHideTimer = null;
|
||||
this.isSessionActive = false;
|
||||
this.animationEndTimer = null;
|
||||
|
||||
if (window.require) {
|
||||
const { ipcRenderer } = window.require('electron');
|
||||
@ -388,7 +374,15 @@ export class AppHeader extends LitElement {
|
||||
}
|
||||
|
||||
toggleVisibility() {
|
||||
if (this.isAnimating) return;
|
||||
if (this.isAnimating) {
|
||||
console.log('[AppHeader] Animation already in progress, ignoring toggle');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.animationEndTimer) {
|
||||
clearTimeout(this.animationEndTimer);
|
||||
this.animationEndTimer = null;
|
||||
}
|
||||
|
||||
this.isAnimating = true;
|
||||
|
||||
@ -403,17 +397,34 @@ export class AppHeader extends LitElement {
|
||||
this.classList.remove('showing', 'hidden');
|
||||
this.classList.add('hiding');
|
||||
this.isVisible = false;
|
||||
|
||||
this.animationEndTimer = setTimeout(() => {
|
||||
if (this.classList.contains('hiding')) {
|
||||
this.handleAnimationEnd({ target: this });
|
||||
}
|
||||
}, 350);
|
||||
}
|
||||
|
||||
show() {
|
||||
this.classList.remove('hiding', 'hidden');
|
||||
this.classList.add('showing');
|
||||
this.isVisible = true;
|
||||
|
||||
this.animationEndTimer = setTimeout(() => {
|
||||
if (this.classList.contains('showing')) {
|
||||
this.handleAnimationEnd({ target: this });
|
||||
}
|
||||
}, 400);
|
||||
}
|
||||
|
||||
handleAnimationEnd(e) {
|
||||
if (e.target !== this) return;
|
||||
|
||||
if (this.animationEndTimer) {
|
||||
clearTimeout(this.animationEndTimer);
|
||||
this.animationEndTimer = null;
|
||||
}
|
||||
|
||||
this.isAnimating = false;
|
||||
|
||||
if (this.classList.contains('hiding')) {
|
||||
@ -434,7 +445,7 @@ export class AppHeader extends LitElement {
|
||||
} else if (this.classList.contains('sliding-in')) {
|
||||
this.classList.remove('sliding-in');
|
||||
this.hasSlidIn = true;
|
||||
console.log('AppHeader slide-in animation completed');
|
||||
console.log('[AppHeader] Slide-in animation completed');
|
||||
}
|
||||
}
|
||||
|
||||
@ -460,6 +471,11 @@ export class AppHeader extends LitElement {
|
||||
super.disconnectedCallback();
|
||||
this.removeEventListener('animationend', this.handleAnimationEnd);
|
||||
|
||||
if (this.animationEndTimer) {
|
||||
clearTimeout(this.animationEndTimer);
|
||||
this.animationEndTimer = null;
|
||||
}
|
||||
|
||||
if (window.require) {
|
||||
const { ipcRenderer } = window.require('electron');
|
||||
ipcRenderer.removeAllListeners('toggle-header-visibility');
|
||||
|
@ -81,11 +81,31 @@ class HeaderTransitionManager {
|
||||
|
||||
if (error) {
|
||||
console.warn('[HeaderController] Login payload indicates verification failure. Proceeding to AppHeader UI only.');
|
||||
this.transitionToAppHeader();
|
||||
// Check permissions before transitioning
|
||||
const permissionResult = await this.checkPermissions();
|
||||
if (permissionResult.success) {
|
||||
this.transitionToAppHeader();
|
||||
} else {
|
||||
console.log('[HeaderController] Permissions not granted after login error');
|
||||
if (this.apiKeyHeader) {
|
||||
this.apiKeyHeader.errorMessage = permissionResult.error || 'Permission setup required';
|
||||
this.apiKeyHeader.requestUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[HeaderController] Sign-in failed', error);
|
||||
this.transitionToAppHeader();
|
||||
// Check permissions before transitioning
|
||||
const permissionResult = await this.checkPermissions();
|
||||
if (permissionResult.success) {
|
||||
this.transitionToAppHeader();
|
||||
} else {
|
||||
console.log('[HeaderController] Permissions not granted after sign-in failure');
|
||||
if (this.apiKeyHeader) {
|
||||
this.apiKeyHeader.errorMessage = permissionResult.error || 'Permission setup required';
|
||||
this.apiKeyHeader.requestUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -125,7 +145,17 @@ class HeaderTransitionManager {
|
||||
console.log('[HeaderController] Firebase sign-in successful via ID token');
|
||||
} else {
|
||||
console.warn('[HeaderController] No ID token received from deeplink, virtual key request may fail');
|
||||
this.transitionToAppHeader();
|
||||
// Check permissions before transitioning
|
||||
const permissionResult = await this.checkPermissions();
|
||||
if (permissionResult.success) {
|
||||
this.transitionToAppHeader();
|
||||
} else {
|
||||
console.log('[HeaderController] Permissions not granted after Firebase auth');
|
||||
if (this.apiKeyHeader) {
|
||||
this.apiKeyHeader.errorMessage = permissionResult.error || 'Permission setup required';
|
||||
this.apiKeyHeader.requestUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[HeaderController] Firebase auth failed:', error);
|
||||
@ -173,11 +203,25 @@ class HeaderTransitionManager {
|
||||
}
|
||||
|
||||
if (user) {
|
||||
console.log('[HeaderController] User is logged in, transitioning to AppHeader');
|
||||
this.transitionToAppHeader(!this.hasApiKey);
|
||||
console.log('[HeaderController] User is logged in, checking permissions...');
|
||||
const permissionResult = await this.checkPermissions();
|
||||
if (permissionResult.success) {
|
||||
this.transitionToAppHeader(!this.hasApiKey);
|
||||
} else {
|
||||
console.log('[HeaderController] Permissions not granted, staying on ApiKeyHeader');
|
||||
if (this.apiKeyHeader) {
|
||||
this.apiKeyHeader.errorMessage = permissionResult.error || 'Permission setup required';
|
||||
this.apiKeyHeader.requestUpdate();
|
||||
}
|
||||
}
|
||||
} else if (this.hasApiKey) {
|
||||
console.log('[HeaderController] No Firebase user but API key exists, showing AppHeader');
|
||||
this.transitionToAppHeader(false);
|
||||
console.log('[HeaderController] No Firebase user but API key exists, checking permissions...');
|
||||
const permissionResult = await this.checkPermissions();
|
||||
if (permissionResult.success) {
|
||||
this.transitionToAppHeader(false);
|
||||
} else {
|
||||
console.log('[HeaderController] Permissions not granted, staying on ApiKeyHeader');
|
||||
}
|
||||
} else {
|
||||
console.log('[HeaderController] No auth & no API key — showing ApiKeyHeader');
|
||||
this.transitionToApiKeyHeader();
|
||||
@ -185,7 +229,6 @@ class HeaderTransitionManager {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
notifyHeaderState(stateOverride) {
|
||||
const state = stateOverride || this.currentHeaderType || 'apikey';
|
||||
if (window.require) {
|
||||
@ -193,34 +236,47 @@ class HeaderTransitionManager {
|
||||
}
|
||||
}
|
||||
|
||||
async _bootstrap() {
|
||||
let storedKey = null;
|
||||
if (window.require) {
|
||||
try {
|
||||
storedKey = await window
|
||||
.require('electron')
|
||||
.ipcRenderer.invoke('get-current-api-key');
|
||||
} catch (_) {}
|
||||
}
|
||||
this.hasApiKey = !!storedKey;
|
||||
async _bootstrap() {
|
||||
let storedKey = null;
|
||||
if (window.require) {
|
||||
try {
|
||||
storedKey = await window
|
||||
.require('electron')
|
||||
.ipcRenderer.invoke('get-current-api-key');
|
||||
} catch (_) {}
|
||||
}
|
||||
this.hasApiKey = !!storedKey;
|
||||
|
||||
const user = await new Promise(resolve => {
|
||||
const unsubscribe = onAuthStateChanged(auth, u => {
|
||||
unsubscribe();
|
||||
resolve(u);
|
||||
});
|
||||
});
|
||||
const user = await new Promise(resolve => {
|
||||
const unsubscribe = onAuthStateChanged(auth, u => {
|
||||
unsubscribe();
|
||||
resolve(u);
|
||||
});
|
||||
});
|
||||
|
||||
if (user || this.hasApiKey) {
|
||||
await this._resizeForApp();
|
||||
this.ensureHeader('app');
|
||||
} else {
|
||||
await this._resizeForApiKey();
|
||||
this.ensureHeader('apikey');
|
||||
}
|
||||
if (user || this.hasApiKey) {
|
||||
const permissionResult = await this.checkPermissions();
|
||||
|
||||
if (permissionResult.success) {
|
||||
await this._resizeForApp();
|
||||
this.ensureHeader('app');
|
||||
} else {
|
||||
await this._resizeForApiKey();
|
||||
this.ensureHeader('apikey');
|
||||
|
||||
setTimeout(() => {
|
||||
if (this.apiKeyHeader) {
|
||||
this.apiKeyHeader.errorMessage = permissionResult.error || 'Permission setup required';
|
||||
this.apiKeyHeader.requestUpdate();
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
} else {
|
||||
await this._resizeForApiKey();
|
||||
this.ensureHeader('apikey');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async transitionToAppHeader(animate = true) {
|
||||
if (this.currentHeaderType === 'app') {
|
||||
return this._resizeForApp();
|
||||
@ -249,23 +305,69 @@ class HeaderTransitionManager {
|
||||
}
|
||||
|
||||
_resizeForApp() {
|
||||
if (!window.require) return;
|
||||
return window
|
||||
.require('electron')
|
||||
.ipcRenderer.invoke('resize-header-window', { width: 353, height: 60 })
|
||||
.catch(() => {});
|
||||
if (!window.require) return;
|
||||
return window
|
||||
.require('electron')
|
||||
.ipcRenderer.invoke('resize-header-window', { width: 353, height: 60 })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async _resizeForApiKey() {
|
||||
if (!window.require) return;
|
||||
return window
|
||||
.require('electron')
|
||||
.ipcRenderer.invoke('resize-header-window', { width: 285, height: 220 })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async transitionToApiKeyHeader() {
|
||||
await this._resizeForApiKey();
|
||||
|
||||
if (this.currentHeaderType !== 'apikey') {
|
||||
this.ensureHeader('apikey');
|
||||
}
|
||||
|
||||
async transitionToApiKeyHeader() {
|
||||
await window.require('electron')
|
||||
.ipcRenderer.invoke('resize-header-window', { width: 285, height: 220 });
|
||||
if (this.apiKeyHeader) this.apiKeyHeader.reset();
|
||||
}
|
||||
|
||||
if (this.currentHeaderType !== 'apikey') {
|
||||
this.ensureHeader('apikey');
|
||||
}
|
||||
async checkPermissions() {
|
||||
if (!window.require) {
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
if (this.apiKeyHeader) this.apiKeyHeader.reset();
|
||||
const { ipcRenderer } = window.require('electron');
|
||||
|
||||
try {
|
||||
// Check permission status
|
||||
const permissions = await ipcRenderer.invoke('check-system-permissions');
|
||||
console.log('[HeaderController] Current permissions:', permissions);
|
||||
|
||||
if (!permissions.needsSetup) {
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
// If permissions are not set up, return false
|
||||
let errorMessage = '';
|
||||
if (!permissions.microphone && !permissions.screen) {
|
||||
errorMessage = 'Microphone and screen recording access required';
|
||||
} else if (!permissions.microphone) {
|
||||
errorMessage = 'Microphone access required';
|
||||
} else if (!permissions.screen) {
|
||||
errorMessage = 'Screen recording access required';
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: errorMessage
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('[HeaderController] Error checking permissions:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: 'Failed to check permissions'
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
|
File diff suppressed because it is too large
Load Diff
12
src/index.js
12
src/index.js
@ -1,9 +1,9 @@
|
||||
try {
|
||||
const reloader = require('electron-reloader');
|
||||
reloader(module, {
|
||||
});
|
||||
} catch (err) {
|
||||
}
|
||||
// try {
|
||||
// const reloader = require('electron-reloader');
|
||||
// reloader(module, {
|
||||
// });
|
||||
// } catch (err) {
|
||||
// }
|
||||
|
||||
require('dotenv').config();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user