UI: голосовой ввод/STT, TTS через OpenAI, настройки инструментов + учёт недопроверенных фич

This commit is contained in:
AidarKC
2026-05-13 02:01:51 +03:00
parent ddeaf82bfd
commit 8de4e95c6a
15 changed files with 716 additions and 5 deletions
+29 -2
View File
@@ -15,6 +15,7 @@ import {
showToast,
softHaptic,
} from '../services/channels-ux.js';
import { openSpeechInputModal } from '../components/speech-input-modal.js';
export const pageMeta = { id: 'channel-view', title: 'Канал' };
@@ -260,13 +261,16 @@ function openAboutChannelModal(channel) {
});
}
function openReplyModal({ onSubmit }) {
function openReplyModal({ onSubmit, navigate }) {
const root = document.getElementById('modal-root');
root.innerHTML = `
<div class="modal" id="reply-modal">
<div class="modal-card stack">
<h3 class="modal-title">Ответ</h3>
<textarea id="reply-text" class="input" rows="5" maxlength="2000" placeholder="Текст ответа"></textarea>
<div class="row wrap-row">
<button class="ghost-btn" id="reply-voice" type="button">🎤 Голосом</button>
</div>
<div class="meta-muted inline-error" id="reply-error"></div>
<div class="form-actions-grid">
<button class="secondary-btn" id="reply-cancel" type="button">Отмена</button>
@@ -293,6 +297,15 @@ function openReplyModal({ onSubmit }) {
};
root.querySelector('#reply-cancel')?.addEventListener('click', close);
root.querySelector('#reply-voice')?.addEventListener('click', async () => {
await openSpeechInputModal({
navigate,
onTextReady: (text) => {
const prev = String(textEl?.value || '').trim();
if (textEl) textEl.value = prev ? `${prev} ${text}` : text;
},
});
});
submitEl?.addEventListener('click', async () => {
if (inFlight) return;
@@ -317,7 +330,7 @@ function openReplyModal({ onSubmit }) {
if (textEl) textEl.focus();
}
function openAddMessageModal({ channelName, onSubmit }) {
function openAddMessageModal({ channelName, onSubmit, navigate }) {
const root = document.getElementById('modal-root');
root.innerHTML = `
<div class="modal" id="channel-message-modal">
@@ -325,6 +338,9 @@ function openAddMessageModal({ channelName, onSubmit }) {
<h3 class="modal-title">Новое сообщение в канале</h3>
<p class="meta-muted">${channelName}</p>
<textarea id="channel-message-text" class="input" rows="6" maxlength="2000" placeholder="Текст сообщения"></textarea>
<div class="row wrap-row">
<button class="ghost-btn" id="channel-message-voice" type="button">🎤 Голосом</button>
</div>
<div class="meta-muted inline-error" id="channel-message-error"></div>
<div class="form-actions-grid">
<button class="secondary-btn" id="channel-message-cancel" type="button">Отмена</button>
@@ -351,6 +367,15 @@ function openAddMessageModal({ channelName, onSubmit }) {
};
root.querySelector('#channel-message-cancel')?.addEventListener('click', close);
root.querySelector('#channel-message-voice')?.addEventListener('click', async () => {
await openSpeechInputModal({
navigate,
onTextReady: (text) => {
const prev = String(textEl?.value || '').trim();
if (textEl) textEl.value = prev ? `${prev} ${text}` : text;
},
});
});
submitEl?.addEventListener('click', async () => {
if (inFlight) return;
@@ -624,6 +649,7 @@ function renderPostCard(post, {
animatePress(event.currentTarget);
revealCounters();
openReplyModal({
navigate,
onSubmit: async (text) => onReply(post.messageRef, text),
});
});
@@ -732,6 +758,7 @@ function renderBody(screen, navigate, routeKey, channelData, handlers) {
animatePress(event.currentTarget);
openAddMessageModal({
channelName: channelData.channel.name,
navigate,
onSubmit: async (bodyText) => handlers.onAddPost(bodyText),
});
});