Improve zoom animation and add black hole for cleanup

This commit is contained in:
Peter Steinberger 2025-06-23 00:45:51 +02:00
parent c574121f21
commit a582ab5188
4 changed files with 120 additions and 7 deletions

View file

@ -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 -->

View file

@ -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(

View file

@ -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>

View file

@ -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;
}
}