main
html 178 lines 5.95 KB
Raw
1 <html>
2 <head>
3 <title>Whisper STT</title>
4 <script type="module">
5 import { store } from "/plugins/_whisper_stt/webui/whisper-stt-store.js";
6 </script>
7 </head>
8
9 <body>
10 <div
11 x-data
12 x-init="$store.whisperStt.initRuntime()"
13 class="speech-plugin-page"
14 >
15 <template x-if="$store.whisperStt">
16 <div>
17 <div class="section-title">Whisper STT</div>
18 <div class="section-description">
19 Built-in Whisper transcription. Dependency installation stays on the Docker/bootstrap path;
20 this plugin only owns the speech-to-text behavior, UI, and routing.
21 </div>
22
23 <div class="speech-plugin-grid">
24 <div class="speech-plugin-card">
25 <div class="field-title">Provider State</div>
26 <div class="status-row">
27 <span class="status-key">Enabled</span>
28 <span class="status-badge" :class="$store.whisperStt.enabled ? 'ok' : 'warn'" x-text="$store.whisperStt.enabled ? 'Yes' : 'No'"></span>
29 </div>
30 <div class="status-row">
31 <span class="status-key">Model</span>
32 <span class="status-badge" :class="$store.whisperStt.statusClass" x-text="$store.whisperStt.statusText"></span>
33 </div>
34 <div class="status-row" x-show="$store.whisperStt.loadedModel">
35 <span class="status-key">Loaded</span>
36 <span class="status-value" x-text="$store.whisperStt.loadedModel"></span>
37 </div>
38 <div class="status-row" x-show="$store.whisperStt.packageVersion">
39 <span class="status-key">Package</span>
40 <span class="status-value" x-text="$store.whisperStt.packageVersion"></span>
41 </div>
42 </div>
43
44 <div class="speech-plugin-card">
45 <div class="field-title">Resolved Config</div>
46 <div class="status-row">
47 <span class="status-key">Model size</span>
48 <span class="status-value" x-text="$store.whisperStt.config.model_size"></span>
49 </div>
50 <div class="status-row">
51 <span class="status-key">Language</span>
52 <span class="status-value" x-text="$store.whisperStt.config.language"></span>
53 </div>
54 <div class="status-row">
55 <span class="status-key">Message</span>
56 <span class="status-value" x-text="$store.whisperStt.messageModeLabel"></span>
57 </div>
58 <div class="status-row">
59 <span class="status-key">Threshold</span>
60 <span class="status-value" x-text="$store.whisperStt.config.silence_threshold"></span>
61 </div>
62 <div class="status-row">
63 <span class="status-key">Silence</span>
64 <span class="status-value" x-text="`${$store.whisperStt.config.silence_duration} ms`"></span>
65 </div>
66 <div class="status-row">
67 <span class="status-key">Wait</span>
68 <span class="status-value" x-text="`${$store.whisperStt.config.waiting_timeout} ms`"></span>
69 </div>
70 </div>
71
72 <div class="speech-plugin-card">
73 <div class="field-title">Microphone</div>
74 <div class="status-row">
75 <span class="status-key">Status</span>
76 <span class="status-badge warn" x-text="$store.whisperStt.micStatus"></span>
77 </div>
78 <label class="device-picker">
79 <span class="status-key">Device</span>
80 <select
81 :value="$store.whisperStt.selectedDevice"
82 @change="$store.whisperStt.selectDevice($event.target.value)"
83 >
84 <option value="">System default</option>
85 <template x-for="device in $store.whisperStt.devices" :key="device.deviceId">
86 <option :value="device.deviceId" x-text="device.label || `Microphone ${device.deviceId}`"></option>
87 </template>
88 </select>
89 </label>
90 <div class="field-description">
91 Device selection is browser-local and applies to the microphone button injected by this plugin.
92 </div>
93 </div>
94 </div>
95
96 <div class="speech-plugin-actions">
97 <button class="btn btn-field" @click="$store.whisperStt.requestMicrophonePermission()">Request Mic Permission</button>
98 <button class="btn btn-field" @click="$store.whisperStt.openConfig()">Open Settings</button>
99 <button class="btn btn-field" @click="$store.whisperStt.refreshStatus()">Refresh</button>
100 </div>
101 </div>
102 </template>
103 </div>
104
105 <style>
106 .speech-plugin-page {
107 display: flex;
108 flex-direction: column;
109 gap: 14px;
110 }
111
112 .speech-plugin-grid {
113 display: grid;
114 gap: 12px;
115 grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
116 }
117
118 .speech-plugin-card {
119 display: flex;
120 flex-direction: column;
121 gap: 10px;
122 padding: 14px;
123 background: var(--color-input);
124 }
125
126 .speech-plugin-actions {
127 display: flex;
128 gap: 8px;
129 flex-wrap: wrap;
130 }
131
132 .status-row,
133 .device-picker {
134 display: flex;
135 align-items: flex-start;
136 justify-content: space-between;
137 gap: 12px;
138 font-size: 0.84rem;
139 }
140
141 .device-picker {
142 flex-direction: column;
143 align-items: stretch;
144 }
145
146 .status-key {
147 opacity: 0.7;
148 min-width: 64px;
149 }
150
151 .status-value {
152 text-align: right;
153 word-break: break-word;
154 }
155
156 .status-badge {
157 padding: 2px 8px;
158 border-radius: 999px;
159 font-size: 0.76rem;
160 font-weight: 600;
161 border: 1px solid transparent;
162 text-transform: capitalize;
163 }
164
165 .status-badge.ok {
166 color: #1b5e20;
167 background: rgba(46, 125, 50, 0.14);
168 border-color: rgba(46, 125, 50, 0.24);
169 }
170
171 .status-badge.warn {
172 color: #8a6100;
173 background: rgba(191, 144, 0, 0.14);
174 border-color: rgba(191, 144, 0, 0.24);
175 }
176 </style>
177 </body>
178 </html>