Merge remote-tracking branch 'origin/frontend-redesign-2'

This commit is contained in:
Peter Steinberger 2025-06-20 06:03:48 +02:00
commit 12cb1e5c8e
10 changed files with 359 additions and 575 deletions

View file

@ -265,9 +265,11 @@ export class VibeTunnelApp extends LitElement {
${this.errorMessage ${this.errorMessage
? html` ? html`
<div class="fixed top-4 right-4 z-50"> <div class="fixed top-4 right-4 z-50">
<div class="bg-vs-warning text-vs-bg px-4 py-2 rounded shadow-lg font-mono text-sm"> <div
class="bg-status-error text-dark-bg px-4 py-2 rounded shadow-lg font-mono text-sm"
>
${this.errorMessage} ${this.errorMessage}
<button @click=${this.clearError} class="ml-2 text-vs-bg hover:text-vs-muted"> <button @click=${this.clearError} class="ml-2 text-dark-bg hover:text-dark-text">
</button> </button>
</div> </div>
@ -277,9 +279,11 @@ export class VibeTunnelApp extends LitElement {
${this.successMessage ${this.successMessage
? html` ? html`
<div class="fixed top-4 right-4 z-50"> <div class="fixed top-4 right-4 z-50">
<div class="bg-vs-link text-vs-bg px-4 py-2 rounded shadow-lg font-mono text-sm"> <div
class="bg-status-success text-dark-bg px-4 py-2 rounded shadow-lg font-mono text-sm"
>
${this.successMessage} ${this.successMessage}
<button @click=${this.clearSuccess} class="ml-2 text-vs-bg hover:text-vs-muted"> <button @click=${this.clearSuccess} class="ml-2 text-dark-bg hover:text-dark-text">
</button> </button>
</div> </div>
@ -298,7 +302,7 @@ export class VibeTunnelApp extends LitElement {
` `
) )
: html` : html`
<div class="max-w-4xl mx-auto" style="background: black;"> <div class="max-w-4xl mx-auto">
<app-header <app-header
.sessions=${this.sessions} .sessions=${this.sessions}
.hideExited=${this.hideExited} .hideExited=${this.hideExited}

View file

@ -1,7 +1,7 @@
import { LitElement, html } from 'lit'; import { LitElement, html } from 'lit';
import { customElement, property, state } from 'lit/decorators.js'; import { customElement, property, state } from 'lit/decorators.js';
import type { Session } from './session-list.js'; import type { Session } from './session-list.js';
import './vibe-logo.js'; import './terminal-icon.js';
@customElement('app-header') @customElement('app-header')
export class AppHeader extends LitElement { export class AppHeader extends LitElement {
@ -46,109 +46,71 @@ export class AppHeader extends LitElement {
} }
return html` return html`
<div class="app-header p-4" style="background: black;"> <div class="app-header bg-dark-bg-secondary border-b border-dark-border p-6">
<!-- Mobile layout --> <!-- Mobile layout -->
<div class="flex flex-col gap-3 sm:hidden"> <div class="flex flex-col gap-4 sm:hidden">
<!-- Centered VibeTunnel title --> <!-- Centered Sessions title with stats -->
<div class="text-center"> <div class="text-center flex flex-col items-center gap-2">
<vibe-logo></vibe-logo> <h1 class="text-2xl font-bold text-accent-green flex items-center gap-3">
<terminal-icon size="28"></terminal-icon>
<span>Sessions</span>
</h1>
<p class="text-dark-text-muted text-sm">
${runningSessions.length} ${runningSessions.length === 1 ? 'Session' : 'Sessions'}
${exitedSessions.length > 0 ? `${exitedSessions.length} Exited` : ''}
</p>
</div> </div>
<!-- Controls row: left buttons and right buttons --> <!-- Controls row: left buttons and right buttons -->
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="flex gap-1"> <div class="flex gap-2">
${exitedSessions.length > 0 ${exitedSessions.length > 0
? html` ? html`
<button <button
class="font-mono px-2 py-1 rounded transition-colors text-xs whitespace-nowrap" class="btn-ghost font-mono text-xs ${this.hideExited
style="background: black; color: #d4d4d4; border: 1px solid ${this.hideExited ? 'text-accent-green border border-accent-green'
? '#23d18b' : ''}"
: '#888'};"
@click=${() => @click=${() =>
this.dispatchEvent( this.dispatchEvent(
new CustomEvent('hide-exited-change', { new CustomEvent('hide-exited-change', {
detail: !this.hideExited, detail: !this.hideExited,
}) })
)} )}
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
const borderColor = this.hideExited ? '#23d18b' : '#888';
btn.style.background = borderColor;
btn.style.color = 'black';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}}
> >
${this.hideExited ${this.hideExited
? `SHOW EXITED (${exitedSessions.length})` ? `Show Exited (${exitedSessions.length})`
: `HIDE EXITED (${exitedSessions.length})`} : `Hide Exited (${exitedSessions.length})`}
</button> </button>
` `
: ''} : ''}
${!this.hideExited && exitedSessions.length > 0 ${!this.hideExited && exitedSessions.length > 0
? html` ? html`
<button <button
class="font-mono px-2 py-1 rounded transition-colors text-xs whitespace-nowrap" class="btn-ghost font-mono text-xs text-status-warning"
style="background: black; color: #d4d4d4; border: 1px solid #d19a66;"
@click=${this.handleCleanExited} @click=${this.handleCleanExited}
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = '#d19a66';
btn.style.color = 'black';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}}
> >
CLEAN EXITED Clean Exited
</button> </button>
` `
: ''} : ''}
${runningSessions.length > 0 && !this.killingAll ${runningSessions.length > 0 && !this.killingAll
? html` ? html`
<button <button
class="font-mono px-2 py-1 rounded transition-colors text-xs whitespace-nowrap" class="btn-ghost font-mono text-xs text-status-error"
style="background: black; color: #d4d4d4; border: 1px solid #d19a66;"
@click=${this.handleKillAll} @click=${this.handleKillAll}
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = '#d19a66';
btn.style.color = 'black';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}}
> >
KILL (${runningSessions.length}) Kill (${runningSessions.length})
</button> </button>
` `
: ''} : ''}
</div> </div>
<div class="flex gap-1"> <div class="flex gap-2">
<button <button
class="font-mono px-2 py-1 rounded transition-colors text-xs whitespace-nowrap" class="btn-primary font-mono text-xs px-4 py-2"
style="background: black; color: #d4d4d4; border: 1px solid #569cd6;"
@click=${this.handleCreateSession} @click=${this.handleCreateSession}
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = '#569cd6';
btn.style.color = 'black';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}}
> >
CREATE Create
</button> </button>
</div> </div>
</div> </div>
@ -156,7 +118,16 @@ export class AppHeader extends LitElement {
<!-- Desktop layout: single row --> <!-- Desktop layout: single row -->
<div class="hidden sm:flex sm:items-center sm:justify-between"> <div class="hidden sm:flex sm:items-center sm:justify-between">
<vibe-logo></vibe-logo> <div class="flex items-center gap-3">
<terminal-icon size="32"></terminal-icon>
<div>
<h1 class="text-xl font-bold text-accent-green">VibeTunnel</h1>
<p class="text-dark-text-muted text-sm">
${runningSessions.length} ${runningSessions.length === 1 ? 'Session' : 'Sessions'}
${exitedSessions.length > 0 ? `${exitedSessions.length} Exited` : ''}
</p>
</div>
</div>
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
${exitedSessions.length > 0 ${exitedSessions.length > 0
? html` ? html`
@ -193,61 +164,28 @@ export class AppHeader extends LitElement {
${!this.hideExited && this.sessions.filter((s) => s.status === 'exited').length > 0 ${!this.hideExited && this.sessions.filter((s) => s.status === 'exited').length > 0
? html` ? html`
<button <button
class="font-mono px-2 py-1 rounded transition-colors text-xs whitespace-nowrap" class="btn-ghost font-mono text-xs text-status-warning"
style="background: black; color: #d4d4d4; border: 1px solid #d19a66;"
@click=${this.handleCleanExited} @click=${this.handleCleanExited}
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = '#d19a66';
btn.style.color = 'black';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}}
> >
CLEAN EXITED Clean Exited
</button> </button>
` `
: ''} : ''}
${runningSessions.length > 0 && !this.killingAll ${runningSessions.length > 0 && !this.killingAll
? html` ? html`
<button <button
class="font-mono px-2 py-1 rounded transition-colors text-xs whitespace-nowrap" class="btn-ghost font-mono text-xs text-status-error"
style="background: black; color: #d4d4d4; border: 1px solid #d19a66;"
@click=${this.handleKillAll} @click=${this.handleKillAll}
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = '#d19a66';
btn.style.color = 'black';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}}
> >
KILL ALL (${runningSessions.length}) Kill All (${runningSessions.length})
</button> </button>
` `
: ''} : ''}
<button <button
class="font-mono px-2 py-1 rounded transition-colors text-xs whitespace-nowrap" class="btn-primary font-mono text-xs px-4 py-2"
style="background: black; color: #d4d4d4; border: 1px solid #569cd6;"
@click=${this.handleCreateSession} @click=${this.handleCreateSession}
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = '#569cd6';
btn.style.color = 'black';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}}
> >
CREATE SESSION Create Session
</button> </button>
</div> </div>
</div> </div>

