mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-05 11:15:57 +00:00
unf auth screen
This commit is contained in:
parent
59aacd8707
commit
ca4ebeba15
2 changed files with 22 additions and 104 deletions
|
|
@ -2,9 +2,7 @@ import { html, LitElement } from 'lit';
|
|||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import type { AuthClient } from '../services/auth-client.js';
|
||||
import { responsiveObserver } from '../utils/responsive-utils.js';
|
||||
import { AppSettings } from './app-settings.js';
|
||||
import './terminal-icon.js';
|
||||
import './auth-quick-keys.js';
|
||||
|
||||
@customElement('auth-login')
|
||||
export class AuthLogin extends LitElement {
|
||||
|
|
@ -25,27 +23,18 @@ export class AuthLogin extends LitElement {
|
|||
disallowUserPassword: false,
|
||||
noAuth: false,
|
||||
};
|
||||
@state() private useDirectKeyboard = false;
|
||||
@state() private isMobile = false;
|
||||
@state() private isKeyboardFocused = false;
|
||||
private unsubscribeResponsive?: () => void;
|
||||
|
||||
async connectedCallback() {
|
||||
super.connectedCallback();
|
||||
console.log('🔌 Auth login component connected');
|
||||
|
||||
// Load preferences
|
||||
const preferences = AppSettings.getPreferences();
|
||||
this.useDirectKeyboard = preferences.useDirectKeyboard;
|
||||
|
||||
// Subscribe to responsive changes
|
||||
this.unsubscribeResponsive = responsiveObserver.subscribe((state) => {
|
||||
this.isMobile = state.isMobile;
|
||||
});
|
||||
|
||||
// Listen for preference changes
|
||||
window.addEventListener('app-preferences-changed', this.handlePreferencesChanged);
|
||||
|
||||
await this.loadUserInfo();
|
||||
}
|
||||
|
||||
|
|
@ -54,14 +43,8 @@ export class AuthLogin extends LitElement {
|
|||
if (this.unsubscribeResponsive) {
|
||||
this.unsubscribeResponsive();
|
||||
}
|
||||
window.removeEventListener('app-preferences-changed', this.handlePreferencesChanged);
|
||||
}
|
||||
|
||||
private handlePreferencesChanged = (e: Event) => {
|
||||
const event = e as CustomEvent;
|
||||
this.useDirectKeyboard = event.detail.useDirectKeyboard;
|
||||
};
|
||||
|
||||
private async loadUserInfo() {
|
||||
try {
|
||||
// Load auth configuration first
|
||||
|
|
@ -159,35 +142,6 @@ export class AuthLogin extends LitElement {
|
|||
this.dispatchEvent(new CustomEvent('show-ssh-key-manager'));
|
||||
}
|
||||
|
||||
private handleKeyCapture = (e: KeyboardEvent) => {
|
||||
if (this.loading) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
if (e.key === 'Enter') {
|
||||
this.handlePasswordLogin(e);
|
||||
} else if (e.key === 'Backspace') {
|
||||
this.loginPassword = this.loginPassword.slice(0, -1);
|
||||
} else if (e.key === 'Escape') {
|
||||
this.loginPassword = '';
|
||||
} else if (e.key.length === 1) {
|
||||
this.loginPassword += e.key;
|
||||
}
|
||||
};
|
||||
|
||||
private handleQuickKey = (key: string) => {
|
||||
if (this.loading) return;
|
||||
this.loginPassword += key;
|
||||
};
|
||||
|
||||
private handleFocus = () => {
|
||||
this.isKeyboardFocused = true;
|
||||
};
|
||||
|
||||
private handleBlur = () => {
|
||||
this.isKeyboardFocused = false;
|
||||
};
|
||||
|
||||
render() {
|
||||
console.log(
|
||||
'🔍 Rendering auth login',
|
||||
|
|
@ -201,12 +155,12 @@ export class AuthLogin extends LitElement {
|
|||
<div class="auth-container">
|
||||
<div class="w-full max-w-sm">
|
||||
<div class="auth-header">
|
||||
<div class="flex flex-col items-center gap-1 sm:gap-3 mb-2 sm:mb-8">
|
||||
<div class="flex flex-col items-center gap-2 sm:gap-3 mb-4 sm:mb-8">
|
||||
<terminal-icon
|
||||
size="${this.isMobile ? '40' : '56'}"
|
||||
size="${this.isMobile ? '48' : '56'}"
|
||||
style="filter: drop-shadow(0 0 15px rgba(124, 230, 161, 0.4));"
|
||||
></terminal-icon>
|
||||
<h2 class="auth-title text-xl sm:text-3xl mt-0 sm:mt-2">VibeTunnel</h2>
|
||||
<h2 class="auth-title text-2xl sm:text-3xl mt-1 sm:mt-2">VibeTunnel</h2>
|
||||
<p class="auth-subtitle text-xs sm:text-sm">Please authenticate to continue</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -254,9 +208,9 @@ export class AuthLogin extends LitElement {
|
|||
!this.authConfig.disallowUserPassword
|
||||
? html`
|
||||
<!-- Password Login Section (Primary) -->
|
||||
<div class="p-4 sm:p-8">
|
||||
<div class="flex flex-col items-center mb-3 sm:mb-6">
|
||||
<div class="w-14 h-14 sm:w-20 sm:h-20 rounded-full mb-2 sm:mb-3 overflow-hidden" style="box-shadow: 0 0 20px rgba(124, 230, 161, 0.3);">
|
||||
<div class="p-5 sm:p-8">
|
||||
<div class="flex flex-col items-center mb-4 sm:mb-6">
|
||||
<div class="w-24 h-24 sm:w-28 sm:h-28 rounded-full mb-3 sm:mb-4 overflow-hidden" style="box-shadow: 0 0 25px rgba(124, 230, 161, 0.3);">
|
||||
${
|
||||
this.userAvatar
|
||||
? html`
|
||||
|
|
@ -270,71 +224,35 @@ export class AuthLogin extends LitElement {
|
|||
`
|
||||
: html`
|
||||
<div class="w-full h-full bg-dark-bg-secondary flex items-center justify-center">
|
||||
<svg class="w-7 h-7 sm:w-10 sm:h-10 text-dark-text-muted" fill="currentColor" viewBox="0 0 20 20">
|
||||
<svg class="w-12 h-12 sm:w-14 sm:h-14 text-dark-text-muted" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" />
|
||||
</svg>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
</div>
|
||||
<p class="text-dark-text text-sm sm:text-lg font-medium">
|
||||
<p class="text-dark-text text-base sm:text-lg font-medium">
|
||||
Welcome back, ${this.currentUserId || '...'}
|
||||
</p>
|
||||
</div>
|
||||
<form @submit=${this.handlePasswordLogin} class="space-y-3">
|
||||
<div>
|
||||
${
|
||||
this.useDirectKeyboard
|
||||
? html`
|
||||
<!-- Direct keyboard capture -->
|
||||
<div
|
||||
class="password-capture-area bg-[#121212] border ${this.isKeyboardFocused ? 'border-accent-green shadow-glow-green-sm' : 'border-dark-border'} rounded-lg px-4 py-3 text-dark-text w-full transition-all duration-200 ease-in-out hover:border-accent-green-darker cursor-text min-h-[48px] flex items-center"
|
||||
tabindex="0"
|
||||
@keydown=${this.handleKeyCapture}
|
||||
@focus=${this.handleFocus}
|
||||
@blur=${this.handleBlur}
|
||||
data-testid="password-capture"
|
||||
>
|
||||
<span class="${this.loginPassword ? 'text-dark-text' : 'text-dark-text-muted'} text-sm">
|
||||
${
|
||||
this.loginPassword
|
||||
? '•'.repeat(this.loginPassword.length)
|
||||
: this.isKeyboardFocused
|
||||
? 'Type your password...'
|
||||
: 'Click to enter password'
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
${
|
||||
this.isMobile
|
||||
? html`
|
||||
<div class="mt-2">
|
||||
<auth-quick-keys .onKeyPress=${this.handleQuickKey}></auth-quick-keys>
|
||||
</div>
|
||||
`
|
||||
: ''
|
||||
}
|
||||
`
|
||||
: html`
|
||||
<!-- Traditional input field -->
|
||||
<input
|
||||
type="password"
|
||||
class="input-field"
|
||||
data-testid="password-input"
|
||||
placeholder="System Password"
|
||||
.value=${this.loginPassword}
|
||||
@input=${(e: Event) => {
|
||||
this.loginPassword = (e.target as HTMLInputElement).value;
|
||||
}}
|
||||
?disabled=${this.loading}
|
||||
required
|
||||
/>
|
||||
`
|
||||
}
|
||||
<input
|
||||
type="password"
|
||||
class="input-field"
|
||||
data-testid="password-input"
|
||||
placeholder="System Password"
|
||||
.value=${this.loginPassword}
|
||||
@input=${(e: Event) => {
|
||||
this.loginPassword = (e.target as HTMLInputElement).value;
|
||||
}}
|
||||
?disabled=${this.loading}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn-primary w-full py-2.5 sm:py-4 mt-2 text-sm sm:text-base"
|
||||
class="btn-primary w-full py-3 sm:py-4 mt-2"
|
||||
data-testid="password-submit"
|
||||
?disabled=${this.loading || !this.loginPassword}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@
|
|||
|
||||
@media (max-width: 768px) {
|
||||
.auth-container {
|
||||
@apply py-4; /* Reduced vertical padding on mobile */
|
||||
@apply py-6; /* Comfortable vertical padding on mobile */
|
||||
min-height: 100vh;
|
||||
min-height: 100dvh;
|
||||
/* Prevent scrolling - content should fit */
|
||||
|
|
|
|||
Loading…
Reference in a new issue