mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-06-25 05:09:06 +00:00
- Apply consistent black theme across all components with colored borders - Add animated VibeTunnel logo with rainbow scrolling gradient - Implement comprehensive mobile input controls with Ctrl+Alpha overlay - Add fit-to-width toggle button in session view header with scroll preservation - Enhance mobile experience with proper viewport handling and keyboard positioning - Update button styling to use black backgrounds with colored borders throughout - Resize scroll-to-bottom button for better mobile accessibility 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
111 lines
3.1 KiB
TypeScript
111 lines
3.1 KiB
TypeScript
import { LitElement, html } from 'lit';
|
|
import { customElement, state } from 'lit/decorators.js';
|
|
|
|
@customElement('vibe-logo')
|
|
export class VibeLogo extends LitElement {
|
|
// Disable shadow DOM to use Tailwind
|
|
createRenderRoot() {
|
|
return this;
|
|
}
|
|
|
|
@state() private frame = 0;
|
|
private animationInterval: number | null = null;
|
|
|
|
connectedCallback() {
|
|
super.connectedCallback();
|
|
this.startAnimation();
|
|
}
|
|
|
|
disconnectedCallback() {
|
|
super.disconnectedCallback();
|
|
this.stopAnimation();
|
|
}
|
|
|
|
private startAnimation() {
|
|
this.animationInterval = window.setInterval(() => {
|
|
this.frame = (this.frame + 1) % 12; // 12 frames for smooth animation
|
|
this.requestUpdate();
|
|
}, 200); // Change frame every 200ms
|
|
}
|
|
|
|
private stopAnimation() {
|
|
if (this.animationInterval) {
|
|
clearInterval(this.animationInterval);
|
|
this.animationInterval = null;
|
|
}
|
|
}
|
|
|
|
private getLogoFrame(): string {
|
|
const frames = [
|
|
'░░░▒▒▓▓█ VibeTunnel █▓▓▒▒░░░',
|
|
'░░▒▒▓▓█░ VibeTunnel ░█▓▓▒▒░░',
|
|
'░▒▒▓▓█░░ VibeTunnel ░░█▓▓▒▒░',
|
|
'▒▒▓▓█░░░ VibeTunnel ░░░█▓▓▒▒',
|
|
'▒▓▓█░░░░ VibeTunnel ░░░░█▓▓▒',
|
|
'▓▓█░░░░░ VibeTunnel ░░░░░█▓▓',
|
|
'▓█░░░░░░ VibeTunnel ░░░░░░█▓',
|
|
'█░░░░░░░ VibeTunnel ░░░░░░░█',
|
|
'░░░░░░░█ VibeTunnel █░░░░░░░',
|
|
'░░░░░░█▓ VibeTunnel ▓█░░░░░░',
|
|
'░░░░░█▓▓ VibeTunnel ▓▓█░░░░░',
|
|
'░░░░█▓▓▒ VibeTunnel ▒▓▓█░░░░',
|
|
];
|
|
|
|
return frames[this.frame];
|
|
}
|
|
|
|
private getRainbowColors() {
|
|
return [
|
|
'#ff0000',
|
|
'#ff4500',
|
|
'#ff8c00',
|
|
'#ffd700',
|
|
'#9acd32',
|
|
'#00ff00',
|
|
'#00ffff',
|
|
'#0080ff',
|
|
'#8000ff',
|
|
'#ff00ff',
|
|
'#ff1493',
|
|
'#ff69b4',
|
|
'#ffc0cb',
|
|
'#ffb6c1',
|
|
'#ffa0b4',
|
|
'#ff8fa3',
|
|
];
|
|
}
|
|
|
|
render() {
|
|
const frame = this.getLogoFrame();
|
|
const colors = this.getRainbowColors();
|
|
|
|
// Parse the frame to apply rainbow colors
|
|
const parts = frame.split(' VibeTunnel ');
|
|
const leftPart = parts[0];
|
|
const rightPart = parts[1];
|
|
|
|
const coloredLeft = leftPart
|
|
.split('')
|
|
.map((char, i) =>
|
|
char === ' ' ? ' ' : html`<span style="color: ${colors[i % colors.length]};">${char}</span>`
|
|
);
|
|
|
|
const coloredRight = rightPart
|
|
.split('')
|
|
.map((char, i) =>
|
|
char === ' '
|
|
? ' '
|
|
: html`<span style="color: ${colors[(leftPart.length - 1 - i) % colors.length]};"
|
|
>${char}</span
|
|
>`
|
|
);
|
|
|
|
return html`
|
|
<div class="font-mono text-sm select-none leading-tight text-center">
|
|
<pre
|
|
class="whitespace-pre"
|
|
>${coloredLeft} <span class="text-vs-user">VibeTunnel</span> ${coloredRight}</pre>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|