View file

@ -145,48 +145,31 @@ export class FileBrowser extends LitElement {
} }
return html` return html`
<div <div class="modal-backdrop flex items-center justify-center">
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center" <div class="modal-content font-mono text-sm w-96 h-96 flex flex-col overflow-hidden">
style="z-index: 9999;" <div class="pb-4 mb-4 border-b border-dark-border flex-shrink-0">
>
<div
class="font-mono text-sm w-96 h-96 flex flex-col"
style="background: black; border: 1px solid #569cd6; border-radius: 4px;"
>
<div class="p-4 flex-shrink-0" style="border-bottom: 1px solid #444;">
<div class="flex justify-between items-center mb-2"> <div class="flex justify-between items-center mb-2">
<div class="text-vs-user text-sm">Select Directory</div> <h2 class="text-accent-green text-lg font-bold">Select Directory</h2>
<button <button
class="font-mono px-2 py-1 text-xs rounded transition-colors" class="btn-secondary font-mono text-xs px-3 py-1"
style="background: black; color: #d4d4d4; border: 1px solid #569cd6; border-radius: 4px;"
@click=${this.handleCreateFolder} @click=${this.handleCreateFolder}
?disabled=${this.loading} ?disabled=${this.loading}
title="Create new folder" title="Create new folder"
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = '#569cd6';
btn.style.color = 'black';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}}
> >
+ folder + Folder
</button> </button>
</div> </div>
<div class="text-vs-muted text-sm break-all">${this.currentPath}</div> <div class="text-dark-text-muted text-sm break-all">${this.currentPath}</div>
</div> </div>
<div class="p-4 flex-1 overflow-y-auto"> <div class="px-4 pb-4 flex-1 overflow-y-auto">
${this.loading ${this.loading
? html` <div class="text-vs-muted">Loading...</div> ` ? html` <div class="text-dark-text-muted">Loading...</div> `
: html` : html`
${this.currentPath !== '/' ${this.currentPath !== '/'
? html` ? html`
<div <div
class="flex items-center gap-2 p-2 hover:bg-vs-nav-hover cursor-pointer text-vs-accent" class="flex items-center gap-2 p-2 hover:bg-dark-bg-tertiary rounded cursor-pointer text-accent-green transition-colors"
@click=${this.handleParentClick} @click=${this.handleParentClick}
> >
<span>📁</span> <span>📁</span>
@ -199,7 +182,7 @@ export class FileBrowser extends LitElement {
.map( .map(
(file) => html` (file) => html`
<div <div
class="flex items-center gap-2 p-2 hover:bg-vs-nav-hover cursor-pointer text-vs-accent" class="flex items-center gap-2 p-2 hover:bg-dark-bg-tertiary rounded cursor-pointer text-accent-green transition-colors"
@click=${() => this.handleDirectoryClick(file.name)} @click=${() => this.handleDirectoryClick(file.name)}
> >
<span>📁</span> <span>📁</span>
@ -211,7 +194,7 @@ export class FileBrowser extends LitElement {
.filter((f) => !f.isDir) .filter((f) => !f.isDir)
.map( .map(
(file) => html` (file) => html`
<div class="flex items-center gap-2 p-2 text-vs-muted"> <div class="flex items-center gap-2 p-2 text-dark-text-muted">
<span>📄</span> <span>📄</span>
<span>${file.name}</span> <span>${file.name}</span>
</div> </div>
@ -223,13 +206,12 @@ export class FileBrowser extends LitElement {
<!-- Create folder dialog --> <!-- Create folder dialog -->
${this.showCreateFolder ${this.showCreateFolder
? html` ? html`
<div class="p-4 border-t border-vs-border flex-shrink-0"> <div class="p-4 border-t border-dark-border flex-shrink-0">
<div class="text-vs-assistant text-sm mb-2">Create New Folder</div> <label class="form-label">Create New Folder</label>
<div class="flex gap-2"> <div class="flex gap-2">
<input <input
type="text" type="text"
class="flex-1 outline-none font-mono px-2 py-1 text-sm" class="input-field text-sm"
style="background: rgba(0, 0, 0, 0.8); color: #d4d4d4; border: 1px solid #444; border-radius: 4px;"
placeholder="Folder name" placeholder="Folder name"
.value=${this.newFolderName} .value=${this.newFolderName}
@input=${this.handleFolderNameInput} @input=${this.handleFolderNameInput}
@ -237,89 +219,27 @@ export class FileBrowser extends LitElement {
?disabled=${this.creating} ?disabled=${this.creating}
/> />
<button <button
class="font-mono px-2 py-1 text-xs transition-colors disabled:opacity-50 disabled:cursor-not-allowed" class="btn-primary font-mono text-xs px-3 py-1 disabled:opacity-50 disabled:cursor-not-allowed"
style="background: black; color: #d4d4d4; border: 1px solid #569cd6; border-radius: 4px;"
@click=${this.createFolder} @click=${this.createFolder}
?disabled=${this.creating || !this.newFolderName.trim()} ?disabled=${this.creating || !this.newFolderName.trim()}
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
if (!btn.hasAttribute('disabled')) {
btn.style.background = '#569cd6';
btn.style.color = 'black';
}
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
if (!btn.hasAttribute('disabled')) {
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}
}}
> >
${this.creating ? '...' : 'create'} ${this.creating ? '...' : 'Create'}
</button> </button>
<button <button
class="font-mono px-2 py-1 text-xs transition-colors disabled:opacity-50 disabled:cursor-not-allowed" class="btn-ghost font-mono text-xs disabled:opacity-50 disabled:cursor-not-allowed"
style="background: black; color: #d4d4d4; border: 1px solid #888; border-radius: 4px;"
@click=${this.handleCancelCreateFolder} @click=${this.handleCancelCreateFolder}
?disabled=${this.creating} ?disabled=${this.creating}
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
if (!btn.hasAttribute('disabled')) {
btn.style.background = '#888';
btn.style.color = 'black';
}
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
if (!btn.hasAttribute('disabled')) {
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}
}}
> >
cancel Cancel
</button> </button>
</div> </div>
</div> </div>
` `
: ''} : ''}
<div class="p-4 border-t border-vs-border flex gap-4 justify-end flex-shrink-0"> <div class="p-4 border-t border-dark-border flex gap-4 justify-end flex-shrink-0">
<button <button class="btn-ghost font-mono" @click=${this.handleCancel}>Cancel</button>
class="font-mono px-4 py-2 transition-colors" <button class="btn-primary font-mono" @click=${this.handleSelect}>Select</button>
style="background: black; color: #d4d4d4; border: 1px solid #888; border-radius: 4px;"
@click=${this.handleCancel}
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = '#888';
btn.style.color = 'black';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}}
>
cancel
</button>
<button
class="font-mono px-4 py-2 transition-colors"
style="background: black; color: #d4d4d4; border: 1px solid #569cd6; border-radius: 4px;"
@click=${this.handleSelect}
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = '#569cd6';
btn.style.color = 'black';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}}
>
select
</button>
</div> </div>
</div> </div>
</div> </div>

View file

@ -156,17 +156,17 @@ export class SessionCard extends LitElement {
render() { render() {
return html` return html`
<div <div
class="bg-vs-bg rounded shadow cursor-pointer overflow-hidden ${this.killing class="card cursor-pointer overflow-hidden ${this.killing ? 'opacity-60' : ''} ${this
? 'opacity-60' .hasEscPrompt
: ''} ${this.hasEscPrompt ? 'border-2 border-orange-500' : 'border border-vs-border'}" ? 'border-2 border-status-warning'
: ''}"
@click=${this.handleCardClick} @click=${this.handleCardClick}
> >
<!-- Compact Header --> <!-- Compact Header -->
<div <div
class="flex justify-between items-center px-3 py-2 border-b border-vs-border" class="flex justify-between items-center px-3 py-2 border-b border-dark-border bg-dark-bg-tertiary"
style="background: black;"
> >
<div class="text-xs font-mono pr-2 flex-1 min-w-0" style="color: #569cd6;"> <div class="text-xs font-mono pr-2 flex-1 min-w-0 text-accent-green">
<div class="truncate" title="${this.session.name || this.session.command}"> <div class="truncate" title="${this.session.name || this.session.command}">
${this.session.name || this.session.command} ${this.session.name || this.session.command}
</div> </div>
@ -174,24 +174,9 @@ export class SessionCard extends LitElement {
${this.session.status === 'running' ${this.session.status === 'running'
? html` ? html`
<button <button
class="font-mono px-2 py-0.5 text-xs disabled:opacity-50 flex-shrink-0 rounded transition-colors" class="btn-ghost font-mono text-xs py-1 text-status-error disabled:opacity-50 flex-shrink-0"
style="background: black; color: #d4d4d4; border: 1px solid #d19a66;"
@click=${this.handleKillClick} @click=${this.handleKillClick}
?disabled=${this.killing} ?disabled=${this.killing}
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
if (!this.killing) {
btn.style.background = '#d19a66';
btn.style.color = 'black';
}
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
if (!this.killing) {
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}
}}
> >
${this.killing ? 'killing...' : 'kill'} ${this.killing ? 'killing...' : 'kill'}
</button> </button>
@ -200,10 +185,10 @@ export class SessionCard extends LitElement {
</div> </div>
<!-- Terminal display (main content) --> <!-- Terminal display (main content) -->
<div class="session-preview bg-black overflow-hidden" style="aspect-ratio: 640/480;"> <div class="session-preview bg-dark-bg overflow-hidden" style="aspect-ratio: 640/480;">
${this.killing ${this.killing
? html` ? html`
<div class="w-full h-full flex items-center justify-center text-vs-warning"> <div class="w-full h-full flex items-center justify-center text-status-error">
<div class="text-center font-mono"> <div class="text-center font-mono">
<div class="text-4xl mb-2">${this.getKillingText()}</div> <div class="text-4xl mb-2">${this.getKillingText()}</div>
<div class="text-sm">Killing session...</div> <div class="text-sm">Killing session...</div>
@ -222,8 +207,7 @@ export class SessionCard extends LitElement {
<!-- Compact Footer --> <!-- Compact Footer -->
<div <div
class="px-3 py-2 text-vs-muted text-xs border-t border-vs-border" class="px-3 py-2 text-dark-text-muted text-xs border-t border-dark-border bg-dark-bg-tertiary"
style="background: black;"
> >
<div class="flex justify-between items-center min-w-0"> <div class="flex justify-between items-center min-w-0">
<span class="${this.getStatusColor()} text-xs flex items-center gap-1 flex-shrink-0"> <span class="${this.getStatusColor()} text-xs flex items-center gap-1 flex-shrink-0">
@ -233,7 +217,7 @@ export class SessionCard extends LitElement {
${this.session.pid ${this.session.pid
? html` ? html`
<span <span
class="cursor-pointer hover:text-vs-accent transition-colors text-xs flex-shrink-0 ml-2" class="cursor-pointer hover:text-accent-green transition-colors text-xs flex-shrink-0 ml-2"
@click=${this.handlePidClick} @click=${this.handlePidClick}
title="Click to copy PID" title="Click to copy PID"
> >
@ -261,15 +245,15 @@ export class SessionCard extends LitElement {
private getStatusColor(): string { private getStatusColor(): string {
if (this.session.waiting) { if (this.session.waiting) {
return 'text-vs-muted'; return 'text-dark-text-muted';
} }
return this.session.status === 'running' ? 'text-vs-user' : 'text-vs-warning'; return this.session.status === 'running' ? 'text-status-success' : 'text-status-warning';
} }
private getStatusDotColor(): string { private getStatusDotColor(): string {
if (this.session.waiting) { if (this.session.waiting) {
return 'bg-gray-500'; return 'bg-dark-text-muted';
} }
return this.session.status === 'running' ? 'bg-green-500' : 'bg-orange-500'; return this.session.status === 'running' ? 'bg-status-success' : 'bg-status-warning';
} }
} }

View file

@ -231,25 +231,18 @@ export class SessionCreateForm extends LitElement {
} }
return html` return html`
<div <div class="modal-backdrop flex items-center justify-center">
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center" <div class="modal-content font-mono text-sm w-96 max-w-full mx-4">
style="z-index: 9999;" <div class="pb-6 mb-6 border-b border-dark-border">
> <h2 class="text-accent-green text-lg font-bold">Create New Session</h2>
<div
class="font-mono text-sm w-96 max-w-full mx-4"
style="background: black; border: 1px solid #569cd6; border-radius: 4px;"
>
<div class="p-4" style="border-bottom: 1px solid #444;">
<div class="text-vs-user text-sm">Create New Session</div>
</div> </div>
<div class="p-4"> <div class="p-4">
<div class="mb-4"> <div class="mb-4">
<div class="text-vs-text mb-2">Session Name (optional):</div> <label class="form-label">Session Name (optional):</label>
<input <input
type="text" type="text"
class="w-full outline-none font-mono px-4 py-2" class="input-field"
style="background: rgba(0, 0, 0, 0.8); color: #d4d4d4; border: 1px solid #444; border-radius: 4px;"
.value=${this.sessionName} .value=${this.sessionName}
@input=${this.handleSessionNameChange} @input=${this.handleSessionNameChange}
placeholder="My Session" placeholder="My Session"
@ -258,44 +251,31 @@ export class SessionCreateForm extends LitElement {
</div> </div>
<div class="mb-4"> <div class="mb-4">
<div class="text-vs-text mb-2">Working Directory:</div> <label class="form-label">Working Directory:</label>
<div class="flex gap-4"> <div class="flex gap-4">
<input <input
type="text" type="text"
class="flex-1 outline-none font-mono px-4 py-2" class="input-field"
style="background: rgba(0, 0, 0, 0.8); color: #d4d4d4; border: 1px solid #444; border-radius: 4px;"
.value=${this.workingDir} .value=${this.workingDir}
@input=${this.handleWorkingDirChange} @input=${this.handleWorkingDirChange}
placeholder="~/" placeholder="~/"
?disabled=${this.disabled || this.isCreating} ?disabled=${this.disabled || this.isCreating}
/> />
<button <button
class="font-mono px-4 py-2 transition-colors" class="btn-secondary font-mono px-4"
style="background: black; color: #d4d4d4; border: 1px solid #569cd6; border-radius: 4px;"
@click=${this.handleBrowse} @click=${this.handleBrowse}
?disabled=${this.disabled || this.isCreating} ?disabled=${this.disabled || this.isCreating}
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = '#569cd6';
btn.style.color = 'black';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}}
> >
browse Browse
</button> </button>
</div> </div>
</div> </div>
<div class="mb-4"> <div class="mb-4">
<div class="text-vs-text mb-2">Command:</div> <label class="form-label">Command:</label>
<input <input
type="text" type="text"
class="w-full outline-none font-mono px-4 py-2" class="input-field"
style="background: rgba(0, 0, 0, 0.8); color: #d4d4d4; border: 1px solid #444; border-radius: 4px;"
.value=${this.command} .value=${this.command}
@input=${this.handleCommandChange} @input=${this.handleCommandChange}
@keydown=${(e: KeyboardEvent) => e.key === 'Enter' && this.handleCreate()} @keydown=${(e: KeyboardEvent) => e.key === 'Enter' && this.handleCreate()}
@ -306,47 +286,21 @@ export class SessionCreateForm extends LitElement {
<div class="flex gap-4 justify-end"> <div class="flex gap-4 justify-end">
<button <button
class="font-mono px-4 py-2 transition-colors" class="btn-ghost font-mono"
style="background: black; color: #d4d4d4; border: 1px solid #888; border-radius: 4px;"
@click=${this.handleCancel} @click=${this.handleCancel}
?disabled=${this.isCreating} ?disabled=${this.isCreating}
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = '#888';
btn.style.color = 'black';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}}
> >
cancel Cancel
</button> </button>
<button <button
class="font-mono px-4 py-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed" class="btn-primary font-mono disabled:opacity-50 disabled:cursor-not-allowed"
style="background: black; color: #d4d4d4; border: 1px solid #569cd6; border-radius: 4px;"
@click=${this.handleCreate} @click=${this.handleCreate}
?disabled=${this.disabled || ?disabled=${this.disabled ||
this.isCreating || this.isCreating ||
!this.workingDir.trim() || !this.workingDir.trim() ||
!this.command.trim()} !this.command.trim()}
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
if (!btn.hasAttribute('disabled')) {
btn.style.background = '#569cd6';
btn.style.color = 'black';
}
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
if (!btn.hasAttribute('disabled')) {
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}
}}
> >
${this.isCreating ? 'creating...' : 'create'} ${this.isCreating ? 'Creating...' : 'Create'}
</button> </button>
</div> </div>
</div> </div>

View file

@ -99,10 +99,10 @@ export class SessionList extends LitElement {
: this.sessions; : this.sessions;
return html` return html`
<div class="font-mono text-sm p-4" style="background: black;"> <div class="font-mono text-sm p-4 bg-dark-bg">
${filteredSessions.length === 0 ${filteredSessions.length === 0
? html` ? html`
<div class="text-vs-muted text-center py-8"> <div class="text-dark-text-muted text-center py-8">
${this.loading ${this.loading
? 'Loading sessions...' ? 'Loading sessions...'
: this.hideExited && this.sessions.length > 0 : this.hideExited && this.sessions.length > 0

View file

@ -821,28 +821,28 @@ export class SessionView extends LitElement {
} }
private getStatusColor(): string { private getStatusColor(): string {
if (!this.session) return 'text-vs-muted'; if (!this.session) return 'text-dark-text-muted';
if ('waiting' in this.session && this.session.waiting) { if ('waiting' in this.session && this.session.waiting) {
return 'text-vs-muted'; return 'text-dark-text-muted';
} }
return this.session.status === 'running' ? 'text-vs-user' : 'text-vs-warning'; return this.session.status === 'running' ? 'text-status-success' : 'text-status-warning';
} }
private getStatusDotColor(): string { private getStatusDotColor(): string {
if (!this.session) return 'bg-gray-500'; if (!this.session) return 'bg-dark-text-muted';
if ('waiting' in this.session && this.session.waiting) { if ('waiting' in this.session && this.session.waiting) {
return 'bg-gray-500'; return 'bg-dark-text-muted';
} }
return this.session.status === 'running' ? 'bg-green-500' : 'bg-orange-500'; return this.session.status === 'running' ? 'bg-status-success' : 'bg-status-warning';
} }
render() { render() {
if (!this.session) { if (!this.session) {
return html` return html`
<div class="fixed inset-0 bg-black flex items-center justify-center"> <div class="fixed inset-0 bg-dark-bg flex items-center justify-center">
<div class="text-vs-text font-mono text-center"> <div class="text-dark-text font-mono text-center">
<div class="text-2xl mb-2">${this.getLoadingText()}</div> <div class="text-2xl mb-2">${this.getLoadingText()}</div>
<div class="text-sm text-vs-muted">Waiting for session...</div> <div class="text-sm text-dark-text-muted">Waiting for session...</div>
</div> </div>
</div> </div>
`; `;
@ -857,40 +857,28 @@ export class SessionView extends LitElement {
box-shadow: none !important; box-shadow: none !important;
} }
session-view:focus { session-view:focus {
outline: 2px solid #007acc !important; outline: 2px solid #00ff88 !important;
outline-offset: -2px; outline-offset: -2px;
} }
</style> </style>
<div <div
class="flex flex-col bg-vs-bg font-mono" class="flex flex-col bg-dark-bg font-mono"
style="height: 100vh; height: 100dvh; outline: none !important; box-shadow: none !important;" style="height: 100vh; height: 100dvh; outline: none !important; box-shadow: none !important;"
> >
<!-- Compact Header --> <!-- Compact Header -->
<div <div
class="flex items-center justify-between px-3 py-2 border-b border-vs-border text-sm min-w-0" class="flex items-center justify-between px-3 py-2 border-b border-dark-border text-sm min-w-0 bg-dark-bg-secondary"
style="background: black;"
> >
<div class="flex items-center gap-3 min-w-0 flex-1"> <div class="flex items-center gap-3 min-w-0 flex-1">
<button <button
class="font-mono px-2 py-1 rounded transition-colors text-xs flex-shrink-0" class="btn-secondary font-mono text-xs px-3 py-1 flex-shrink-0"
style="background: black; color: #d4d4d4; border: 1px solid #569cd6;"
@click=${this.handleBack} @click=${this.handleBack}
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = '#569cd6';
btn.style.color = 'black';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}}
> >
BACK Back
</button> </button>
<div class="text-vs-text min-w-0 flex-1 overflow-hidden"> <div class="text-dark-text min-w-0 flex-1 overflow-hidden">
<div <div
class="text-vs-accent text-xs sm:text-sm overflow-hidden text-ellipsis whitespace-nowrap" class="text-accent-green text-xs sm:text-sm overflow-hidden text-ellipsis whitespace-nowrap"
title="${this.session.name || this.session.command}" title="${this.session.name || this.session.command}"
> >
${this.session.name || this.session.command} ${this.session.name || this.session.command}
@ -906,7 +894,7 @@ export class SessionView extends LitElement {
${this.terminalCols > 0 && this.terminalRows > 0 ${this.terminalCols > 0 && this.terminalRows > 0
? html` ? html`
<span <span
class="text-vs-muted text-xs opacity-60" class="text-dark-text-muted text-xs opacity-60"
style="font-size: 10px; line-height: 1;" style="font-size: 10px; line-height: 1;"
> >
${this.terminalCols}×${this.terminalRows} ${this.terminalCols}×${this.terminalRows}
@ -919,7 +907,7 @@ export class SessionView extends LitElement {
<!-- Terminal Container --> <!-- Terminal Container -->
<div <div
class="flex-1 bg-black overflow-hidden min-h-0 relative" class="flex-1 bg-dark-bg overflow-hidden min-h-0 relative"
id="terminal-container" id="terminal-container"
style="max-width: 100vw; height: 100%;" style="max-width: 100vw; height: 100%;"
> >
@ -927,11 +915,11 @@ export class SessionView extends LitElement {
? html` ? html`
<!-- Loading overlay --> <!-- Loading overlay -->
<div <div
class="absolute inset-0 bg-black bg-opacity-80 flex items-center justify-center z-10" class="absolute inset-0 bg-dark-bg bg-opacity-80 flex items-center justify-center z-10"
> >
<div class="text-vs-text font-mono text-center"> <div class="text-dark-text font-mono text-center">
<div class="text-2xl mb-2">${this.getLoadingText()}</div> <div class="text-2xl mb-2">${this.getLoadingText()}</div>
<div class="text-sm text-vs-muted">Connecting to session...</div> <div class="text-sm text-dark-text-muted">Connecting to session...</div>
</div> </div>
</div> </div>
` `
@ -954,70 +942,26 @@ export class SessionView extends LitElement {
<!-- First row: Arrow keys --> <!-- First row: Arrow keys -->
<div class="flex gap-2 mb-2"> <div class="flex gap-2 mb-2">
<button <button
class="flex-1 font-mono px-3 py-2 text-sm transition-all cursor-pointer" class="flex-1 font-mono px-3 py-2 text-sm transition-all cursor-pointer quick-start-btn"
@click=${() => this.handleSpecialKey('arrow_up')} @click=${() => this.handleSpecialKey('arrow_up')}
style="background: rgba(0, 0, 0, 0.8); color: #d4d4d4; border: 1px solid #444; border-radius: 4px;"
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'rgba(0, 0, 0, 0.9)';
btn.style.borderColor = '#666';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'rgba(0, 0, 0, 0.8)';
btn.style.borderColor = '#444';
}}
> >
<span class="text-xl"></span> <span class="text-xl"></span>
</button> </button>
<button <button
class="flex-1 font-mono px-3 py-2 text-sm transition-all cursor-pointer" class="flex-1 font-mono px-3 py-2 text-sm transition-all cursor-pointer quick-start-btn"
@click=${() => this.handleSpecialKey('arrow_down')} @click=${() => this.handleSpecialKey('arrow_down')}
style="background: rgba(0, 0, 0, 0.8); color: #d4d4d4; border: 1px solid #444; border-radius: 4px;"
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'rgba(0, 0, 0, 0.9)';
btn.style.borderColor = '#666';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'rgba(0, 0, 0, 0.8)';
btn.style.borderColor = '#444';
}}
> >
<span class="text-xl"></span> <span class="text-xl"></span>
</button> </button>
<button <button
class="flex-1 font-mono px-3 py-2 text-sm transition-all cursor-pointer" class="flex-1 font-mono px-3 py-2 text-sm transition-all cursor-pointer quick-start-btn"
@click=${() => this.handleSpecialKey('arrow_left')} @click=${() => this.handleSpecialKey('arrow_left')}
style="background: rgba(0, 0, 0, 0.8); color: #d4d4d4; border: 1px solid #444; border-radius: 4px;"
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'rgba(0, 0, 0, 0.9)';
btn.style.borderColor = '#666';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'rgba(0, 0, 0, 0.8)';
btn.style.borderColor = '#444';
}}
> >
<span class="text-xl"></span> <span class="text-xl"></span>
</button> </button>
<button <button
class="flex-1 font-mono px-3 py-2 text-sm transition-all cursor-pointer" class="flex-1 font-mono px-3 py-2 text-sm transition-all cursor-pointer quick-start-btn"
@click=${() => this.handleSpecialKey('arrow_right')} @click=${() => this.handleSpecialKey('arrow_right')}
style="background: rgba(0, 0, 0, 0.8); color: #d4d4d4; border: 1px solid #444; border-radius: 4px;"
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'rgba(0, 0, 0, 0.9)';
btn.style.borderColor = '#666';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'rgba(0, 0, 0, 0.8)';
btn.style.borderColor = '#444';
}}
> >
<span class="text-xl"></span> <span class="text-xl"></span>
</button> </button>
@ -1026,87 +970,32 @@ export class SessionView extends LitElement {
<!-- Second row: Special keys --> <!-- Second row: Special keys -->
<div class="flex gap-2"> <div class="flex gap-2">
<button <button
class="font-mono text-sm transition-all cursor-pointer w-16" class="font-mono text-sm transition-all cursor-pointer w-16 quick-start-btn"
@click=${() => this.handleSpecialKey('escape')} @click=${() => this.handleSpecialKey('escape')}
style="background: rgba(0, 0, 0, 0.8); color: #d4d4d4; border: 1px solid #444; border-radius: 4px; padding: 8px 4px;"
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'rgba(0, 0, 0, 0.9)';
btn.style.borderColor = '#666';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'rgba(0, 0, 0, 0.8)';
btn.style.borderColor = '#444';
}}
> >
ESC ESC
</button> </button>
<button <button
class="font-mono text-sm transition-all cursor-pointer w-16" class="font-mono text-sm transition-all cursor-pointer w-16 quick-start-btn"
@click=${() => this.handleSpecialKey('\t')} @click=${() => this.handleSpecialKey('\t')}
style="background: rgba(0, 0, 0, 0.8); color: #d4d4d4; border: 1px solid #444; border-radius: 4px; padding: 8px 4px;"
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'rgba(0, 0, 0, 0.9)';
btn.style.borderColor = '#666';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'rgba(0, 0, 0, 0.8)';
btn.style.borderColor = '#444';
}}
> >
<span class="text-xl"></span> <span class="text-xl"></span>
</button> </button>
<button <button
class="flex-1 font-mono px-3 py-2 text-sm transition-all cursor-pointer" class="flex-1 font-mono px-3 py-2 text-sm transition-all cursor-pointer quick-start-btn"
@click=${this.handleMobileInputToggle} @click=${this.handleMobileInputToggle}
style="background: rgba(0, 0, 0, 0.8); color: #d4d4d4; border: 1px solid #444; border-radius: 4px;"
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'rgba(0, 0, 0, 0.9)';
btn.style.borderColor = '#666';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'rgba(0, 0, 0, 0.8)';
btn.style.borderColor = '#444';
}}
> >
ABC123 ABC123
</button> </button>
<button <button
class="font-mono text-sm transition-all cursor-pointer w-16" class="font-mono text-sm transition-all cursor-pointer w-16 quick-start-btn"
@click=${this.handleCtrlAlphaToggle} @click=${this.handleCtrlAlphaToggle}
style="background: rgba(0, 0, 0, 0.8); color: #d4d4d4; border: 1px solid #444; border-radius: 4px; padding: 8px 4px;"
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'rgba(0, 0, 0, 0.9)';
btn.style.borderColor = '#666';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'rgba(0, 0, 0, 0.8)';
btn.style.borderColor = '#444';
}}
> >
CTRL CTRL
</button> </button>
<button <button
class="font-mono text-sm transition-all cursor-pointer w-16" class="font-mono text-sm transition-all cursor-pointer w-16 quick-start-btn"
@click=${() => this.handleSpecialKey('enter')} @click=${() => this.handleSpecialKey('enter')}
style="background: rgba(0, 0, 0, 0.8); color: #d4d4d4; border: 1px solid #444; border-radius: 4px; padding: 8px 4px;"
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'rgba(0, 0, 0, 0.9)';
btn.style.borderColor = '#666';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'rgba(0, 0, 0, 0.8)';
btn.style.borderColor = '#444';
}}
> >
<span class="text-xl"></span> <span class="text-xl"></span>
</button> </button>
@ -1167,63 +1056,22 @@ export class SessionView extends LitElement {
<!-- Controls --> <!-- Controls -->
<div class="p-4 flex gap-2" style="border-top: 1px solid #444;"> <div class="p-4 flex gap-2" style="border-top: 1px solid #444;">
<button <button
class="font-mono px-3 py-2 text-xs transition-colors" class="font-mono px-3 py-2 text-xs transition-colors btn-ghost"
@click=${() => (this.showMobileInput = false)} @click=${() => (this.showMobileInput = false)}
style="background: black; color: #d4d4d4; border: 1px solid #888; border-radius: 4px;"
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = '#888';
btn.style.color = 'black';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}}
> >
CANCEL CANCEL
</button> </button>
<button <button
class="flex-1 font-mono px-3 py-2 text-xs transition-colors disabled:opacity-50 disabled:cursor-not-allowed" class="flex-1 font-mono px-3 py-2 text-xs transition-colors disabled:opacity-50 disabled:cursor-not-allowed btn-ghost"
@click=${this.handleMobileInputSendOnly} @click=${this.handleMobileInputSendOnly}
?disabled=${!this.mobileInputText.trim()} ?disabled=${!this.mobileInputText.trim()}
style="background: black; color: #d4d4d4; border: 1px solid #888; border-radius: 4px;"
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
if (!btn.hasAttribute('disabled')) {
btn.style.background = '#888';
btn.style.color = 'black';
}
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
if (!btn.hasAttribute('disabled')) {
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}
}}
> >
SEND SEND
</button> </button>
<button <button
class="flex-1 font-mono px-3 py-2 text-xs transition-colors disabled:opacity-50 disabled:cursor-not-allowed" class="flex-1 font-mono px-3 py-2 text-xs transition-colors disabled:opacity-50 disabled:cursor-not-allowed btn-secondary"
@click=${this.handleMobileInputSend} @click=${this.handleMobileInputSend}
?disabled=${!this.mobileInputText.trim()} ?disabled=${!this.mobileInputText.trim()}
style="background: black; color: #d4d4d4; border: 1px solid #569cd6; border-radius: 4px;"
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
if (!btn.hasAttribute('disabled')) {
btn.style.background = '#569cd6';
btn.style.color = 'black';
}
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
if (!btn.hasAttribute('disabled')) {
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}
}}
> >
SEND + SEND +
</button> </button>
@ -1297,19 +1145,8 @@ export class SessionView extends LitElement {
].map( ].map(
(letter) => html` (letter) => html`
<button <button
class="font-mono text-xs transition-all cursor-pointer aspect-square flex items-center justify-center" class="font-mono text-xs transition-all cursor-pointer aspect-square flex items-center justify-center quick-start-btn"
style="background: rgba(0, 0, 0, 0.8); color: #d4d4d4; border: 1px solid #444; border-radius: 4px;"
@click=${() => this.handleCtrlKey(letter)} @click=${() => this.handleCtrlKey(letter)}
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = '#569cd6';
btn.style.color = 'black';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'rgba(0, 0, 0, 0.8)';
btn.style.color = '#d4d4d4';
}}
> >
${letter} ${letter}
</button> </button>
@ -1325,55 +1162,22 @@ export class SessionView extends LitElement {
<!-- Action buttons --> <!-- Action buttons -->
<div class="flex gap-2 justify-center"> <div class="flex gap-2 justify-center">
<button <button
class="font-mono px-4 py-2 text-sm transition-all cursor-pointer" class="font-mono px-4 py-2 text-sm transition-all cursor-pointer btn-ghost"
style="background: black; color: #d4d4d4; border: 1px solid #888; border-radius: 4px;"
@click=${() => (this.showCtrlAlpha = false)} @click=${() => (this.showCtrlAlpha = false)}
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = '#888';
btn.style.color = 'black';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}}
> >
CANCEL CANCEL
</button> </button>
${this.ctrlSequence.length > 0 ${this.ctrlSequence.length > 0
? html` ? html`
<button <button
class="font-mono px-3 py-2 text-sm transition-all cursor-pointer" class="font-mono px-3 py-2 text-sm transition-all cursor-pointer btn-ghost"
style="background: black; color: #d4d4d4; border: 1px solid #888; border-radius: 4px;"
@click=${this.handleClearCtrlSequence} @click=${this.handleClearCtrlSequence}
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = '#888';
btn.style.color = 'black';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}}
> >
CLEAR CLEAR
</button> </button>
<button <button
class="font-mono px-3 py-2 text-sm transition-all cursor-pointer" class="font-mono px-3 py-2 text-sm transition-all cursor-pointer btn-secondary"
style="background: black; color: #d4d4d4; border: 1px solid #569cd6; border-radius: 4px;"
@click=${this.handleSendCtrlSequence} @click=${this.handleSendCtrlSequence}
@mouseover=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = '#569cd6';
btn.style.color = 'black';
}}
@mouseout=${(e: Event) => {
const btn = e.target as HTMLElement;
btn.style.background = 'black';
btn.style.color = '#d4d4d4';
}}
> >
SEND SEND
</button> </button>

