feat: integrate Tauri v2 with Android widget and voice assistant

- Add Tauri v2 shell (Cargo, tauri.conf.json, capabilities, plugins)
- Migrate all fetch() calls to apiFetch() for Tauri-aware HTTP
- Migrate WebSocket endpoints to resolveEndpoints() for dynamic URLs
- Add ServerConfigDialog for remote server URL configuration
- Add tauri.ts lib with isTauri detection, apiFetch wrapper, plugin helpers
- Add server-config Pinia store with persistence via plugin-store
- Conditional PWA (disabled in Tauri builds)
- Android: home screen transcript widget (last 5 messages, 30s refresh)
- Android: voice command / share activity (SpeechRecognizer + WebSocket)
- Android: signed release APK with auto-copy to installers/
- Remove stale frontend/src-tauri directory
This commit is contained in:
2026-02-23 15:33:43 -06:00
parent 6dc0c5ff6f
commit e1aa8b1bdb
108 changed files with 8155 additions and 151 deletions

View File

@@ -10,19 +10,27 @@ import FloatingTranscriptDebug from './components/FloatingTranscriptDebug.vue'
import TerminalFabStack from './components/transcript-debug/TerminalFabStack.vue'
import PwaInstallBanner from './components/PwaInstallBanner.vue'
import HooksApprovalModal from './components/HooksApprovalModal.vue'
import ServerConfigDialog from './components/ServerConfigDialog.vue'
import { useGlobalApproval } from './composables/useGlobalApproval'
import { initWebMCP, getWebMCP } from './services/webmcp'
import { initTorch, destroyTorch } from './services/torch'
import { initSessionStateWS, destroySessionStateWS } from './services/session-state-ws'
import { endpoints } from './config/endpoints'
import { initToolRegistry, activatePageTools, initToolsOnRefresh } from './services/toolRegistry'
import { setResponseControls } from './services/tools/handlers/responseHandlers'
import { useCanvasStore } from './stores/canvas'
import { useProjectCanvasStore } from './stores/projectCanvas'
import { useSessionState } from './stores/session-state'
import { isTauri } from './lib/tauri'
import { useServerConfig } from './stores/server-config'
const route = useRoute()
const router = useRouter()
// Tauri server config
const serverConfig = isTauri ? useServerConfig() : null
const showServerConfig = ref(false)
const needsServerConfig = computed(() => isTauri && serverConfig && !serverConfig.isConfigured)
const showVoice = ref(false)
const showTranscriptDebug = ref(false)
const showDebugConsole = ref(false)
@@ -293,6 +301,20 @@ watch(() => route.name, (newPage) => {
activatePageTools(newPage as PageName)
}
})
// Watch for Tauri server config changes — re-init services when server is configured
if (serverConfig) {
watch(() => serverConfig!.isConfigured, async (configured) => {
if (configured) {
showServerConfig.value = false
// Re-initialize all services with the new server URL
initSessionStateWS()
initWhisperSocket()
await initWebMCP()
await initTorch()
}
})
}
</script>
<template>
@@ -325,7 +347,12 @@ watch(() => route.name, (newPage) => {
</svg>
<span v-if="debugLogs.length" class="log-count">{{ debugLogs.length }}</span>
</button>
<PwaInstallBanner />
<PwaInstallBanner v-if="!isTauri" />
<button v-if="isTauri" class="server-config-btn" @click="showServerConfig = true" title="Server settings">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="2" y="2" width="20" height="8" rx="2" ry="2"/><rect x="2" y="14" width="20" height="8" rx="2" ry="2"/><line x1="6" y1="6" x2="6.01" y2="6"/><line x1="6" y1="18" x2="6.01" y2="18"/>
</svg>
</button>
</div>
<div class="header-right">
<button
@@ -427,6 +454,9 @@ watch(() => route.name, (newPage) => {
<!-- Global Hooks Approval Modal -->
<HooksApprovalModal />
<!-- Tauri Server Config Dialog -->
<ServerConfigDialog v-if="needsServerConfig || showServerConfig" />
<!-- Debug Console Panel -->
<Teleport to="body">
<Transition name="debug-slide">
@@ -1031,6 +1061,28 @@ watch(() => route.name, (newPage) => {
}
}
/* Server Config Button (Tauri) */
.server-config-btn {
display: flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
padding: 0;
background: transparent;
border: 1px solid var(--border-color);
border-radius: 5px;
color: var(--text-secondary);
cursor: pointer;
transition: all 0.15s ease;
}
.server-config-btn:hover {
background: var(--bg-hover);
color: var(--accent, #6366f1);
border-color: var(--accent, #6366f1);
}
/* Debug Console Button */
.debug-btn {
display: flex;