feat: Add Git page with branch selector, commit history, and diff viewer

Includes FileTree, CommitList, BranchSelector and DiffViewer components,
Git API routes, and mobile keyboard visibility handling for FAB buttons
This commit is contained in:
2026-02-14 05:49:16 -06:00
parent 2133e2d057
commit a856fefd98
18 changed files with 3015 additions and 13 deletions

View File

@@ -84,6 +84,7 @@ const awaitingPermission = ref(false) // Waiting for user permission (highest pr
const showSessionStart = ref(false) // Session start animation (3s)
const showNotification = ref(false) // Notification pulse (2s)
const showToolFlash = ref(false) // Tool use flash (500ms)
const keyboardVisible = ref(false) // Virtual keyboard visible
let statusWs: WebSocket | null = null
let statusReconnectTimeout: number | null = null
@@ -311,6 +312,16 @@ onMounted(async () => {
}
})
// Detect virtual keyboard on mobile
if (window.visualViewport) {
const initialHeight = window.visualViewport.height
window.visualViewport.addEventListener('resize', () => {
const currentHeight = window.visualViewport!.height
// If viewport shrinks significantly, keyboard is likely open
keyboardVisible.value = currentHeight < initialHeight * 0.75
})
}
// Start polling for token if not connected
const webmcp = getWebMCP()
if (!webmcp?.isConnected) {
@@ -387,7 +398,8 @@ watch(() => route.name, (newPage) => {
'session-start': showSessionStart,
notification: showNotification,
'tool-flash': showToolFlash,
'sheet-open': showTerminal || showVoice
'sheet-open': showTerminal || showVoice,
'keyboard-visible': keyboardVisible
}"
@click="showTerminal = !showTerminal"
:title="awaitingPermission ? `Permiso requerido: ${claudeTool || 'herramienta'}` : isProcessing ? `Claude: ${claudeTool || 'processing'}` : 'Toggle Terminal'"
@@ -460,7 +472,7 @@ watch(() => route.name, (newPage) => {
<!-- Voice FAB Button -->
<button
class="voice-fab"
:class="{ active: showVoice, 'sheet-open': showTerminal || showVoice, 'ptt-active': voicePTTActive }"
:class="{ active: showVoice, 'sheet-open': showTerminal || showVoice, 'ptt-active': voicePTTActive, 'keyboard-visible': keyboardVisible }"
@click="handleVoiceFabClick"
@touchstart="handleVoiceFabTouchStart"
@touchend="handleVoiceFabTouchEnd"
@@ -1014,7 +1026,7 @@ watch(() => route.name, (newPage) => {
@media (max-width: 768px) {
.terminal-fab {
bottom: 16px;
bottom: 80px;
right: 16px;
width: 54px;
height: 54px;
@@ -1038,7 +1050,7 @@ watch(() => route.name, (newPage) => {
}
.voice-fab {
bottom: 16px;
bottom: 80px;
left: 16px;
width: 44px;
height: 44px;
@@ -1055,7 +1067,17 @@ watch(() => route.name, (newPage) => {
.terminal-fab.sheet-open,
.voice-fab.sheet-open {
bottom: calc(10vh + 16px);
bottom: calc(15vh + 100px);
}
.terminal-fab.keyboard-visible,
.voice-fab.keyboard-visible {
bottom: 35vh;
}
.terminal-fab.keyboard-visible.sheet-open,
.voice-fab.keyboard-visible.sheet-open {
bottom: 45vh;
}
}