4
|
1 'use strict';
|
|
2
|
29
|
3 let chat;
|
|
4
|
|
5 function setChat(newChat) {
|
32
|
6 let audioChanged = chat && (chat.language_region != newChat.language_region || chat.voice != newChat.voice);
|
29
|
7 chat = newChat;
|
|
8 document.querySelector('[content] [name]').textContent = chat.name;
|
31
|
9 if(audioChanged) {
|
32
|
10 let lang = `lang=${chat.language_region}&`;
|
|
11 let voice = `voice=${chat.voice}&`;
|
31
|
12 let audios = document.querySelectorAll('audio[src]');
|
|
13 for( let audio of audios ) {
|
32
|
14 let src = audio.src;
|
|
15 src = src.replace(/lang=[^&]+&/,lang);
|
|
16 src = src.replace(/voice=[^&]+&/,voice);
|
|
17 audio.src = src;
|
31
|
18 }
|
|
19 }
|
29
|
20 }
|
|
21
|
|
22 function editChat(name) {
|
|
23 let dialog = document.querySelector('dialog[edit]');
|
|
24 dialog.querySelector('input[name=name]').value = chat.name;
|
32
|
25 dialog.querySelector('select[name=language_region]').value = chat.language_region;
|
31
|
26 dialog.querySelector('select[name=voice]').value = chat.voice;
|
34
|
27 dialog.querySelector('input[name=show_text]').checked = chat.show_text;
|
36
|
28 dialog.querySelector('input[name=autoplay]').checked = chat.autoplay;
|
41
|
29 dialog.querySelector('input[name=is_private]').checked = chat.is_private;
|
4
|
30 dialog.showModal();
|
|
31 }
|
|
32
|
29
|
33 function saveChat(form) {
|
|
34 closeModal(form);
|
|
35 ajaxForm('save_chat.js',form);
|
4
|
36 }
|
|
37
|
|
38 function deleteChat() {
|
|
39 let dialog = document.querySelector('dialog[delete]');
|
|
40 dialog.showModal();
|
|
41 }
|
|
42
|
|
43 function doDeleteChat(el) {
|
|
44 closeModal(el);
|
29
|
45 ajax(`delete_chat.js?chat=${chat.id}`);
|
4
|
46 }
|
|
47
|
9
|
48 function systemPrompt() {
|
|
49 let dialog = document.querySelector('dialog[system_prompt]');
|
|
50 dialog.showModal();
|
|
51 }
|
|
52
|
4
|
53 function showWaitingAiIcon() {
|
|
54 document.querySelector('[waiting-ai-icon]').style.display = 'block';
|
|
55 }
|
|
56
|
5
|
57 function hideWaitingAiIcon() {
|
|
58 document.querySelector('[waiting-ai-icon]').style.display = 'none';
|
|
59 }
|
|
60
|
25
|
61 function playLastMessage() {
|
32
|
62 let audios = document.querySelectorAll('[messages] audio');
|
25
|
63 if( audios.length >= 1 ) {
|
|
64 let audio = audios[audios.length-1];
|
|
65 audio.play();
|
|
66 }
|
|
67 }
|
|
68
|
29
|
69 function handleChatMarkdown() {
|
31
|
70 handleMarkdown(chat.language_region,chat.voice);
|
29
|
71 }
|
|
72
|
27
|
73 function scrollToEnd() {
|
|
74 window.scrollTo(0, document.body.scrollHeight);
|
|
75 }
|
|
76
|
5
|
77 function updateAi(html) {
|
|
78 hideWaitingAiIcon();
|
13
|
79 document.querySelector('div[messages]').insertAdjacentHTML('beforeend',html);
|
29
|
80 handleChatMarkdown();
|
5
|
81 document.querySelector('textarea').focus();
|
27
|
82 scrollToEnd();
|
36
|
83 if( chat.autoplay )
|
|
84 playLastMessage();
|
5
|
85 }
|
|
86
|
4
|
87 const isMobile = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
|
|
88
|
|
89 function textareaKey(event) {
|
|
90 if( event.keyCode===13 && !event.shiftKey && !event.ctrlKey && !isMobile ) {
|
|
91 event.preventDefault();
|
|
92 askAi();
|
|
93 }
|
|
94 }
|
|
95
|
28
|
96 let audio;
|
|
97
|
11
|
98 function fixTextarea(textarea) {
|
4
|
99 textarea.style.height = 'initial';
|
|
100 textarea.style.height = (textarea.scrollHeight+2) + 'px';
|
14
|
101 textarea.parentNode.scrollIntoViewIfNeeded(false);
|
28
|
102 if( !audio )
|
|
103 audio = document.querySelector('div[buttons] audio');
|
31
|
104 audio.src = `/tts.mp3?lang=${chat.language_region}&voice=${chat.voice}&text=${encodeURIComponent(textarea.value)}`;
|
4
|
105 }
|
|
106
|
|
107 function askAi() {
|
|
108 let input = document.querySelector('textarea');
|
29
|
109 let url = `ai_ask.js?chat=${chat.id}&input=${encodeURIComponent(input.value)}`;
|
4
|
110 ajax(url);
|
|
111 input.value = '';
|
11
|
112 fixTextarea(input);
|
4
|
113 showWaitingAiIcon();
|
|
114 }
|
14
|
115
|
|
116
|
|
117 function setText(text) {
|
|
118 let textarea = document.querySelector('textarea');
|
|
119 textarea.value = text;
|
|
120 fixTextarea(textarea);
|
|
121 }
|
|
122
|
|
123 let recorder = null;
|
|
124 let chunks;
|
|
125
|
|
126 function startRecording() {
|
|
127 chunks = [];
|
|
128 function record(stream) {
|
|
129 recorder = new MediaRecorder( stream, { mimeType: 'audio/webm;codecs=opus' } );
|
|
130 recorder.ondataavailable = function(event) {
|
|
131 chunks.push(event.data);
|
|
132 };
|
|
133 recorder.onstop = function(event) {
|
|
134 recorder = null;
|
|
135 let blob = new Blob(chunks, { type: 'audio/webm' });
|
|
136 let formData = new FormData();
|
|
137 formData.append('audio', blob, 'recording.webm');
|
|
138 ajax('stt.js',formData);
|
|
139 document.querySelector('button[record]').textContent = 'Record';
|
|
140 };
|
|
141 recorder.start();
|
|
142 }
|
|
143 navigator.mediaDevices.getUserMedia({ audio: { channelCount: 1 } }).then(record);
|
|
144 document.querySelector('button[record]').textContent = 'Stop Recording';
|
|
145 }
|
|
146
|
|
147 function toggleRecording() {
|
|
148 if( recorder === null ) {
|
|
149 startRecording();
|
|
150 } else {
|
|
151 recorder.stop();
|
|
152 }
|
|
153 }
|