refactor: remove legacy chat/agent implementations, keep transcript-debug
Remove FloatingTerminal (#1), AgentBar/FloatBubble (#2), and all related components, composables, types, handlers, routes, and CSS. Clean up orphaned references in ToolsDropdown, whisperSocket, and claude-hook comments. Transcript-debug is now the sole chat/agent system. Deleted: 15 files (~3500 lines) Edited: 12 files (-717 lines net)
This commit is contained in:
@@ -3,12 +3,10 @@ import { ref, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { RouterView, useRoute, useRouter } from 'vue-router'
|
||||
import Toolbar from './components/Toolbar.vue'
|
||||
import TorchButton from './components/TorchButton.vue'
|
||||
import FloatingTerminal from './components/FloatingTerminal.vue'
|
||||
import FloatingResponse from './components/FloatingResponse.vue'
|
||||
import { initWhisperSocket } from './services/whisperSocket'
|
||||
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'
|
||||
import { useGlobalApproval } from './composables/useGlobalApproval'
|
||||
@@ -16,14 +14,12 @@ import { initWebMCP, getWebMCP } from './services/webmcp'
|
||||
import { initTorch, destroyTorch } from './services/torch'
|
||||
import { endpoints } from './config/endpoints'
|
||||
import { initToolRegistry, activatePageTools, initToolsOnRefresh } from './services/toolRegistry'
|
||||
import { setTerminalControls } from './services/tools/handlers/terminalHandlers'
|
||||
import { setResponseControls } from './services/tools/handlers/responseHandlers'
|
||||
import { useCanvasStore } from './stores/canvas'
|
||||
import { useProjectCanvasStore } from './stores/projectCanvas'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const showTerminal = ref(false)
|
||||
const showVoice = ref(false)
|
||||
const showTranscriptDebug = ref(false)
|
||||
const showDebugConsole = ref(false)
|
||||
@@ -68,7 +64,6 @@ function copyDebugLogs() {
|
||||
function clearDebugLogs() {
|
||||
debugLogs.value = []
|
||||
}
|
||||
const terminalRef = ref<InstanceType<typeof FloatingTerminal> | null>(null)
|
||||
const responseRef = ref<InstanceType<typeof FloatingResponse> | null>(null)
|
||||
const voiceRef = ref<InstanceType<typeof FloatingVoice> | null>(null)
|
||||
const transcriptDebugRef = ref<InstanceType<typeof FloatingTranscriptDebug> | null>(null)
|
||||
@@ -81,28 +76,8 @@ const voicePTTActive = ref(false)
|
||||
let voiceTouchStarted = false
|
||||
let voicePTTTimeout: number | null = null
|
||||
|
||||
// Claude status state (for FAB animations)
|
||||
type ClaudeStatus = 'idle' | 'processing' | 'toolUse' | 'toolDone' | 'reading' | 'writing' | 'sessionStart' | 'subagentStart' | 'subagentStop' | 'notification' | 'permissionRequest' | 'thinking'
|
||||
|
||||
const claudeStatus = ref<ClaudeStatus>('idle')
|
||||
const claudeTool = ref<string | null>(null)
|
||||
const isProcessing = ref(false) // Main processing state (UserPromptSubmit → Stop)
|
||||
const isReading = ref(false) // Reading files
|
||||
const isWriting = ref(false) // Writing files
|
||||
const hasSubagent = ref(false) // Subagent active
|
||||
const awaitingPermission = ref(false) // Waiting for user permission (highest priority)
|
||||
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
|
||||
let processingTimeout: number | null = null
|
||||
let sessionStartTimeout: number | null = null
|
||||
let notificationTimeout: number | null = null
|
||||
let toolFlashTimeout: number | null = null
|
||||
|
||||
function hardRefresh() {
|
||||
location.reload()
|
||||
}
|
||||
@@ -161,117 +136,7 @@ function handleVoiceFabTouchEnd(e: TouchEvent) {
|
||||
setTimeout(() => { voiceTouchStarted = false }, 100)
|
||||
}
|
||||
|
||||
function connectStatusWs() {
|
||||
if (statusWs?.readyState === WebSocket.OPEN) return
|
||||
|
||||
statusWs = new WebSocket(endpoints.claudeStatus)
|
||||
|
||||
statusWs.onopen = () => {
|
||||
console.log('[App] Status WebSocket connected')
|
||||
}
|
||||
|
||||
statusWs.onmessage = (event) => {
|
||||
try {
|
||||
const msg = JSON.parse(event.data)
|
||||
if (msg.type === 'claude-status') {
|
||||
const status = msg.status as ClaudeStatus
|
||||
const agent = msg.agent || 'main'
|
||||
console.log('[App] Claude status:', status, msg.tool, agent)
|
||||
|
||||
// Only animate the main FAB for 'main' agent — other agents use AgentBar
|
||||
if (agent !== 'main') return
|
||||
|
||||
claudeStatus.value = status
|
||||
claudeTool.value = msg.tool || null
|
||||
|
||||
// Handle different status types
|
||||
switch (status) {
|
||||
case 'processing':
|
||||
case 'thinking':
|
||||
isProcessing.value = true
|
||||
// Auto-reset after 2 minutes (safety)
|
||||
if (processingTimeout) clearTimeout(processingTimeout)
|
||||
processingTimeout = window.setTimeout(() => {
|
||||
isProcessing.value = false
|
||||
}, 120000)
|
||||
break
|
||||
|
||||
case 'idle':
|
||||
isProcessing.value = false
|
||||
isReading.value = false
|
||||
isWriting.value = false
|
||||
awaitingPermission.value = false
|
||||
if (processingTimeout) clearTimeout(processingTimeout)
|
||||
break
|
||||
|
||||
case 'permissionRequest':
|
||||
awaitingPermission.value = true
|
||||
break
|
||||
|
||||
case 'reading':
|
||||
isReading.value = true
|
||||
triggerToolFlash()
|
||||
break
|
||||
|
||||
case 'writing':
|
||||
isWriting.value = true
|
||||
triggerToolFlash()
|
||||
break
|
||||
|
||||
case 'toolUse':
|
||||
triggerToolFlash()
|
||||
break
|
||||
|
||||
case 'toolDone':
|
||||
isReading.value = false
|
||||
isWriting.value = false
|
||||
awaitingPermission.value = false
|
||||
break
|
||||
|
||||
case 'sessionStart':
|
||||
showSessionStart.value = true
|
||||
if (sessionStartTimeout) clearTimeout(sessionStartTimeout)
|
||||
sessionStartTimeout = window.setTimeout(() => {
|
||||
showSessionStart.value = false
|
||||
}, 3000)
|
||||
break
|
||||
|
||||
case 'subagentStart':
|
||||
hasSubagent.value = true
|
||||
break
|
||||
|
||||
case 'subagentStop':
|
||||
hasSubagent.value = false
|
||||
break
|
||||
|
||||
case 'notification':
|
||||
showNotification.value = true
|
||||
if (notificationTimeout) clearTimeout(notificationTimeout)
|
||||
notificationTimeout = window.setTimeout(() => {
|
||||
showNotification.value = false
|
||||
}, 2000)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
} catch { /* ignore non-JSON messages */ }
|
||||
}
|
||||
|
||||
statusWs.onclose = () => {
|
||||
isProcessing.value = false
|
||||
statusReconnectTimeout = window.setTimeout(connectStatusWs, 2000)
|
||||
}
|
||||
}
|
||||
|
||||
function triggerToolFlash() {
|
||||
showToolFlash.value = true
|
||||
if (toolFlashTimeout) clearTimeout(toolFlashTimeout)
|
||||
toolFlashTimeout = window.setTimeout(() => {
|
||||
showToolFlash.value = false
|
||||
}, 500)
|
||||
}
|
||||
|
||||
type PageName = 'home' | 'canvas' | 'components' | 'themes' | 'project-canvas' | 'database' | 'source' | 'terminal' | 'tools' | 'agents'
|
||||
type PageName = 'home' | 'canvas' | 'components' | 'themes' | 'project-canvas' | 'database' | 'source' | 'tools' | 'agents'
|
||||
|
||||
function syncThemeColor() {
|
||||
const bg = getComputedStyle(document.documentElement).getPropertyValue('--bg-primary').trim()
|
||||
@@ -284,9 +149,6 @@ onMounted(async () => {
|
||||
// Sync Windows titlebar color with CSS variable
|
||||
syncThemeColor()
|
||||
|
||||
// Connect to WebSocket for Claude status updates
|
||||
connectStatusWs()
|
||||
|
||||
// Connect global hooks approval WS
|
||||
connectApproval()
|
||||
fetchApprovalPending()
|
||||
@@ -307,43 +169,6 @@ onMounted(async () => {
|
||||
const currentPage = (route.name as string) || 'canvas'
|
||||
initToolsOnRefresh(currentPage as PageName)
|
||||
|
||||
// Setup terminal controls for MCP tools
|
||||
setTerminalControls({
|
||||
open: (x?: number, y?: number) => {
|
||||
if (terminalRef.value) {
|
||||
terminalRef.value.open(x, y)
|
||||
} else {
|
||||
showTerminal.value = true
|
||||
}
|
||||
},
|
||||
close: () => {
|
||||
if (terminalRef.value) {
|
||||
terminalRef.value.close()
|
||||
} else {
|
||||
showTerminal.value = false
|
||||
}
|
||||
},
|
||||
toggle: () => {
|
||||
if (terminalRef.value) {
|
||||
terminalRef.value.toggle()
|
||||
} else {
|
||||
showTerminal.value = !showTerminal.value
|
||||
}
|
||||
},
|
||||
move: (x: number, y: number) => {
|
||||
terminalRef.value?.move(x, y)
|
||||
},
|
||||
resize: (w: number, h: number) => {
|
||||
terminalRef.value?.resize(w, h)
|
||||
},
|
||||
getState: () => {
|
||||
if (terminalRef.value) {
|
||||
return terminalRef.value.getState()
|
||||
}
|
||||
return { isOpen: showTerminal.value, position: { x: 0, y: 0 }, size: { w: 580, h: 360 } }
|
||||
}
|
||||
})
|
||||
|
||||
// Setup response controls for MCP tools
|
||||
setResponseControls({
|
||||
addMessage: (message: string, type?: 'info' | 'success' | 'warning' | 'error') => {
|
||||
@@ -388,12 +213,6 @@ onUnmounted(() => {
|
||||
document.removeEventListener('keydown', handleGlobalKeydown)
|
||||
destroyTorch()
|
||||
disconnectApproval()
|
||||
if (statusReconnectTimeout) clearTimeout(statusReconnectTimeout)
|
||||
if (processingTimeout) clearTimeout(processingTimeout)
|
||||
if (sessionStartTimeout) clearTimeout(sessionStartTimeout)
|
||||
if (notificationTimeout) clearTimeout(notificationTimeout)
|
||||
if (toolFlashTimeout) clearTimeout(toolFlashTimeout)
|
||||
statusWs?.close()
|
||||
})
|
||||
|
||||
// Watch for route changes and update tools
|
||||
@@ -468,94 +287,10 @@ watch(() => route.name, (newPage) => {
|
||||
</RouterView>
|
||||
</main>
|
||||
|
||||
<!-- Floating Terminal Toggle Button (Claude Life FAB) -->
|
||||
<button
|
||||
class="terminal-fab"
|
||||
:class="{
|
||||
active: showTerminal,
|
||||
processing: isProcessing,
|
||||
reading: isReading,
|
||||
writing: isWriting,
|
||||
subagent: hasSubagent,
|
||||
permission: awaitingPermission,
|
||||
'session-start': showSessionStart,
|
||||
notification: showNotification,
|
||||
'tool-flash': showToolFlash,
|
||||
'sheet-open': showTerminal || showVoice || showTranscriptDebug,
|
||||
'keyboard-visible': keyboardVisible
|
||||
}"
|
||||
@click="showTerminal = !showTerminal"
|
||||
:title="awaitingPermission ? `Permiso requerido: ${claudeTool || 'herramienta'}` : isProcessing ? `Claude: ${claudeTool || 'processing'}` : 'Toggle Terminal'"
|
||||
>
|
||||
<!-- Subagent orbital ring -->
|
||||
<svg v-if="hasSubagent" class="orbital-ring" viewBox="0 0 100 100">
|
||||
<circle cx="50" cy="50" r="45" fill="none" stroke="currentColor" stroke-width="2" stroke-dasharray="20 10" />
|
||||
</svg>
|
||||
|
||||
<!-- Session start ripples -->
|
||||
<div v-if="showSessionStart" class="session-ripples">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
|
||||
<!-- Permission request icon (alert/hand) - highest priority -->
|
||||
<svg v-if="awaitingPermission" class="permission-icon" xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>
|
||||
<line x1="12" y1="9" x2="12" y2="13"/>
|
||||
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
||||
</svg>
|
||||
|
||||
<!-- Processing animation (three dots) -->
|
||||
<div v-else-if="isProcessing && !isReading && !isWriting" class="thinking-dots">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
|
||||
<!-- Reading icon (eye) -->
|
||||
<svg v-else-if="isReading" class="status-icon" xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
</svg>
|
||||
|
||||
<!-- Writing icon (pencil) -->
|
||||
<svg v-else-if="isWriting" class="status-icon" xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 19l7-7 3 3-7 7-3-3z"/>
|
||||
<path d="M18 13l-1.5-7.5L2 2l3.5 14.5L13 18l5-5z"/>
|
||||
<path d="M2 2l7.586 7.586"/>
|
||||
<circle cx="11" cy="11" r="2"/>
|
||||
</svg>
|
||||
|
||||
<!-- Normal icons -->
|
||||
<template v-else>
|
||||
<!-- Terminal closed: Nucleo atom icon -->
|
||||
<svg v-if="!showTerminal" class="nucleo-icon" width="28" height="28" viewBox="0 0 24 24" fill="none">
|
||||
<!-- Core nucleus -->
|
||||
<circle cx="12" cy="12" r="3.5" fill="white" opacity="0.95"/>
|
||||
<!-- Orbital rings -->
|
||||
<ellipse cx="12" cy="12" rx="8" ry="3.5" stroke="white" stroke-width="1.3" fill="none" opacity="0.7" transform="rotate(-30 12 12)"/>
|
||||
<ellipse cx="12" cy="12" rx="8" ry="3.5" stroke="white" stroke-width="1.3" fill="none" opacity="0.5" transform="rotate(30 12 12)"/>
|
||||
<ellipse cx="12" cy="12" rx="8" ry="3.5" stroke="white" stroke-width="1.3" fill="none" opacity="0.6" transform="rotate(90 12 12)"/>
|
||||
<!-- Electrons -->
|
||||
<circle cx="12" cy="4" r="1.8" fill="white" opacity="0.9"/>
|
||||
<circle cx="19" cy="14" r="1.8" fill="white" opacity="0.7"/>
|
||||
<circle cx="5" cy="14" r="1.8" fill="white" opacity="0.8"/>
|
||||
</svg>
|
||||
<!-- Terminal open: Minimize chevron with connection indicator -->
|
||||
<template v-else>
|
||||
<span class="connection-dot"></span>
|
||||
<svg class="minimize-icon" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</template>
|
||||
</template>
|
||||
</button>
|
||||
|
||||
<!-- Transcript Debug FAB Button (pixel art ocean) -->
|
||||
<button
|
||||
class="transcript-fab"
|
||||
:class="{ active: showTranscriptDebug, 'sheet-open': showTerminal || showVoice || showTranscriptDebug, 'keyboard-visible': keyboardVisible }"
|
||||
:class="{ active: showTranscriptDebug, 'sheet-open': showVoice || showTranscriptDebug, 'keyboard-visible': keyboardVisible }"
|
||||
@click="showTranscriptDebug = !showTranscriptDebug"
|
||||
title="Transcript Debug"
|
||||
>
|
||||
@@ -577,7 +312,7 @@ watch(() => route.name, (newPage) => {
|
||||
<!-- Voice FAB Button -->
|
||||
<button
|
||||
class="voice-fab"
|
||||
:class="{ active: showVoice, 'sheet-open': showTerminal || showVoice || showTranscriptDebug, 'ptt-active': voicePTTActive, 'keyboard-visible': keyboardVisible }"
|
||||
:class="{ active: showVoice, 'sheet-open': showVoice || showTranscriptDebug, 'ptt-active': voicePTTActive, 'keyboard-visible': keyboardVisible }"
|
||||
@click="handleVoiceFabClick"
|
||||
@touchstart="handleVoiceFabTouchStart"
|
||||
@touchend="handleVoiceFabTouchEnd"
|
||||
@@ -592,12 +327,6 @@ watch(() => route.name, (newPage) => {
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Agent Bar (bottom pills) -->
|
||||
<AgentBar />
|
||||
|
||||
<!-- Floating Terminal -->
|
||||
<FloatingTerminal ref="terminalRef" v-model="showTerminal" />
|
||||
|
||||
<!-- Floating Response (Agent UI messages) -->
|
||||
<FloatingResponse ref="responseRef" />
|
||||
|
||||
@@ -951,368 +680,6 @@ watch(() => route.name, (newPage) => {
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
|
||||
/* Terminal FAB - Claude Life */
|
||||
.terminal-fab {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
width: 58px;
|
||||
height: 58px;
|
||||
border-radius: 18px;
|
||||
background: linear-gradient(145deg, #7c3aed 0%, #6366f1 50%, #8b5cf6 100%);
|
||||
color: white;
|
||||
border: 2px solid rgba(255, 255, 255, 0.15);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow:
|
||||
0 4px 15px rgba(124, 58, 237, 0.4),
|
||||
0 8px 30px rgba(99, 102, 241, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
z-index: 9998;
|
||||
overflow: visible;
|
||||
backdrop-filter: blur(10px);
|
||||
/* Prevent text selection and touch gestures */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-touch-callout: none;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
.terminal-fab::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -3px;
|
||||
border-radius: 21px;
|
||||
background: linear-gradient(145deg, rgba(139, 92, 246, 0.5), rgba(99, 102, 241, 0.2));
|
||||
z-index: -1;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.terminal-fab:hover {
|
||||
transform: translateY(-3px) scale(1.05);
|
||||
box-shadow:
|
||||
0 8px 25px rgba(124, 58, 237, 0.5),
|
||||
0 15px 40px rgba(99, 102, 241, 0.35),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
|
||||
.terminal-fab:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Nucleo atom icon animation */
|
||||
.nucleo-icon {
|
||||
animation: nucleo-orbit 8s linear infinite;
|
||||
filter: drop-shadow(0 0 6px rgba(255, 255, 255, 0.6));
|
||||
}
|
||||
|
||||
.nucleo-icon ellipse {
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
/* Terminal active - Connected state */
|
||||
.terminal-fab.active {
|
||||
background: linear-gradient(145deg, #6366f1 0%, #4f46e5 50%, #7c3aed 100%);
|
||||
border-color: rgba(16, 185, 129, 0.5);
|
||||
box-shadow:
|
||||
0 4px 15px rgba(99, 102, 241, 0.4),
|
||||
0 8px 30px rgba(79, 70, 229, 0.3),
|
||||
0 0 20px rgba(16, 185, 129, 0.2),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.terminal-fab.active:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow:
|
||||
0 6px 20px rgba(99, 102, 241, 0.5),
|
||||
0 12px 35px rgba(79, 70, 229, 0.35),
|
||||
0 0 25px rgba(16, 185, 129, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
|
||||
/* Connection indicator dot */
|
||||
.connection-dot {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: #10b981;
|
||||
border-radius: 50%;
|
||||
border: 2px solid rgba(255, 255, 255, 0.9);
|
||||
box-shadow: 0 0 8px rgba(16, 185, 129, 0.8);
|
||||
animation: connection-pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* Minimize icon */
|
||||
.minimize-icon {
|
||||
opacity: 0.9;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.terminal-fab.active:hover .minimize-icon {
|
||||
transform: translateY(2px);
|
||||
}
|
||||
|
||||
/* Processing state (UserPromptSubmit → Stop) - Orange pulsing */
|
||||
.terminal-fab.processing {
|
||||
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%) !important;
|
||||
box-shadow: 0 8px 24px rgba(245, 158, 11, 0.4) !important;
|
||||
animation: processing-pulse 2s ease-in-out infinite !important;
|
||||
transform: rotate(0deg) !important;
|
||||
}
|
||||
|
||||
.terminal-fab.processing:hover {
|
||||
box-shadow: 0 12px 32px rgba(245, 158, 11, 0.5) !important;
|
||||
transform: scale(1.05) !important;
|
||||
}
|
||||
|
||||
/* Reading state - Cyan scanning */
|
||||
.terminal-fab.reading {
|
||||
background: linear-gradient(135deg, #06b6d4 0%, #0891b2 100%) !important;
|
||||
box-shadow: 0 8px 24px rgba(6, 182, 212, 0.4) !important;
|
||||
animation: reading-scan 1.5s ease-in-out infinite !important;
|
||||
}
|
||||
|
||||
/* Writing state - Green pulse */
|
||||
.terminal-fab.writing {
|
||||
background: linear-gradient(135deg, #10b981 0%, #059669 100%) !important;
|
||||
box-shadow: 0 8px 24px rgba(16, 185, 129, 0.4) !important;
|
||||
animation: writing-pulse 0.8s ease-in-out infinite !important;
|
||||
}
|
||||
|
||||
/* Subagent active - Purple with orbital ring */
|
||||
.terminal-fab.subagent {
|
||||
box-shadow: 0 8px 24px rgba(139, 92, 246, 0.5) !important;
|
||||
}
|
||||
|
||||
/* Session start - Green wake-up effect */
|
||||
.terminal-fab.session-start {
|
||||
animation: session-wake 3s ease-out forwards !important;
|
||||
}
|
||||
|
||||
/* Notification - Yellow bounce */
|
||||
.terminal-fab.notification {
|
||||
animation: notification-bounce 0.5s ease-in-out 4 !important;
|
||||
}
|
||||
|
||||
/* Permission Request - HIGHEST PRIORITY - Red pulsing alert */
|
||||
.terminal-fab.permission {
|
||||
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%) !important;
|
||||
box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7) !important;
|
||||
animation: permission-pulse 1s ease-in-out infinite !important;
|
||||
transform: rotate(0deg) scale(1.1) !important;
|
||||
z-index: 10000 !important;
|
||||
}
|
||||
|
||||
.terminal-fab.permission:hover {
|
||||
transform: scale(1.15) !important;
|
||||
}
|
||||
|
||||
/* Permission icon animation */
|
||||
.permission-icon {
|
||||
animation: permission-shake 0.5s ease-in-out infinite;
|
||||
filter: drop-shadow(0 0 8px rgba(255, 255, 255, 0.8));
|
||||
}
|
||||
|
||||
/* Tool flash - Quick white flash */
|
||||
.terminal-fab.tool-flash::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -4px;
|
||||
border-radius: 20px;
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
animation: tool-flash 0.5s ease-out forwards;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Thinking dots animation */
|
||||
.thinking-dots {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.thinking-dots span {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
animation: thinking-dot 1.4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.thinking-dots span:nth-child(1) { animation-delay: 0s; }
|
||||
.thinking-dots span:nth-child(2) { animation-delay: 0.2s; }
|
||||
.thinking-dots span:nth-child(3) { animation-delay: 0.4s; }
|
||||
|
||||
/* Status icons */
|
||||
.status-icon {
|
||||
animation: icon-breathe 1s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* Orbital ring for subagent */
|
||||
.orbital-ring {
|
||||
position: absolute;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
animation: orbit-spin 3s linear infinite;
|
||||
color: rgba(139, 92, 246, 0.8);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Session start ripples */
|
||||
.session-ripples {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.session-ripples span {
|
||||
position: absolute;
|
||||
inset: -10px;
|
||||
border: 2px solid rgba(16, 185, 129, 0.6);
|
||||
border-radius: 20px;
|
||||
animation: ripple-expand 3s ease-out forwards;
|
||||
}
|
||||
|
||||
.session-ripples span:nth-child(1) { animation-delay: 0s; }
|
||||
.session-ripples span:nth-child(2) { animation-delay: 0.5s; }
|
||||
.session-ripples span:nth-child(3) { animation-delay: 1s; }
|
||||
|
||||
/* Keyframes */
|
||||
@keyframes thinking-dot {
|
||||
0%, 80%, 100% { transform: scale(0.6); opacity: 0.5; }
|
||||
40% { transform: scale(1); opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes processing-pulse {
|
||||
0%, 100% { box-shadow: 0 8px 24px rgba(245, 158, 11, 0.4); }
|
||||
50% { box-shadow: 0 8px 40px rgba(245, 158, 11, 0.7); }
|
||||
}
|
||||
|
||||
@keyframes reading-scan {
|
||||
0%, 100% {
|
||||
box-shadow: 0 8px 24px rgba(6, 182, 212, 0.4);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
25% { transform: rotate(-2deg); }
|
||||
75% { transform: rotate(2deg); }
|
||||
50% {
|
||||
box-shadow: 0 8px 40px rgba(6, 182, 212, 0.7);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes writing-pulse {
|
||||
0%, 100% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 8px 24px rgba(16, 185, 129, 0.4);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 8px 32px rgba(16, 185, 129, 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes session-wake {
|
||||
0% {
|
||||
transform: scale(0.8);
|
||||
opacity: 0.5;
|
||||
box-shadow: 0 0 0 rgba(16, 185, 129, 0);
|
||||
}
|
||||
30% {
|
||||
transform: scale(1.15);
|
||||
box-shadow: 0 0 60px rgba(16, 185, 129, 0.6);
|
||||
}
|
||||
60% { transform: scale(0.95); }
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
box-shadow: 0 8px 24px rgba(99, 102, 241, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes notification-bounce {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-8px); }
|
||||
}
|
||||
|
||||
@keyframes tool-flash {
|
||||
0% { opacity: 1; transform: scale(1); }
|
||||
100% { opacity: 0; transform: scale(1.3); }
|
||||
}
|
||||
|
||||
@keyframes icon-breathe {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.7; }
|
||||
}
|
||||
|
||||
@keyframes orbit-spin {
|
||||
from { transform: translate(-50%, -50%) rotate(0deg); }
|
||||
to { transform: translate(-50%, -50%) rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes ripple-expand {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: scale(2);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes permission-pulse {
|
||||
0%, 100% {
|
||||
box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7), 0 8px 24px rgba(239, 68, 68, 0.5);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 0 15px rgba(239, 68, 68, 0), 0 8px 40px rgba(239, 68, 68, 0.8);
|
||||
transform: scale(1.15);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes permission-shake {
|
||||
0%, 100% { transform: rotate(0deg); }
|
||||
25% { transform: rotate(-5deg); }
|
||||
75% { transform: rotate(5deg); }
|
||||
}
|
||||
|
||||
@keyframes nucleo-orbit {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
filter: drop-shadow(0 0 6px rgba(255, 255, 255, 0.6));
|
||||
}
|
||||
50% {
|
||||
filter: drop-shadow(0 0 10px rgba(255, 255, 255, 0.9));
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
filter: drop-shadow(0 0 6px rgba(255, 255, 255, 0.6));
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes connection-pulse {
|
||||
0%, 100% {
|
||||
box-shadow: 0 0 8px rgba(16, 185, 129, 0.8);
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 12px rgba(16, 185, 129, 1), 0 0 20px rgba(16, 185, 129, 0.4);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Voice FAB */
|
||||
.voice-fab {
|
||||
position: fixed;
|
||||
@@ -1408,30 +775,6 @@ watch(() => route.name, (newPage) => {
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.terminal-fab {
|
||||
bottom: 80px;
|
||||
right: 16px;
|
||||
width: 54px;
|
||||
height: 54px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.terminal-fab::before {
|
||||
border-radius: 19px;
|
||||
}
|
||||
|
||||
.connection-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
top: 6px;
|
||||
right: 6px;
|
||||
}
|
||||
|
||||
.orbital-ring {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
.voice-fab {
|
||||
bottom: 80px;
|
||||
left: 16px;
|
||||
@@ -1449,26 +792,22 @@ watch(() => route.name, (newPage) => {
|
||||
|
||||
/* Mobile: FABs above bottom sheets */
|
||||
@media (max-width: 1024px) and (pointer: coarse) {
|
||||
.terminal-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,
|
||||
.transcript-fab.sheet-open {
|
||||
bottom: calc(15vh + 100px);
|
||||
}
|
||||
|
||||
.terminal-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,
|
||||
.transcript-fab.keyboard-visible.sheet-open {
|
||||
bottom: 45vh;
|
||||
|
||||
@@ -104,13 +104,6 @@ onMounted(() => {
|
||||
</svg>
|
||||
</RouterLink>
|
||||
|
||||
<RouterLink to="/terminal" class="toolbar-btn" :class="{ active: route.path === '/terminal' }" title="Terminal">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="4 17 10 11 4 5"/>
|
||||
<line x1="12" y1="19" x2="20" y2="19"/>
|
||||
</svg>
|
||||
</RouterLink>
|
||||
|
||||
<RouterLink to="/tools" class="toolbar-btn" :class="{ active: route.path === '/tools' }" title="Tools">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>
|
||||
|
||||
@@ -21,8 +21,7 @@ const categoryTools: Record<ToolCategory, string[]> = {
|
||||
theme: ['get_design_tokens', 'get_active_theme', 'set_theme_variable', 'save_theme', 'list_themes', 'switch_theme', 'reset_theme'],
|
||||
database: ['list_tables', 'get_table_schema', 'get_table_data', 'get_database_stats', 'execute_query'],
|
||||
source: ['get_repo_info', 'list_repo_files', 'read_repo_file', 'search_repo_code'],
|
||||
project: ['list_canvases', 'create_canvas', 'get_canvas', 'update_canvas', 'delete_canvas', 'clone_canvas', 'add_component_to_canvas', 'remove_component_from_canvas', 'get_canvas_components'],
|
||||
terminal: ['terminal_open', 'terminal_close', 'terminal_toggle', 'terminal_move', 'terminal_resize']
|
||||
project: ['list_canvases', 'create_canvas', 'get_canvas', 'update_canvas', 'delete_canvas', 'clone_canvas', 'add_component_to_canvas', 'remove_component_from_canvas', 'get_canvas_components']
|
||||
}
|
||||
|
||||
const categories = computed(() => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { ref, computed, watch, nextTick } from 'vue'
|
||||
import VoiceMicButton from './VoiceMicButton.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -36,6 +36,17 @@ function handleSend() {
|
||||
}
|
||||
|
||||
function handleKeydown(e: KeyboardEvent) {
|
||||
if (e.key === 'Enter' && e.ctrlKey) {
|
||||
e.preventDefault()
|
||||
const ta = e.target as HTMLTextAreaElement
|
||||
const start = ta.selectionStart
|
||||
const end = ta.selectionEnd
|
||||
input.value = input.value.slice(0, start) + '\n' + input.value.slice(end)
|
||||
nextTick(() => {
|
||||
ta.selectionStart = ta.selectionEnd = start + 1
|
||||
})
|
||||
return
|
||||
}
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault()
|
||||
handleSend()
|
||||
@@ -70,7 +81,6 @@ watch(() => props.voiceTranscript, (newText) => {
|
||||
class="input-field"
|
||||
:style="{ maxHeight: maxH }"
|
||||
:placeholder="notReady ? 'Starting terminal...' : processing ? 'Wait for agent to finish...' : 'Continue this conversation...'"
|
||||
rows="1"
|
||||
:disabled="processing || notReady"
|
||||
@keydown="handleKeydown"
|
||||
/>
|
||||
@@ -171,7 +181,7 @@ watch(() => props.voiceTranscript, (newText) => {
|
||||
field-sizing: content;
|
||||
min-height: 1lh;
|
||||
overflow-y: auto;
|
||||
padding: 0.15rem 0.25rem;
|
||||
padding: 0 0.25rem;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,11 +39,6 @@ const router = createRouter({
|
||||
name: 'source',
|
||||
component: () => import('../pages/SourceCodePage.vue')
|
||||
},
|
||||
{
|
||||
path: '/terminal',
|
||||
name: 'terminal',
|
||||
component: () => import('../pages/TerminalPage.vue')
|
||||
},
|
||||
{
|
||||
path: '/tools',
|
||||
name: 'tools',
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
createDatabaseHandlers,
|
||||
createProjectCanvasHandlers,
|
||||
createSourceCodeHandlers,
|
||||
createTerminalHandlers,
|
||||
createResponseHandlers,
|
||||
createGitHandlers,
|
||||
createTorchHandlers,
|
||||
@@ -33,7 +32,7 @@ import { setRouter } from './tools/handlers/globalHandlers'
|
||||
import { setGiteaCredentials, clearGiteaCredentials } from './tools/handlers/sourceCodeHandlers'
|
||||
import { ALL_TOOL_METAS, getAllToolNames, type ToolCategory } from './tools/toolDefinitions'
|
||||
|
||||
export type PageName = 'home' | 'canvas' | 'components' | 'themes' | 'project-canvas' | 'database' | 'source' | 'terminal' | 'tools' | 'git' | 'agents' | 'transcript-debug'
|
||||
export type PageName = 'home' | 'canvas' | 'components' | 'themes' | 'project-canvas' | 'database' | 'source' | 'tools' | 'git' | 'agents' | 'transcript-debug'
|
||||
|
||||
// Internal webmcp functions (not exported for external use)
|
||||
let webmcpInstance: any = null
|
||||
@@ -131,7 +130,6 @@ function getToolConfigs(): Map<string, ToolConfig> {
|
||||
...createDatabaseHandlers(),
|
||||
...createProjectCanvasHandlers(),
|
||||
...createSourceCodeHandlers(),
|
||||
...createTerminalHandlers(),
|
||||
...createResponseHandlers(),
|
||||
...createGitHandlers(),
|
||||
...createTorchHandlers(),
|
||||
@@ -155,25 +153,23 @@ const categoryTools: Record<ToolCategory, string[]> = {
|
||||
database: ['list_tables', 'get_table_schema', 'get_table_data', 'get_database_stats', 'execute_query'],
|
||||
source: ['get_repo_info', 'list_repo_files', 'read_repo_file', 'search_repo_code'],
|
||||
project: ['list_canvases', 'create_canvas', 'get_canvas', 'update_canvas', 'delete_canvas', 'clone_canvas', 'add_component_to_canvas', 'remove_component_from_canvas', 'get_canvas_components'],
|
||||
terminal: ['terminal_open', 'terminal_close', 'terminal_toggle', 'terminal_move', 'terminal_resize', 'bubbleResponse'],
|
||||
git: ['get_git_status', 'get_git_diff', 'compare_commits', 'git_log', 'get_git_branches'],
|
||||
torch: ['list_torch_clients', 'get_torch_status', 'transfer_torch', 'request_torch', 'release_torch']
|
||||
}
|
||||
|
||||
// Page to categories mapping
|
||||
const pageCategories: Record<PageName, ToolCategory[]> = {
|
||||
home: ['global', 'torch', 'canvas', 'component', 'project', 'terminal'],
|
||||
canvas: ['global', 'torch', 'canvas', 'component', 'project', 'terminal'],
|
||||
'project-canvas': ['global', 'torch', 'canvas', 'component', 'project', 'terminal'],
|
||||
components: ['global', 'torch', 'component', 'terminal'],
|
||||
themes: ['global', 'torch', 'theme', 'terminal'],
|
||||
database: ['global', 'torch', 'database', 'terminal'],
|
||||
source: ['global', 'torch', 'source', 'terminal'],
|
||||
terminal: ['global', 'torch', 'terminal'],
|
||||
tools: ['global', 'torch', 'terminal'],
|
||||
git: ['global', 'torch', 'git', 'terminal'],
|
||||
agents: ['global', 'torch', 'terminal'],
|
||||
'transcript-debug': ['global', 'torch', 'terminal']
|
||||
home: ['global', 'torch', 'canvas', 'component', 'project'],
|
||||
canvas: ['global', 'torch', 'canvas', 'component', 'project'],
|
||||
'project-canvas': ['global', 'torch', 'canvas', 'component', 'project'],
|
||||
components: ['global', 'torch', 'component'],
|
||||
themes: ['global', 'torch', 'theme'],
|
||||
database: ['global', 'torch', 'database'],
|
||||
source: ['global', 'torch', 'source'],
|
||||
tools: ['global', 'torch'],
|
||||
git: ['global', 'torch', 'git'],
|
||||
agents: ['global', 'torch'],
|
||||
'transcript-debug': ['global', 'torch']
|
||||
}
|
||||
|
||||
let currentPage: PageName | null = null
|
||||
|
||||
@@ -10,8 +10,6 @@ export { createThemeHandlers } from './themeHandlers'
|
||||
export { createDatabaseHandlers } from './databaseHandlers'
|
||||
export { createProjectCanvasHandlers } from './projectCanvasHandlers'
|
||||
export { createSourceCodeHandlers } from './sourceCodeHandlers'
|
||||
export { createTerminalHandlers, setTerminalControls } from './terminalHandlers'
|
||||
export type { TerminalControls } from './terminalHandlers'
|
||||
export { createResponseHandlers, setResponseControls } from './responseHandlers'
|
||||
export type { ResponseControls } from './responseHandlers'
|
||||
export { createGitHandlers } from './gitHandlers'
|
||||
@@ -24,7 +22,7 @@ export type ToolHandler = (args: any) => string | Promise<string>
|
||||
export interface ToolConfig {
|
||||
name: string
|
||||
description: string
|
||||
category: 'global' | 'canvas' | 'component' | 'theme' | 'database' | 'source' | 'project' | 'terminal' | 'git' | 'torch'
|
||||
category: 'global' | 'canvas' | 'component' | 'theme' | 'database' | 'source' | 'project' | 'git' | 'torch'
|
||||
schema: object
|
||||
handler: ToolHandler
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export function createResponseHandlers(): ToolConfig[] {
|
||||
{
|
||||
name: 'bubbleResponse',
|
||||
description: 'Responde al usuario mostrando un mensaje en la UI (terminal flotante) en lugar de en Claude Code',
|
||||
category: 'terminal',
|
||||
category: 'global',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export type ToolCategory = 'global' | 'canvas' | 'component' | 'theme' | 'database' | 'source' | 'project' | 'terminal' | 'git' | 'torch'
|
||||
export type ToolCategory = 'global' | 'canvas' | 'component' | 'theme' | 'database' | 'source' | 'project' | 'git' | 'torch'
|
||||
|
||||
export interface ToolMeta {
|
||||
name: string
|
||||
@@ -72,16 +72,6 @@ export const ALL_TOOL_METAS: ToolMeta[] = [
|
||||
{ name: 'remove_component_from_canvas', description: 'Remueve un componente de un canvas', category: 'project' },
|
||||
{ name: 'get_canvas_components', description: 'Obtiene los componentes de un canvas', category: 'project' },
|
||||
|
||||
// Terminal UI tools
|
||||
{ name: 'terminal_open', description: 'Abre la ventana flotante del terminal', category: 'terminal' },
|
||||
{ name: 'terminal_close', description: 'Cierra la ventana flotante del terminal', category: 'terminal' },
|
||||
{ name: 'terminal_toggle', description: 'Alterna abrir/cerrar el terminal', category: 'terminal' },
|
||||
{ name: 'terminal_move', description: 'Mueve la ventana del terminal a una posicion', category: 'terminal' },
|
||||
{ name: 'terminal_resize', description: 'Cambia el tamano de la ventana del terminal', category: 'terminal' },
|
||||
|
||||
// Response UI tools
|
||||
{ name: 'bubbleResponse', description: 'Muestra un mensaje del agente en la UI', category: 'terminal' },
|
||||
|
||||
// Git tools
|
||||
{ name: 'get_git_status', description: 'Obtiene el estado actual del repositorio Git', category: 'git' },
|
||||
{ name: 'get_git_diff', description: 'Obtiene el diff de cambios no commiteados', category: 'git' },
|
||||
@@ -121,7 +111,6 @@ export const CATEGORY_INFO: Record<ToolCategory, { label: string; color: string;
|
||||
database: { label: 'Database', color: '#3b82f6', icon: 'M12 2C7 2 3 3.5 3 5v14c0 1.5 4 3 9 3s9-1.5 9-3V5c0-1.5-4-3-9-3z' },
|
||||
source: { label: 'Source', color: '#8b5cf6', icon: 'M16 18l6-6-6-6M8 6l-6 6 6 6' },
|
||||
project: { label: 'Project', color: '#06b6d4', icon: 'M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z' },
|
||||
terminal: { label: 'Terminal', color: '#22c55e', icon: 'M4 17l6-6-6-6M12 19h8' },
|
||||
git: { label: 'Git', color: '#f97316', icon: 'M6 3v12M18 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM6 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM18 9a9 9 0 0 1-9 9' },
|
||||
torch: { label: 'Torch', color: '#eab308', icon: 'M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z' }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Singleton Whisper WebSocket Service
|
||||
* One shared connection used by all voice components (FloatingVoice, useVoiceCapture, etc.)
|
||||
* One shared connection used by all voice components (FloatingVoice, useVoiceInput, etc.)
|
||||
*/
|
||||
|
||||
import { ref } from 'vue'
|
||||
|
||||
@@ -107,7 +107,7 @@ export async function handleClaudeHook(req: Request): Promise<Response | null> {
|
||||
console.error('[claude-hook] Failed to forward hook to terminal server:', e)
|
||||
}
|
||||
|
||||
// 2. Forward PermissionRequest to /claude-permission so PromptBar WS listener picks it up
|
||||
// 2. Forward PermissionRequest to /claude-permission for hooks approval system
|
||||
if (body.hook_event_name === 'PermissionRequest') {
|
||||
try {
|
||||
await fetch(`http://localhost:${PORT_TERMINAL}/claude-permission`, {
|
||||
@@ -120,7 +120,7 @@ export async function handleClaudeHook(req: Request): Promise<Response | null> {
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Derive status and broadcast for backward compat (App.vue/AgentBar.vue)
|
||||
// 3. Derive status and broadcast via WebSocket
|
||||
const { status, tool } = deriveStatus(body)
|
||||
try {
|
||||
await fetch(`http://localhost:${PORT_TERMINAL}/claude-status`, {
|
||||
|
||||
Reference in New Issue
Block a user