Tailwind 4 migration

This commit is contained in:
Peter Steinberger 2025-07-30 13:04:30 +02:00
parent 5611b58789
commit 78bd4d79e9
31 changed files with 906 additions and 561 deletions

View file

@ -77,6 +77,8 @@
"test:e2e:parallel": "PLAYWRIGHT_PARALLEL=true playwright test",
"test:e2e:parallel:headed": "PLAYWRIGHT_PARALLEL=true playwright test --headed",
"test:e2e:parallel:workers": "PLAYWRIGHT_PARALLEL=true PLAYWRIGHT_WORKERS=4 playwright test",
"test:e2e:fast": "./scripts/test-fast.sh",
"test:e2e:perf": "playwright test --config=playwright.config.performance.ts",
"prepare": "husky"
},
"pnpm": {
@ -123,6 +125,7 @@
"@open-wc/testing": "^4.0.0",
"@playwright/test": "^1.54.1",
"@prettier/plugin-oxc": "^0.0.4",
"@tailwindcss/postcss": "^4.1.11",
"@testing-library/dom": "^10.4.1",
"@types/compression": "^1.8.1",
"@types/express": "^5.0.3",
@ -146,10 +149,11 @@
"lint-staged": "^16.1.2",
"node-fetch": "^3.3.2",
"postcss": "^8.5.6",
"postcss-cli": "^11.0.1",
"prettier": "^3.6.2",
"puppeteer": "^24.15.0",
"supertest": "^7.1.4",
"tailwindcss": "^3.4.17",
"tailwindcss": "^4.1.11",
"tsx": "^4.20.3",
"typescript": "^5.8.3",
"uuid": "^11.1.0",

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,5 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
'@tailwindcss/postcss': {},
},
}

View file

@ -14,7 +14,7 @@ execSync('node scripts/copy-assets.js', { stdio: 'inherit' });
// Build CSS
console.log('Building CSS...');
execSync('pnpm exec tailwindcss -i ./src/client/styles.css -o ./public/bundle/styles.css --minify', { stdio: 'inherit' });
execSync('pnpm exec postcss ./src/client/styles.css -o ./public/bundle/styles.css', { stdio: 'inherit' });
// Bundle client JavaScript
console.log('Bundling client JavaScript...');

View file

