Enhance VibeTunnel web interface with modern visual design

- Implement modern color scheme with cyan/teal primary colors
- Redesign sidebar with card-based sessions and enhanced status indicators
- Create unified header design with gradient backgrounds
- Add JetBrains Mono font and improve typography throughout
- Implement smooth micro-interactions and animations
- Enhance terminal area with better focus states and loading overlays
- Update all buttons and inputs with consistent hover/focus effects
This commit is contained in:
Peter Steinberger 2025-07-01 15:17:50 +01:00
parent 9e862f96d5
commit a09563ec62
6 changed files with 360 additions and 111 deletions

View file

@ -365,6 +365,9 @@ export class SessionCard extends LitElement {
} }
private getStatusColor(): string { private getStatusColor(): string {
if (this.killing) {
return 'text-status-error';
}
if (this.session.active === false) { if (this.session.active === false) {
return 'text-dark-text-muted'; return 'text-dark-text-muted';
} }
@ -372,6 +375,9 @@ export class SessionCard extends LitElement {
} }
private getStatusDotColor(): string { private getStatusDotColor(): string {
if (this.killing) {
return 'bg-status-error animate-pulse';
}
if (this.session.active === false) { if (this.session.active === false) {
return 'bg-dark-text-muted'; return 'bg-dark-text-muted';
} }

View file

@ -237,45 +237,57 @@ export class SessionList extends LitElement {
${ ${
this.compactMode this.compactMode
? html` ? html`
<!-- Compact list item for sidebar --> <!-- Enhanced compact list item for sidebar -->
<div <div
class="group flex items-center gap-3 p-3 rounded-md cursor-pointer transition-all hover:bg-dark-bg-tertiary hover:shadow-md ${ class="group flex items-center gap-3 p-3 rounded-lg cursor-pointer transition-all duration-200 animate-fade-in ${
session.id === this.selectedSessionId session.id === this.selectedSessionId
? 'bg-dark-bg-tertiary border border-accent-green shadow-sm' ? 'bg-dark-bg-elevated border border-accent-primary shadow-card-hover'
: 'border border-transparent hover:border-dark-border' : 'bg-dark-bg-secondary border border-dark-border hover:bg-dark-bg-tertiary hover:border-dark-border-light hover:shadow-card'
}" }"
@click=${() => @click=${() =>
this.handleSessionSelect({ detail: session } as CustomEvent)} this.handleSessionSelect({ detail: session } as CustomEvent)}
> >
<!-- Activity indicator on the left --> <!-- Enhanced activity indicator with pulse animation -->
<div <div class="relative flex-shrink-0">
class="w-2 h-2 rounded-full flex-shrink-0 ${ <div
session.status === 'running' class="w-2.5 h-2.5 rounded-full ${
? session.activityStatus?.specificStatus session.status === 'running'
? 'bg-status-warning' // Claude active - orange, no pulse ? session.activityStatus?.specificStatus
: session.activityStatus?.isActive ? 'bg-status-warning animate-pulse-primary' // Claude active - amber with pulse
? 'bg-status-success' // Generic active : session.activityStatus?.isActive
: 'bg-status-success ring-1 ring-status-success' // Idle (outline) ? 'bg-status-success' // Generic active
: 'bg-status-warning' : 'bg-status-success ring-1 ring-status-success ring-opacity-50' // Idle (subtle outline)
}" : 'bg-status-error'
title="${ }"
session.status === 'running' && session.activityStatus title="${
? session.activityStatus.specificStatus session.status === 'running' && session.activityStatus
? `Active: ${session.activityStatus.specificStatus.app}` ? session.activityStatus.specificStatus
: session.activityStatus.isActive ? `Active: ${session.activityStatus.specificStatus.app}`
? 'Active' : session.activityStatus.isActive
: 'Idle' ? 'Active'
: session.status : 'Idle'
}" : session.status
></div> }"
></div>
<!-- Pulse ring for active sessions -->
${
session.status === 'running' && session.activityStatus?.isActive
? html`<div class="absolute inset-0 w-2.5 h-2.5 rounded-full bg-status-success opacity-30 animate-ping"></div>`
: ''
}
</div>
<!-- Divider line --> <!-- Elegant divider line -->
<div class="w-px h-8 bg-dark-border"></div> <div class="w-px h-8 bg-gradient-to-b from-transparent via-dark-border to-transparent"></div>
<!-- Session content --> <!-- Session content -->
<div class="flex-1 min-w-0"> <div class="flex-1 min-w-0">
<div <div
class="text-sm font-mono text-accent-green truncate" class="text-sm font-mono truncate ${
session.id === this.selectedSessionId
? 'text-accent-primary font-medium'
: 'text-dark-text group-hover:text-accent-primary transition-colors'
}"
title="${ title="${
session.name || session.name ||
(Array.isArray(session.command) (Array.isArray(session.command)
@ -332,11 +344,11 @@ export class SessionList extends LitElement {
session.status === 'running' || session.status === 'exited' session.status === 'running' || session.status === 'exited'
? html` ? html`
<button <button
class="btn-ghost text-status-error p-1 rounded transition-opacity absolute right-0 ${ class="btn-ghost text-status-error p-1.5 rounded-md transition-all absolute right-0 ${
'ontouchstart' in window 'ontouchstart' in window
? 'opacity-100 static ml-2' ? 'opacity-100 static ml-2'
: 'opacity-0 group-hover:opacity-100' : 'opacity-0 group-hover:opacity-100'
} hover:bg-dark-bg hover:shadow-sm" } hover:bg-dark-bg-elevated hover:shadow-sm hover:scale-110"
@click=${async (e: Event) => { @click=${async (e: Event) => {
e.stopPropagation(); e.stopPropagation();
// Kill the session // Kill the session

View file

@ -1064,30 +1064,30 @@ export class SessionView extends LitElement {
}} }}
></session-header> ></session-header>
<!-- Terminal Container --> <!-- Enhanced Terminal Container -->
<div <div
class="${this.terminalContainerHeight === '100%' ? 'flex-1' : ''} bg-black overflow-hidden min-h-0 relative ${ class="${this.terminalContainerHeight === '100%' ? 'flex-1' : ''} bg-black overflow-hidden min-h-0 relative border-t border-dark-border shadow-inner ${
this.session?.status === 'exited' ? 'session-exited' : '' this.session?.status === 'exited' ? 'session-exited opacity-90' : ''
}" }"
id="terminal-container" id="terminal-container"
style="${this.terminalContainerHeight !== '100%' ? `height: ${this.terminalContainerHeight}; flex: none; max-height: ${this.terminalContainerHeight};` : ''} transition: height 0.3s ease-out;" style="${this.terminalContainerHeight !== '100%' ? `height: ${this.terminalContainerHeight}; flex: none; max-height: ${this.terminalContainerHeight};` : ''} transition: all 0.3s ease-out;"
> >
${ ${
this.loadingAnimationManager.isLoading() this.loadingAnimationManager.isLoading()
? html` ? html`
<!-- Loading overlay --> <!-- Enhanced Loading overlay -->
<div <div
class="absolute inset-0 bg-dark-bg bg-opacity-80 flex items-center justify-center z-10" class="absolute inset-0 bg-dark-bg bg-opacity-90 backdrop-filter backdrop-blur-sm flex items-center justify-center z-10 animate-fade-in"
> >
<div class="text-dark-text font-mono text-center"> <div class="text-dark-text font-mono text-center">
<div class="text-2xl mb-2">${this.loadingAnimationManager.getLoadingText()}</div> <div class="text-2xl mb-3 text-accent-primary animate-pulse-primary">${this.loadingAnimationManager.getLoadingText()}</div>
<div class="text-sm text-dark-text-muted">Connecting to session...</div> <div class="text-sm text-dark-text-muted">Connecting to session...</div>
</div> </div>
</div> </div>
` `
: '' : ''
} }
<!-- Terminal Component --> <!-- Enhanced Terminal Component -->
<vibe-terminal <vibe-terminal
.sessionId=${this.session?.id || ''} .sessionId=${this.session?.id || ''}
.sessionStatus=${this.session?.status || 'running'} .sessionStatus=${this.session?.status || 'running'}
@ -1100,7 +1100,7 @@ export class SessionView extends LitElement {
.initialRows=${this.session?.initialRows || 0} .initialRows=${this.session?.initialRows || 0}
.disableClick=${this.isMobile && this.useDirectKeyboard} .disableClick=${this.isMobile && this.useDirectKeyboard}
.hideScrollButton=${this.showQuickKeys} .hideScrollButton=${this.showQuickKeys}
class="w-full h-full p-0 m-0" class="w-full h-full p-0 m-0 terminal-container"
@click=${this.handleTerminalClick} @click=${this.handleTerminalClick}
@terminal-input=${this.handleTerminalInput} @terminal-input=${this.handleTerminalInput}
></vibe-terminal> ></vibe-terminal>
@ -1114,9 +1114,12 @@ export class SessionView extends LitElement {
class="fixed inset-0 flex items-center justify-center pointer-events-none z-[25]" class="fixed inset-0 flex items-center justify-center pointer-events-none z-[25]"
> >
<div <div
class="bg-dark-bg-secondary border border-dark-border text-status-warning font-medium text-sm tracking-wide px-4 py-2 rounded-lg shadow-lg" class="bg-dark-bg-elevated border border-status-warning text-status-warning font-medium text-sm tracking-wide px-6 py-3 rounded-lg shadow-elevated animate-scale-in"
> >
SESSION EXITED <span class="flex items-center gap-2">
<span class="w-2 h-2 rounded-full bg-status-warning"></span>
SESSION EXITED
</span>
</div> </div>
</div> </div>
` `

View file

@ -75,10 +75,10 @@ export class SessionHeader extends LitElement {
if (!this.session) return null; if (!this.session) return null;
return html` return html`
<!-- Compact Header --> <!-- Enhanced Header with gradient background -->
<div <div
class="flex items-center justify-between border-b border-dark-border text-sm min-w-0 bg-dark-bg-secondary p-3" class="flex items-center justify-between border-b border-dark-border text-sm min-w-0 bg-gradient-to-r from-dark-bg-secondary to-dark-bg-tertiary p-4 shadow-sm"
style="padding-top: max(0.75rem, calc(0.75rem + env(safe-area-inset-top))); padding-left: max(0.75rem, env(safe-area-inset-left)); padding-right: max(0.75rem, env(safe-area-inset-right));" style="padding-top: max(1rem, calc(1rem + env(safe-area-inset-top))); padding-left: max(1rem, env(safe-area-inset-left)); padding-right: max(1rem, env(safe-area-inset-right));"
> >
<div class="flex items-center gap-3 min-w-0 flex-1"> <div class="flex items-center gap-3 min-w-0 flex-1">
<!-- Sidebar Toggle and Create Session Buttons (shown when sidebar is collapsed) --> <!-- Sidebar Toggle and Create Session Buttons (shown when sidebar is collapsed) -->
@ -87,7 +87,7 @@ export class SessionHeader extends LitElement {
? html` ? html`
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<button <button
class="bg-dark-bg-tertiary border border-dark-border rounded-lg p-1.5 font-mono text-dark-text-muted transition-all duration-300 hover:text-dark-text hover:bg-dark-bg hover:border-accent-green flex-shrink-0" class="bg-dark-bg-elevated border border-dark-border rounded-lg p-2 font-mono text-dark-text-muted transition-all duration-200 hover:text-accent-primary hover:bg-dark-surface-hover hover:border-accent-primary hover:shadow-sm flex-shrink-0"
@click=${() => this.onSidebarToggle?.()} @click=${() => this.onSidebarToggle?.()}
title="Show sidebar (⌘B)" title="Show sidebar (⌘B)"
aria-label="Show sidebar" aria-label="Show sidebar"
@ -100,9 +100,9 @@ export class SessionHeader extends LitElement {
</svg> </svg>
</button> </button>
<!-- Create Session button --> <!-- Create Session button with primary color -->
<button <button
class="bg-dark-bg-tertiary border border-accent-green text-accent-green rounded-lg p-1.5 font-mono transition-all duration-300 hover:bg-accent-green hover:text-dark-bg flex-shrink-0" class="bg-accent-primary bg-opacity-10 border border-accent-primary text-accent-primary rounded-lg p-2 font-mono transition-all duration-200 hover:bg-accent-primary hover:text-dark-bg hover:shadow-glow-primary-sm flex-shrink-0"
@click=${() => this.onCreateSession?.()} @click=${() => this.onCreateSession?.()}
title="Create New Session (⌘K)" title="Create New Session (⌘K)"
> >
@ -128,7 +128,7 @@ export class SessionHeader extends LitElement {
} }
<div class="text-dark-text min-w-0 flex-1 overflow-hidden max-w-[50vw] sm:max-w-none"> <div class="text-dark-text min-w-0 flex-1 overflow-hidden max-w-[50vw] sm:max-w-none">
<div <div
class="text-accent-green text-xs sm:text-sm overflow-hidden text-ellipsis whitespace-nowrap" class="text-dark-text-bright font-medium text-xs sm:text-sm overflow-hidden text-ellipsis whitespace-nowrap"
title="${ title="${
this.session.name || this.session.name ||
(Array.isArray(this.session.command) (Array.isArray(this.session.command)
@ -153,7 +153,7 @@ export class SessionHeader extends LitElement {
</div> </div>
<div class="flex items-center gap-2 text-xs flex-shrink-0 ml-2 relative"> <div class="flex items-center gap-2 text-xs flex-shrink-0 ml-2 relative">
<button <button
class="btn-secondary font-mono text-xs p-1 flex-shrink-0" class="bg-dark-bg-elevated border border-dark-border rounded-lg p-2 font-mono text-dark-text-muted transition-all duration-200 hover:text-accent-primary hover:bg-dark-surface-hover hover:border-accent-primary hover:shadow-sm flex-shrink-0"
@click=${() => this.onOpenFileBrowser?.()} @click=${() => this.onOpenFileBrowser?.()}
title="Browse Files (⌘O)" title="Browse Files (⌘O)"
> >
@ -164,7 +164,7 @@ export class SessionHeader extends LitElement {
</svg> </svg>
</button> </button>
<button <button
class="btn-secondary font-mono text-xs p-1 flex-shrink-0" class="bg-dark-bg-elevated border border-dark-border rounded-lg p-2 font-mono text-dark-text-muted transition-all duration-200 hover:text-accent-primary hover:bg-dark-surface-hover hover:border-accent-primary hover:shadow-sm flex-shrink-0"
@click=${() => this.onOpenImagePicker?.()} @click=${() => this.onOpenImagePicker?.()}
title="Upload File" title="Upload File"
> >
@ -174,7 +174,7 @@ export class SessionHeader extends LitElement {
</svg> </svg>
</button> </button>
<button <button
class="btn-secondary font-mono text-xs px-2 py-1 flex-shrink-0 width-selector-button" class="bg-dark-bg-elevated border border-dark-border rounded-lg px-3 py-2 font-mono text-xs text-dark-text-muted transition-all duration-200 hover:text-accent-primary hover:bg-dark-surface-hover hover:border-accent-primary hover:shadow-sm flex-shrink-0 width-selector-button"
@click=${() => this.onMaxWidthToggle?.()} @click=${() => this.onMaxWidthToggle?.()}
title="${this.widthTooltip}" title="${this.widthTooltip}"
> >
@ -190,8 +190,17 @@ export class SessionHeader extends LitElement {
.onClose=${() => this.handleCloseWidthSelector()} .onClose=${() => this.handleCloseWidthSelector()}
></width-selector> ></width-selector>
<div class="flex flex-col items-end gap-0"> <div class="flex flex-col items-end gap-0">
<span class="${this.getStatusColor()} text-xs flex items-center gap-1"> <span class="text-xs flex items-center gap-2 font-medium ${
<div class="w-2 h-2 rounded-full ${this.getStatusDotColor()}"></div> this.getStatusText() === 'running' ? 'text-status-success' : 'text-status-warning'
}">
<div class="relative">
<div class="w-2.5 h-2.5 rounded-full ${this.getStatusDotColor()}"></div>
${
this.getStatusText() === 'running'
? html`<div class="absolute inset-0 w-2.5 h-2.5 rounded-full bg-status-success animate-ping opacity-50"></div>`
: ''
}
</div>
${this.getStatusText().toUpperCase()} ${this.getStatusText().toUpperCase()}
</span> </span>
${ ${

View file

@ -99,23 +99,50 @@
@apply hover:text-dark-text hover:bg-dark-bg-tertiary; @apply hover:text-dark-text hover:bg-dark-bg-tertiary;
} }
/* Card styles */ /* Enhanced card styles */
.card { .card {
@apply bg-black border border-dark-border rounded-lg p-0; @apply bg-dark-bg-secondary border border-dark-border rounded-lg p-0;
@apply transition-all duration-200 ease-in-out; @apply transition-all duration-200 ease-in-out shadow-sm;
@apply hover:border-accent-green-darker hover:shadow-glow-green-sm; @apply hover:bg-dark-bg-tertiary hover:border-dark-border-light hover:shadow-card-hover;
}
.card-elevated {
@apply bg-dark-bg-elevated rounded-xl p-6 shadow-card;
@apply hover:shadow-elevated hover:bg-dark-surface-hover;
@apply transition-all duration-200;
}
/* Status badges */
.status-badge {
@apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium;
}
.status-badge-success {
@apply bg-accent-green bg-opacity-20 text-accent-green;
}
.status-badge-warning {
@apply bg-status-warning bg-opacity-20 text-status-warning;
}
.status-badge-error {
@apply bg-status-error bg-opacity-20 text-status-error;
}
.status-badge-info {
@apply bg-status-info bg-opacity-20 text-status-info;
} }
/* Quick start buttons */ /* Quick start buttons */
.quick-start-btn { .quick-start-btn {
@apply bg-dark-bg-tertiary border border-dark-border rounded-lg px-4 py-3 h-12; @apply bg-dark-bg-tertiary border border-dark-border rounded-lg px-4 py-3 h-12;
@apply transition-all duration-200 ease-in-out text-dark-text-muted; @apply transition-all duration-200 ease-in-out text-dark-text-muted;
@apply hover:border-accent-green hover:text-accent-green hover:shadow-glow-green-sm; @apply hover:border-accent-primary hover:text-accent-primary hover:shadow-glow-primary-sm;
@apply active:scale-95; @apply active:scale-95;
} }
.quick-start-btn.active { .quick-start-btn.active {
@apply bg-accent-green text-dark-bg border-accent-green shadow-glow-green-sm; @apply bg-accent-primary text-dark-bg border-accent-primary shadow-glow-primary-sm;
} }
/* Modal backdrop */ /* Modal backdrop */
@ -135,13 +162,13 @@
@apply text-dark-text-muted text-sm font-medium mb-2 flex items-center gap-2; @apply text-dark-text-muted text-sm font-medium mb-2 flex items-center gap-2;
} }
/* Responsive session grid layout */ /* Enhanced responsive session grid layout */
.session-flex-responsive { .session-flex-responsive {
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
grid-auto-rows: 380px; grid-auto-rows: 400px;
gap: 1rem; gap: 1.25rem;
padding: 0 0.5rem; padding: 0 0.75rem;
/* Enable smooth grid transitions */ /* Enable smooth grid transitions */
transition: grid-template-columns 0.3s ease-out; transition: grid-template-columns 0.3s ease-out;
} }
@ -153,15 +180,16 @@
@media (max-width: 420px) { @media (max-width: 420px) {
.session-flex-responsive { .session-flex-responsive {
padding: 0; padding: 0 0.5rem;
grid-template-columns: 1fr; grid-template-columns: 1fr;
grid-auto-rows: 300px; grid-auto-rows: 320px;
gap: 1rem;
} }
} }
/* Authentication styles */ /* Enhanced authentication styles */
.auth-container { .auth-container {
@apply bg-dark-bg flex items-center justify-center p-4; @apply bg-gradient-to-br from-dark-bg to-dark-bg-secondary flex items-center justify-center p-4;
min-height: 100vh; min-height: 100vh;
min-height: 100dvh; /* Dynamic viewport height for mobile */ min-height: 100dvh; /* Dynamic viewport height for mobile */
} }
@ -181,7 +209,9 @@
} }
.auth-title { .auth-title {
@apply text-2xl font-mono font-bold text-dark-text; @apply text-3xl font-mono font-bold text-dark-text-bright;
font-family: 'JetBrains Mono', monospace;
letter-spacing: -0.02em;
} }
.auth-subtitle { .auth-subtitle {
@ -189,7 +219,7 @@
} }
.auth-form { .auth-form {
@apply bg-dark-bg-secondary border border-dark-border rounded-lg p-0 w-full; @apply bg-dark-bg-elevated border border-dark-border rounded-xl shadow-card p-0 w-full;
} }
.auth-divider { .auth-divider {
@ -225,6 +255,100 @@
} }
} }
/* Micro-interactions and animations */
@layer utilities {
/* Smooth transitions for interactive elements */
.interactive {
@apply transition-all duration-200 ease-out;
}
.interactive-fast {
@apply transition-all duration-150 ease-out;
}
.interactive-slow {
@apply transition-all duration-300 ease-out;
}
/* Hover lift effect for cards */
.hover-lift {
@apply transition-transform duration-200 ease-out;
}
.hover-lift:hover {
@apply -translate-y-0.5 shadow-elevated;
}
/* Hover glow effect */
.hover-glow-primary:hover {
@apply shadow-glow-primary;
}
.hover-glow-green:hover {
@apply shadow-glow-green;
}
/* Focus within styles */
.focus-within-glow:focus-within {
@apply shadow-glow-primary-sm ring-2 ring-accent-primary ring-opacity-50;
}
/* Smooth color transitions */
.transition-colors-all {
@apply transition-colors duration-200 ease-out;
}
/* Scale on click */
.active-scale:active {
@apply scale-95;
}
/* Pulse animation for activity indicators */
.pulse-slow {
animation: pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: .7;
}
}
/* Slide animations */
.slide-in-from-right {
animation: slideInFromRight 0.3s ease-out;
}
.slide-in-from-bottom {
animation: slideInFromBottom 0.3s ease-out;
}
@keyframes slideInFromRight {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideInFromBottom {
from {
transform: translateY(100%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
}
/* Fira Code Variable Font */ /* Fira Code Variable Font */
@font-face { @font-face {
font-family: 'Fira Code'; font-family: 'Fira Code';
@ -570,28 +694,31 @@ body.initial-session-load .session-flex-responsive > session-card:nth-child(n +
touch-action: pan-x pan-y; touch-action: pan-x pan-y;
} }
/* XTerm terminal styling */ /* Enhanced XTerm terminal styling */
.xterm { .xterm {
padding: 0 !important; padding: 0 !important;
font-family: font-family:
'Hack Nerd Font Mono', 'Fira Code', ui-monospace, SFMono-Regular, 'SF Mono', Consolas, 'JetBrains Mono', 'Hack Nerd Font Mono', 'Fira Code', ui-monospace, SFMono-Regular, 'SF Mono', Consolas,
'Liberation Mono', Menlo, monospace !important; 'Liberation Mono', Menlo, monospace !important;
font-variant-ligatures: none; font-variant-ligatures: contextual;
font-feature-settings: font-feature-settings:
'liga' 0, 'liga' 1,
'clig' 0, 'clig' 1,
'calt' 0; 'calt' 1;
text-rendering: optimizeSpeed; text-rendering: optimizeSpeed;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
line-height: 1.4;
} }
/* Terminal container styling */ /* Enhanced terminal container styling */
.terminal-container { .terminal-container {
color: #e4e4e4; color: #e4e4e4;
white-space: pre; white-space: pre;
overflow: hidden; overflow: hidden;
background-color: #000000; background-color: #0a0a0a;
border-radius: 0.5rem;
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.5);
} }
/* Terminal line styling */ /* Terminal line styling */
@ -643,19 +770,23 @@ body.initial-session-load .session-flex-responsive > session-card:nth-child(n +
opacity: 0; opacity: 0;
} }
/* Cursor styling */ /* Enhanced cursor styling */
.terminal-char.cursor { .terminal-char.cursor {
animation: cursor-blink 1s infinite; animation: cursor-blink 1s infinite;
background-color: #00D9FF;
color: #0a0a0a;
} }
@keyframes cursor-blink { @keyframes cursor-blink {
0%, 0%,
50% { 50% {
opacity: 1; opacity: 1;
background-color: #00D9FF;
} }
51%, 51%,
100% { 100% {
opacity: 0.3; opacity: 0.4;
background-color: #00D9FF;
} }
} }
@ -671,24 +802,40 @@ body.initial-session-load .session-flex-responsive > session-card:nth-child(n +
width: 100%; width: 100%;
} }
/* Terminal focus indicator */ /* Enhanced terminal focus indicator */
.terminal-focused { .terminal-focused {
box-shadow: 0 0 0 2px #00ff88; box-shadow: 0 0 0 2px #00D9FF, 0 0 20px rgba(0, 217, 255, 0.3);
border-color: #00ff88 !important; border-color: #00D9FF !important;
transition: all 0.2s ease-out;
} }
/* Keyboard capture indicator */ /* Enhanced keyboard capture indicator */
.keyboard-capture-indicator { .keyboard-capture-indicator {
position: fixed; position: fixed;
top: 10px; top: 10px;
right: 10px; right: 10px;
background: rgba(0, 255, 136, 0.1); background: rgba(0, 217, 255, 0.1);
border: 1px solid #00ff88; backdrop-filter: blur(8px);
color: #00ff88; border: 1px solid #00D9FF;
padding: 4px 8px; color: #00D9FF;
border-radius: 4px; padding: 6px 12px;
border-radius: 6px;
font-size: 12px; font-size: 12px;
font-weight: 500;
z-index: 1000; z-index: 1000;
animation: slideInFromTop 0.3s ease-out;
box-shadow: 0 2px 8px rgba(0, 217, 255, 0.2);
}
@keyframes slideInFromTop {
from {
transform: translateY(-100%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
} }
/* Force XTerm terminal to fit within session card bounds */ /* Force XTerm terminal to fit within session card bounds */

View file

@ -4,34 +4,47 @@ module.exports = {
theme: { theme: {
extend: { extend: {
colors: { colors: {
// Dark theme colors // Enhanced Dark theme colors with better depth
"dark-bg": "#0a0a0a", "dark-bg": "#0a0a0a",
"dark-bg-secondary": "#1a1a1a", "dark-bg-secondary": "#141414",
"dark-bg-tertiary": "#242424", "dark-bg-tertiary": "#1f1f1f",
"dark-surface": "#1f1f1f", "dark-bg-elevated": "#262626",
"dark-surface": "#1a1a1a",
"dark-surface-hover": "#2a2a2a",
"dark-border": "#2a2a2a", "dark-border": "#2a2a2a",
"dark-border-light": "#3a3a3a", "dark-border-light": "#3a3a3a",
"dark-border-focus": "#4a4a4a",
// Text colors // Enhanced Text colors
"dark-text": "#e4e4e4", "dark-text": "#e4e4e4",
"dark-text-muted": "#7a7a7a", "dark-text-bright": "#ffffff",
"dark-text-dim": "#5a5a5a", "dark-text-muted": "#888888",
"dark-text-dim": "#666666",
// Green accent colors (multiple shades) // Modern accent colors - Cyan/Teal primary
"accent-green": "#00ff88", "accent-primary": "#00D9FF",
"accent-green-dark": "#00cc66", "accent-primary-dark": "#00B8E6",
"accent-green-darker": "#009944", "accent-primary-darker": "#0096CC",
"accent-green-light": "#44ffaa", "accent-primary-light": "#33E1FF",
"accent-green-glow": "#00ff8866", "accent-primary-glow": "#00D9FF66",
// Green accent colors (success/active)
"accent-green": "#4ADE80",
"accent-green-dark": "#22C55E",
"accent-green-darker": "#16A34A",
"accent-green-light": "#86EFAC",
"accent-green-glow": "#4ADE8066",
// Secondary accent colors // Secondary accent colors
"accent-cyan": "#00ffcc", "accent-purple": "#A78BFA",
"accent-teal": "#00ccaa", "accent-blue": "#60A5FA",
"accent-amber": "#FFA726",
// Status colors // Enhanced Status colors
"status-error": "#cc3333", "status-error": "#FF6B6B",
"status-warning": "#cc8833", "status-warning": "#FFA726",
"status-success": "#00cc66", "status-success": "#4ADE80",
"status-info": "#60A5FA",
// Legacy VS Code theme colors (for compatibility) // Legacy VS Code theme colors (for compatibility)
"vs-bg": "#0a0a0a", "vs-bg": "#0a0a0a",
@ -52,12 +65,25 @@ module.exports = {
"vs-highlight": "#8b6914", "vs-highlight": "#8b6914",
}, },
boxShadow: { boxShadow: {
'glow-green': '0 0 20px rgba(0, 255, 136, 0.4)', // Updated glow effects with new colors
'glow-green-sm': '0 0 10px rgba(0, 255, 136, 0.3)', 'glow-primary': '0 0 20px rgba(0, 217, 255, 0.4)',
'glow-green-lg': '0 0 30px rgba(0, 255, 136, 0.5)', 'glow-primary-sm': '0 0 10px rgba(0, 217, 255, 0.3)',
'glow-primary-lg': '0 0 30px rgba(0, 217, 255, 0.5)',
'glow-green': '0 0 20px rgba(74, 222, 128, 0.4)',
'glow-green-sm': '0 0 10px rgba(74, 222, 128, 0.3)',
'glow-green-lg': '0 0 30px rgba(74, 222, 128, 0.5)',
// New subtle shadows
'card': '0 1px 3px rgba(0, 0, 0, 0.3), 0 1px 2px rgba(0, 0, 0, 0.4)',
'card-hover': '0 4px 6px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.4)',
'elevated': '0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2)',
}, },
animation: { animation: {
'pulse-green': 'pulseGreen 2s cubic-bezier(0.4, 0, 0.6, 1) infinite', 'pulse-green': 'pulseGreen 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
'pulse-primary': 'pulsePrimary 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
'slide-in-right': 'slideInRight 0.3s ease-out',
'slide-in-bottom': 'slideInBottom 0.3s ease-out',
'fade-in': 'fadeIn 0.2s ease-out',
'scale-in': 'scaleIn 0.2s ease-out',
}, },
keyframes: { keyframes: {
pulseGreen: { pulseGreen: {
@ -68,6 +94,52 @@ module.exports = {
opacity: '.8', opacity: '.8',
}, },
}, },
pulsePrimary: {
'0%, 100%': {
opacity: '1',
},
'50%': {
opacity: '.7',
},
},
slideInRight: {
'0%': {
transform: 'translateX(100%)',
opacity: '0',
},
'100%': {
transform: 'translateX(0)',
opacity: '1',
},
},
slideInBottom: {
'0%': {
transform: 'translateY(100%)',
opacity: '0',
},
'100%': {
transform: 'translateY(0)',
opacity: '1',
},
},
fadeIn: {
'0%': {
opacity: '0',
},
'100%': {
opacity: '1',
},
},
scaleIn: {
'0%': {
transform: 'scale(0.95)',
opacity: '0',
},
'100%': {
transform: 'scale(1)',
opacity: '1',
},
},
}, },
}, },
}, },