mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-18 13:25:52 +00:00
Merge remote-tracking branch 'origin/frontend-redesign-2'
This commit is contained in:
commit
12cb1e5c8e
10 changed files with 359 additions and 575 deletions
|
|
@ -265,9 +265,11 @@ export class VibeTunnelApp extends LitElement {
|
|||
${this.errorMessage
|
||||
? html`
|
||||
<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}
|
||||
<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>
|
||||
</div>
|
||||
|
|
@ -277,9 +279,11 @@ export class VibeTunnelApp extends LitElement {
|
|||
${this.successMessage
|
||||
? html`
|
||||
<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}
|
||||
<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>
|
||||
</div>
|
||||
|
|
@ -298,7 +302,7 @@ export class VibeTunnelApp extends LitElement {
|
|||
`
|
||||
)
|
||||
: html`
|
||||
<div class="max-w-4xl mx-auto" style="background: black;">
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<app-header
|
||||
.sessions=${this.sessions}
|
||||
.hideExited=${this.hideExited}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { LitElement, html } from 'lit';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import type { Session } from './session-list.js';
|
||||
import './vibe-logo.js';
|
||||
import './terminal-icon.js';
|
||||
|
||||
@customElement('app-header')
|
||||
export class AppHeader extends LitElement {
|
||||
|
|
@ -46,109 +46,71 @@ export class AppHeader extends LitElement {
|
|||
}
|
||||
|
||||
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 -->
|
||||
<div class="flex flex-col gap-3 sm:hidden">
|
||||
<!-- Centered VibeTunnel title -->
|
||||
<div class="text-center">
|
||||
<vibe-logo></vibe-logo>
|
||||
<div class="flex flex-col gap-4 sm:hidden">
|
||||
<!-- Centered Sessions title with stats -->
|
||||
<div class="text-center flex flex-col items-center gap-2">
|
||||
<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>
|
||||
|
||||
<!-- Controls row: left buttons and right buttons -->
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex gap-1">
|
||||
<div class="flex gap-2">
|
||||
${exitedSessions.length > 0
|
||||
? html`
|
||||
<button
|
||||
class="font-mono px-2 py-1 rounded transition-colors text-xs whitespace-nowrap"
|
||||
style="background: black; color: #d4d4d4; border: 1px solid ${this.hideExited
|
||||
? '#23d18b'
|
||||
: '#888'};"
|
||||
class="btn-ghost font-mono text-xs ${this.hideExited
|
||||
? 'text-accent-green border border-accent-green'
|
||||
: ''}"
|
||||
@click=${() =>
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('hide-exited-change', {
|
||||
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
|
||||
? `SHOW EXITED (${exitedSessions.length})`
|
||||
: `HIDE EXITED (${exitedSessions.length})`}
|
||||
? `Show Exited (${exitedSessions.length})`
|
||||
: `Hide Exited (${exitedSessions.length})`}
|
||||
</button>
|
||||
`
|
||||
: ''}
|
||||
${!this.hideExited && exitedSessions.length > 0
|
||||
? html`
|
||||
<button
|
||||
class="font-mono px-2 py-1 rounded transition-colors text-xs whitespace-nowrap"
|
||||
style="background: black; color: #d4d4d4; border: 1px solid #d19a66;"
|
||||
class="btn-ghost font-mono text-xs text-status-warning"
|
||||
@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>
|
||||
`
|
||||
: ''}
|
||||
${runningSessions.length > 0 && !this.killingAll
|
||||
? html`
|
||||
<button
|
||||
class="font-mono px-2 py-1 rounded transition-colors text-xs whitespace-nowrap"
|
||||
style="background: black; color: #d4d4d4; border: 1px solid #d19a66;"
|
||||
class="btn-ghost font-mono text-xs text-status-error"
|
||||
@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>
|
||||
`
|
||||
: ''}
|
||||
</div>
|
||||
|
||||
<div class="flex gap-1">
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="font-mono px-2 py-1 rounded transition-colors text-xs whitespace-nowrap"
|
||||
style="background: black; color: #d4d4d4; border: 1px solid #569cd6;"
|
||||
class="btn-primary font-mono text-xs px-4 py-2"
|
||||
@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>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -156,7 +118,16 @@ export class AppHeader extends LitElement {
|
|||
|
||||
<!-- Desktop layout: single row -->
|
||||
<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">
|
||||
${exitedSessions.length > 0
|
||||
? html`
|
||||
|
|
@ -193,61 +164,28 @@ export class AppHeader extends LitElement {
|
|||
${!this.hideExited && this.sessions.filter((s) => s.status === 'exited').length > 0
|
||||
? html`
|
||||
<button
|
||||
class="font-mono px-2 py-1 rounded transition-colors text-xs whitespace-nowrap"
|
||||
style="background: black; color: #d4d4d4; border: 1px solid #d19a66;"
|
||||
class="btn-ghost font-mono text-xs text-status-warning"
|
||||
@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>
|
||||
`
|
||||
: ''}
|
||||
${runningSessions.length > 0 && !this.killingAll
|
||||
? html`
|
||||
<button
|
||||
class="font-mono px-2 py-1 rounded transition-colors text-xs whitespace-nowrap"
|
||||
style="background: black; color: #d4d4d4; border: 1px solid #d19a66;"
|
||||
class="btn-ghost font-mono text-xs text-status-error"
|
||||
@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
|
||||
class="font-mono px-2 py-1 rounded transition-colors text-xs whitespace-nowrap"
|
||||
style="background: black; color: #d4d4d4; border: 1px solid #569cd6;"
|
||||
class="btn-primary font-mono text-xs px-4 py-2"
|
||||
@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>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -145,48 +145,31 @@ export class FileBrowser extends LitElement {
|
|||
}
|
||||
|
||||
return html`
|
||||
<div
|
||||
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center"
|
||||
style="z-index: 9999;"
|
||||
>
|
||||
<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="modal-backdrop flex items-center justify-center">
|
||||
<div class="modal-content font-mono text-sm w-96 h-96 flex flex-col overflow-hidden">
|
||||
<div class="pb-4 mb-4 border-b border-dark-border flex-shrink-0">
|
||||
<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
|
||||
class="font-mono px-2 py-1 text-xs rounded transition-colors"
|
||||
style="background: black; color: #d4d4d4; border: 1px solid #569cd6; border-radius: 4px;"
|
||||
class="btn-secondary font-mono text-xs px-3 py-1"
|
||||
@click=${this.handleCreateFolder}
|
||||
?disabled=${this.loading}
|
||||
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>
|
||||
</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 class="p-4 flex-1 overflow-y-auto">
|
||||
<div class="px-4 pb-4 flex-1 overflow-y-auto">
|
||||
${this.loading
|
||||
? html` <div class="text-vs-muted">Loading...</div> `
|
||||
? html` <div class="text-dark-text-muted">Loading...</div> `
|
||||
: html`
|
||||
${this.currentPath !== '/'
|
||||
? html`
|
||||
<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}
|
||||
>
|
||||
<span>📁</span>
|
||||
|
|
@ -199,7 +182,7 @@ export class FileBrowser extends LitElement {
|
|||
.map(
|
||||
(file) => html`
|
||||
<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)}
|
||||
>
|
||||
<span>📁</span>
|
||||
|
|
@ -211,7 +194,7 @@ export class FileBrowser extends LitElement {
|
|||
.filter((f) => !f.isDir)
|
||||
.map(
|
||||
(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>${file.name}</span>
|
||||
</div>
|
||||
|
|
@ -223,13 +206,12 @@ export class FileBrowser extends LitElement {
|
|||
<!-- Create folder dialog -->
|
||||
${this.showCreateFolder
|
||||
? html`
|
||||
<div class="p-4 border-t border-vs-border flex-shrink-0">
|
||||
<div class="text-vs-assistant text-sm mb-2">Create New Folder</div>
|
||||
<div class="p-4 border-t border-dark-border flex-shrink-0">
|
||||
<label class="form-label">Create New Folder</label>
|
||||
<div class="flex gap-2">
|
||||
<input
|
||||
type="text"
|
||||
class="flex-1 outline-none font-mono px-2 py-1 text-sm"
|
||||
style="background: rgba(0, 0, 0, 0.8); color: #d4d4d4; border: 1px solid #444; border-radius: 4px;"
|
||||
class="input-field text-sm"
|
||||
placeholder="Folder name"
|
||||
.value=${this.newFolderName}
|
||||
@input=${this.handleFolderNameInput}
|
||||
|
|
@ -237,89 +219,27 @@ export class FileBrowser extends LitElement {
|
|||
?disabled=${this.creating}
|
||||
/>
|
||||
<button
|
||||
class="font-mono px-2 py-1 text-xs transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
style="background: black; color: #d4d4d4; border: 1px solid #569cd6; border-radius: 4px;"
|
||||
class="btn-primary font-mono text-xs px-3 py-1 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
@click=${this.createFolder}
|
||||
?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
|
||||
class="font-mono px-2 py-1 text-xs transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
style="background: black; color: #d4d4d4; border: 1px solid #888; border-radius: 4px;"
|
||||
class="btn-ghost font-mono text-xs disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
@click=${this.handleCancelCreateFolder}
|
||||
?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>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
: ''}
|
||||
|
||||
<div class="p-4 border-t border-vs-border flex gap-4 justify-end flex-shrink-0">
|
||||
<button
|
||||
class="font-mono px-4 py-2 transition-colors"
|
||||
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 class="p-4 border-t border-dark-border flex gap-4 justify-end flex-shrink-0">
|
||||
<button class="btn-ghost font-mono" @click=${this.handleCancel}>Cancel</button>
|
||||
<button class="btn-primary font-mono" @click=${this.handleSelect}>Select</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -156,17 +156,17 @@ export class SessionCard extends LitElement {
|
|||
render() {
|
||||
return html`
|
||||
<div
|
||||
class="bg-vs-bg rounded shadow cursor-pointer overflow-hidden ${this.killing
|
||||
? 'opacity-60'
|
||||
: ''} ${this.hasEscPrompt ? 'border-2 border-orange-500' : 'border border-vs-border'}"
|
||||
class="card cursor-pointer overflow-hidden ${this.killing ? 'opacity-60' : ''} ${this
|
||||
.hasEscPrompt
|
||||
? 'border-2 border-status-warning'
|
||||
: ''}"
|
||||
@click=${this.handleCardClick}
|
||||
>
|
||||
<!-- Compact Header -->
|
||||
<div
|
||||
class="flex justify-between items-center px-3 py-2 border-b border-vs-border"
|
||||
style="background: black;"
|
||||
class="flex justify-between items-center px-3 py-2 border-b border-dark-border bg-dark-bg-tertiary"
|
||||
>
|
||||
<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}">
|
||||
${this.session.name || this.session.command}
|
||||
</div>
|
||||
|
|
@ -174,24 +174,9 @@ export class SessionCard extends LitElement {
|
|||
${this.session.status === 'running'
|
||||
? html`
|
||||
<button
|
||||
class="font-mono px-2 py-0.5 text-xs disabled:opacity-50 flex-shrink-0 rounded transition-colors"
|
||||
style="background: black; color: #d4d4d4; border: 1px solid #d19a66;"
|
||||
class="btn-ghost font-mono text-xs py-1 text-status-error disabled:opacity-50 flex-shrink-0"
|
||||
@click=${this.handleKillClick}
|
||||
?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'}
|
||||
</button>
|
||||
|
|
@ -200,10 +185,10 @@ export class SessionCard extends LitElement {
|
|||
</div>
|
||||
|
||||
<!-- 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
|
||||
? 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-4xl mb-2">${this.getKillingText()}</div>
|
||||
<div class="text-sm">Killing session...</div>
|
||||
|
|
@ -222,8 +207,7 @@ export class SessionCard extends LitElement {
|
|||
|
||||
<!-- Compact Footer -->
|
||||
<div
|
||||
class="px-3 py-2 text-vs-muted text-xs border-t border-vs-border"
|
||||
style="background: black;"
|
||||
class="px-3 py-2 text-dark-text-muted text-xs border-t border-dark-border bg-dark-bg-tertiary"
|
||||
>
|
||||
<div class="flex justify-between items-center min-w-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
|
||||
? html`
|
||||
<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}
|
||||
title="Click to copy PID"
|
||||
>
|
||||
|
|
@ -261,15 +245,15 @@ export class SessionCard extends LitElement {
|
|||
|
||||
private getStatusColor(): string {
|
||||
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 {
|
||||
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';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -231,25 +231,18 @@ export class SessionCreateForm extends LitElement {
|
|||
}
|
||||
|
||||
return html`
|
||||
<div
|
||||
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center"
|
||||
style="z-index: 9999;"
|
||||
>
|
||||
<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 class="modal-backdrop flex items-center justify-center">
|
||||
<div class="modal-content font-mono text-sm w-96 max-w-full mx-4">
|
||||
<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>
|
||||
|
||||
<div class="p-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
|
||||
type="text"
|
||||
class="w-full outline-none font-mono px-4 py-2"
|
||||
style="background: rgba(0, 0, 0, 0.8); color: #d4d4d4; border: 1px solid #444; border-radius: 4px;"
|
||||
class="input-field"
|
||||
.value=${this.sessionName}
|
||||
@input=${this.handleSessionNameChange}
|
||||
placeholder="My Session"
|
||||
|
|
@ -258,44 +251,31 @@ export class SessionCreateForm extends LitElement {
|
|||
</div>
|
||||
|
||||
<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">
|
||||
<input
|
||||
type="text"
|
||||
class="flex-1 outline-none font-mono px-4 py-2"
|
||||
style="background: rgba(0, 0, 0, 0.8); color: #d4d4d4; border: 1px solid #444; border-radius: 4px;"
|
||||
class="input-field"
|
||||
.value=${this.workingDir}
|
||||
@input=${this.handleWorkingDirChange}
|
||||
placeholder="~/"
|
||||
?disabled=${this.disabled || this.isCreating}
|
||||
/>
|
||||
<button
|
||||
class="font-mono px-4 py-2 transition-colors"
|
||||
style="background: black; color: #d4d4d4; border: 1px solid #569cd6; border-radius: 4px;"
|
||||
class="btn-secondary font-mono px-4"
|
||||
@click=${this.handleBrowse}
|
||||
?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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="text-vs-text mb-2">Command:</div>
|
||||
<label class="form-label">Command:</label>
|
||||
<input
|
||||
type="text"
|
||||
class="w-full outline-none font-mono px-4 py-2"
|
||||
style="background: rgba(0, 0, 0, 0.8); color: #d4d4d4; border: 1px solid #444; border-radius: 4px;"
|
||||
class="input-field"
|
||||
.value=${this.command}
|
||||
@input=${this.handleCommandChange}
|
||||
@keydown=${(e: KeyboardEvent) => e.key === 'Enter' && this.handleCreate()}
|
||||
|
|
@ -306,47 +286,21 @@ export class SessionCreateForm extends LitElement {
|
|||
|
||||
<div class="flex gap-4 justify-end">
|
||||
<button
|
||||
class="font-mono px-4 py-2 transition-colors"
|
||||
style="background: black; color: #d4d4d4; border: 1px solid #888; border-radius: 4px;"
|
||||
class="btn-ghost font-mono"
|
||||
@click=${this.handleCancel}
|
||||
?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
|
||||
class="font-mono px-4 py-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
style="background: black; color: #d4d4d4; border: 1px solid #569cd6; border-radius: 4px;"
|
||||
class="btn-primary font-mono disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
@click=${this.handleCreate}
|
||||
?disabled=${this.disabled ||
|
||||
this.isCreating ||
|
||||
!this.workingDir.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>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -99,10 +99,10 @@ export class SessionList extends LitElement {
|
|||
: this.sessions;
|
||||
|
||||
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
|
||||
? html`
|
||||
<div class="text-vs-muted text-center py-8">
|
||||
<div class="text-dark-text-muted text-center py-8">
|
||||
${this.loading
|
||||
? 'Loading sessions...'
|
||||
: this.hideExited && this.sessions.length > 0
|
||||
|
|
|
|||
|
|
@ -821,28 +821,28 @@ export class SessionView extends LitElement {
|
|||
}
|
||||
|
||||
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) {
|
||||
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 {
|
||||
if (!this.session) return 'bg-gray-500';
|
||||
if (!this.session) return 'bg-dark-text-muted';
|
||||
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() {
|
||||
if (!this.session) {
|
||||
return html`
|
||||
<div class="fixed inset-0 bg-black flex items-center justify-center">
|
||||
<div class="text-vs-text font-mono text-center">
|
||||
<div class="fixed inset-0 bg-dark-bg flex items-center justify-center">
|
||||
<div class="text-dark-text font-mono text-center">
|
||||
<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>
|
||||
`;
|
||||
|
|
@ -857,40 +857,28 @@ export class SessionView extends LitElement {
|
|||
box-shadow: none !important;
|
||||
}
|
||||
session-view:focus {
|
||||
outline: 2px solid #007acc !important;
|
||||
outline: 2px solid #00ff88 !important;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
</style>
|
||||
<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;"
|
||||
>
|
||||
<!-- Compact Header -->
|
||||
<div
|
||||
class="flex items-center justify-between px-3 py-2 border-b border-vs-border text-sm min-w-0"
|
||||
style="background: black;"
|
||||
class="flex items-center justify-between px-3 py-2 border-b border-dark-border text-sm min-w-0 bg-dark-bg-secondary"
|
||||
>
|
||||
<div class="flex items-center gap-3 min-w-0 flex-1">
|
||||
<button
|
||||
class="font-mono px-2 py-1 rounded transition-colors text-xs flex-shrink-0"
|
||||
style="background: black; color: #d4d4d4; border: 1px solid #569cd6;"
|
||||
class="btn-secondary font-mono text-xs px-3 py-1 flex-shrink-0"
|
||||
@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>
|
||||
<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
|
||||
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}"
|
||||
>
|
||||
${this.session.name || this.session.command}
|
||||
|
|
@ -906,7 +894,7 @@ export class SessionView extends LitElement {
|
|||
${this.terminalCols > 0 && this.terminalRows > 0
|
||||
? html`
|
||||
<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;"
|
||||
>
|
||||
${this.terminalCols}×${this.terminalRows}
|
||||
|
|
@ -919,7 +907,7 @@ export class SessionView extends LitElement {
|
|||
|
||||
<!-- Terminal Container -->
|
||||
<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"
|
||||
style="max-width: 100vw; height: 100%;"
|
||||
>
|
||||
|
|
@ -927,11 +915,11 @@ export class SessionView extends LitElement {
|
|||
? html`
|
||||
<!-- Loading overlay -->
|
||||
<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-sm text-vs-muted">Connecting to session...</div>
|
||||
<div class="text-sm text-dark-text-muted">Connecting to session...</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
|
@ -954,70 +942,26 @@ export class SessionView extends LitElement {
|
|||
<!-- First row: Arrow keys -->
|
||||
<div class="flex gap-2 mb-2">
|
||||
<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')}
|
||||
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>
|
||||
</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')}
|
||||
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>
|
||||
</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')}
|
||||
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>
|
||||
</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')}
|
||||
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>
|
||||
</button>
|
||||
|
|
@ -1026,87 +970,32 @@ export class SessionView extends LitElement {
|
|||
<!-- Second row: Special keys -->
|
||||
<div class="flex gap-2">
|
||||
<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')}
|
||||
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
|
||||
</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')}
|
||||
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>
|
||||
</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}
|
||||
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
|
||||
</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}
|
||||
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
|
||||
</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')}
|
||||
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>
|
||||
</button>
|
||||
|
|
@ -1167,63 +1056,22 @@ export class SessionView extends LitElement {
|
|||
<!-- Controls -->
|
||||
<div class="p-4 flex gap-2" style="border-top: 1px solid #444;">
|
||||
<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)}
|
||||
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
|
||||
</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}
|
||||
?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
|
||||
</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}
|
||||
?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 + ⏎
|
||||
</button>
|
||||
|
|
@ -1297,19 +1145,8 @@ export class SessionView extends LitElement {
|
|||
].map(
|
||||
(letter) => html`
|
||||
<button
|
||||
class="font-mono text-xs transition-all cursor-pointer aspect-square flex items-center justify-center"
|
||||
style="background: rgba(0, 0, 0, 0.8); color: #d4d4d4; border: 1px solid #444; border-radius: 4px;"
|
||||
class="font-mono text-xs transition-all cursor-pointer aspect-square flex items-center justify-center quick-start-btn"
|
||||
@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}
|
||||
</button>
|
||||
|
|
@ -1325,55 +1162,22 @@ export class SessionView extends LitElement {
|
|||
<!-- Action buttons -->
|
||||
<div class="flex gap-2 justify-center">
|
||||
<button
|
||||
class="font-mono px-4 py-2 text-sm transition-all cursor-pointer"
|
||||
style="background: black; color: #d4d4d4; border: 1px solid #888; border-radius: 4px;"
|
||||
class="font-mono px-4 py-2 text-sm transition-all cursor-pointer btn-ghost"
|
||||
@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
|
||||
</button>
|
||||
${this.ctrlSequence.length > 0
|
||||
? html`
|
||||
<button
|
||||
class="font-mono px-3 py-2 text-sm transition-all cursor-pointer"
|
||||
style="background: black; color: #d4d4d4; border: 1px solid #888; border-radius: 4px;"
|
||||
class="font-mono px-3 py-2 text-sm transition-all cursor-pointer btn-ghost"
|
||||
@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
|
||||
</button>
|
||||
<button
|
||||
class="font-mono px-3 py-2 text-sm transition-all cursor-pointer"
|
||||
style="background: black; color: #d4d4d4; border: 1px solid #569cd6; border-radius: 4px;"
|
||||
class="font-mono px-3 py-2 text-sm transition-all cursor-pointer btn-secondary"
|
||||
@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
|
||||
</button>
|
||||
|
|
|
|||
39
web/src/client/components/terminal-icon.ts
Normal file
39
web/src/client/components/terminal-icon.ts
Normal 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>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,93 @@
|
|||
@tailwind components;
|
||||
@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 */
|
||||
@font-face {
|
||||
font-family: 'Fira Code';
|
||||
|
|
@ -112,9 +199,10 @@ body {
|
|||
|
||||
/* Terminal container styling */
|
||||
.terminal-container {
|
||||
color: #d4d4d4;
|
||||
color: #e4e4e4;
|
||||
white-space: pre;
|
||||
overflow: hidden;
|
||||
background-color: #0a0a0a;
|
||||
}
|
||||
|
||||
/* Terminal line styling */
|
||||
|
|
@ -196,8 +284,8 @@ body {
|
|||
|
||||
/* Terminal focus indicator */
|
||||
.terminal-focused {
|
||||
box-shadow: 0 0 0 2px #00ff00;
|
||||
border-color: #00ff00 !important;
|
||||
box-shadow: 0 0 0 2px #00ff88;
|
||||
border-color: #00ff88 !important;
|
||||
}
|
||||
|
||||
/* Keyboard capture indicator */
|
||||
|
|
@ -205,9 +293,9 @@ body {
|
|||
position: fixed;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
background: rgba(0, 255, 0, 0.1);
|
||||
border: 1px solid #00ff00;
|
||||
color: #00ff00;
|
||||
background: rgba(0, 255, 136, 0.1);
|
||||
border: 1px solid #00ff88;
|
||||
color: #00ff88;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
|
|
@ -517,14 +605,14 @@ body {
|
|||
left: 12px;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
border: 1px solid #444;
|
||||
background: rgba(26, 26, 26, 0.9);
|
||||
border: 1px solid #2a2a2a;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
color: #d4d4d4;
|
||||
color: #e4e4e4;
|
||||
font-size: 24px;
|
||||
transition: all 0.2s ease;
|
||||
user-select: none;
|
||||
|
|
@ -532,9 +620,11 @@ body {
|
|||
}
|
||||
|
||||
.scroll-to-bottom:hover {
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
border-color: #666;
|
||||
background: rgba(26, 26, 26, 1);
|
||||
border-color: #00ff88;
|
||||
color: #00ff88;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 0 10px rgba(0, 255, 136, 0.3);
|
||||
}
|
||||
|
||||
.scroll-to-bottom:active {
|
||||
|
|
@ -546,13 +636,13 @@ body {
|
|||
position: absolute;
|
||||
bottom: 8px;
|
||||
right: 8px;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
border: 1px solid #444;
|
||||
background: rgba(26, 26, 26, 0.9);
|
||||
border: 1px solid #2a2a2a;
|
||||
border-radius: 4px;
|
||||
padding: 8px 12px;
|
||||
font-family: 'Fira Code', monospace;
|
||||
font-size: 11px;
|
||||
color: #d4d4d4;
|
||||
color: #e4e4e4;
|
||||
user-select: none;
|
||||
z-index: 10;
|
||||
line-height: 1.4;
|
||||
|
|
@ -580,14 +670,14 @@ body {
|
|||
right: 12px;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
border: 1px solid #444;
|
||||
background: rgba(26, 26, 26, 0.9);
|
||||
border: 1px solid #2a2a2a;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
color: #d4d4d4;
|
||||
color: #e4e4e4;
|
||||
font-size: 20px;
|
||||
transition: all 0.2s ease;
|
||||
user-select: none;
|
||||
|
|
@ -595,9 +685,11 @@ body {
|
|||
}
|
||||
|
||||
.fit-toggle:hover {
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
border-color: #666;
|
||||
background: rgba(26, 26, 26, 1);
|
||||
border-color: #00ff88;
|
||||
color: #00ff88;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 0 10px rgba(0, 255, 136, 0.3);
|
||||
}
|
||||
|
||||
.fit-toggle:active {
|
||||
|
|
@ -605,6 +697,7 @@ body {
|
|||
}
|
||||
|
||||
.fit-toggle.active {
|
||||
border-color: #569cd6;
|
||||
color: #569cd6;
|
||||
border-color: #00ff88;
|
||||
color: #00ff88;
|
||||
box-shadow: 0 0 10px rgba(0, 255, 136, 0.3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,24 +4,72 @@ module.exports = {
|
|||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
"vs-bg": "#1e1e1e",
|
||||
"vs-text": "#d4d4d4",
|
||||
"vs-muted": "#8c8c8c",
|
||||
"vs-accent": "#569cd6",
|
||||
"vs-user": "#6a9955",
|
||||
"vs-assistant": "#ce9178",
|
||||
"vs-warning": "#f48771",
|
||||
"vs-function": "#dcdcaa",
|
||||
"vs-type": "#4ec9b0",
|
||||
"vs-border": "#3e3e42",
|
||||
"vs-border-light": "#5a5a5e",
|
||||
"vs-bg-secondary": "#2d2d30",
|
||||
"vs-nav": "#3e3e42",
|
||||
"vs-nav-hover": "#4a4a4e",
|
||||
"vs-nav-active": "#f48771",
|
||||
// Dark theme colors
|
||||
"dark-bg": "#0a0a0a",
|
||||
"dark-bg-secondary": "#1a1a1a",
|
||||
"dark-bg-tertiary": "#242424",
|
||||
"dark-border": "#2a2a2a",
|
||||
"dark-border-light": "#3a3a3a",
|
||||
|
||||
// Text colors
|
||||
"dark-text": "#e4e4e4",
|
||||
"dark-text-muted": "#7a7a7a",
|
||||
"dark-text-dim": "#5a5a5a",
|
||||
|
||||
// Green accent colors (multiple shades)
|
||||
"accent-green": "#00ff88",
|
||||
"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",
|
||||
},
|
||||
},
|
||||
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: [],
|
||||
};
|
||||
Loading…
Reference in a new issue