@ -22,7 +22,7 @@ async function build() {
// Build CSS
console.log('Building CSS...');
execSync('pnpm exec tailwindcss -i ./src/client/styles.css -o ./public/bundle/styles.css --minify', { stdio: 'inherit' });
execSync('pnpm exec postcss ./src/client/styles.css -o ./public/bundle/styles.css', { stdio: 'inherit' });
// Bundle client JavaScript
console.log('Bundling client JavaScript...');

View file

@ -109,12 +109,12 @@ for (let i = 0; i < allArgs.length; i++) {
console.log('Initial build...');
require('child_process').execSync('node scripts/ensure-dirs.js', { stdio: 'inherit' });
require('child_process').execSync('node scripts/copy-assets.js', { stdio: 'inherit' });
require('child_process').execSync('pnpm exec tailwindcss -i ./src/client/styles.css -o ./public/bundle/styles.css', { stdio: 'inherit' });
require('child_process').execSync('pnpm exec postcss ./src/client/styles.css -o ./public/bundle/styles.css', { stdio: 'inherit' });
// Build the command parts
const commands = [
// Watch CSS
['pnpm', ['exec', 'tailwindcss', '-i', './src/client/styles.css', '-o', './public/bundle/styles.css', '--watch']],
['pnpm', ['exec', 'postcss', './src/client/styles.css', '-o', './public/bundle/styles.css', '--watch']],
// Watch assets
['pnpm', ['exec', 'chokidar', 'src/client/assets/**/*', '-c', 'node scripts/copy-assets.js']],
];

View file

@ -1757,7 +1757,7 @@ export class VibeTunnelApp extends LitElement {
<div
class="fixed inset-0 sm:hidden transition-all ${
this.isInSidebarDismissMode
? 'bg-bg bg-opacity-50 backdrop-blur-sm'
? 'bg-bg/50 backdrop-blur-sm'
: 'bg-transparent pointer-events-none'
}"
style="z-index: ${Z_INDEX.MOBILE_OVERLAY}; transition-duration: ${TRANSITIONS.MOBILE_SLIDE}ms;"

View file

@ -162,7 +162,7 @@ export class AuthLogin extends LitElement {
<div class="auth-container">
<!-- Settings button in top right corner -->
<button
class="absolute top-4 right-4 p-2 text-muted hover:text-primary transition-colors"
class="absolute top-4 right-4 p-2 text-text-muted hover:text-primary transition-colors"
@click=${this.handleOpenSettings}
title="Settings"
>
@ -187,7 +187,7 @@ export class AuthLogin extends LitElement {
this.error
? html`
<div
class="bg-status-error text-base px-3 py-1.5 rounded mb-3 font-mono text-xs sm:text-sm"
class="bg-status-error text-bg px-3 py-1.5 rounded mb-3 font-mono text-xs sm:text-sm"
data-testid="error-message"
>
${this.error}
@ -195,7 +195,7 @@ export class AuthLogin extends LitElement {
@click=${() => {
this.error = '';
}}
class="ml-2 text-base hover:text-primary"
class="ml-2 text-bg hover:text-primary"
data-testid="error-close"
>
@ -208,14 +208,14 @@ export class AuthLogin extends LitElement {
this.success
? html`
<div
class="bg-status-success text-base px-3 py-1.5 rounded mb-3 font-mono text-xs sm:text-sm"
class="bg-status-success text-bg px-3 py-1.5 rounded mb-3 font-mono text-xs sm:text-sm"
>
${this.success}
<button
@click=${() => {
this.success = '';
}}
class="ml-2 text-base hover:text-primary"
class="ml-2 text-bg hover:text-primary"
>
</button>
@ -248,10 +248,10 @@ export class AuthLogin extends LitElement {
`
: html`
<div
class="w-full h-full bg-secondary flex items-center justify-center"
class="w-full h-full bg-bg-secondary flex items-center justify-center"
>
<svg
class="w-12 h-12 sm:w-14 sm:h-14 text-muted"
class="w-12 h-12 sm:w-14 sm:h-14 text-text-muted"
fill="currentColor"
viewBox="0 0 20 20"
>
@ -300,7 +300,7 @@ export class AuthLogin extends LitElement {
<div class="ssh-key-item p-6 sm:p-8">
<div class="flex flex-col items-center mb-4 sm:mb-6">
<div
class="w-16 h-16 sm:w-20 sm:h-20 rounded-full mb-2 sm:mb-3 overflow-hidden border-2 border-base"
class="w-16 h-16 sm:w-20 sm:h-20 rounded-full mb-2 sm:mb-3 overflow-hidden border-2 border-border"
>
${
this.userAvatar
@ -315,10 +315,10 @@ export class AuthLogin extends LitElement {
`
: html`
<div
class="w-full h-full bg-secondary flex items-center justify-center"
class="w-full h-full bg-bg-secondary flex items-center justify-center"
>
<svg
class="w-8 h-8 sm:w-10 sm:h-10 text-muted"
class="w-8 h-8 sm:w-10 sm:h-10 text-text-muted"
fill="currentColor"
viewBox="0 0 20 20"
>
@ -335,7 +335,7 @@ export class AuthLogin extends LitElement {
: 'Please authenticate to continue'
}
</p>
<p class="text-muted text-xs mt-1 sm:mt-2">
<p class="text-text-muted text-xs mt-1 sm:mt-2">
SSH key authentication required
</p>
</div>
@ -374,11 +374,11 @@ export class AuthLogin extends LitElement {
</div>
<div class="space-y-3">
<div class="bg-base border border-base rounded p-3">
<p class="text-muted text-xs mb-2">
<div class="bg-bg border border-border rounded p-3">
<p class="text-text-muted text-xs mb-2">
Generate SSH keys for browser-based authentication
</p>
<p class="text-muted text-xs">
<p class="text-text-muted text-xs">
💡 SSH keys work in both browser and terminal
</p>
</div>

View file

@ -33,13 +33,13 @@ export class AuthQuickKeys extends LitElement {
render() {
return html`
<div class="quick-keys-bar bg-secondary border-t border-base p-2">
<div class="quick-keys-bar bg-bg-secondary border-t border-border p-2">
<div class="flex gap-1 overflow-x-auto scrollbar-hide">
${QUICK_KEYS.map(
({ key, label }) => html`
<button
type="button"
class="quick-key-btn px-3 py-1.5 bg-tertiary text-primary text-xs font-mono rounded border border-base hover:bg-surface hover:border-primary transition-all whitespace-nowrap flex-shrink-0"
class="quick-key-btn px-3 py-1.5 bg-bg-tertiary text-primary text-xs font-mono rounded border border-border hover:bg-surface hover:border-primary transition-all whitespace-nowrap flex-shrink-0"
@click=${() => this.handleKeyPress(key)}
>
${label}

View file

@ -405,7 +405,7 @@ export class FileBrowser extends LitElement {
private renderPreview() {
if (this.previewLoading) {
return html`
<div class="flex items-center justify-center h-full text-muted">
<div class="flex items-center justify-center h-full text-text-muted">
Loading preview...
</div>
`;
@ -417,7 +417,7 @@ export class FileBrowser extends LitElement {
if (!this.preview) {
return html`
<div class="flex flex-col items-center justify-center h-full text-muted">
<div class="flex flex-col items-center justify-center h-full text-text-muted">
${UIIcons.preview}
<div>Select a file to preview</div>
</div>
@ -451,11 +451,11 @@ export class FileBrowser extends LitElement {
case 'binary':
return html`
<div class="flex flex-col items-center justify-center h-full text-muted">
<div class="flex flex-col items-center justify-center h-full text-text-muted">
${UIIcons.binary}
<div class="text-lg mb-2">Binary File</div>
<div class="text-sm">${this.preview.humanSize || `${this.preview.size} bytes`}</div>
<div class="text-sm text-muted mt-2">
<div class="text-sm text-text-muted mt-2">
${this.preview.mimeType || 'Unknown type'}
</div>
</div>
@ -467,7 +467,7 @@ export class FileBrowser extends LitElement {
// For new files (added or untracked), we might not have a diff but we have diffContent
if (!this.diffContent && (!this.diff || !this.diff.diff)) {
return html`
<div class="flex items-center justify-center h-full text-muted">
<div class="flex items-center justify-center h-full text-text-muted">
No changes in this file
</div>
`;
@ -496,7 +496,7 @@ export class FileBrowser extends LitElement {
return html`
<div class="overflow-auto h-full p-4 font-mono text-xs">
${lines.map((line) => {
let className = 'text-muted';
let className = 'text-text-muted';
if (line.startsWith('+')) className = 'text-status-success bg-status-success/10';
else if (line.startsWith('-')) className = 'text-status-error bg-status-error/10';
else if (line.startsWith('@@')) className = 'text-status-info font-semibold';
@ -518,7 +518,7 @@ export class FileBrowser extends LitElement {
${
this.isMobile && this.mobileView === 'preview'
? html`
<div class="absolute top-1/2 left-2 -translate-y-1/2 text-muted opacity-50">
<div class="absolute top-1/2 left-2 -translate-y-1/2 text-text-muted opacity-50">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
@ -537,12 +537,12 @@ export class FileBrowser extends LitElement {
>
<!-- Compact Header (like session-view) -->
<div
class="flex items-center justify-between px-3 py-2 border-b border-border/50 text-sm min-w-0 bg-secondary"
class="flex items-center justify-between px-3 py-2 border-b border-border/50 text-sm min-w-0 bg-bg-secondary"
style="padding-top: max(0.5rem, env(safe-area-inset-top)); padding-left: max(0.75rem, env(safe-area-inset-left)); padding-right: max(0.75rem, env(safe-area-inset-right));"
>
<div class="flex items-center gap-3 min-w-0 flex-1">
<button
class="text-muted hover:text-primary font-mono text-xs px-2 py-1 flex-shrink-0 transition-colors flex items-center gap-1"
class="text-text-muted hover:text-primary font-mono text-xs px-2 py-1 flex-shrink-0 transition-colors flex items-center gap-1"
@click=${this.handleCancel}
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@ -585,7 +585,7 @@ export class FileBrowser extends LitElement {
${
this.gitStatus?.branch
? html`
<span class="text-muted text-xs flex items-center gap-1 font-mono flex-shrink-0">
<span class="text-text-muted text-xs flex items-center gap-1 font-mono flex-shrink-0">
${UIIcons.git} ${this.gitStatus.branch}
</span>
`
@ -614,16 +614,16 @@ export class FileBrowser extends LitElement {
<div
class="${this.isMobile && this.mobileView === 'preview' ? 'hidden' : ''} ${
this.isMobile ? 'w-full' : 'w-80'
} bg-secondary border-r border-border/50 flex flex-col"
} bg-bg-secondary border-r border-border/50 flex flex-col"
>
<!-- File list header with toggles -->
<div
class="bg-secondary border-b border-border/50 p-3 flex items-center justify-between"
class="bg-bg-secondary border-b border-border/50 p-3 flex items-center justify-between"
>
<div class="flex gap-2">
<button
class="btn-secondary text-xs px-2 py-1 font-mono ${
this.gitFilter === 'changed' ? 'bg-primary text-text-bright' : ''
this.gitFilter === 'changed' ? 'bg-primary text-bg' : ''
}"
@click=${this.toggleGitFilter}
title="Show only Git changes"
@ -632,7 +632,7 @@ export class FileBrowser extends LitElement {
</button>
<button
class="btn-secondary text-xs px-2 py-1 font-mono ${
this.showHidden ? 'bg-primary text-text-bright' : ''
this.showHidden ? 'bg-primary text-bg' : ''
}"
@click=${this.toggleHidden}
title="Show hidden files"
@ -649,7 +649,7 @@ export class FileBrowser extends LitElement {
${
this.loading
? html`
<div class="flex items-center justify-center h-full text-muted">
<div class="flex items-center justify-center h-full text-text-muted">
Loading...
</div>
`
@ -662,7 +662,7 @@ export class FileBrowser extends LitElement {
@click=${this.handleParentClick}
>
${getParentDirectoryIcon()}
<span class="text-muted">..</span>
<span class="text-text-muted">..</span>
</div>
`
: ''
@ -684,7 +684,7 @@ export class FileBrowser extends LitElement {
file.isSymlink
? html`
<svg
class="w-3 h-3 text-muted absolute -bottom-1 -right-1"
class="w-3 h-3 text-text-muted absolute -bottom-1 -right-1">
fill="currentColor"
viewBox="0 0 20 20"
>
@ -726,7 +726,7 @@ export class FileBrowser extends LitElement {
this.selectedFile
? html`
<div
class="bg-secondary border-b border-border/50 p-3 ${
class="bg-bg-secondary border-b border-border/50 p-3 ${
this.isMobile ? 'space-y-2' : 'flex items-center justify-between'
}"
>
@ -738,7 +738,7 @@ export class FileBrowser extends LitElement {
@click=${() => {
this.mobileView = 'list';
}}
class="text-muted hover:text-primary transition-colors flex-shrink-0"
class="text-text-muted hover:text-primary transition-colors flex-shrink-0"
title="Back to files"
>
<svg
@ -821,7 +821,7 @@ export class FileBrowser extends LitElement {
? html`
<button
class="btn-secondary text-xs px-2 py-1 font-mono ${
this.showDiff ? 'bg-primary text-text-bright' : ''
this.showDiff ? 'bg-primary text-bg' : ''
} ${
this.isMobile &&
this.selectedFile.type === 'file' &&

View file

@ -247,7 +247,7 @@ export class FilePicker extends LitElement {
}
return html`
<div class="fixed inset-0 bg-bg bg-opacity-80 backdrop-blur-sm flex items-center justify-center animate-fade-in" style="z-index: ${Z_INDEX.FILE_PICKER};" @click=${this.handleCancel}>
<div class="fixed inset-0 bg-bg/80 backdrop-blur-sm flex items-center justify-center animate-fade-in" style="z-index: ${Z_INDEX.FILE_PICKER};" @click=${this.handleCancel}>
<div class="bg-elevated border border-border/50 rounded-xl shadow-2xl p-8 m-4 max-w-sm w-full animate-scale-in" @click=${(e: Event) => e.stopPropagation()}>
<h3 class="text-xl font-bold text-primary mb-6">
Select File
@ -258,10 +258,10 @@ export class FilePicker extends LitElement {
? html`
<div class="mb-6">
<div class="flex items-center justify-between mb-3">
<span class="text-sm text-muted font-mono">Uploading...</span>
<span class="text-sm text-text-muted font-mono">Uploading...</span>
<span class="text-sm text-primary font-mono font-medium">${Math.round(this.uploadProgress)}%</span>
</div>
<div class="w-full bg-secondary rounded-full h-2 overflow-hidden">
<div class="w-full bg-bg-secondary rounded-full h-2 overflow-hidden">
<div
class="bg-gradient-to-r from-primary to-primary-light h-2 rounded-full transition-all duration-300 shadow-glow-sm"
style="width: ${this.uploadProgress}%"
@ -289,7 +289,7 @@ export class FilePicker extends LitElement {
<button
id="file-picker-cancel-button"
@click=${this.handleCancel}
class="w-full bg-secondary border border-border/50 text-primary font-mono py-3 px-6 rounded-lg transition-all duration-200 hover:bg-surface hover:border-primary active:scale-95"
class="w-full bg-bg-secondary border border-border/50 text-primary font-mono py-3 px-6 rounded-lg transition-all duration-200 hover:bg-surface hover:border-primary active:scale-95"
?disabled=${this.uploading}
>
Cancel

View file

@ -282,7 +282,7 @@ export class LogViewer extends LitElement {
if (this.loading) {
return html`
<div class="flex items-center justify-center h-screen bg-base text-primary">
<div class="flex items-center justify-center h-screen bg-bg text-primary">
<div class="text-center">
<div
class="animate-spin rounded-full h-12 w-12 border-4 border-primary border-t-transparent mb-4 mx-auto"
@ -297,16 +297,16 @@ export class LogViewer extends LitElement {
return html`
${scrollbarStyles}
<div class="flex flex-col h-full bg-base text-primary font-mono">
<div class="flex flex-col h-full bg-bg text-primary font-mono">
<!-- Header - single row on desktop, two rows on mobile -->
<div class="bg-secondary border-b border-border/50 p-3 sm:p-4">
<div class="bg-bg-secondary border-b border-border/50 p-3 sm:p-4">
<!-- Mobile layout (two rows) -->
<div class="sm:hidden">
<!-- Top row with back button and title -->
<div class="flex items-center gap-2 mb-3">
<!-- Back button -->
<button
class="p-2 bg-base border border-border/50 rounded text-sm text-primary hover:border-primary hover:text-primary transition-colors flex items-center gap-1 flex-shrink-0"
class="p-2 bg-bg border border-border/50 rounded text-sm text-primary hover:border-primary hover:text-primary transition-colors flex items-center gap-1 flex-shrink-0"
@click=${() => {
window.location.href = '/';
}}
@ -335,8 +335,8 @@ export class LogViewer extends LitElement {
<button
class="p-2 text-xs uppercase font-bold rounded transition-colors ${
this.autoScroll
? 'bg-primary text-base'
: 'bg-tertiary text-muted border border-border/50'
? 'bg-primary text-bg'
: 'bg-bg-tertiary text-text-muted border border-border/50'
}"
@click=${() => {
this.autoScroll = !this.autoScroll;
@ -362,7 +362,7 @@ export class LogViewer extends LitElement {
<!-- Search input -->
<input
type="text"
class="px-3 py-1.5 bg-base border border-border/50 rounded text-sm text-primary placeholder-muted focus:outline-none focus:border-primary transition-colors w-full"
class="px-3 py-1.5 bg-bg border border-border/50 rounded text-sm text-primary placeholder-text-muted focus:outline-none focus:border-primary transition-colors w-full"
placeholder="Filter logs..."
.value=${this.filter}
@input=${(e: Event) => {
@ -380,12 +380,12 @@ export class LogViewer extends LitElement {
class="px-1.5 py-1 text-xs uppercase font-bold rounded transition-colors ${
this.levelFilter.has(level)
? level === 'error'
? 'bg-status-error bg-opacity-20 text-status-error border border-status-error'
? 'bg-status-error/20 text-status-error border border-status-error'
: level === 'warn'
? 'bg-status-warning bg-opacity-20 text-status-warning border border-status-warning'
? 'bg-status-warning/20 text-status-warning border border-status-warning'
: level === 'debug'
? 'bg-bg-tertiary text-text-muted border border-border'
: 'bg-primary bg-opacity-20 text-primary border border-primary'
: 'bg-primary/20 text-primary border border-primary'
: 'bg-bg-tertiary text-text-muted border border-border'
}"
@click=${() => this.toggleLevel(level)}
@ -410,7 +410,7 @@ export class LogViewer extends LitElement {
<button
class="px-1.5 py-1 text-xs uppercase font-bold rounded transition-colors ${
this.showClient
? 'bg-status-warning bg-opacity-20 text-status-warning border border-status-warning'
? 'bg-status-warning/20 text-status-warning border border-status-warning'
: 'bg-bg-tertiary text-text-muted border border-border'
}"
@click=${() => {
@ -423,7 +423,7 @@ export class LogViewer extends LitElement {
<button
class="px-1.5 py-1 text-xs uppercase font-bold rounded transition-colors ${
this.showServer
? 'bg-primary bg-opacity-20 text-primary border border-primary'
? 'bg-primary/20 text-primary border border-primary'
: 'bg-bg-tertiary text-text-muted border border-border'
}"
@click=${() => {
@ -485,12 +485,12 @@ export class LogViewer extends LitElement {
class="px-2 py-1 text-xs uppercase font-bold rounded transition-colors ${
this.levelFilter.has(level)
? level === 'error'
? 'bg-status-error bg-opacity-20 text-status-error border border-status-error'
? 'bg-status-error/20 text-status-error border border-status-error'
: level === 'warn'
? 'bg-status-warning bg-opacity-20 text-status-warning border border-status-warning'
? 'bg-status-warning/20 text-status-warning border border-status-warning'
: level === 'debug'
? 'bg-bg-tertiary text-text-muted border border-border'
: 'bg-primary bg-opacity-20 text-primary border border-primary'
: 'bg-primary/20 text-primary border border-primary'
: 'bg-bg-tertiary text-text-muted border border-border'
}"
@click=${() => this.toggleLevel(level)}
@ -506,7 +506,7 @@ export class LogViewer extends LitElement {
<button
class="px-2 py-1 text-xs uppercase font-bold rounded transition-colors ${
this.showClient
? 'bg-status-warning bg-opacity-20 text-status-warning border border-status-warning'
? 'bg-status-warning/20 text-status-warning border border-status-warning'
: 'bg-bg-tertiary text-text-muted border border-border'
}"
@click=${() => {
@ -518,7 +518,7 @@ export class LogViewer extends LitElement {
<button
class="px-2 py-1 text-xs uppercase font-bold rounded transition-colors ${
this.showServer
? 'bg-primary bg-opacity-20 text-primary border border-primary'
? 'bg-primary/20 text-primary border border-primary'
: 'bg-bg-tertiary text-text-muted border border-border'
}"
@click=${() => {
@ -533,7 +533,7 @@ export class LogViewer extends LitElement {
<button
class="px-3 py-1 text-xs uppercase font-bold rounded transition-colors ${
this.autoScroll
? 'bg-primary bg-opacity-20 text-primary border border-primary'
? 'bg-primary/20 text-primary border border-primary'
: 'bg-bg-tertiary text-text-muted border border-border'
}"
@click=${() => {

View file

@ -373,7 +373,7 @@ export class SessionCard extends LitElement {
>
<!-- Compact Header -->
<div
class="flex justify-between items-center px-3 py-2 border-b border-base bg-gradient-to-r from-secondary to-tertiary"
class="flex justify-between items-center px-3 py-2 border-b border-border bg-gradient-to-r from-bg-secondary to-bg-tertiary"
>
<div class="text-xs font-mono pr-2 flex-1 min-w-0 text-primary">
<div class="flex items-center gap-2">
@ -421,8 +421,8 @@ export class SessionCard extends LitElement {
<button
class="p-1 rounded-full transition-all duration-200 disabled:opacity-50 flex-shrink-0 ${
this.session.status === 'running'
? 'text-status-error hover:bg-status-error hover:bg-opacity-20'
: 'text-status-warning hover:bg-status-warning hover:bg-opacity-20'
? 'text-status-error hover:bg-status-error/20'
: 'text-status-warning hover:bg-status-warning/20'
}"
@click=${this.handleKillClick}
?disabled=${this.killing}
@ -491,7 +491,7 @@ export class SessionCard extends LitElement {
<!-- Compact Footer -->
<div
class="px-3 py-2 text-muted text-xs border-t border-base bg-gradient-to-r from-tertiary to-secondary"
class="px-3 py-2 text-text-muted text-xs border-t border-border bg-gradient-to-r from-bg-tertiary to-bg-secondary"
>
<div class="flex justify-between items-center min-w-0">
<span
@ -601,7 +601,7 @@ export class SessionCard extends LitElement {
return 'text-status-error';
}
if (this.session.active === false) {
return 'text-muted';
return 'text-text-muted';
}
if (this.session.status === 'running' && this.session.activityStatus?.specificStatus) {
return 'text-status-warning';
@ -622,7 +622,7 @@ export class SessionCard extends LitElement {
} else if (this.session.activityStatus?.isActive || this.isActive) {
return 'bg-status-success'; // Generic active - solid green
} else {
return 'bg-status-success ring-1 ring-status-success ring-opacity-50'; // Idle - green with ring
return 'bg-status-success ring-1 ring-status-success/50'; // Idle - green with ring
}
}
return 'bg-status-warning';

View file

@ -99,7 +99,7 @@ export class QuickStartSection extends LitElement {
@click=${() => this.handleQuickStartClick(command)}
class="${
this.selectedCommand === command
? 'px-2 py-1.5 sm:px-3 sm:py-2 lg:px-4 lg:py-3 rounded-lg border text-left transition-all bg-primary bg-opacity-10 border-primary/50 text-primary hover:bg-opacity-20 font-medium text-[10px] sm:text-xs lg:text-sm'
? 'px-2 py-1.5 sm:px-3 sm:py-2 lg:px-4 lg:py-3 rounded-lg border text-left transition-all bg-primary/10 border-primary/50 text-primary hover:bg-primary/20 font-medium text-[10px] sm:text-xs lg:text-sm'
: 'px-2 py-1.5 sm:px-3 sm:py-2 lg:px-4 lg:py-3 rounded-lg border text-left transition-all bg-bg-elevated border-border/50 text-text hover:bg-hover hover:border-primary/50 hover:text-primary text-[10px] sm:text-xs lg:text-sm'
}"
?disabled=${this.disabled || this.isCreating}

View file

@ -1102,7 +1102,7 @@ export class SessionList extends LitElement {
!this.hideExited && exitedSessions.length > 0
? html`
<button
class="font-mono text-xs px-3 py-1.5 rounded-md border transition-all duration-200 border-status-warning bg-status-warning bg-opacity-10 text-status-warning hover:bg-opacity-20 hover:shadow-glow-warning-sm active:scale-95 disabled:opacity-50"
class="font-mono text-xs px-3 py-1.5 rounded-md border transition-all duration-200 border-status-warning bg-status-warning/10 text-status-warning hover:bg-status-warning/20 hover:shadow-glow-warning-sm active:scale-95 disabled:opacity-50"
id="clean-exited-button"
@click=${this.handleCleanupExited}
?disabled=${this.cleaningExited}
@ -1128,7 +1128,7 @@ export class SessionList extends LitElement {
runningSessions.length > 0
? html`
<button
class="font-mono text-xs px-3 py-1.5 rounded-md border transition-all duration-200 border-status-error bg-status-error bg-opacity-10 text-status-error hover:bg-opacity-20 hover:shadow-glow-error-sm active:scale-95"
class="font-mono text-xs px-3 py-1.5 rounded-md border transition-all duration-200 border-status-error bg-status-error/10 text-status-error hover:bg-status-error/20 hover:shadow-glow-error-sm active:scale-95"
id="kill-all-button"
@click=${() => this.dispatchEvent(new CustomEvent('kill-all-sessions'))}
data-testid="kill-all-button"

View file

@ -72,7 +72,7 @@ export class CompactSessionCard extends LitElement {
if (session.activityStatus?.isActive === false) {
// Idle
return html`<div class="w-2.5 h-2.5 rounded-full bg-status-success ring-1 ring-status-success ring-opacity-50"></div>`;
return html`<div class="w-2.5 h-2.5 rounded-full bg-status-success ring-1 ring-status-success/50"></div>`;
}
// Active

View file

@ -955,10 +955,10 @@ export class SessionView extends LitElement {
render() {
if (!this.session) {
return html`
<div class="fixed inset-0 bg-base flex items-center justify-center">
<div class="fixed inset-0 bg-bg flex items-center justify-center">
<div class="text-primary font-mono text-center">
<div class="text-2xl mb-2">${this.loadingAnimationManager.getLoadingText()}</div>
<div class="text-sm text-muted">Waiting for session...</div>
<div class="text-sm text-text-muted">Waiting for session...</div>
</div>
</div>
`;
@ -1130,11 +1130,11 @@ export class SessionView extends LitElement {
? html`
<!-- Enhanced Loading overlay -->
<div
class="absolute inset-0 bg-bg bg-opacity-90 backdrop-filter backdrop-blur-sm flex items-center justify-center z-10 animate-fade-in"
class="absolute inset-0 bg-bg/90 backdrop-filter backdrop-blur-sm flex items-center justify-center z-10 animate-fade-in"
>
<div class="text-primary font-mono text-center">
<div class="text-2xl mb-3 text-primary animate-pulse-primary">${this.loadingAnimationManager.getLoadingText()}</div>
<div class="text-sm text-muted">Connecting to session...</div>
<div class="text-sm text-text-muted">Connecting to session...</div>
</div>
</div>
`
@ -1179,7 +1179,7 @@ export class SessionView extends LitElement {
${
uiState.isMobile && !uiState.showMobileInput && !uiState.useDirectKeyboard
? html`
<div class="p-4 bg-secondary">
<div class="p-4 bg-bg-secondary">
<!-- First row: Arrow keys -->
<div class="flex gap-2 mb-2">
<button

View file

@ -214,7 +214,7 @@ export class CompactMenu extends LitElement {
return html`
<div class="relative w-[44px] flex-shrink-0">
<button
class="p-2 ${this.showMenu ? 'text-primary border-primary' : 'text-primary border-base'} hover:border-primary hover:text-primary rounded-lg"
class="p-2 bg-bg-tertiary border ${this.showMenu ? 'text-primary border-primary' : 'text-primary border-border'} hover:border-primary hover:text-primary hover:bg-surface-hover rounded-lg transition-all duration-200"
@click=${this.toggleMenu}
@keydown=${this.handleMenuButtonKeyDown}
title="More actions"
@ -235,13 +235,13 @@ export class CompactMenu extends LitElement {
let menuItemIndex = 0;
return html`
<div
class="absolute right-0 top-full mt-2 bg-surface border border-base rounded-lg shadow-xl py-1 min-w-[250px]"
class="absolute right-0 top-full mt-2 bg-surface border border-border rounded-lg shadow-xl py-1 min-w-[250px]"
style="z-index: ${Z_INDEX.WIDTH_SELECTOR_DROPDOWN};"
>
<!-- New Session -->
<button
class="w-full text-left px-4 py-3 text-sm font-mono text-primary hover:bg-secondary hover:text-primary flex items-center gap-3 ${this.focusedIndex === menuItemIndex++ ? 'bg-secondary text-primary' : ''}"
class="w-full text-left px-4 py-3 text-sm font-mono text-primary hover:bg-surface-hover hover:text-primary flex items-center gap-3 ${this.focusedIndex === menuItemIndex++ ? 'bg-surface-hover text-primary' : ''}"
@click=${() => this.handleAction(this.onCreateSession)}
data-testid="compact-new-session"
tabindex="${this.showMenu ? '0' : '-1'}"
@ -252,11 +252,11 @@ export class CompactMenu extends LitElement {
New Session
</button>
<div class="border-t border-base my-1"></div>
<div class="border-t border-border my-1"></div>
<!-- File Browser -->
<button
class="w-full text-left px-4 py-3 text-sm font-mono text-primary hover:bg-secondary hover:text-primary flex items-center gap-3 ${this.focusedIndex === menuItemIndex++ ? 'bg-secondary text-primary' : ''}"
class="w-full text-left px-4 py-3 text-sm font-mono text-primary hover:bg-surface-hover hover:text-primary flex items-center gap-3 ${this.focusedIndex === menuItemIndex++ ? 'bg-surface-hover text-primary' : ''}"
@click=${() => this.handleAction(this.onOpenFileBrowser)}
data-testid="compact-file-browser"
tabindex="${this.showMenu ? '0' : '-1'}"
@ -269,7 +269,7 @@ export class CompactMenu extends LitElement {
<!-- Upload Image -->
<button
class="w-full text-left px-4 py-3 text-sm font-mono text-primary hover:bg-secondary hover:text-primary flex items-center gap-3 ${this.focusedIndex === menuItemIndex++ ? 'bg-secondary text-primary' : ''}"
class="w-full text-left px-4 py-3 text-sm font-mono text-primary hover:bg-surface-hover hover:text-primary flex items-center gap-3 ${this.focusedIndex === menuItemIndex++ ? 'bg-surface-hover text-primary' : ''}"
@click=${() => this.handleAction(this.onUploadImage)}
data-testid="compact-upload-image"
tabindex="${this.showMenu ? '0' : '-1'}"
@ -282,7 +282,7 @@ export class CompactMenu extends LitElement {
<!-- Width Settings -->
<button
class="w-full text-left px-4 py-3 text-sm font-mono text-primary hover:bg-secondary hover:text-primary flex items-center gap-3 ${this.focusedIndex === menuItemIndex++ ? 'bg-secondary text-primary' : ''}"
class="w-full text-left px-4 py-3 text-sm font-mono text-primary hover:bg-surface-hover hover:text-primary flex items-center gap-3 ${this.focusedIndex === menuItemIndex++ ? 'bg-surface-hover text-primary' : ''}"
@click=${() => this.handleAction(this.onMaxWidthToggle)}
data-testid="compact-width-settings"
tabindex="${this.showMenu ? '0' : '-1'}"
@ -298,7 +298,7 @@ export class CompactMenu extends LitElement {
this.hasGitRepo
? html`
<button
class="w-full text-left px-4 py-3 text-sm font-mono text-primary hover:bg-secondary hover:text-primary flex items-center gap-3 ${this.focusedIndex === menuItemIndex++ ? 'bg-secondary text-primary' : ''}"
class="w-full text-left px-4 py-3 text-sm font-mono text-primary hover:bg-surface-hover hover:text-primary flex items-center gap-3 ${this.focusedIndex === menuItemIndex++ ? 'bg-surface-hover text-primary' : ''}"
@click=${() => this.handleAction(this.onToggleViewMode)}
data-testid="compact-worktree-toggle"
tabindex="${this.showMenu ? '0' : '-1'}"
@ -314,7 +314,7 @@ export class CompactMenu extends LitElement {
<!-- Theme Toggle -->
<button
class="w-full text-left px-4 py-3 text-sm font-mono text-primary hover:bg-secondary hover:text-primary flex items-center gap-3 ${this.focusedIndex === menuItemIndex++ ? 'bg-secondary text-primary' : ''}"
class="w-full text-left px-4 py-3 text-sm font-mono text-primary hover:bg-surface-hover hover:text-primary flex items-center gap-3 ${this.focusedIndex === menuItemIndex++ ? 'bg-surface-hover text-primary' : ''}"
@click=${() => this.handleThemeChange()}
data-testid="compact-theme-toggle"
tabindex="${this.showMenu ? '0' : '-1'}"
@ -325,7 +325,7 @@ export class CompactMenu extends LitElement {
<!-- Settings -->
<button
class="w-full text-left px-4 py-3 text-sm font-mono text-primary hover:bg-secondary hover:text-primary flex items-center gap-3 ${this.focusedIndex === menuItemIndex++ ? 'bg-secondary text-primary' : ''}"
class="w-full text-left px-4 py-3 text-sm font-mono text-primary hover:bg-surface-hover hover:text-primary flex items-center gap-3 ${this.focusedIndex === menuItemIndex++ ? 'bg-surface-hover text-primary' : ''}"
@click=${() => this.handleAction(this.onOpenSettings)}
data-testid="compact-settings"
tabindex="${this.showMenu ? '0' : '-1'}"
@ -339,14 +339,14 @@ export class CompactMenu extends LitElement {
${
this.session
? html`
<div class="border-t border-base my-1"></div>
<div class="border-t border-border my-1"></div>
<!-- Session Actions -->
${
this.session.status === 'running'
? html`
<button
class="w-full text-left px-4 py-3 text-sm font-mono text-status-error hover:bg-secondary flex items-center gap-3 ${this.focusedIndex === menuItemIndex++ ? 'bg-secondary' : ''}"
class="w-full text-left px-4 py-3 text-sm font-mono text-status-error hover:bg-surface-hover flex items-center gap-3 ${this.focusedIndex === menuItemIndex++ ? 'bg-surface-hover' : ''}"
@click=${() => this.handleAction(this.onTerminateSession)}
data-testid="compact-terminate-session"
tabindex="${this.showMenu ? '0' : '-1'}"
@ -359,7 +359,7 @@ export class CompactMenu extends LitElement {
`
: html`
<button
class="w-full text-left px-4 py-3 text-sm font-mono text-muted hover:bg-secondary hover:text-primary flex items-center gap-3 ${this.focusedIndex === menuItemIndex++ ? 'bg-secondary text-primary' : ''}"
class="w-full text-left px-4 py-3 text-sm font-mono text-text-muted hover:bg-surface-hover hover:text-primary flex items-center gap-3 ${this.focusedIndex === menuItemIndex++ ? 'bg-surface-hover text-primary' : ''}"
@click=${() => this.handleAction(this.onClearSession)}
data-testid="compact-clear-session"
tabindex="${this.showMenu ? '0' : '-1'}"

View file

@ -50,7 +50,7 @@ export class CtrlAlphaOverlay extends LitElement {
<div class="text-primary text-center mb-2 font-bold">Ctrl + Key</div>
<!-- Help text -->
<div class="text-xs text-muted text-center mb-3 opacity-70">
<div class="text-xs text-text-muted text-center mb-3 opacity-70">
Build sequences like ctrl+c ctrl+c
</div>
@ -58,8 +58,8 @@ export class CtrlAlphaOverlay extends LitElement {
${
this.ctrlSequence.length > 0
? html`
<div class="text-center mb-4 p-2 border border-base rounded bg-base">
<div class="text-xs text-muted mb-1">Current sequence:</div>
<div class="text-center mb-4 p-2 border border-border rounded bg-bg">
<div class="text-xs text-text-muted mb-1">Current sequence:</div>
<div class="text-sm text-primary font-bold">
${this.ctrlSequence.map((letter) => `Ctrl+${letter}`).join(' ')}
</div>
@ -110,7 +110,7 @@ export class CtrlAlphaOverlay extends LitElement {
</div>
<!-- Common shortcuts info -->
<div class="text-xs text-muted text-center mb-3">
<div class="text-xs text-text-muted text-center mb-3">
<div>Common: C=interrupt, X=exit, O=save, W=search</div>
</div>

View file

@ -245,7 +245,7 @@ export class ImageUploadMenu extends LitElement {
<div class="relative">
<vt-tooltip content="Upload Image (⌘U)" .show=${!this.isMobile}>
<button
class="bg-bg-tertiary border border-border rounded-lg p-2 font-mono text-muted transition-all duration-200 hover:text-primary hover:bg-surface-hover hover:border-primary hover:shadow-sm flex-shrink-0"
class="bg-bg-tertiary border border-border rounded-lg p-2 font-mono text-text-muted transition-all duration-200 hover:text-primary hover:bg-surface-hover hover:border-primary hover:shadow-sm flex-shrink-0"
@click=${this.toggleMenu}
@keydown=${this.handleMenuButtonKeyDown}
title="Upload Image"

View file

@ -181,7 +181,7 @@ export class OverlaysContainer extends LitElement {
${
this.uiState.isDragOver
? html`
<div class="fixed inset-0 bg-bg bg-opacity-90 backdrop-blur-sm flex items-center justify-center z-50 pointer-events-none animate-fade-in">
<div class="fixed inset-0 bg-bg/90 backdrop-blur-sm flex items-center justify-center z-50 pointer-events-none animate-fade-in">
<div class="bg-elevated border-2 border-dashed border-primary rounded-xl p-10 text-center max-w-md mx-4 shadow-2xl animate-scale-in">
<div class="relative mb-6">
<div class="w-24 h-24 mx-auto bg-gradient-to-br from-primary to-primary-light rounded-full flex items-center justify-center shadow-glow">
@ -192,10 +192,10 @@ export class OverlaysContainer extends LitElement {
<div class="absolute -bottom-2 left-1/2 transform -translate-x-1/2 w-32 h-1 bg-gradient-to-r from-transparent via-primary to-transparent opacity-50"></div>
</div>
<h3 class="text-2xl font-bold text-primary mb-3">Drop files here</h3>
<p class="text-sm text-muted mb-4">Files will be uploaded and the path sent to terminal</p>
<div class="inline-flex items-center gap-2 text-xs text-dim bg-secondary px-4 py-2 rounded-lg">
<p class="text-sm text-text-muted mb-4">Files will be uploaded and the path sent to terminal</p>
<div class="inline-flex items-center gap-2 text-xs text-text-dim bg-bg-secondary px-4 py-2 rounded-lg">
<span class="opacity-75">Or press</span>
<kbd class="px-2 py-1 bg-tertiary border border-base rounded text-primary font-mono text-xs">V</kbd>
<kbd class="px-2 py-1 bg-bg-tertiary border border-border rounded text-primary font-mono text-xs">V</kbd>
<span class="opacity-75">to paste from clipboard</span>
</div>
</div>

View file

@ -152,9 +152,9 @@ export class SessionHeader extends LitElement {
}
private getStatusDotColor(): string {
if (!this.session) return 'bg-muted';
if (!this.session) return 'bg-bg-muted';
if ('active' in this.session && this.session.active === false) {
return 'bg-muted';
return 'bg-bg-muted';
}
return this.session.status === 'running' ? 'bg-status-success' : 'bg-status-warning';
}
@ -418,7 +418,7 @@ export class SessionHeader extends LitElement {
<!-- Terminal size button -->
<button
class="bg-bg-tertiary border border-border rounded-lg px-3 py-2 font-mono text-xs text-muted transition-all duration-200 hover:text-primary hover:bg-surface-hover hover:border-primary hover:shadow-sm flex-shrink-0 width-selector-button"
class="bg-bg-tertiary border border-border rounded-lg px-3 py-2 font-mono text-xs text-text-muted transition-all duration-200 hover:text-primary hover:bg-surface-hover hover:border-primary hover:shadow-sm flex-shrink-0 width-selector-button"
@click=${() => this.onMaxWidthToggle?.()}
title="${this.widthTooltip}"
>

View file

@ -197,7 +197,7 @@ export class SessionStatusDropdown extends LitElement {
height="10"
viewBox="0 0 10 10"
fill="currentColor"
class="transition-transform text-muted ${this.showMenu ? 'rotate-180' : ''}"
class="transition-transform text-text-muted ${this.showMenu ? 'rotate-180' : ''}"
>
<path d="M5 7L1 3h8z" />
</svg>
@ -235,7 +235,7 @@ export class SessionStatusDropdown extends LitElement {
`
: html`
<button
class="w-full text-left px-6 py-3 text-sm font-mono text-muted hover:bg-bg-secondary hover:text-primary flex items-center gap-3 ${
class="w-full text-left px-6 py-3 text-sm font-mono text-text-muted hover:bg-bg-secondary hover:text-primary flex items-center gap-3 ${
this.focusedIndex === menuItemIndex++ ? 'bg-bg-secondary text-primary' : ''
}"
@click=${() => this.handleAction(this.onClear)}

View file

@ -37,7 +37,7 @@ export class TerminalDimensions extends LitElement {
return html`
<span
class="hidden sm:inline text-muted text-xs opacity-60"
class="hidden sm:inline text-text-muted text-xs opacity-60"
style="font-size: 10px; line-height: 1;"
>
${this.cols}×${this.rows}

View file

@ -174,7 +174,7 @@ export class TerminalSettingsModal extends LitElement {
<div class="flex items-center justify-between mb-6">
<h2 id="terminal-settings-title" class="text-lg font-semibold text-text-bright">Terminal Settings</h2>
<button
class="text-muted hover:text-primary transition-colors p-1"
class="text-text-muted hover:text-primary transition-colors p-1"
@click=${() => this.handleClose()}
title="Close"
aria-label="Close terminal settings"

View file

@ -385,7 +385,7 @@ export class Settings extends LitElement {
<div class="p-4 pb-4 border-b border-border/50 relative flex-shrink-0">
<h2 class="text-primary text-lg font-bold">Settings</h2>
<button
class="absolute top-4 right-4 text-muted hover:text-primary transition-colors p-1"
class="absolute top-4 right-4 text-text-muted hover:text-primary transition-colors p-1"
@click=${this.handleClose}
title="Close"
aria-label="Close settings"
@ -431,7 +431,7 @@ export class Settings extends LitElement {
${
!this.isNotificationsSupported
? html`
<div class="p-4 bg-status-warning bg-opacity-10 border border-status-warning rounded-lg">
<div class="p-4 bg-status-warning/10 border border-status-warning rounded-lg">
${
isIOSSafari && !isStandalone
? html`
@ -452,7 +452,7 @@ export class Settings extends LitElement {
`
: html`
<!-- Main toggle -->
<div class="flex items-center justify-between p-4 bg-tertiary rounded-lg border border-border/50">
<div class="flex items-center justify-between p-4 bg-bg-tertiary rounded-lg border border-border/50">
<div class="flex-1">
<label class="text-primary font-medium">Enable Notifications</label>
<p class="text-muted text-xs mt-1">
@ -482,8 +482,8 @@ export class Settings extends LitElement {
<!-- Notification types -->
<div class="mt-4 space-y-4">
<div>
<h4 class="text-sm font-medium text-muted mb-3">Notification Types</h4>
<div class="space-y-2 bg-base rounded-lg p-3">
<h4 class="text-sm font-medium text-text-muted mb-3">Notification Types</h4>
<div class="space-y-2 bg-bg rounded-lg p-3">
${this.renderNotificationToggle('sessionExit', 'Session Exit', 'When a session terminates or crashes (shows exit code)')}
${this.renderNotificationToggle('sessionStart', 'Session Start', 'When a new session starts (useful for shared terminals)')}
${this.renderNotificationToggle('commandError', 'Session Errors', 'When commands fail with non-zero exit codes')}
@ -495,8 +495,8 @@ export class Settings extends LitElement {
<!-- Sound and vibration -->
<div>
<h4 class="text-sm font-medium text-muted mb-3">Notification Behavior</h4>
<div class="space-y-2 bg-base rounded-lg p-3">
<h4 class="text-sm font-medium text-text-muted mb-3">Notification Behavior</h4>
<div class="space-y-2 bg-bg rounded-lg p-3">
${this.renderNotificationToggle('soundEnabled', 'Sound', 'Play a notification sound when alerts are triggered')}
${this.renderNotificationToggle('vibrationEnabled', 'Vibration', 'Vibrate device with notifications (mobile devices only)')}
</div>
@ -562,7 +562,7 @@ export class Settings extends LitElement {
${
this.mediaState.isMobile
? html`
<div class="flex items-center justify-between p-4 bg-tertiary rounded-lg border border-border/50">
<div class="flex items-center justify-between p-4 bg-bg-tertiary rounded-lg border border-border/50">
<div class="flex-1">
<label class="text-primary font-medium">
Use Direct Keyboard
@ -592,7 +592,7 @@ export class Settings extends LitElement {
<!-- Repository Base Path -->
<div class="p-4 bg-tertiary rounded-lg border border-border/50">
<div class="p-4 bg-bg-tertiary rounded-lg border border-border/50">
<div class="mb-3">
<div class="flex items-center justify-between">
<label class="text-primary font-medium">Repository Base Path</label>

View file

@ -176,7 +176,7 @@ export class SSHKeyManager extends LitElement {
return html`
<div
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-[1000]"
class="fixed inset-0 bg-black/50 flex items-center justify-center p-4 z-[1000]"
@click=${this.handleBackdropClick}
>
<div
@ -190,7 +190,7 @@ export class SSHKeyManager extends LitElement {
<h2 class="text-2xl font-mono text-primary text-center">🔑 SSH Key Manager</h2>
<button
@click=${this.handleClose}
class="absolute top-0 right-0 w-8 h-8 flex items-center justify-center text-muted hover:text-primary hover:bg-surface rounded transition-colors"
class="absolute top-0 right-0 w-8 h-8 flex items-center justify-center text-text-muted hover:text-primary hover:bg-surface rounded transition-colors"
title="Close"
>
@ -277,7 +277,7 @@ export class SSHKeyManager extends LitElement {
<div>
<label class="form-label">Algorithm</label>
<div
class="input-field bg-secondary text-muted cursor-not-allowed"
class="input-field bg-bg-secondary text-text-muted cursor-not-allowed"
>
Ed25519 (recommended)
</div>
@ -296,7 +296,7 @@ export class SSHKeyManager extends LitElement {
}}
?disabled=${this.loading}
/>
<p class="text-muted text-xs mt-1">
<p class="text-text-muted text-xs mt-1">
💡 Leave empty for unencrypted key. Password is required when using the
key for signing.
</p>
@ -346,7 +346,7 @@ export class SSHKeyManager extends LitElement {
}}
?disabled=${this.loading}
></textarea>
<p class="text-muted text-xs mt-1">
<p class="text-text-muted text-xs mt-1">
💡 If the key is password-protected, you'll be prompted for the password
when using it for authentication.
</p>
@ -383,7 +383,7 @@ export class SSHKeyManager extends LitElement {
@click=${() => {
this.showInstructions = false;
}}
class="w-8 h-8 flex items-center justify-center text-muted hover:text-primary hover:bg-bg rounded transition-colors"
class="w-8 h-8 flex items-center justify-center text-text-muted hover:text-primary hover:bg-bg rounded transition-colors"
title="Close instructions"
>
@ -391,7 +391,7 @@ export class SSHKeyManager extends LitElement {
</div>
<div class="space-y-6">
<div class="bg-bg border border-border rounded-lg p-4">
<p class="text-muted text-sm mb-3 font-medium">
<p class="text-text-muted text-sm mb-3 font-medium">
1. Add the public key to your authorized_keys file:
</p>
<div class="relative">
@ -415,7 +415,7 @@ echo "${this.sshAgent.getPublicKey(this.instructionsKeyId)}" >> ~/.ssh/authorize
</div>
</div>
<div class="bg-bg border border-border rounded-lg p-4">
<p class="text-muted text-sm mb-3 font-medium">2. Or copy the public key:</p>
<p class="text-text-muted text-sm mb-3 font-medium">2. Or copy the public key:</p>
<div class="relative">
<pre
class="bg-secondary p-3 rounded-lg text-xs overflow-x-auto text-primary pr-20 font-mono"
@ -437,8 +437,8 @@ ${this.sshAgent.getPublicKey(this.instructionsKeyId)}</pre
</button>
</div>
</div>
<div class="bg-blue-50 border border-blue-200 rounded-lg p-3">
<p class="text-blue-700 text-sm font-mono flex items-center gap-2">
<div class="bg-status-info/10 border border-status-info/30 rounded-lg p-3">
<p class="text-status-info text-sm font-mono flex items-center gap-2">
💡 <strong>Tip:</strong> Make sure ~/.ssh/authorized_keys has correct permissions (600)
</p>
</div>
@ -453,7 +453,7 @@ ${this.sshAgent.getPublicKey(this.instructionsKeyId)}</pre
${
this.keys.length === 0
? html`
<div class="text-center py-12 text-muted border border-border rounded-lg bg-surface">
<div class="text-center py-12 text-text-muted border border-border rounded-lg bg-surface">
<div class="text-4xl mb-4">🔑</div>
<p class="font-mono text-lg mb-2 text-primary">No SSH keys found</p>
<p class="text-sm">Generate or import a key to get started</p>
@ -473,7 +473,7 @@ ${this.sshAgent.getPublicKey(this.instructionsKeyId)}</pre
: ''
}
</div>
<div class="text-sm text-muted font-mono space-y-1">
<div class="text-sm text-text-muted font-mono space-y-1">
<div>ID: ${key.id}</div>
<div>Fingerprint: ${key.fingerprint}</div>
<div>Created: ${new Date(key.createdAt).toLocaleString()}</div>

View file

@ -418,7 +418,7 @@ export class TerminalQuickKeys extends LitElement {
<button
type="button"
tabindex="-1"
class="quick-key-btn ${this.getButtonFontClass(label)} min-w-0 ${this.getButtonSizeClass(label)} bg-tertiary text-primary font-mono rounded border border-base hover:bg-surface hover:border-primary transition-all whitespace-nowrap ${modifier ? 'modifier-key' : ''} ${arrow ? 'arrow-key' : ''} ${toggle ? 'toggle-key' : ''} ${toggle && ((key === 'CtrlExpand' && this.showCtrlKeys) || (key === 'F' && this.showFunctionKeys)) ? 'active' : ''} ${modifier && key === 'Option' && this.activeModifiers.has('Option') ? 'active' : ''}"
class="quick-key-btn ${this.getButtonFontClass(label)} min-w-0 ${this.getButtonSizeClass(label)} bg-bg-tertiary text-primary font-mono rounded border border-border hover:bg-surface hover:border-primary transition-all whitespace-nowrap ${modifier ? 'modifier-key' : ''} ${arrow ? 'arrow-key' : ''} ${toggle ? 'toggle-key' : ''} ${toggle && ((key === 'CtrlExpand' && this.showCtrlKeys) || (key === 'F' && this.showFunctionKeys)) ? 'active' : ''} ${modifier && key === 'Option' && this.activeModifiers.has('Option') ? 'active' : ''}"
@mousedown=${(e: Event) => {
e.preventDefault();
e.stopPropagation();
@ -470,7 +470,7 @@ export class TerminalQuickKeys extends LitElement {
<button
type="button"
tabindex="-1"
class="ctrl-shortcut-btn ${this.getButtonFontClass(label)} min-w-0 ${this.getButtonSizeClass(label)} bg-tertiary text-primary font-mono rounded border border-base hover:bg-surface hover:border-primary transition-all whitespace-nowrap ${combo ? 'combo-key' : ''} ${special ? 'special-key' : ''}"
class="ctrl-shortcut-btn ${this.getButtonFontClass(label)} min-w-0 ${this.getButtonSizeClass(label)} bg-bg-tertiary text-primary font-mono rounded border border-border hover:bg-surface hover:border-primary transition-all whitespace-nowrap ${combo ? 'combo-key' : ''} ${special ? 'special-key' : ''}"
@mousedown=${(e: Event) => {
e.preventDefault();
e.stopPropagation();
@ -505,7 +505,7 @@ export class TerminalQuickKeys extends LitElement {
<button
type="button"
tabindex="-1"
class="func-key-btn ${this.getButtonFontClass(label)} min-w-0 ${this.getButtonSizeClass(label)} bg-tertiary text-primary font-mono rounded border border-base hover:bg-surface hover:border-primary transition-all whitespace-nowrap"
class="func-key-btn ${this.getButtonFontClass(label)} min-w-0 ${this.getButtonSizeClass(label)} bg-bg-tertiary text-primary font-mono rounded border border-border hover:bg-surface hover:border-primary transition-all whitespace-nowrap"
@mousedown=${(e: Event) => {
e.preventDefault();
e.stopPropagation();
@ -539,7 +539,7 @@ export class TerminalQuickKeys extends LitElement {
<button
type="button"
tabindex="-1"
class="quick-key-btn ${this.getButtonFontClass(label)} min-w-0 ${this.getButtonSizeClass(label)} bg-tertiary text-primary font-mono rounded border border-base hover:bg-surface hover:border-primary transition-all whitespace-nowrap ${modifier ? 'modifier-key' : ''} ${combo ? 'combo-key' : ''} ${special ? 'special-key' : ''} ${toggle ? 'toggle-key' : ''} ${toggle && this.showFunctionKeys ? 'active' : ''}"
class="quick-key-btn ${this.getButtonFontClass(label)} min-w-0 ${this.getButtonSizeClass(label)} bg-bg-tertiary text-primary font-mono rounded border border-border hover:bg-surface hover:border-primary transition-all whitespace-nowrap ${modifier ? 'modifier-key' : ''} ${combo ? 'combo-key' : ''} ${special ? 'special-key' : ''} ${toggle ? 'toggle-key' : ''} ${toggle && this.showFunctionKeys ? 'active' : ''}"
@mousedown=${(e: Event) => {
e.preventDefault();
e.stopPropagation();
@ -578,7 +578,7 @@ export class TerminalQuickKeys extends LitElement {
<button
type="button"
tabindex="-1"
class="quick-key-btn ${this.getButtonFontClass(label)} min-w-0 ${this.getButtonSizeClass(label)} bg-tertiary text-primary font-mono rounded border border-base hover:bg-surface hover:border-primary transition-all whitespace-nowrap ${modifier ? 'modifier-key' : ''} ${combo ? 'combo-key' : ''} ${special ? 'special-key' : ''} ${modifier && key === 'Option' && this.activeModifiers.has('Option') ? 'active' : ''}"
class="quick-key-btn ${this.getButtonFontClass(label)} min-w-0 ${this.getButtonSizeClass(label)} bg-bg-tertiary text-primary font-mono rounded border border-border hover:bg-surface hover:border-primary transition-all whitespace-nowrap ${modifier ? 'modifier-key' : ''} ${combo ? 'combo-key' : ''} ${special ? 'special-key' : ''} ${modifier && key === 'Option' && this.activeModifiers.has('Option') ? 'active' : ''}"
@mousedown=${(e: Event) => {
e.preventDefault();
e.stopPropagation();

View file

@ -244,7 +244,7 @@ export class VibeTerminalBuffer extends LitElement {
this.error
? html`
<div class="absolute inset-0 flex items-center justify-center">
<div class="text-red-500 text-sm">${this.error}</div>
<div class="text-status-error text-sm">${this.error}</div>
</div>
`
: html`

View file

@ -1,36 +1,141 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "tailwindcss";
/* Define CSS variables first */
:root {
/* Light mode colors (default) */
--color-bg: rgb(250 250 250);
--color-bg-secondary: rgb(245 245 245);
--color-bg-tertiary: rgb(237 237 237);
--color-bg-elevated: rgb(255 255 255);
--color-surface: rgb(245 245 245);
--color-surface-hover: rgb(237 237 237);
--color-border: rgb(226 226 226);
--color-border-light: rgb(237 237 237);
--color-border-focus: rgb(200 200 200);
--color-text: rgb(23 23 23);
--color-text-bright: rgb(0 0 0);
--color-text-muted: rgb(75 75 75);
--color-text-dim: rgb(115 115 115);
--color-primary: #10B981;
--color-primary-hover: #059669;
--color-primary-dark: #047857;
--color-primary-light: #34D399;
--color-primary-muted: #10B98133;
--color-primary-glow: #10B98166;
--color-status-error: #EF4444;
--color-status-warning: #F59E0B;
--color-status-success: #10B981;
--color-status-info: #3B82F6;
}
/* Tailwind v4 Theme Configuration */
@theme {
/* Register custom colors using CSS variables */
--color-bg: var(--color-bg);
--color-bg-secondary: var(--color-bg-secondary);
--color-bg-tertiary: var(--color-bg-tertiary);
--color-bg-elevated: var(--color-bg-elevated);
--color-surface: var(--color-surface);
--color-surface-hover: var(--color-surface-hover);
--color-border: var(--color-border);
--color-border-light: var(--color-border-light);
--color-border-focus: var(--color-border-focus);
--color-text: var(--color-text);
--color-text-bright: var(--color-text-bright);
--color-text-muted: var(--color-text-muted);
--color-text-dim: var(--color-text-dim);
--color-primary: var(--color-primary);
--color-primary-hover: var(--color-primary-hover);
--color-primary-dark: var(--color-primary-dark);
--color-primary-light: var(--color-primary-light);
--color-primary-muted: var(--color-primary-muted);
--color-primary-glow: var(--color-primary-glow);
--color-accent-primary: var(--color-primary);
--color-accent-green: var(--color-status-success);
--color-accent-red: var(--color-status-error);
--color-status-error: var(--color-status-error);
--color-status-warning: var(--color-status-warning);
--color-status-success: var(--color-status-success);
--color-status-info: var(--color-status-info);
/* Box Shadows */
--shadow-glow: 0 0 20px rgb(16 185 129 / 0.4);
--shadow-glow-sm: 0 0 10px rgb(16 185 129 / 0.3);
--shadow-glow-lg: 0 0 30px rgb(16 185 129 / 0.5);
--shadow-glow-intense: 0 0 40px rgb(16 185 129 / 0.6);
--shadow-glow-error: 0 0 20px rgb(239 68 68 / 0.4);
--shadow-glow-error-sm: 0 0 10px rgb(239 68 68 / 0.3);
--shadow-glow-error-lg: 0 0 30px rgb(239 68 68 / 0.5);
--shadow-glow-warning: 0 0 20px rgb(245 158 11 / 0.4);
--shadow-glow-warning-sm: 0 0 10px rgb(245 158 11 / 0.3);
--shadow-glow-warning-lg: 0 0 30px rgb(245 158 11 / 0.5);
--shadow-card: 0 1px 3px rgb(0 0 0 / 0.3), 0 1px 2px rgb(0 0 0 / 0.4);
--shadow-card-hover: 0 4px 6px rgb(0 0 0 / 0.3), 0 2px 4px rgb(0 0 0 / 0.4);
--shadow-elevated: 0 10px 15px -3px rgb(0 0 0 / 0.3), 0 4px 6px -2px rgb(0 0 0 / 0.2);
/* Animations */
--animate-pulse-primary: pulsePrimary 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
--animate-slide-in-right: slideInRight 0.3s ease-out;
--animate-slide-in-bottom: slideInBottom 0.3s ease-out;
--animate-fade-in: fadeIn 0.2s ease-out;
--animate-scale-in: scaleIn 0.2s ease-out;
}
/* Keyframes */
@keyframes pulsePrimary {
0%, 100% {
opacity: 1;
}
50% {
opacity: .7;
}
}
@keyframes slideInRight {
0% {
transform: translateX(100%);
opacity: 0;
}
100% {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideInBottom {
0% {
transform: translateY(100%);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes scaleIn {
0% {
transform: scale(0.95);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 1;
}
}
/* CSS Custom Properties for VibeTunnel constants */
:root {
/* Theme Colors - Light Mode (Default) */
--color-bg: 250 250 250; /* #fafafa - Off-white background */
--color-bg-secondary: 245 245 245; /* #f5f5f5 - Subtle gray */
--color-bg-tertiary: 237 237 237; /* #ededed - Card backgrounds */
--color-bg-elevated: 255 255 255; /* #ffffff - Pure white for elevated surfaces */
--color-surface: 245 245 245; /* #f5f5f5 */
--color-surface-hover: 237 237 237; /* #ededed */
--color-border: 226 226 226; /* #e2e2e2 - Subtle gray borders */
--color-border-light: 237 237 237; /* #ededed */
--color-border-focus: 200 200 200; /* #c8c8c8 */
/* Text colors - Light Mode */
--color-text: 23 23 23; /* #171717 - Near black for high contrast */
--color-text-bright: 0 0 0; /* #000000 - Pure black for emphasis */
--color-text-muted: 75 75 75; /* #4b4b4b - Darker muted gray for AAA compliance */
--color-text-dim: 115 115 115; /* #737373 - Darker dim gray for AA compliance */
/* Primary color (same across themes) */
--color-primary: 16 185 129; /* #10B981 - VibeTunnel green */
--color-primary-hover: 5 150 105; /* #059669 */
--color-primary-dark: 4 120 87; /* #047857 */
/* Status colors */
--color-status-error: 239 68 68; /* #EF4444 */
--color-status-warning: 245 158 11; /* #F59E0B */
--color-status-success: 16 185 129; /* #10B981 */
--color-status-info: 59 130 246; /* #3B82F6 */
/* Breakpoints */
--vt-breakpoint-mobile: 768px;
--vt-breakpoint-tablet: 1024px;
@ -60,57 +165,39 @@
/* Dark mode theme colors */
[data-theme="dark"] {
--color-bg: 23 23 23; /* #171717 - Lighter neutral dark */
--color-bg-secondary: 32 32 32; /* #202020 - Slightly darker for sidebar */
--color-bg-tertiary: 40 40 40; /* #282828 - For buttons and cards */
--color-bg-elevated: 48 48 48; /* #303030 - Elevated surfaces */
--color-surface: 36 36 36; /* #242424 - Surface color */
--color-surface-hover: 52 52 52; /* #343434 - Hover states */
--color-border: 64 64 64; /* #404040 - Subtle borders */
--color-border-light: 72 72 72; /* #484848 - Light borders */
--color-border-focus: 80 80 80; /* #505050 - Focus borders */
/* Text colors - Dark Mode */
--color-text: 228 228 228; /* #e4e4e4 */
--color-text-bright: 255 255 255; /* #ffffff */
--color-text-muted: 163 163 163; /* #a3a3a3 */
--color-text-dim: 115 115 115; /* #737373 */
/* Primary color (same as light mode) */
--color-primary: 16 185 129; /* #10B981 */
--color-primary-hover: 5 150 105; /* #059669 */
--color-primary-dark: 4 120 87; /* #047857 */
/* Status colors (same as light mode) */
--color-status-error: 239 68 68; /* #EF4444 */
--color-status-warning: 245 158 11; /* #F59E0B */
--color-status-success: 16 185 129; /* #10B981 */
--color-status-info: 59 130 246; /* #3B82F6 */
/* Override CSS variables for dark mode */
--color-bg: rgb(23 23 23);
--color-bg-secondary: rgb(32 32 32);
--color-bg-tertiary: rgb(40 40 40);
--color-bg-elevated: rgb(48 48 48);
--color-surface: rgb(36 36 36);
--color-surface-hover: rgb(52 52 52);
--color-border: rgb(64 64 64);
--color-border-light: rgb(72 72 72);
--color-border-focus: rgb(80 80 80);
--color-text: rgb(228 228 228);
--color-text-bright: rgb(255 255 255);
--color-text-muted: rgb(163 163 163);
--color-text-dim: rgb(115 115 115);
/* Primary and status colors remain the same */
}
/* System preference detection */
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--color-bg: 23 23 23;
--color-bg-secondary: 32 32 32;
--color-bg-tertiary: 40 40 40;
--color-bg-elevated: 48 48 48;
--color-surface: 36 36 36;
--color-surface-hover: 52 52 52;
--color-border: 64 64 64;
--color-border-light: 72 72 72;
--color-border-focus: 80 80 80;
--color-text: 228 228 228;
--color-text-bright: 255 255 255;
--color-text-muted: 163 163 163;
--color-text-dim: 115 115 115;
--color-primary: 16 185 129;
--color-primary-hover: 5 150 105;
--color-primary-dark: 4 120 87;
--color-status-error: 239 68 68;
--color-status-warning: 245 158 11;
--color-status-success: 16 185 129;
--color-status-info: 59 130 246;
--color-bg: rgb(23 23 23);
--color-bg-secondary: rgb(32 32 32);
--color-bg-tertiary: rgb(40 40 40);
--color-bg-elevated: rgb(48 48 48);
--color-surface: rgb(36 36 36);
--color-surface-hover: rgb(52 52 52);
--color-border: rgb(64 64 64);
--color-border-light: rgb(72 72 72);
--color-border-focus: rgb(80 80 80);
--color-text: rgb(228 228 228);
--color-text-bright: rgb(255 255 255);
--color-text-muted: rgb(163 163 163);
--color-text-dim: rgb(115 115 115);
}
}
@ -124,6 +211,126 @@
@apply bg-bg text-text;
transition: background-color 0.3s ease, color 0.3s ease;
}
/* Checkbox styling */
input[type="checkbox"] {
@apply w-4 h-4 rounded border border-border bg-bg cursor-pointer;
@apply transition-all duration-200;
@apply focus:outline-none focus:ring-2 focus:ring-primary/50;
@apply checked:bg-primary checked:border-primary;
appearance: none;
-webkit-appearance: none;
position: relative;
}
input[type="checkbox"]:checked::before {
content: '✓';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-size: 12px;
font-weight: bold;
}
/* Select element styling */
select {
@apply w-full rounded border border-border bg-bg cursor-pointer;
@apply transition-all duration-200;
@apply focus:outline-none focus:ring-2 focus:ring-primary/50;
@apply hover:border-primary/50;
@apply px-3 py-2 pr-8;
appearance: none;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
background-position: right 0.5rem center;
background-repeat: no-repeat;
background-size: 1.5em 1.5em;
}
/* Dark mode select arrow */
[data-theme="dark"] select {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%239ca3af' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
}
/* Textarea styling */
textarea {
@apply w-full rounded border border-border bg-bg;
@apply transition-all duration-200;
@apply focus:outline-none focus:ring-2 focus:ring-primary/50;
@apply hover:border-primary/50 focus:border-primary;
@apply px-3 py-2;
resize: vertical;
min-height: 80px;
}
/* Radio button styling */
input[type="radio"] {
@apply w-4 h-4 border border-border bg-bg cursor-pointer;
@apply transition-all duration-200;
@apply focus:outline-none focus:ring-2 focus:ring-primary/50;
appearance: none;
border-radius: 50%;
position: relative;
}
input[type="radio"]:checked {
@apply bg-primary border-primary;
}
input[type="radio"]:checked::before {
content: '';
position: absolute;
width: 6px;
height: 6px;
border-radius: 50%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: white;
}
/* File input styling */
input[type="file"] {
@apply w-full rounded border border-border bg-bg cursor-pointer;
@apply transition-all duration-200;
@apply focus:outline-none focus:ring-2 focus:ring-primary/50;
@apply hover:border-primary/50;
@apply px-3 py-2;
@apply text-sm text-text;
}
input[type="file"]::file-selector-button {
@apply mr-3 px-3 py-1 rounded bg-primary text-bg;
@apply hover:bg-primary-hover;
@apply cursor-pointer transition-colors duration-200;
border: none;
font-weight: 500;
}
/* Range input styling */
input[type="range"] {
@apply w-full h-2 bg-bg-tertiary rounded-lg appearance-none cursor-pointer;
@apply focus:outline-none focus:ring-2 focus:ring-primary/50;
}
input[type="range"]::-webkit-slider-thumb {
@apply appearance-none w-4 h-4 rounded-full bg-primary cursor-pointer;
@apply hover:bg-primary-hover transition-colors duration-200;
@apply shadow-sm hover:shadow-md;
}
input[type="range"]::-moz-range-thumb {
@apply appearance-none w-4 h-4 rounded-full bg-primary cursor-pointer;
@apply hover:bg-primary-hover transition-colors duration-200;
@apply shadow-sm hover:shadow-md;
border: none;
}
/* Session toggle checkbox specific styling */
.session-toggle-checkbox {
@apply w-3 h-3;
}
/* Default focus styles */
:focus {
@ -174,31 +381,49 @@
}
.btn-sm {
@apply btn px-3 py-1.5 text-sm;
@apply font-medium rounded-lg transition-all duration-200 ease-in-out;
@apply focus:outline-none focus:ring-2 focus:ring-primary/50;
@apply disabled:opacity-50 disabled:cursor-not-allowed;
@apply px-3 py-1.5 text-sm;
}
.btn-md {
@apply btn px-4 py-2;
@apply font-medium rounded-lg transition-all duration-200 ease-in-out;
@apply focus:outline-none focus:ring-2 focus:ring-primary/50;
@apply disabled:opacity-50 disabled:cursor-not-allowed;
@apply px-4 py-2;
}
.btn-lg {
@apply btn px-6 py-3 text-lg;
@apply font-medium rounded-lg transition-all duration-200 ease-in-out;
@apply focus:outline-none focus:ring-2 focus:ring-primary/50;
@apply disabled:opacity-50 disabled:cursor-not-allowed;
@apply px-6 py-3 text-lg;
}
.btn-primary {
@apply btn-md bg-primary text-bg;
@apply font-medium rounded-lg transition-all duration-200 ease-in-out;
@apply focus:outline-none focus:ring-2 focus:ring-primary/50;
@apply disabled:opacity-50 disabled:cursor-not-allowed;
@apply px-4 py-2 bg-primary text-bg;
@apply hover:bg-primary-hover hover:shadow-glow;
@apply active:scale-95;
}
.btn-secondary {
@apply btn-md border border-primary text-primary;
@apply font-medium rounded-lg transition-all duration-200 ease-in-out;
@apply focus:outline-none focus:ring-2 focus:ring-primary/50;
@apply disabled:opacity-50 disabled:cursor-not-allowed;
@apply px-4 py-2 border border-primary text-primary;
@apply hover:bg-primary/10 hover:border-primary-hover hover:shadow-glow-sm;
@apply active:scale-95;
}
.btn-ghost {
@apply btn-md text-text-muted;
@apply font-medium rounded-lg transition-all duration-200 ease-in-out;
@apply focus:outline-none focus:ring-2 focus:ring-primary/50;
@apply disabled:opacity-50 disabled:cursor-not-allowed;
@apply px-4 py-2 text-text-muted;
@apply hover:text-text hover:bg-bg-tertiary;
}
@ -249,19 +474,19 @@
}
.status-badge-success {
@apply bg-status-success bg-opacity-20 text-status-success;
@apply bg-status-success/20 text-status-success;
}
.status-badge-warning {
@apply bg-status-warning bg-opacity-20 text-status-warning;
@apply bg-status-warning/20 text-status-warning;
}
.status-badge-error {
@apply bg-status-error bg-opacity-20 text-status-error;
@apply bg-status-error/20 text-status-error;
}
.status-badge-info {
@apply bg-status-info bg-opacity-20 text-status-info;
@apply bg-status-info/20 text-status-info;
}
/* Quick start buttons */
@ -280,7 +505,7 @@
/* Modal backdrop */
.modal-backdrop {
@apply fixed inset-0 bg-bg bg-opacity-80;
@apply fixed inset-0 bg-bg/80;
z-index: 1000;
backdrop-filter: blur(4px);
/* Disable all transitions and animations */
@ -291,7 +516,7 @@
/* Modal content */
.modal-content {
@apply bg-bg-secondary border border-border rounded-xl;
@apply shadow-2xl shadow-black/50;
@apply shadow-2xl;
position: relative;
z-index: 1001;
/* Disable all transitions and animations */
@ -521,7 +746,7 @@
/* Focus within styles */
.focus-within-glow:focus-within {
@apply shadow-glow-sm ring-2 ring-primary ring-opacity-50;
@apply shadow-glow-sm ring-2 ring-primary/50;
}
/* Smooth color transitions */
@ -1660,7 +1885,6 @@ body.initial-session-load .session-flex-responsive > session-card:nth-child(n +
/* Black hole collapse animation for session removal */
@keyframes black-hole-collapse {
0% {
@ -1935,4 +2159,4 @@ body.initial-session-load .session-flex-responsive > session-card:nth-child(n +
/* Focus visible for keyboard navigation */
.session-toggle-checkbox:focus-visible {
box-shadow: 0 0 0 2px rgb(var(--color-primary) / 0.4);
}
}

View file

@ -1,118 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{html,js,ts,jsx,tsx}",
"./public/**/*.html",
"!./node_modules/**/*"
],
theme: {
extend: {
colors: {
// Theme-aware colors using CSS variables
"bg": "rgb(var(--color-bg) / <alpha-value>)",
"bg-secondary": "rgb(var(--color-bg-secondary) / <alpha-value>)",
"bg-tertiary": "rgb(var(--color-bg-tertiary) / <alpha-value>)",
"bg-elevated": "rgb(var(--color-bg-elevated) / <alpha-value>)",
"surface": "rgb(var(--color-surface) / <alpha-value>)",
"surface-hover": "rgb(var(--color-surface-hover) / <alpha-value>)",
"border": "rgb(var(--color-border) / <alpha-value>)",
"border-light": "rgb(var(--color-border-light) / <alpha-value>)",
"border-focus": "rgb(var(--color-border-focus) / <alpha-value>)",
// Text colors
"text": "rgb(var(--color-text) / <alpha-value>)",
"text-bright": "rgb(var(--color-text-bright) / <alpha-value>)",
"text-muted": "rgb(var(--color-text-muted) / <alpha-value>)",
"text-dim": "rgb(var(--color-text-dim) / <alpha-value>)",
// Unified accent color - Vibrant teal-green (stays the same across themes)
"primary": "#10B981",
"primary-hover": "#059669",
"primary-dark": "#047857",
"primary-light": "#34D399",
"primary-muted": "#10B98133",
"primary-glow": "#10B98166",
// Status colors
"status-error": "#EF4444",
"status-warning": "#F59E0B",
"status-success": "#10B981",
"status-info": "#3B82F6",
},
boxShadow: {
// Unified glow effects with primary color
'glow': '0 0 20px rgba(16, 185, 129, 0.4)',
'glow-sm': '0 0 10px rgba(16, 185, 129, 0.3)',
'glow-lg': '0 0 30px rgba(16, 185, 129, 0.5)',
'glow-intense': '0 0 40px rgba(16, 185, 129, 0.6)',
// Status-specific glow effects
'glow-error': '0 0 20px rgba(239, 68, 68, 0.4)',
'glow-error-sm': '0 0 10px rgba(239, 68, 68, 0.3)',
'glow-error-lg': '0 0 30px rgba(239, 68, 68, 0.5)',
'glow-warning': '0 0 20px rgba(245, 158, 11, 0.4)',
'glow-warning-sm': '0 0 10px rgba(245, 158, 11, 0.3)',
'glow-warning-lg': '0 0 30px rgba(245, 158, 11, 0.5)',
// Subtle shadows for depth
'card': '0 1px 3px rgba(0, 0, 0, 0.3), 0 1px 2px rgba(0, 0, 0, 0.4)',
'card-hover': '0 4px 6px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.4)',
'elevated': '0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2)',
},
animation: {
'pulse-primary': 'pulsePrimary 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
'slide-in-right': 'slideInRight 0.3s ease-out',
'slide-in-bottom': 'slideInBottom 0.3s ease-out',
'fade-in': 'fadeIn 0.2s ease-out',
'scale-in': 'scaleIn 0.2s ease-out',
},
keyframes: {
pulsePrimary: {
'0%, 100%': {
opacity: '1',
},
'50%': {
opacity: '.7',
},
},
slideInRight: {
'0%': {
transform: 'translateX(100%)',
opacity: '0',
},
'100%': {
transform: 'translateX(0)',
opacity: '1',
},
},
slideInBottom: {
'0%': {
transform: 'translateY(100%)',
opacity: '0',
},
'100%': {
transform: 'translateY(0)',
opacity: '1',
},
},
fadeIn: {
'0%': {
opacity: '0',
},
'100%': {
opacity: '1',
},
},
scaleIn: {
'0%': {
transform: 'scale(0.95)',
opacity: '0',
},
'100%': {
transform: 'scale(1)',
opacity: '1',
},
},
},
},
},
plugins: [],
};