mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-17 13:15:53 +00:00
Improve zoom animation and add black hole for cleanup
This commit is contained in:
parent
c574121f21
commit
a582ab5188
4 changed files with 120 additions and 7 deletions
|
|
@ -225,8 +225,9 @@ export class SessionCard extends LitElement {
|
|||
<div
|
||||
class="card cursor-pointer overflow-hidden ${this.killing ? 'opacity-60' : ''} ${this
|
||||
.isActive && this.session.status === 'running'
|
||||
? 'border-2 border-accent-green'
|
||||
? 'shadow-[0_0_0_2px_theme(colors.accent.green)] shadow-glow-green-sm'
|
||||
: ''}"
|
||||
style="view-transition-name: session-${this.session.id}"
|
||||
@click=${this.handleCardClick}
|
||||
>
|
||||
<!-- Compact Header -->
|
||||
|
|
|
|||
|
|
@ -61,11 +61,34 @@ export class SessionList extends LitElement {
|
|||
);
|
||||
}
|
||||
|
||||
private handleSessionKilled(e: CustomEvent) {
|
||||
const { sessionId } = e.detail;
|
||||
private async handleSessionKilled(e: CustomEvent) {
|
||||
const { sessionId, session } = e.detail;
|
||||
logger.debug(`session ${sessionId} killed, updating session list`);
|
||||
|
||||
// Immediately remove the session from the local state for instant UI feedback
|
||||
// Check if this is a cleanup action (exited session) vs kill action (running session)
|
||||
const isCleanup = session?.status === 'exited';
|
||||
|
||||
if (isCleanup) {
|
||||
// Find the session card element for cleanup animation
|
||||
const sessionCards = this.querySelectorAll('session-card');
|
||||
let targetCard: HTMLElement | null = null;
|
||||
|
||||
sessionCards.forEach((card) => {
|
||||
const sessionCard = card as HTMLElement & { session?: { id: string } };
|
||||
if (sessionCard.session?.id === sessionId) {
|
||||
targetCard = sessionCard;
|
||||
}
|
||||
});
|
||||
|
||||
// Apply black hole animation to the card
|
||||
if (targetCard) {
|
||||
(targetCard as HTMLElement).classList.add('black-hole-collapsing');
|
||||
// Wait for animation to complete
|
||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the session from the local state
|
||||
this.sessions = this.sessions.filter((session) => session.id !== sessionId);
|
||||
|
||||
// Then trigger a refresh to get the latest server state
|
||||
|
|
@ -96,6 +119,35 @@ export class SessionList extends LitElement {
|
|||
});
|
||||
|
||||
if (response.ok) {
|
||||
// Get the list of exited sessions before cleanup
|
||||
const exitedSessions = this.sessions.filter((s) => s.status === 'exited');
|
||||
|
||||
// Apply black hole animation to all exited sessions
|
||||
if (exitedSessions.length > 0) {
|
||||
const sessionCards = this.querySelectorAll('session-card');
|
||||
const exitedCards: HTMLElement[] = [];
|
||||
|
||||
sessionCards.forEach((card) => {
|
||||
const sessionCard = card as HTMLElement & { session?: { id: string; status: string } };
|
||||
if (sessionCard.session?.status === 'exited') {
|
||||
exitedCards.push(sessionCard);
|
||||
}
|
||||
});
|
||||
|
||||
// Apply animation to all exited cards
|
||||
exitedCards.forEach((card) => {
|
||||
card.classList.add('black-hole-collapsing');
|
||||
});
|
||||
|
||||
// Wait for animation to complete
|
||||
if (exitedCards.length > 0) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||
}
|
||||
|
||||
// Remove all exited sessions at once
|
||||
this.sessions = this.sessions.filter((session) => session.status !== 'exited');
|
||||
}
|
||||
|
||||
this.dispatchEvent(new CustomEvent('refresh'));
|
||||
} else {
|
||||
this.dispatchEvent(
|
||||
|
|
|
|||
|
|
@ -1235,7 +1235,7 @@ export class Terminal extends LitElement {
|
|||
class="terminal-container w-full h-full overflow-hidden"
|
||||
tabindex="0"
|
||||
contenteditable="false"
|
||||
style="view-transition-name: terminal-${this.sessionId}"
|
||||
style="view-transition-name: session-${this.sessionId}"
|
||||
@paste=${this.handlePaste}
|
||||
@click=${this.handleClick}
|
||||
></div>
|
||||
|
|
|
|||
|
|
@ -722,8 +722,8 @@ body {
|
|||
/* View Transitions */
|
||||
@supports (view-transition-name: none) {
|
||||
::view-transition {
|
||||
/* Set the transition duration to 0.1s as requested */
|
||||
--transition-duration: 100ms;
|
||||
/* Set the transition duration to 0.2s */
|
||||
--transition-duration: 200ms;
|
||||
}
|
||||
|
||||
::view-transition-group(*) {
|
||||
|
|
@ -850,4 +850,64 @@ body {
|
|||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Black hole collapse animation for session removal */
|
||||
@keyframes black-hole-collapse {
|
||||
0% {
|
||||
transform: scale(1) rotate(0deg);
|
||||
opacity: 1;
|
||||
filter: brightness(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(0.3) rotate(180deg);
|
||||
opacity: 0.8;
|
||||
filter: brightness(0.6) hue-rotate(90deg);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0) rotate(360deg);
|
||||
opacity: 0;
|
||||
filter: brightness(0) hue-rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Zoom animations for session navigation */
|
||||
@keyframes zoom-in {
|
||||
from {
|
||||
transform: scale(0.7);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zoom-out {
|
||||
from {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
transform: scale(1.3);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Session navigation transitions */
|
||||
::view-transition-old(session-*) {
|
||||
animation: zoom-out 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
::view-transition-new(session-*) {
|
||||
animation: zoom-in 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
/* Black hole collapse animation class */
|
||||
.black-hole-collapsing {
|
||||
animation: black-hole-collapse 0.3s cubic-bezier(0.4, 0, 1, 1) forwards;
|
||||
transform-origin: center;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue