agent-zero/webui/index.html
Alessandro 675afa8dee
Some checks are pending
Build And Publish Docker Images / plan (push) Waiting to run
Build And Publish Docker Images / build (push) Blocked by required conditions
Refactor speech stack into built-in Kokoro TTS and Whisper STT plugins
Split the legacy core speech stack into two built-in, independently toggleable plugins: `_kokoro_tts` for TTS and `_whisper_stt` for STT.

This refactor keeps dependency installation and bootstrap concerns in Docker/bootstrap/preload, while moving speech-specific tooling, APIs, prompts, UI, and runtime behavior into the plugins. Core now exposes engine-agnostic `tts-service` and `stt-service` brokers, with browser-native TTS preserved as the fallback when Kokoro is disabled.

Included in this change:
- add built-in `_kokoro_tts` plugin with plugin-owned synth API, config, status UI, and provider registration
- add built-in `_whisper_stt` plugin with plugin-owned transcribe API, mic runtime, device UI, prompt injection, and provider registration
- remove legacy core speech APIs/helpers/settings/UI and delete unused `webui/js/speech_browser.js`
- replace the old hardcoded speech settings section with a generic voice surface backed by plugin extensions
- update preload/docs/tests to match the new plugin-owned speech architecture

Behavioral intent:
- both plugins are built-in but not `always_enabled`
- users can now hot-switch TTS and STT independently
- browser TTS remains available when `_kokoro_tts` is off
- Whisper mic UI only appears when `_whisper_stt` is enabled
2026-05-21 05:41:59 +02:00

164 lines
7.3 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>Agent Zero</title>
<link rel="icon" type="image/svg+xml" href="public/favicon.svg">
<link rel="stylesheet" href="index.css">
<link rel="stylesheet" href="css/messages.css">
<link rel="stylesheet" href="components/messages/action-buttons/simple-action-buttons.css">
<link rel="stylesheet" href="components/messages/process-group/process-group.css">
<link rel="stylesheet" href="css/toast.css">
<link rel="stylesheet" href="css/settings.css">
<link rel="stylesheet" href="css/modals.css">
<link rel="stylesheet" href="css/surfaces.css">
<link rel="stylesheet" href="css/scheduler-datepicker.css">
<link rel="stylesheet" href="css/scheduler.css">
<link rel="stylesheet" href="css/notification.css">
<link rel="stylesheet" href="css/buttons.css">
<link rel="stylesheet" href="css/tables.css">
<link rel="stylesheet" href="components/canvas/right-canvas.css">
<!-- Flatpickr for datetime picker -->
<link rel="stylesheet" href="vendor/flatpickr/flatpickr.min.css">
<script src="vendor/flatpickr/flatpickr.min.js"></script>
<script>
globalThis.safeCall = function (name, ...args) {
if (window[name]) window[name](...args)
}
</script>
<script type="module" src="index.js"></script>
<!-- Bootstrap JS (only for logic, importing bundled CSS => UI conflicts) -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
crossorigin="anonymous"></script>
<!-- Then load Alpine.js -->
<!-- <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.3/dist/cdn.min.js"></script> -->
<script type="module" src="js/initFw.js"></script>
<script src="vendor/ace-min/ace.js"></script>
<link href="vendor/ace-min/ace.min.css" rel="stylesheet">
<!-- KaTeX CSS -->
<link rel="stylesheet" href="vendor/katex/katex.min.css" crossorigin="anonymous">
<!-- KaTeX javascript -->
<script defer src="vendor/katex/katex.min.js" crossorigin="anonymous"></script>
<script defer src="vendor/katex/katex.auto-render.min.js" crossorigin="anonymous"></script>
<!-- QR Code Library -->
<script src="vendor/qrcode.min.js"></script>
<!-- Google Icons -->
<link rel="stylesheet" href="vendor/google/google-icons.css" />
<!-- Link the PWA manifest file -->
<link rel="manifest" href="js/manifest.json">
<script>
// Expose git info for sidebar component
globalThis.gitinfo = { version: "{{version_no}}", commit_time: "{{version_time}}" };
// Expose runtime info for frontend components (development gating, runtime-scoped cookies).
globalThis.runtimeInfo = {
...(globalThis.runtimeInfo || {}),
id: "{{runtime_id}}",
isDevelopment: "{{runtime_is_development}}" === "true",
loggedIn: "{{logged_in}}" === "true",
};
</script>
<!-- Plugin head injections (scripts, stylesheets) -->
<x-extension id="page-head"></x-extension>
</head>
<body class="dark-mode device-pointer" x-data>
<div class="container">
<!-- Sidebar Overlay -->
<div id="sidebar-overlay" x-data>
<template x-if="$store.sidebar">
<div class="sidebar-overlay" :class="{'visible': $store.sidebar.isOpen && $store.sidebar.isMobile()}" @click="$store.sidebar.close()"></div>
</template>
</div>
<!-- Left Sidebar (Header Icons, Quick Actions, Tabs, Chats, Tasks) -->
<x-component path="sidebar/left-sidebar.html"></x-component>
<!-- Right Panel (Message History and Input Section) -->
<div id="right-panel" class="panel" >
<!-- top section with time, status etc. -->
<x-component path="chat/top-section/chat-top.html"></x-component>
<!-- Welcome Screen Component -->
<div x-data x-show="$store.welcomeStore && $store.welcomeStore.isVisible">
<x-component path="welcome/welcome-screen.html"></x-component>
</div>
<!-- Message History (actual messages) -->
<div id="chat-area-wrapper" x-data x-show="!$store.welcomeStore || !$store.welcomeStore.isVisible">
<div id="chat-history"></div>
<!-- Chat Navigation Buttons (floating over chat area) -->
<div id="chat-nav-buttons" aria-label="Chat navigation" x-cloak>
<button class="btn-icon-action" title="Scroll to top" @click="$store.chatNavigation.scrollToTop()">
<span class="material-symbols-outlined">vertical_align_top</span>
</button>
<button class="btn-icon-action" title="Previous user message" @click="$store.chatNavigation.scrollToPrevUserMessage()">
<span class="material-symbols-outlined">keyboard_arrow_up</span>
</button>
<button class="btn-icon-action" title="Next user message" @click="$store.chatNavigation.scrollToNextUserMessage()">
<span class="material-symbols-outlined">keyboard_arrow_down</span>
</button>
<button class="btn-icon-action" title="Scroll to bottom" @click="$store.chatNavigation.scrollToBottom()">
<span class="material-symbols-outlined">vertical_align_bottom</span>
</button>
</div>
</div>
<!-- NEW: Toast Stack Component -->
<div style="position: relative; height: 0;">
<x-component path="notifications/notification-toast-stack.html"></x-component>
</div>
<div id="toast" class="toast">
<div class="toast__content">
<div class="toast__title"></div>
<div class="toast__separator"></div>
<div class="toast__message"></div>
</div>
<button class="toast__copy" style="display: none;">Copy</button>
<button class="toast__close">Close</button>
</div>
<!-- Chat Input Area -->
<div x-show="!$store.welcomeStore || !$store.welcomeStore.isVisible">
<!-- Progress Bar -->
<x-component path="chat/input/progress.html"></x-component>
<!-- Input Section -->
<x-component path="chat/input/chat-bar.html"></x-component>
</div>
</div>
<x-component path="canvas/right-canvas.html"></x-component>
</div>
<!-- Drag and Drop Overlay Component -->
<x-component path="chat/attachments/dragDropOverlay.html"></x-component>
<!-- Register Service Worker for offline support and caching -->
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('js/sw.js').then(registration => {
console.log('SW registered: ', registration);
}).catch(registrationError => {
console.log('SW registration failed: ', registrationError);
});
});
}
</script>
</body>
</html>