| 1 | <html> |
| 2 | <head> |
| 3 | <script type="module"> |
| 4 | import { store as chatNavStore } from "/components/chat/navigation/chat-navigation-store.js"; |
| 5 | </script> |
| 6 | </head> |
| 7 | <body> |
| 8 | <div id="progress-bar-box" x-data> |
| 9 | <x-extension id="chat-input-progress-start"></x-extension> |
| 10 | |
| 11 | <!-- Hidden legacy progress-bar element (keeps JS updater happy, not displayed) --> |
| 12 | <span id="progress-bar" style="display:none;"></span> |
| 13 | |
| 14 | <div id="progress-bar-right"> |
| 15 | <h4 |
| 16 | id="progress-bar-stop-speech" |
| 17 | x-data="{ |
| 18 | speaking: window.ttsService?.isSpeaking?.() || false, |
| 19 | _listener: null, |
| 20 | init() { |
| 21 | this._listener = (event) => { |
| 22 | this.speaking = !!event?.detail?.isSpeaking; |
| 23 | }; |
| 24 | window.ttsService?.addEventListener?.('statechange', this._listener); |
| 25 | }, |
| 26 | cleanup() { |
| 27 | if (this._listener) { |
| 28 | window.ttsService?.removeEventListener?.('statechange', this._listener); |
| 29 | } |
| 30 | }, |
| 31 | }" |
| 32 | x-init="init()" |
| 33 | x-destroy="cleanup()" |
| 34 | x-cloak |
| 35 | x-show="speaking" |
| 36 | > |
| 37 | <span id="stop-speech" @click="window.ttsService?.stop?.()" style="cursor: pointer" title="Stop Speech" aria-label="Stop Speech"> |
| 38 | <x-icon class="icon" name="volume_off"></x-icon> |
| 39 | </span> |
| 40 | </h4> |
| 41 | <x-extension id="chat-nav-after"></x-extension> |
| 42 | </div> |
| 43 | <x-extension id="chat-input-progress-end"></x-extension> |
| 44 | </div> |
| 45 | |
| 46 | |
| 47 | <style> |
| 48 | #progress-bar-box { background-color: var(--color-panel); padding: var(--spacing-xs) var(--spacing-md) 0 var(--spacing-md); display: flex; flex-wrap: nowrap; justify-content: flex-start; align-items: center; z-index: 1001; gap: var(--spacing-sm); min-height: 0; } |
| 49 | #progress-bar-box > x-extension { display: contents; } |
| 50 | #progress-bar-box h4 { margin: 0; } |
| 51 | #progress-bar-right { display: flex; align-items: center; gap: var(--spacing-sm); flex-shrink: 0; margin-left: auto;} |
| 52 | #progress-bar-right > x-extension { display: contents; } |
| 53 | .shiny-text { background: linear-gradient(to right, var(--color-primary-dark) 20%, var(--color-text) 40%, var(--color-text) 60%, var(--color-primary-dark) 60%); background-size: 200% auto; color: transparent; -webkit-background-clip: text; background-clip: text; animation: shine 1s linear infinite; } |
| 54 | @keyframes shine { to { background-position: -200% center; } } |
| 55 | </style> |
| 56 | </body> |
| 57 | </html> |