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 { RouterView, useRoute, useRouter } from 'vue-router'
|
||||||
import Toolbar from './components/Toolbar.vue'
|
import Toolbar from './components/Toolbar.vue'
|
||||||
import TorchButton from './components/TorchButton.vue'
|
import TorchButton from './components/TorchButton.vue'
|
||||||
import FloatingTerminal from './components/FloatingTerminal.vue'
|
|
||||||
import FloatingResponse from './components/FloatingResponse.vue'
|
import FloatingResponse from './components/FloatingResponse.vue'
|
||||||
import { initWhisperSocket } from './services/whisperSocket'
|
import { initWhisperSocket } from './services/whisperSocket'
|
||||||
import FloatingVoice from './components/FloatingVoice.vue'
|
import FloatingVoice from './components/FloatingVoice.vue'
|
||||||
import FloatingTranscriptDebug from './components/FloatingTranscriptDebug.vue'
|
import FloatingTranscriptDebug from './components/FloatingTranscriptDebug.vue'
|
||||||
import AgentBar from './components/AgentBar.vue'
|
|
||||||
import PwaInstallBanner from './components/PwaInstallBanner.vue'
|
import PwaInstallBanner from './components/PwaInstallBanner.vue'
|
||||||
import HooksApprovalModal from './components/HooksApprovalModal.vue'
|
import HooksApprovalModal from './components/HooksApprovalModal.vue'
|
||||||
import { useGlobalApproval } from './composables/useGlobalApproval'
|
import { useGlobalApproval } from './composables/useGlobalApproval'
|
||||||
@@ -16,14 +14,12 @@ import { initWebMCP, getWebMCP } from './services/webmcp'
|
|||||||
import { initTorch, destroyTorch } from './services/torch'
|
import { initTorch, destroyTorch } from './services/torch'
|
||||||
import { endpoints } from './config/endpoints'
|
import { endpoints } from './config/endpoints'
|
||||||
import { initToolRegistry, activatePageTools, initToolsOnRefresh } from './services/toolRegistry'
|
import { initToolRegistry, activatePageTools, initToolsOnRefresh } from './services/toolRegistry'
|
||||||
import { setTerminalControls } from './services/tools/handlers/terminalHandlers'
|
|
||||||
import { setResponseControls } from './services/tools/handlers/responseHandlers'
|
import { setResponseControls } from './services/tools/handlers/responseHandlers'
|
||||||
import { useCanvasStore } from './stores/canvas'
|
import { useCanvasStore } from './stores/canvas'
|
||||||
import { useProjectCanvasStore } from './stores/projectCanvas'
|
import { useProjectCanvasStore } from './stores/projectCanvas'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const showTerminal = ref(false)
|
|
||||||
const showVoice = ref(false)
|
const showVoice = ref(false)
|
||||||
const showTranscriptDebug = ref(false)
|
const showTranscriptDebug = ref(false)
|
||||||
const showDebugConsole = ref(false)
|
const showDebugConsole = ref(false)
|
||||||
@@ -68,7 +64,6 @@ function copyDebugLogs() {
|
|||||||
function clearDebugLogs() {
|
function clearDebugLogs() {
|
||||||
debugLogs.value = []
|
debugLogs.value = []
|
||||||
}
|
}
|
||||||
const terminalRef = ref<InstanceType<typeof FloatingTerminal> | null>(null)
|
|
||||||
const responseRef = ref<InstanceType<typeof FloatingResponse> | null>(null)
|
const responseRef = ref<InstanceType<typeof FloatingResponse> | null>(null)
|
||||||
const voiceRef = ref<InstanceType<typeof FloatingVoice> | null>(null)
|
const voiceRef = ref<InstanceType<typeof FloatingVoice> | null>(null)
|
||||||
const transcriptDebugRef = ref<InstanceType<typeof FloatingTranscriptDebug> | null>(null)
|
const transcriptDebugRef = ref<InstanceType<typeof FloatingTranscriptDebug> | null>(null)
|
||||||
@@ -81,28 +76,8 @@ const voicePTTActive = ref(false)
|
|||||||
let voiceTouchStarted = false
|
let voiceTouchStarted = false
|
||||||
let voicePTTTimeout: number | null = null
|
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
|
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() {
|
function hardRefresh() {
|
||||||
location.reload()
|
location.reload()
|
||||||
}
|
}
|
||||||
@@ -161,117 +136,7 @@ function handleVoiceFabTouchEnd(e: TouchEvent) {
|
|||||||
setTimeout(() => { voiceTouchStarted = false }, 100)
|
setTimeout(() => { voiceTouchStarted = false }, 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectStatusWs() {
|
type PageName = 'home' | 'canvas' | 'components' | 'themes' | 'project-canvas' | 'database' | 'source' | 'tools' | 'agents'
|
||||||
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'
|
|
||||||
|
|
||||||
function syncThemeColor() {
|
function syncThemeColor() {
|
||||||
const bg = getComputedStyle(document.documentElement).getPropertyValue('--bg-primary').trim()
|
const bg = getComputedStyle(document.documentElement).getPropertyValue('--bg-primary').trim()
|
||||||
@@ -284,9 +149,6 @@ onMounted(async () => {
|
|||||||
// Sync Windows titlebar color with CSS variable
|
// Sync Windows titlebar color with CSS variable
|
||||||
syncThemeColor()
|
syncThemeColor()
|
||||||
|
|
||||||
// Connect to WebSocket for Claude status updates
|
|
||||||
connectStatusWs()
|
|
||||||
|
|
||||||
// Connect global hooks approval WS
|
// Connect global hooks approval WS
|
||||||
connectApproval()
|
connectApproval()
|
||||||
fetchApprovalPending()
|
fetchApprovalPending()
|
||||||
@@ -307,43 +169,6 @@ onMounted(async () => {
|
|||||||
const currentPage = (route.name as string) || 'canvas'
|
const currentPage = (route.name as string) || 'canvas'
|
||||||
initToolsOnRefresh(currentPage as PageName)
|
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
|
// Setup response controls for MCP tools
|
||||||
setResponseControls({
|
setResponseControls({
|
||||||
addMessage: (message: string, type?: 'info' | 'success' | 'warning' | 'error') => {
|
addMessage: (message: string, type?: 'info' | 'success' | 'warning' | 'error') => {
|
||||||
@@ -388,12 +213,6 @@ onUnmounted(() => {
|
|||||||
document.removeEventListener('keydown', handleGlobalKeydown)
|
document.removeEventListener('keydown', handleGlobalKeydown)
|
||||||
destroyTorch()
|
destroyTorch()
|
||||||
disconnectApproval()
|
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
|
// Watch for route changes and update tools
|
||||||
@@ -468,94 +287,10 @@ watch(() => route.name, (newPage) => {
|
|||||||
</RouterView>
|
</RouterView>
|
||||||
</main>
|
</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) -->
|
<!-- Transcript Debug FAB Button (pixel art ocean) -->
|
||||||
<button
|
<button
|
||||||
class="transcript-fab"
|
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"
|
@click="showTranscriptDebug = !showTranscriptDebug"
|
||||||
title="Transcript Debug"
|
title="Transcript Debug"
|
||||||
>
|
>
|
||||||
@@ -577,7 +312,7 @@ watch(() => route.name, (newPage) => {
|
|||||||
<!-- Voice FAB Button -->
|
<!-- Voice FAB Button -->
|
||||||
<button
|
<button
|
||||||
class="voice-fab"
|
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"
|
@click="handleVoiceFabClick"
|
||||||
@touchstart="handleVoiceFabTouchStart"
|
@touchstart="handleVoiceFabTouchStart"
|
||||||
@touchend="handleVoiceFabTouchEnd"
|
@touchend="handleVoiceFabTouchEnd"
|
||||||
@@ -592,12 +327,6 @@ watch(() => route.name, (newPage) => {
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Agent Bar (bottom pills) -->
|
|
||||||
<AgentBar />
|
|
||||||
|
|
||||||
<!-- Floating Terminal -->
|
|
||||||
<FloatingTerminal ref="terminalRef" v-model="showTerminal" />
|
|
||||||
|
|
||||||
<!-- Floating Response (Agent UI messages) -->
|
<!-- Floating Response (Agent UI messages) -->
|
||||||
<FloatingResponse ref="responseRef" />
|
<FloatingResponse ref="responseRef" />
|
||||||
|
|
||||||
@@ -951,368 +680,6 @@ watch(() => route.name, (newPage) => {
|
|||||||
transform: translateX(-20px);
|
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 */
|
||||||
.voice-fab {
|
.voice-fab {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -1408,30 +775,6 @@ watch(() => route.name, (newPage) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@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 {
|
.voice-fab {
|
||||||
bottom: 80px;
|
bottom: 80px;
|
||||||
left: 16px;
|
left: 16px;
|
||||||
@@ -1449,26 +792,22 @@ watch(() => route.name, (newPage) => {
|
|||||||
|
|
||||||
/* Mobile: FABs above bottom sheets */
|
/* Mobile: FABs above bottom sheets */
|
||||||
@media (max-width: 1024px) and (pointer: coarse) {
|
@media (max-width: 1024px) and (pointer: coarse) {
|
||||||
.terminal-fab,
|
|
||||||
.voice-fab,
|
.voice-fab,
|
||||||
.transcript-fab {
|
.transcript-fab {
|
||||||
z-index: 10001;
|
z-index: 10001;
|
||||||
transition: bottom 0.25s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s ease;
|
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 {
|
.transcript-fab.sheet-open {
|
||||||
bottom: calc(15vh + 100px);
|
bottom: calc(15vh + 100px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal-fab.keyboard-visible,
|
|
||||||
.voice-fab.keyboard-visible,
|
.voice-fab.keyboard-visible,
|
||||||
.transcript-fab.keyboard-visible {
|
.transcript-fab.keyboard-visible {
|
||||||
bottom: 35vh;
|
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 {
|
.transcript-fab.keyboard-visible.sheet-open {
|
||||||
bottom: 45vh;
|
bottom: 45vh;
|
||||||
|
|||||||
@@ -104,13 +104,6 @@ onMounted(() => {
|
|||||||
</svg>
|
</svg>
|
||||||
</RouterLink>
|
</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">
|
<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">
|
<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"/>
|
<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'],
|
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'],
|
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'],
|
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'],
|
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']
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const categories = computed(() => {
|
const categories = computed(() => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, watch } from 'vue'
|
import { ref, computed, watch, nextTick } from 'vue'
|
||||||
import VoiceMicButton from './VoiceMicButton.vue'
|
import VoiceMicButton from './VoiceMicButton.vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -36,6 +36,17 @@ function handleSend() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleKeydown(e: KeyboardEvent) {
|
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) {
|
if (e.key === 'Enter' && !e.shiftKey) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
handleSend()
|
handleSend()
|
||||||
@@ -70,7 +81,6 @@ watch(() => props.voiceTranscript, (newText) => {
|
|||||||
class="input-field"
|
class="input-field"
|
||||||
:style="{ maxHeight: maxH }"
|
:style="{ maxHeight: maxH }"
|
||||||
:placeholder="notReady ? 'Starting terminal...' : processing ? 'Wait for agent to finish...' : 'Continue this conversation...'"
|
:placeholder="notReady ? 'Starting terminal...' : processing ? 'Wait for agent to finish...' : 'Continue this conversation...'"
|
||||||
rows="1"
|
|
||||||
:disabled="processing || notReady"
|
:disabled="processing || notReady"
|
||||||
@keydown="handleKeydown"
|
@keydown="handleKeydown"
|
||||||
/>
|
/>
|
||||||
@@ -171,7 +181,7 @@ watch(() => props.voiceTranscript, (newText) => {
|
|||||||
field-sizing: content;
|
field-sizing: content;
|
||||||
min-height: 1lh;
|
min-height: 1lh;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 0.15rem 0.25rem;
|
padding: 0 0.25rem;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,11 +39,6 @@ const router = createRouter({
|
|||||||
name: 'source',
|
name: 'source',
|
||||||
component: () => import('../pages/SourceCodePage.vue')
|
component: () => import('../pages/SourceCodePage.vue')
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/terminal',
|
|
||||||
name: 'terminal',
|
|
||||||
component: () => import('../pages/TerminalPage.vue')
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/tools',
|
path: '/tools',
|
||||||
name: 'tools',
|
name: 'tools',
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import {
|
|||||||
createDatabaseHandlers,
|
createDatabaseHandlers,
|
||||||
createProjectCanvasHandlers,
|
createProjectCanvasHandlers,
|
||||||
createSourceCodeHandlers,
|
createSourceCodeHandlers,
|
||||||
createTerminalHandlers,
|
|
||||||
createResponseHandlers,
|
createResponseHandlers,
|
||||||
createGitHandlers,
|
createGitHandlers,
|
||||||
createTorchHandlers,
|
createTorchHandlers,
|
||||||
@@ -33,7 +32,7 @@ import { setRouter } from './tools/handlers/globalHandlers'
|
|||||||
import { setGiteaCredentials, clearGiteaCredentials } from './tools/handlers/sourceCodeHandlers'
|
import { setGiteaCredentials, clearGiteaCredentials } from './tools/handlers/sourceCodeHandlers'
|
||||||
import { ALL_TOOL_METAS, getAllToolNames, type ToolCategory } from './tools/toolDefinitions'
|
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)
|
// Internal webmcp functions (not exported for external use)
|
||||||
let webmcpInstance: any = null
|
let webmcpInstance: any = null
|
||||||
@@ -131,7 +130,6 @@ function getToolConfigs(): Map<string, ToolConfig> {
|
|||||||
...createDatabaseHandlers(),
|
...createDatabaseHandlers(),
|
||||||
...createProjectCanvasHandlers(),
|
...createProjectCanvasHandlers(),
|
||||||
...createSourceCodeHandlers(),
|
...createSourceCodeHandlers(),
|
||||||
...createTerminalHandlers(),
|
|
||||||
...createResponseHandlers(),
|
...createResponseHandlers(),
|
||||||
...createGitHandlers(),
|
...createGitHandlers(),
|
||||||
...createTorchHandlers(),
|
...createTorchHandlers(),
|
||||||
@@ -155,25 +153,23 @@ const categoryTools: Record<ToolCategory, string[]> = {
|
|||||||
database: ['list_tables', 'get_table_schema', 'get_table_data', 'get_database_stats', 'execute_query'],
|
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'],
|
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'],
|
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'],
|
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']
|
torch: ['list_torch_clients', 'get_torch_status', 'transfer_torch', 'request_torch', 'release_torch']
|
||||||
}
|
}
|
||||||
|
|
||||||
// Page to categories mapping
|
// Page to categories mapping
|
||||||
const pageCategories: Record<PageName, ToolCategory[]> = {
|
const pageCategories: Record<PageName, ToolCategory[]> = {
|
||||||
home: ['global', 'torch', 'canvas', 'component', 'project', 'terminal'],
|
home: ['global', 'torch', 'canvas', 'component', 'project'],
|
||||||
canvas: ['global', 'torch', 'canvas', 'component', 'project', 'terminal'],
|
canvas: ['global', 'torch', 'canvas', 'component', 'project'],
|
||||||
'project-canvas': ['global', 'torch', 'canvas', 'component', 'project', 'terminal'],
|
'project-canvas': ['global', 'torch', 'canvas', 'component', 'project'],
|
||||||
components: ['global', 'torch', 'component', 'terminal'],
|
components: ['global', 'torch', 'component'],
|
||||||
themes: ['global', 'torch', 'theme', 'terminal'],
|
themes: ['global', 'torch', 'theme'],
|
||||||
database: ['global', 'torch', 'database', 'terminal'],
|
database: ['global', 'torch', 'database'],
|
||||||
source: ['global', 'torch', 'source', 'terminal'],
|
source: ['global', 'torch', 'source'],
|
||||||
terminal: ['global', 'torch', 'terminal'],
|
tools: ['global', 'torch'],
|
||||||
tools: ['global', 'torch', 'terminal'],
|
git: ['global', 'torch', 'git'],
|
||||||
git: ['global', 'torch', 'git', 'terminal'],
|
agents: ['global', 'torch'],
|
||||||
agents: ['global', 'torch', 'terminal'],
|
'transcript-debug': ['global', 'torch']
|
||||||
'transcript-debug': ['global', 'torch', 'terminal']
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentPage: PageName | null = null
|
let currentPage: PageName | null = null
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ export { createThemeHandlers } from './themeHandlers'
|
|||||||
export { createDatabaseHandlers } from './databaseHandlers'
|
export { createDatabaseHandlers } from './databaseHandlers'
|
||||||
export { createProjectCanvasHandlers } from './projectCanvasHandlers'
|
export { createProjectCanvasHandlers } from './projectCanvasHandlers'
|
||||||
export { createSourceCodeHandlers } from './sourceCodeHandlers'
|
export { createSourceCodeHandlers } from './sourceCodeHandlers'
|
||||||
export { createTerminalHandlers, setTerminalControls } from './terminalHandlers'
|
|
||||||
export type { TerminalControls } from './terminalHandlers'
|
|
||||||
export { createResponseHandlers, setResponseControls } from './responseHandlers'
|
export { createResponseHandlers, setResponseControls } from './responseHandlers'
|
||||||
export type { ResponseControls } from './responseHandlers'
|
export type { ResponseControls } from './responseHandlers'
|
||||||
export { createGitHandlers } from './gitHandlers'
|
export { createGitHandlers } from './gitHandlers'
|
||||||
@@ -24,7 +22,7 @@ export type ToolHandler = (args: any) => string | Promise<string>
|
|||||||
export interface ToolConfig {
|
export interface ToolConfig {
|
||||||
name: string
|
name: string
|
||||||
description: 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
|
schema: object
|
||||||
handler: ToolHandler
|
handler: ToolHandler
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export function createResponseHandlers(): ToolConfig[] {
|
|||||||
{
|
{
|
||||||
name: 'bubbleResponse',
|
name: 'bubbleResponse',
|
||||||
description: 'Responde al usuario mostrando un mensaje en la UI (terminal flotante) en lugar de en Claude Code',
|
description: 'Responde al usuario mostrando un mensaje en la UI (terminal flotante) en lugar de en Claude Code',
|
||||||
category: 'terminal',
|
category: 'global',
|
||||||
schema: {
|
schema: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
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 {
|
export interface ToolMeta {
|
||||||
name: string
|
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: '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' },
|
{ 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
|
// Git tools
|
||||||
{ name: 'get_git_status', description: 'Obtiene el estado actual del repositorio Git', category: 'git' },
|
{ 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' },
|
{ 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' },
|
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' },
|
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' },
|
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' },
|
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' }
|
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
|
* 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'
|
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)
|
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') {
|
if (body.hook_event_name === 'PermissionRequest') {
|
||||||
try {
|
try {
|
||||||
await fetch(`http://localhost:${PORT_TERMINAL}/claude-permission`, {
|
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)
|
const { status, tool } = deriveStatus(body)
|
||||||
try {
|
try {
|
||||||
await fetch(`http://localhost:${PORT_TERMINAL}/claude-status`, {
|
await fetch(`http://localhost:${PORT_TERMINAL}/claude-status`, {
|
||||||
|
|||||||
Reference in New Issue
Block a user