feat: animated border on active FAB and grayscale inactive FABs
Active terminal FAB shows a rotating conic-gradient border animation (cyan/indigo) on all three locations: main FAB (T1), AgentBadge, and mini FABs (T2-T5). Inactive FABs appear in grayscale with reduced brightness, brightening slightly on hover.
This commit is contained in:
@@ -85,6 +85,13 @@ const keyboardVisible = ref(false) // Virtual keyboard visible
|
||||
// Whether any terminal exists (T1+)
|
||||
const hasTerminals = computed(() => sessionState.terminalRegistry.length > 0)
|
||||
|
||||
// Whether terminal 1 is the currently active terminal
|
||||
const isT1Active = computed(() => {
|
||||
const reg = sessionState.terminalRegistry
|
||||
if (!reg.length) return false
|
||||
return reg[0].transcriptSessionId === transcriptDebugRef.value?.activeTerminalSessionId
|
||||
})
|
||||
|
||||
// Extra terminals (T2-T5) from Pinia store — fully reactive, no template ref dependency
|
||||
const extraTerminals = computed(() => {
|
||||
const reg = sessionState.terminalRegistry
|
||||
@@ -366,7 +373,7 @@ watch(() => route.name, (newPage) => {
|
||||
<span class="fab-bubble b3"></span>
|
||||
<button
|
||||
class="transcript-fab"
|
||||
:class="{ active: showTranscriptDebug }"
|
||||
:class="{ active: showTranscriptDebug, 't1-active': isT1Active }"
|
||||
@click="handleMainFabClick"
|
||||
@contextmenu.prevent
|
||||
title="Transcript Debug"
|
||||
@@ -845,8 +852,8 @@ watch(() => route.name, (newPage) => {
|
||||
box-shadow:
|
||||
0 2px 4px rgba(0, 0, 0, 0.5),
|
||||
0 6px 16px rgba(0, 0, 0, 0.6),
|
||||
0 12px 28px rgba(0, 0, 0, 0.4),
|
||||
inset 0 1px 0 rgba(14, 165, 233, 0.12);
|
||||
0 12px 28px rgba(0, 0, 0, 0.4);
|
||||
filter: grayscale(1) brightness(0.6);
|
||||
transition: all 0.2s ease;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
@@ -867,15 +874,14 @@ watch(() => route.name, (newPage) => {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.transcript-fab:hover {
|
||||
.transcript-fab:not(.t1-active):hover {
|
||||
transform: translateY(-2px);
|
||||
border-color: rgba(14, 165, 233, 0.35);
|
||||
filter: grayscale(0.5) brightness(0.8);
|
||||
box-shadow:
|
||||
0 4px 8px rgba(0, 0, 0, 0.5),
|
||||
0 10px 24px rgba(0, 0, 0, 0.6),
|
||||
0 16px 36px rgba(0, 0, 0, 0.4),
|
||||
0 0 14px rgba(14, 165, 233, 0.15),
|
||||
inset 0 1px 0 rgba(14, 165, 233, 0.2);
|
||||
0 0 14px rgba(14, 165, 233, 0.15);
|
||||
color: #38bdf8;
|
||||
}
|
||||
|
||||
@@ -888,6 +894,31 @@ watch(() => route.name, (newPage) => {
|
||||
color: #a5f3fc;
|
||||
}
|
||||
|
||||
.transcript-fab.t1-active {
|
||||
border: 2px solid;
|
||||
border-image: conic-gradient(
|
||||
from var(--border-angle, 0deg),
|
||||
rgba(34, 211, 238, 1),
|
||||
rgba(99, 102, 241, 0.7),
|
||||
rgba(34, 211, 238, 0.15),
|
||||
rgba(99, 102, 241, 0.7),
|
||||
rgba(34, 211, 238, 1)
|
||||
) 1;
|
||||
filter: none;
|
||||
box-shadow: 0 0 12px rgba(34, 211, 238, 0.3);
|
||||
animation: border-spin 3s linear infinite;
|
||||
}
|
||||
|
||||
@property --border-angle {
|
||||
syntax: "<angle>";
|
||||
initial-value: 0deg;
|
||||
inherits: false;
|
||||
}
|
||||
|
||||
@keyframes border-spin {
|
||||
to { --border-angle: 360deg; }
|
||||
}
|
||||
|
||||
.fab-button-area {
|
||||
position: relative;
|
||||
width: 44px;
|
||||
|
||||
@@ -625,7 +625,7 @@ onBeforeUnmount(() => {
|
||||
<AgentBadge
|
||||
v-if="selectedAgent"
|
||||
:agent="selectedAgent"
|
||||
:connected="isRealtime"
|
||||
:connected="!!activeTerminalSessionId"
|
||||
:terminals="openTerminals"
|
||||
:active-session-id="activeTerminalSessionId"
|
||||
:model="conversation?.model"
|
||||
|
||||
@@ -147,14 +147,32 @@ onBeforeUnmount(() => document.removeEventListener('mousedown', onClickOutside))
|
||||
}
|
||||
|
||||
.agent-badge-wrapper.connected {
|
||||
border-color: rgba(34, 197, 94, 0.35);
|
||||
border: 2px solid;
|
||||
border-image: conic-gradient(
|
||||
from var(--border-angle, 0deg),
|
||||
rgba(34, 211, 238, 1),
|
||||
rgba(99, 102, 241, 0.7),
|
||||
rgba(34, 211, 238, 0.15),
|
||||
rgba(99, 102, 241, 0.7),
|
||||
rgba(34, 211, 238, 1)
|
||||
) 1;
|
||||
background: rgba(34, 197, 94, 0.08);
|
||||
box-shadow: 0 0 8px rgba(34, 197, 94, 0.15);
|
||||
box-shadow: 0 0 12px rgba(34, 211, 238, 0.3);
|
||||
animation: border-spin 3s linear infinite;
|
||||
}
|
||||
|
||||
.agent-badge-wrapper.connected:hover {
|
||||
background: rgba(34, 197, 94, 0.15);
|
||||
border-color: rgba(34, 197, 94, 0.5);
|
||||
}
|
||||
|
||||
@property --border-angle {
|
||||
syntax: "<angle>";
|
||||
initial-value: 0deg;
|
||||
inherits: false;
|
||||
}
|
||||
|
||||
@keyframes border-spin {
|
||||
to { --border-angle: 360deg; }
|
||||
}
|
||||
|
||||
.agent-label {
|
||||
|
||||
@@ -86,27 +86,51 @@ const artVariants = [
|
||||
pointer-events: auto;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
filter: grayscale(1) brightness(0.6);
|
||||
}
|
||||
|
||||
.terminal-fab:hover {
|
||||
.terminal-fab:not(.active):hover {
|
||||
transform: translateY(-2px);
|
||||
border-color: rgba(14, 165, 233, 0.35);
|
||||
filter: grayscale(0.5) brightness(0.8);
|
||||
box-shadow:
|
||||
0 4px 8px rgba(0, 0, 0, 0.5),
|
||||
0 10px 24px rgba(0, 0, 0, 0.6),
|
||||
0 0 14px rgba(14, 165, 233, 0.15),
|
||||
inset 0 1px 0 rgba(14, 165, 233, 0.2);
|
||||
0 0 14px rgba(14, 165, 233, 0.15);
|
||||
}
|
||||
|
||||
.terminal-fab:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.terminal-fab:focus,
|
||||
.terminal-fab:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.terminal-fab.active {
|
||||
border-color: rgba(34, 211, 238, 0.4);
|
||||
box-shadow:
|
||||
0 0 6px rgba(34, 211, 238, 0.15),
|
||||
inset 0 0 4px rgba(34, 211, 238, 0.08);
|
||||
border: 2px solid;
|
||||
border-image: conic-gradient(
|
||||
from var(--border-angle, 0deg),
|
||||
rgba(34, 211, 238, 1),
|
||||
rgba(99, 102, 241, 0.7),
|
||||
rgba(34, 211, 238, 0.15),
|
||||
rgba(99, 102, 241, 0.7),
|
||||
rgba(34, 211, 238, 1)
|
||||
) 1;
|
||||
filter: none;
|
||||
box-shadow: 0 0 12px rgba(34, 211, 238, 0.3);
|
||||
animation: border-spin 3s linear infinite;
|
||||
}
|
||||
|
||||
@property --border-angle {
|
||||
syntax: "<angle>";
|
||||
initial-value: 0deg;
|
||||
inherits: false;
|
||||
}
|
||||
|
||||
@keyframes border-spin {
|
||||
to { --border-angle: 360deg; }
|
||||
}
|
||||
|
||||
.fab-number {
|
||||
|
||||
Reference in New Issue
Block a user