feat: Add FloatingTranscriptDebug with pixel art dark theme

Floating chat window reusing ChatContainer with draggable/resizable
window, agent/session selector overlay, and pixel art decorations
(galaxy, minecraft dirt block, LED strip) on black transparent backdrop.
This commit is contained in:
2026-02-19 03:35:53 -06:00
parent 06b48ebda3
commit badde06ef9
2 changed files with 1005 additions and 6 deletions

View File

@@ -6,6 +6,7 @@ import TorchButton from './components/TorchButton.vue'
import FloatingTerminal from './components/FloatingTerminal.vue'
import FloatingResponse from './components/FloatingResponse.vue'
import FloatingVoice from './components/FloatingVoice.vue'
import FloatingTranscriptDebug from './components/FloatingTranscriptDebug.vue'
import AgentBar from './components/AgentBar.vue'
import PwaInstallBanner from './components/PwaInstallBanner.vue'
import HooksApprovalModal from './components/HooksApprovalModal.vue'
@@ -23,6 +24,7 @@ const route = useRoute()
const router = useRouter()
const showTerminal = ref(false)
const showVoice = ref(false)
const showTranscriptDebug = ref(false)
const showDebugConsole = ref(false)
const toolbarVisible = ref(true)
const forceWco = ref(false)
@@ -450,7 +452,7 @@ watch(() => route.name, (newPage) => {
'session-start': showSessionStart,
notification: showNotification,
'tool-flash': showToolFlash,
'sheet-open': showTerminal || showVoice,
'sheet-open': showTerminal || showVoice || showTranscriptDebug,
'keyboard-visible': keyboardVisible
}"
@click="showTerminal = !showTerminal"
@@ -521,10 +523,22 @@ watch(() => route.name, (newPage) => {
</template>
</button>
<!-- Transcript Debug FAB Button -->
<button
class="transcript-fab"
:class="{ active: showTranscriptDebug, 'sheet-open': showTerminal || showVoice || showTranscriptDebug, 'keyboard-visible': keyboardVisible }"
@click="showTranscriptDebug = !showTranscriptDebug"
title="Transcript Debug"
>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
</svg>
</button>
<!-- Voice FAB Button -->
<button
class="voice-fab"
:class="{ active: showVoice, 'sheet-open': showTerminal || showVoice, 'ptt-active': voicePTTActive, 'keyboard-visible': keyboardVisible }"
:class="{ active: showVoice, 'sheet-open': showTerminal || showVoice || showTranscriptDebug, 'ptt-active': voicePTTActive, 'keyboard-visible': keyboardVisible }"
@click="handleVoiceFabClick"
@touchstart="handleVoiceFabTouchStart"
@touchend="handleVoiceFabTouchEnd"
@@ -552,6 +566,9 @@ watch(() => route.name, (newPage) => {
<!-- Floating Voice Input -->
<FloatingVoice ref="voiceRef" v-model="showVoice" />
<!-- Floating Transcript Debug -->
<FloatingTranscriptDebug v-model="showTranscriptDebug" />
<!-- Global Hooks Approval Modal -->
<HooksApprovalModal />
@@ -1309,6 +1326,49 @@ watch(() => route.name, (newPage) => {
50% { box-shadow: 0 0 50px rgba(249, 115, 22, 0.9); }
}
/* Transcript Debug FAB */
.transcript-fab {
position: fixed;
bottom: 20px;
left: 80px;
width: 44px;
height: 44px;
border-radius: 4px;
background: rgba(15, 15, 20, 0.85);
color: #818cf8;
border: 1px solid rgba(99, 102, 241, 0.2);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
transition: all 0.2s ease;
z-index: 9998;
-webkit-user-select: none;
user-select: none;
-webkit-touch-callout: none;
touch-action: manipulation;
backdrop-filter: blur(12px);
}
.transcript-fab:hover {
transform: translateY(-2px);
border-color: rgba(99, 102, 241, 0.4);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.5), 0 0 12px rgba(99, 102, 241, 0.15);
color: #a5b4fc;
}
.transcript-fab.active {
background: rgba(99, 102, 241, 0.15);
border-color: rgba(99, 102, 241, 0.4);
color: #c7d2fe;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4), 0 0 8px rgba(99, 102, 241, 0.2);
}
.transcript-fab.active:hover {
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.5), 0 0 12px rgba(99, 102, 241, 0.3);
}
@media (max-width: 768px) {
.terminal-fab {
bottom: 80px;
@@ -1340,28 +1400,39 @@ watch(() => route.name, (newPage) => {
width: 44px;
height: 44px;
}
.transcript-fab {
bottom: 80px;
left: 68px;
width: 40px;
height: 40px;
}
}
/* Mobile: FABs above bottom sheets */
@media (max-width: 1024px) and (pointer: coarse) {
.terminal-fab,
.voice-fab {
.voice-fab,
.transcript-fab {
z-index: 10001;
transition: bottom 0.25s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s ease;
}
.terminal-fab.sheet-open,
.voice-fab.sheet-open {
.voice-fab.sheet-open,
.transcript-fab.sheet-open {
bottom: calc(15vh + 100px);
}
.terminal-fab.keyboard-visible,
.voice-fab.keyboard-visible {
.voice-fab.keyboard-visible,
.transcript-fab.keyboard-visible {
bottom: 35vh;
}
.terminal-fab.keyboard-visible.sheet-open,
.voice-fab.keyboard-visible.sheet-open {
.voice-fab.keyboard-visible.sheet-open,
.transcript-fab.keyboard-visible.sheet-open {
bottom: 45vh;
}
}