ux/ui fix
This commit is contained in:
parent
d62dad6992
commit
f18fae6c90
@ -381,11 +381,10 @@ class ModelStateService extends EventEmitter {
|
||||
|
||||
async removeApiKey(provider) {
|
||||
if (this.state.apiKeys[provider]) {
|
||||
delete this.state.apiKeys[provider];
|
||||
this._saveState();
|
||||
|
||||
this.state.apiKeys[provider] = null;
|
||||
await providerSettingsRepository.remove(provider);
|
||||
await this._saveState();
|
||||
this._autoSelectAvailableModels([]);
|
||||
|
||||
this._broadcastToAllWindows('model-state:updated', this.state);
|
||||
this._broadcastToAllWindows('settings-updated');
|
||||
return true;
|
||||
@ -527,8 +526,11 @@ class ModelStateService extends EventEmitter {
|
||||
|
||||
// Auto warm-up for Ollama models
|
||||
if (type === 'llm' && modelId && modelId !== previousModelId) {
|
||||
const provider = this.getProviderForModel('llm', modelId);
|
||||
if (provider === 'ollama') {
|
||||
this._autoWarmUpOllamaModel(modelId, previousModelId);
|
||||
}
|
||||
}
|
||||
|
||||
this._broadcastToAllWindows('model-state:updated', this.state);
|
||||
this._broadcastToAllWindows('settings-updated');
|
||||
|
@ -532,6 +532,7 @@ async function handleFirebaseAuthCallback(params) {
|
||||
};
|
||||
|
||||
// 1. Sync user data to local DB
|
||||
userRepository.setAuthService(authService);
|
||||
userRepository.findOrCreate(firebaseUser);
|
||||
console.log('[Auth] User data synced with local DB.');
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,18 +1,20 @@
|
||||
import './MainHeader.js';
|
||||
import './ApiKeyHeader.js';
|
||||
import './PermissionHeader.js';
|
||||
import './WelcomeHeader.js';
|
||||
|
||||
class HeaderTransitionManager {
|
||||
constructor() {
|
||||
this.headerContainer = document.getElementById('header-container');
|
||||
this.currentHeaderType = null; // 'apikey' | 'main' | 'permission'
|
||||
this.currentHeaderType = null; // 'welcome' | 'apikey' | 'main' | 'permission'
|
||||
this.welcomeHeader = null;
|
||||
this.apiKeyHeader = null;
|
||||
this.mainHeader = null;
|
||||
this.permissionHeader = null;
|
||||
|
||||
/**
|
||||
* only one header window is allowed
|
||||
* @param {'apikey'|'main'|'permission'} type
|
||||
* @param {'welcome'|'apikey'|'main'|'permission'} type
|
||||
*/
|
||||
this.ensureHeader = (type) => {
|
||||
console.log('[HeaderController] ensureHeader: Ensuring header of type:', type);
|
||||
@ -23,14 +25,25 @@ class HeaderTransitionManager {
|
||||
|
||||
this.headerContainer.innerHTML = '';
|
||||
|
||||
this.welcomeHeader = null;
|
||||
this.apiKeyHeader = null;
|
||||
this.mainHeader = null;
|
||||
this.permissionHeader = null;
|
||||
|
||||
// Create new header element
|
||||
if (type === 'apikey') {
|
||||
if (type === 'welcome') {
|
||||
this.welcomeHeader = document.createElement('welcome-header');
|
||||
this.welcomeHeader.loginCallback = () => this.handleLoginOption();
|
||||
this.welcomeHeader.apiKeyCallback = () => this.handleApiKeyOption();
|
||||
this.headerContainer.appendChild(this.welcomeHeader);
|
||||
console.log('[HeaderController] ensureHeader: Header of type:', type, 'created.');
|
||||
} else if (type === 'apikey') {
|
||||
this.apiKeyHeader = document.createElement('apikey-header');
|
||||
this.apiKeyHeader.stateUpdateCallback = (userState) => this.handleStateUpdate(userState);
|
||||
this.apiKeyHeader.backCallback = () => this.transitionToWelcomeHeader();
|
||||
this.apiKeyHeader.addEventListener('request-resize', e => {
|
||||
this._resizeForApiKey(e.detail.height);
|
||||
});
|
||||
this.headerContainer.appendChild(this.apiKeyHeader);
|
||||
console.log('[HeaderController] ensureHeader: Header of type:', type, 'created.');
|
||||
} else if (type === 'permission') {
|
||||
@ -49,6 +62,10 @@ class HeaderTransitionManager {
|
||||
|
||||
console.log('[HeaderController] Manager initialized');
|
||||
|
||||
// WelcomeHeader 콜백 메서드들
|
||||
this.handleLoginOption = this.handleLoginOption.bind(this);
|
||||
this.handleApiKeyOption = this.handleApiKeyOption.bind(this);
|
||||
|
||||
this._bootstrap();
|
||||
|
||||
if (window.api) {
|
||||
@ -66,8 +83,14 @@ class HeaderTransitionManager {
|
||||
});
|
||||
window.api.headerController.onForceShowApiKeyHeader(async () => {
|
||||
console.log('[HeaderController] Received broadcast to show apikey header. Switching now.');
|
||||
const isConfigured = await window.api.apiKeyHeader.areProvidersConfigured();
|
||||
if (!isConfigured) {
|
||||
await this._resizeForWelcome();
|
||||
this.ensureHeader('welcome');
|
||||
} else {
|
||||
await this._resizeForApiKey();
|
||||
this.ensureHeader('apikey');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -88,7 +111,7 @@ class HeaderTransitionManager {
|
||||
this.handleStateUpdate(userState);
|
||||
} else {
|
||||
// Fallback for non-electron environment (testing/web)
|
||||
this.ensureHeader('apikey');
|
||||
this.ensureHeader('welcome');
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,10 +133,38 @@ class HeaderTransitionManager {
|
||||
this.transitionToMainHeader();
|
||||
}
|
||||
} else {
|
||||
await this._resizeForApiKey();
|
||||
this.ensureHeader('apikey');
|
||||
// 프로바이더가 설정되지 않았으면 WelcomeHeader 먼저 표시
|
||||
await this._resizeForWelcome();
|
||||
this.ensureHeader('welcome');
|
||||
}
|
||||
}
|
||||
|
||||
// WelcomeHeader 콜백 메서드들
|
||||
async handleLoginOption() {
|
||||
console.log('[HeaderController] Login option selected');
|
||||
if (window.api) {
|
||||
await window.api.common.startFirebaseAuth();
|
||||
}
|
||||
}
|
||||
|
||||
async handleApiKeyOption() {
|
||||
console.log('[HeaderController] API key option selected');
|
||||
await this._resizeForApiKey(400);
|
||||
this.ensureHeader('apikey');
|
||||
// ApiKeyHeader에 뒤로가기 콜백 설정
|
||||
if (this.apiKeyHeader) {
|
||||
this.apiKeyHeader.backCallback = () => this.transitionToWelcomeHeader();
|
||||
}
|
||||
}
|
||||
|
||||
async transitionToWelcomeHeader() {
|
||||
if (this.currentHeaderType === 'welcome') {
|
||||
return this._resizeForWelcome();
|
||||
}
|
||||
|
||||
await this._resizeForWelcome();
|
||||
this.ensureHeader('welcome');
|
||||
}
|
||||
//////// after_modelStateService ////////
|
||||
|
||||
async transitionToPermissionHeader() {
|
||||
@ -161,15 +212,13 @@ class HeaderTransitionManager {
|
||||
async _resizeForMain() {
|
||||
if (!window.api) return;
|
||||
console.log('[HeaderController] _resizeForMain: Resizing window to 353x47');
|
||||
return window.api.headerController.resizeHeaderWindow({ width: 353, height: 47 })
|
||||
.catch(() => {});
|
||||
return window.api.headerController.resizeHeaderWindow({ width: 353, height: 47 }).catch(() => {});
|
||||
}
|
||||
|
||||
async _resizeForApiKey() {
|
||||
async _resizeForApiKey(height = 370) {
|
||||
if (!window.api) return;
|
||||
console.log('[HeaderController] _resizeForApiKey: Resizing window to 350x300');
|
||||
return window.api.headerController.resizeHeaderWindow({ width: 350, height: 300 })
|
||||
.catch(() => {});
|
||||
console.log(`[HeaderController] _resizeForApiKey: Resizing window to 456x${height}`);
|
||||
return window.api.headerController.resizeHeaderWindow({ width: 456, height: height }).catch(() => {});
|
||||
}
|
||||
|
||||
async _resizeForPermissionHeader() {
|
||||
@ -178,6 +227,13 @@ class HeaderTransitionManager {
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async _resizeForWelcome() {
|
||||
if (!window.api) return;
|
||||
console.log('[HeaderController] _resizeForWelcome: Resizing window to 456x370');
|
||||
return window.api.headerController.resizeHeaderWindow({ width: 456, height: 364 })
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async checkPermissions() {
|
||||
if (!window.api) {
|
||||
return { success: true };
|
||||
|
234
src/ui/app/WelcomeHeader.js
Normal file
234
src/ui/app/WelcomeHeader.js
Normal file
@ -0,0 +1,234 @@
|
||||
import { html, css, LitElement } from '../assets/lit-core-2.7.4.min.js';
|
||||
|
||||
export class WelcomeHeader extends LitElement {
|
||||
static styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
font-family:
|
||||
'Inter',
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
sans-serif;
|
||||
}
|
||||
.container {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
height: auto;
|
||||
padding: 24px 16px;
|
||||
background: rgba(0, 0, 0, 0.64);
|
||||
box-shadow: 0px 0px 0px 1.5px rgba(255, 255, 255, 0.64) inset;
|
||||
border-radius: 16px;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
gap: 32px;
|
||||
display: inline-flex;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
.close-button {
|
||||
-webkit-app-region: no-drag;
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.15s ease;
|
||||
z-index: 10;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
padding: 0;
|
||||
}
|
||||
.close-button:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
.header-section {
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
display: flex;
|
||||
}
|
||||
.title {
|
||||
color: white;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.subtitle {
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.option-card {
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
display: inline-flex;
|
||||
}
|
||||
.divider {
|
||||
width: 1px;
|
||||
align-self: stretch;
|
||||
position: relative;
|
||||
background: #bebebe;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.option-content {
|
||||
flex: 1 1 0;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
display: inline-flex;
|
||||
min-width: 0;
|
||||
}
|
||||
.option-title {
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.option-description {
|
||||
color: #dcdcdc;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
letter-spacing: 0.12px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.action-button {
|
||||
-webkit-app-region: no-drag;
|
||||
padding: 8px 10px;
|
||||
background: rgba(132.6, 132.6, 132.6, 0.8);
|
||||
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.16);
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
.action-button:hover {
|
||||
background: rgba(150, 150, 150, 0.9);
|
||||
}
|
||||
.button-text {
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.button-icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.arrow-icon {
|
||||
border: solid white;
|
||||
border-width: 0 1.2px 1.2px 0;
|
||||
display: inline-block;
|
||||
padding: 3px;
|
||||
transform: rotate(-45deg);
|
||||
-webkit-transform: rotate(-45deg);
|
||||
}
|
||||
.footer {
|
||||
align-self: stretch;
|
||||
text-align: center;
|
||||
color: #dcdcdc;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 19.2px;
|
||||
}
|
||||
.footer-link {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
static properties = {
|
||||
loginCallback: { type: Function },
|
||||
apiKeyCallback: { type: Function },
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.loginCallback = () => {};
|
||||
this.apiKeyCallback = () => {};
|
||||
this.handleClose = this.handleClose.bind(this);
|
||||
}
|
||||
|
||||
updated(changedProperties) {
|
||||
super.updated(changedProperties);
|
||||
this.dispatchEvent(new CustomEvent('content-changed', { bubbles: true, composed: true }));
|
||||
}
|
||||
|
||||
handleClose() {
|
||||
if (window.require) {
|
||||
window.require('electron').ipcRenderer.invoke('quit-application');
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="container">
|
||||
<button class="close-button" @click=${this.handleClose}>×</button>
|
||||
<div class="header-section">
|
||||
<div class="title">Welcome to Glass</div>
|
||||
<div class="subtitle">Choose how to connect your AI model</div>
|
||||
</div>
|
||||
<div class="option-card">
|
||||
<div class="divider"></div>
|
||||
<div class="option-content">
|
||||
<div class="option-title">Quick start with default API key</div>
|
||||
<div class="option-description">
|
||||
100% free with Pickle's OpenAI key<br/>No personal data collected<br/>Sign up with Google in seconds
|
||||
</div>
|
||||
</div>
|
||||
<button class="action-button" @click=${this.loginCallback}>
|
||||
<div class="button-text">Open Browser to Log in</div>
|
||||
<div class="button-icon"><div class="arrow-icon"></div></div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="option-card">
|
||||
<div class="divider"></div>
|
||||
<div class="option-content">
|
||||
<div class="option-title">Use Personal API keys</div>
|
||||
<div class="option-description">
|
||||
Costs may apply based on your API usage<br/>No personal data collected<br/>Use your own API keys (OpenAI, Gemini, etc.)
|
||||
</div>
|
||||
</div>
|
||||
<button class="action-button" @click=${this.apiKeyCallback}>
|
||||
<div class="button-text">Enter Your API Key</div>
|
||||
<div class="button-icon"><div class="arrow-icon"></div></div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="footer">
|
||||
Glass does not collect your personal data —
|
||||
<span class="footer-link" @click=${this.openPrivacyPolicy}>See details</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
openPrivacyPolicy() {
|
||||
if (window.require) {
|
||||
window.require('electron').shell.openExternal('https://pickleglass.com/privacy');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('welcome-header', WelcomeHeader);
|
Loading…
x
Reference in New Issue
Block a user