View file

@ -0,0 +1,39 @@
import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';
@customElement('terminal-icon')
export class TerminalIcon extends LitElement {
@property({ type: Number }) size = 24;
static styles = css`
:host {
display: inline-flex;
align-items: center;
justify-content: center;
}
svg {
display: block;
width: var(--icon-size, 24px);
height: var(--icon-size, 24px);
}
`;
render() {
return html`
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
style="--icon-size: ${this.size}px"
class="terminal-icon"
>
<polyline points="4 17 10 11 4 5"></polyline>
<line x1="12" y1="19" x2="20" y2="19"></line>
</svg>
`;
}
}

View file

@ -2,6 +2,93 @@
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
/* Global dark theme styles */
@layer base {
body {
@apply bg-dark-bg text-dark-text;
}
/* Default focus styles */
:focus {
outline: none;
box-shadow: 0 0 0 2px rgba(0, 255, 136, 0.3);
}
}
/* Custom components */
@layer components {
/* Glowing terminal icon */
.terminal-icon {
@apply text-accent-green;
filter: drop-shadow(0 0 10px rgba(0, 255, 136, 0.6));
}
/* Input fields with glow effect */
.input-field {
@apply bg-dark-bg-secondary border border-dark-border rounded-lg px-4 py-3 text-dark-text w-full;
@apply transition-all duration-200 ease-in-out;
@apply hover:border-accent-green-darker focus:border-accent-green;
@apply focus:shadow-glow-green-sm;
}
/* Button styles */
.btn-primary {
@apply bg-accent-green text-dark-bg font-medium px-6 py-3 rounded-lg;
@apply transition-all duration-200 ease-in-out;
@apply hover:bg-accent-green-light hover:shadow-glow-green;
@apply active:scale-95;
}
.btn-secondary {
@apply border border-accent-green text-accent-green px-6 py-3 rounded-lg;
@apply transition-all duration-200 ease-in-out;
@apply hover:bg-accent-green hover:text-dark-bg hover:shadow-glow-green;
@apply active:scale-95;
}
.btn-ghost {
@apply text-dark-text-muted px-4 py-2 rounded-lg;
@apply transition-all duration-200 ease-in-out;
@apply hover:text-dark-text hover:bg-dark-bg-tertiary;
}
/* Card styles */
.card {
@apply bg-dark-bg-secondary border border-dark-border rounded-lg p-6;
@apply transition-all duration-200 ease-in-out;
@apply hover:border-accent-green-darker hover:shadow-glow-green-sm;
}
/* Quick start buttons */
.quick-start-btn {
@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 hover:border-accent-green hover:text-accent-green hover:shadow-glow-green-sm;
@apply active:scale-95;
}
.quick-start-btn.active {
@apply bg-accent-green text-dark-bg border-accent-green shadow-glow-green-sm;
}
/* Modal backdrop */
.modal-backdrop {
@apply fixed inset-0 bg-black bg-opacity-80 z-40;
backdrop-filter: blur(4px);
}
/* Modal content */
.modal-content {
@apply bg-dark-bg-secondary border border-dark-border rounded-xl p-8;
@apply shadow-2xl shadow-black/50;
}
/* Labels */
.form-label {
@apply text-dark-text-muted text-sm font-medium mb-2 flex items-center gap-2;
}
}
/* Fira Code Variable Font */ /* Fira Code Variable Font */
@font-face { @font-face {
font-family: 'Fira Code'; font-family: 'Fira Code';
@ -112,9 +199,10 @@ body {
/* Terminal container styling */ /* Terminal container styling */
.terminal-container { .terminal-container {
color: #d4d4d4; color: #e4e4e4;
white-space: pre; white-space: pre;
overflow: hidden; overflow: hidden;
background-color: #0a0a0a;
} }
/* Terminal line styling */ /* Terminal line styling */
@ -196,8 +284,8 @@ body {
/* Terminal focus indicator */ /* Terminal focus indicator */
.terminal-focused { .terminal-focused {
box-shadow: 0 0 0 2px #00ff00; box-shadow: 0 0 0 2px #00ff88;
border-color: #00ff00 !important; border-color: #00ff88 !important;
} }
/* Keyboard capture indicator */ /* Keyboard capture indicator */
@ -205,9 +293,9 @@ body {
position: fixed; position: fixed;
top: 10px; top: 10px;
right: 10px; right: 10px;
background: rgba(0, 255, 0, 0.1); background: rgba(0, 255, 136, 0.1);
border: 1px solid #00ff00; border: 1px solid #00ff88;
color: #00ff00; color: #00ff88;
padding: 4px 8px; padding: 4px 8px;
border-radius: 4px; border-radius: 4px;
font-size: 12px; font-size: 12px;
@ -517,14 +605,14 @@ body {
left: 12px; left: 12px;
width: 48px; width: 48px;
height: 48px; height: 48px;
background: rgba(0, 0, 0, 0.8); background: rgba(26, 26, 26, 0.9);
border: 1px solid #444; border: 1px solid #2a2a2a;
border-radius: 6px; border-radius: 6px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
cursor: pointer; cursor: pointer;
color: #d4d4d4; color: #e4e4e4;
font-size: 24px; font-size: 24px;
transition: all 0.2s ease; transition: all 0.2s ease;
user-select: none; user-select: none;
@ -532,9 +620,11 @@ body {
} }
.scroll-to-bottom:hover { .scroll-to-bottom:hover {
background: rgba(0, 0, 0, 0.9); background: rgba(26, 26, 26, 1);
border-color: #666; border-color: #00ff88;
color: #00ff88;
transform: translateY(-1px); transform: translateY(-1px);
box-shadow: 0 0 10px rgba(0, 255, 136, 0.3);
} }
.scroll-to-bottom:active { .scroll-to-bottom:active {
@ -546,13 +636,13 @@ body {
position: absolute; position: absolute;
bottom: 8px; bottom: 8px;
right: 8px; right: 8px;
background: rgba(0, 0, 0, 0.8); background: rgba(26, 26, 26, 0.9);
border: 1px solid #444; border: 1px solid #2a2a2a;
border-radius: 4px; border-radius: 4px;
padding: 8px 12px; padding: 8px 12px;
font-family: 'Fira Code', monospace; font-family: 'Fira Code', monospace;
font-size: 11px; font-size: 11px;
color: #d4d4d4; color: #e4e4e4;
user-select: none; user-select: none;
z-index: 10; z-index: 10;
line-height: 1.4; line-height: 1.4;
@ -580,14 +670,14 @@ body {
right: 12px; right: 12px;
width: 48px; width: 48px;
height: 48px; height: 48px;
background: rgba(0, 0, 0, 0.8); background: rgba(26, 26, 26, 0.9);
border: 1px solid #444; border: 1px solid #2a2a2a;
border-radius: 6px; border-radius: 6px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
cursor: pointer; cursor: pointer;
color: #d4d4d4; color: #e4e4e4;
font-size: 20px; font-size: 20px;
transition: all 0.2s ease; transition: all 0.2s ease;
user-select: none; user-select: none;
@ -595,9 +685,11 @@ body {
} }
.fit-toggle:hover { .fit-toggle:hover {
background: rgba(0, 0, 0, 0.9); background: rgba(26, 26, 26, 1);
border-color: #666; border-color: #00ff88;
color: #00ff88;
transform: translateY(-1px); transform: translateY(-1px);
box-shadow: 0 0 10px rgba(0, 255, 136, 0.3);
} }
.fit-toggle:active { .fit-toggle:active {
@ -605,6 +697,7 @@ body {
} }
.fit-toggle.active { .fit-toggle.active {
border-color: #569cd6; border-color: #00ff88;
color: #569cd6; color: #00ff88;
box-shadow: 0 0 10px rgba(0, 255, 136, 0.3);
} }

View file

@ -4,24 +4,72 @@ module.exports = {
theme: { theme: {
extend: { extend: {
colors: { colors: {
"vs-bg": "#1e1e1e", // Dark theme colors
"vs-text": "#d4d4d4", "dark-bg": "#0a0a0a",
"vs-muted": "#8c8c8c", "dark-bg-secondary": "#1a1a1a",
"vs-accent": "#569cd6", "dark-bg-tertiary": "#242424",
"vs-user": "#6a9955", "dark-border": "#2a2a2a",
"vs-assistant": "#ce9178", "dark-border-light": "#3a3a3a",
"vs-warning": "#f48771",
"vs-function": "#dcdcaa", // Text colors
"vs-type": "#4ec9b0", "dark-text": "#e4e4e4",
"vs-border": "#3e3e42", "dark-text-muted": "#7a7a7a",
"vs-border-light": "#5a5a5e", "dark-text-dim": "#5a5a5a",
"vs-bg-secondary": "#2d2d30",
"vs-nav": "#3e3e42", // Green accent colors (multiple shades)
"vs-nav-hover": "#4a4a4e", "accent-green": "#00ff88",
"vs-nav-active": "#f48771", "accent-green-dark": "#00cc66",
"accent-green-darker": "#009944",
"accent-green-light": "#44ffaa",
"accent-green-glow": "#00ff8866",
// Secondary accent colors
"accent-cyan": "#00ffcc",
"accent-teal": "#00ccaa",
// Status colors
"status-error": "#cc3333",
"status-warning": "#cc8833",
"status-success": "#00cc66",
// Legacy VS Code theme colors (for compatibility)
"vs-bg": "#0a0a0a",
"vs-text": "#e4e4e4",
"vs-muted": "#7a7a7a",
"vs-accent": "#00ff88",
"vs-user": "#00ff88",
"vs-assistant": "#00ccaa",
"vs-warning": "#ffaa44",
"vs-function": "#44ffaa",
"vs-type": "#00ffcc",
"vs-border": "#2a2a2a",
"vs-border-light": "#3a3a3a",
"vs-bg-secondary": "#1a1a1a",
"vs-nav": "#1a1a1a",
"vs-nav-hover": "#242424",
"vs-nav-active": "#00ff88",
"vs-highlight": "#8b6914", "vs-highlight": "#8b6914",
}, },
}, },
boxShadow: {
'glow-green': '0 0 20px rgba(0, 255, 136, 0.4)',
'glow-green-sm': '0 0 10px rgba(0, 255, 136, 0.3)',
'glow-green-lg': '0 0 30px rgba(0, 255, 136, 0.5)',
},
animation: {
'pulse-green': 'pulseGreen 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
},
keyframes: {
pulseGreen: {
'0%, 100%': {
opacity: '1',
},
'50%': {
opacity: '.8',
},
},
},
},
}, },
plugins: [], plugins: [],
}; };