File size: 31,951 Bytes
b39ce2c
e9c4dc3
0618646
e9c4dc3
 
 
 
 
 
b39ce2c
 
 
 
 
 
b791a35
98d8027
2a53137
cfb1733
 
9fb61e6
6dc5da0
a3f3ca9
b996b77
6b4e8ea
ee42d46
cfb1733
 
 
b791a35
9fb61e6
76699a6
b39ce2c
e9c4dc3
 
0d358c4
701dfa6
38856b5
701dfa6
80b54e5
ee42d46
701dfa6
 
 
c299d8c
701dfa6
 
80b54e5
701dfa6
0618646
ee42d46
b2d64ee
 
 
c299d8c
80b54e5
b2d64ee
ee42d46
b2d64ee
 
 
c299d8c
80b54e5
b2d64ee
 
 
 
ee42d46
fe3346f
 
 
c299d8c
80b54e5
fe3346f
 
ee42d46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0d358c4
b39ce2c
e9c4dc3
2a53137
cfb1733
b39ce2c
 
 
0d358c4
b39ce2c
b996b77
98d8027
9fb61e6
6dc5da0
a3f3ca9
b996b77
6b4e8ea
ee42d46
b39ce2c
cfb1733
 
 
 
 
98d8027
b39ce2c
 
 
 
 
0d358c4
cfb1733
 
 
b39ce2c
 
0d358c4
2a53137
 
b791a35
cfb1733
b791a35
 
f3c02b8
b996b77
 
ee42d46
 
 
b996b77
 
b791a35
9fb61e6
38856b5
76699a6
6dc5da0
9fb61e6
 
 
a3f3ca9
 
 
b39ce2c
 
 
 
701dfa6
b2d64ee
701dfa6
 
 
b996b77
701dfa6
b2d64ee
 
 
 
701dfa6
 
 
d0862ed
 
 
 
 
 
 
 
 
 
b39ce2c
 
 
 
6cb7c42
b39ce2c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e9c4dc3
b39ce2c
0618646
e9c4dc3
b39ce2c
0618646
 
0d358c4
6cb7c42
 
 
 
 
 
 
cfb1733
 
 
 
 
c2e5d5f
cfb1733
 
 
c2e5d5f
cfb1733
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0618646
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2a53137
0618646
 
 
 
2a53137
0618646
 
 
 
 
 
 
 
 
 
 
 
 
b39ce2c
 
1c0a00b
8161b72
b39ce2c
1c0a00b
8161b72
b39ce2c
 
 
 
98d8027
b39ce2c
9aeba67
b39ce2c
 
 
9aeba67
b39ce2c
 
98d8027
 
 
 
b39ce2c
 
dfcbb49
 
 
 
fa02110
 
0d358c4
 
b996b77
0d358c4
 
fa02110
b996b77
0d358c4
dfcbb49
b996b77
0d358c4
fa02110
b996b77
fa02110
dfcbb49
 
f3c02b8
 
 
 
0546163
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dfcbb49
1c21e1a
5314ca6
f029128
5314ca6
f029128
e1ee750
 
 
 
 
 
 
b996b77
 
 
 
 
 
a788e7c
f029128
5754184
1c21e1a
5754184
 
 
31c04be
5314ca6
31c04be
 
 
e1ee750
 
 
 
31c04be
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5314ca6
5754184
 
 
 
1c21e1a
 
5754184
a63e3c6
 
5754184
f029128
a788e7c
 
a63e3c6
a788e7c
 
a63e3c6
a788e7c
5754184
 
 
5314ca6
f029128
 
 
 
 
 
 
 
 
 
 
 
 
2a53137
 
5314ca6
a3f3ca9
5314ca6
 
 
 
b996b77
b791a35
6cb7c42
b996b77
0546163
b791a35
5314ca6
b996b77
b791a35
6cb7c42
b996b77
2a53137
b791a35
a3f3ca9
b996b77
 
 
 
 
 
 
 
 
2a53137
 
 
b996b77
2a53137
a3f3ca9
 
 
 
 
ee42d46
 
 
 
 
 
e8bde23
 
 
ee42d46
 
 
 
 
 
 
 
 
 
b791a35
 
b996b77
 
b791a35
1c70651
b791a35
 
 
 
cfb1733
 
 
 
 
 
 
 
 
b791a35
5314ca6
 
1c21e1a
e91da60
5314ca6
 
 
e91da60
 
 
5314ca6
 
 
701dfa6
5314ca6
 
 
 
 
b791a35
 
 
 
 
 
 
cfb1733
 
 
 
 
 
 
 
e7d04a4
38856b5
91464d9
9fb61e6
 
 
 
 
 
ee42d46
9fb61e6
 
 
 
 
 
 
 
91464d9
ee42d46
 
91464d9
9fb61e6
91464d9
ee42d46
9fb61e6
a28d5b0
 
9fb61e6
91464d9
 
 
9fb61e6
ae0274b
9fb61e6
 
9b74535
a28d5b0
 
 
76699a6
9fb61e6
 
 
 
38856b5
 
9fb61e6
91464d9
 
 
 
ee42d46
9fb61e6
 
 
ee42d46
 
 
 
 
 
9fb61e6
 
 
 
38856b5
a28d5b0
9fb61e6
 
 
38856b5
 
 
 
 
9a6e0bf
 
9fb61e6
 
76699a6
 
9fb61e6
e8bde23
 
 
 
 
9fb61e6
 
fe3346f
9fb61e6
 
 
ee42d46
 
 
 
 
 
 
 
 
38856b5
 
 
 
 
 
 
 
0d358c4
0618646
b39ce2c
 
0618646
e9c4dc3
0d358c4
0618646
0d358c4
cfb1733
dfcbb49
 
38856b5
a788e7c
 
c6c08bc
 
 
ae0274b
 
c6c08bc
 
 
ae0274b
c6c08bc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a788e7c
 
 
714e09c
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768

// custom javascript here

const MAX_HISTORY_LENGTH = 32;

var key_down_history = [];
var currentIndex = -1;
var user_input_ta;

var gradioContainer = null;
var user_input_ta = null;
var user_input_tb = null;
var userInfoDiv = null;
var appTitleDiv = null;
var chatbot = null;
var chatbotWrap = null;
var apSwitch = null;
var messageBotDivs = null;
var loginUserForm = null;
var logginUser = null;
var updateToast = null;
var sendBtn = null;
var cancelBtn = null;
var sliders = null;
var updateChuanhuBtn = null;
var statusDisplay = null;

var userLogged = false;
var usernameGotten = false;
var historyLoaded = false;
var updateInfoGotten = false;
var isLatestVersion = localStorage.getItem('isLatestVersion') || false;

var ga = document.getElementsByTagName("gradio-app");
var targetNode = ga[0];
var isInIframe = (window.self !== window.top);
var language = navigator.language.slice(0,2);
var currentTime = new Date().getTime();

// i18n
const forView_i18n = {
    'zh': "仅供查看",
    'en': "For viewing only",
    'ja': "閲覧専用",
    'ko': "읽기 전용",
    'fr': "Pour consultation seulement",
    'es': "Solo para visualización",
    'sv': "Endast för visning",
};

const deleteConfirm_i18n_pref = {
    'zh': "你真的要删除 ",
    'en': "Are you sure you want to delete ",
    'ja': "本当に ",
    'ko': "정말로 ",
    'sv': "Är du säker på att du vill ta bort "
};
const deleteConfirm_i18n_suff = {
    'zh': " 吗?",
    'en': " ?",
    'ja': " を削除してもよろしいですか?",
    'ko': " 을(를) 삭제하시겠습니까?",
    'sv': " ?"
};
var deleteConfirm_msg_pref = "Are you sure you want to delete ";
var deleteConfirm_msg_suff = " ?";

const usingLatest_i18n = {
    'zh': "您使用的就是最新版!",
    'en': "You are using the latest version!",
    'ja': "最新バージョンを使用しています!",
    'ko': "최신 버전을 사용하고 있습니다!",
    'sv': "Du använder den senaste versionen!"
};

const updateSuccess_i18n = {
    'zh': "更新成功,请重启本程序。",
    'en': "Updated successfully, please restart this program.",
    'ja': "更新が成功しました、このプログラムを再起動してください。",
    'ko': "업데이트 성공, 이 프로그램을 재시작 해주세요.",
    'sv': "Uppdaterat framgångsrikt, starta om programmet."
}
const updateFailure_i18n = {
    'zh': '更新失败,请尝试<a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新" target="_blank">手动更新</a>。',
    'en': 'Update failed, please try <a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新" target="_blank">manually updating</a>.',
    'ja': '更新に失敗しました、<a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新" target="_blank">手動での更新</a>をお試しください。',
    'ko': '업데이트 실패, <a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新" target="_blank">수동 업데이트</a>를 시도하십시오.',
    'sv': 'Uppdateringen misslyckades, prova att <a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新" target="_blank">uppdatera manuellt</a>.'
}

// gradio 页面加载好了么??? 我能动你的元素了么??
function gradioLoaded(mutations) {
    for (var i = 0; i < mutations.length; i++) {
        if (mutations[i].addedNodes.length) {
            loginUserForm = document.querySelector(".gradio-container > .main > .wrap > .panel > .form")
            gradioContainer = document.querySelector(".gradio-container");
            user_input_tb = document.getElementById('user_input_tb');
            userInfoDiv = document.getElementById("user_info");
            appTitleDiv = document.getElementById("app_title");
            chatbot = document.querySelector('#chuanhu_chatbot');
            chatbotWrap = document.querySelector('#chuanhu_chatbot > .wrapper > .wrap');
            apSwitch = document.querySelector('.apSwitch input[type="checkbox"]');
            updateToast = document.querySelector("#toast-update");
            sendBtn = document.getElementById("submit_btn");
            cancelBtn = document.getElementById("cancel_btn");
            sliders = document.querySelectorAll('input[type="range"]');
            updateChuanhuBtn = document.getElementById("update_chuanhu_btn");
            statusDisplay = document.querySelector('#status_display');

            if (loginUserForm) {
                localStorage.setItem("userLogged", true);
                userLogged = true;
            }

            if (gradioContainer && apSwitch) {  // gradioCainter 加载出来了没?
                adjustDarkMode();
            }
            if (user_input_tb) {  // user_input_tb 加载出来了没?
                selectHistory();
            }
            if (userInfoDiv && appTitleDiv) {  // userInfoDiv 和 appTitleDiv 加载出来了没?
                if (!usernameGotten) {
                    getUserInfo();
                }
                setTimeout(showOrHideUserInfo(), 2000);
            }
            if (chatbot) {  // chatbot 加载出来了没?
                setChatbotHeight();
            }
            if (chatbotWrap) {
                if (!historyLoaded) {
                    loadHistoryHtml();
                }
                setChatbotScroll();
                mObserver.observe(chatbotWrap, { attributes: true, childList: true, subtree: true, characterData: true});
            }
            if (statusDisplay) {
                // statusObserver.observe(statusDisplay, { childList: true, subtree: true, characterData: true});
            }
            if (sliders) {
                setSlider();
            }
            if (updateToast) {
                const lastCheckTime = localStorage.getItem('lastCheckTime') || 0;
                const longTimeNoCheck = currentTime - lastCheckTime > 3 * 24 * 60 * 60 * 1000;
                if (longTimeNoCheck && !updateInfoGotten && !isLatestVersion || isLatestVersion && !updateInfoGotten) {
                    updateLatestVersion();
                }
            }
            if (cancelBtn) {
                submitObserver.observe(cancelBtn, { attributes: true, characterData: true});
            }
        }
    }
}

function webLocale() {
    // console.log("webLocale", language);
    if (forView_i18n.hasOwnProperty(language)) {
        var forView = forView_i18n[language];
        var forViewStyle = document.createElement('style');
        forViewStyle.innerHTML = '.wrapper>.wrap>.history-message>:last-child::after { content: "' + forView + '"!important; }';
        document.head.appendChild(forViewStyle);
    }
    if (deleteConfirm_i18n_pref.hasOwnProperty(language)) {
        deleteConfirm_msg_pref = deleteConfirm_i18n_pref[language];
        deleteConfirm_msg_suff = deleteConfirm_i18n_suff[language];
    }
}

function showConfirmationDialog(a, file, c) {
    if (file != "") {
        var result = confirm(deleteConfirm_msg_pref + file + deleteConfirm_msg_suff);
        if (result) {
            return [a, file, c];
        }
    }
    return [a, "CANCELED", c];
}

function selectHistory() {
    user_input_ta = user_input_tb.querySelector("textarea");
    if (user_input_ta) {
        observer.disconnect(); // 停止监听
        disableSendBtn();
        // 在 textarea 上监听 keydown 事件
        user_input_ta.addEventListener("keydown", function (event) {
            var value = user_input_ta.value.trim();
            // 判断按下的是否为方向键
            if (event.code === 'ArrowUp' || event.code === 'ArrowDown') {
                // 如果按下的是方向键,且输入框中有内容,且历史记录中没有该内容,则不执行操作
                if (value && key_down_history.indexOf(value) === -1)
                    return;
                // 对于需要响应的动作,阻止默认行为。
                event.preventDefault();
                var length = key_down_history.length;
                if (length === 0) {
                    currentIndex = -1; // 如果历史记录为空,直接将当前选中的记录重置
                    return;
                }
                if (currentIndex === -1) {
                    currentIndex = length;
                }
                if (event.code === 'ArrowUp' && currentIndex > 0) {
                    currentIndex--;
                    user_input_ta.value = key_down_history[currentIndex];
                } else if (event.code === 'ArrowDown' && currentIndex < length - 1) {
                    currentIndex++;
                    user_input_ta.value = key_down_history[currentIndex];
                }
                user_input_ta.selectionStart = user_input_ta.value.length;
                user_input_ta.selectionEnd = user_input_ta.value.length;
                const input_event = new InputEvent("input", { bubbles: true, cancelable: true });
                user_input_ta.dispatchEvent(input_event);
            } else if (event.code === "Enter") {
                if (value) {
                    currentIndex = -1;
                    if (key_down_history.indexOf(value) === -1) {
                        key_down_history.push(value);
                        if (key_down_history.length > MAX_HISTORY_LENGTH) {
                            key_down_history.shift();
                        }
                    }
                }
            }
        });
    }
}

function disableSendBtn() {
    sendBtn.disabled = user_input_ta.value.trim() === '';
    user_input_ta.addEventListener('input', () => {
        sendBtn.disabled = user_input_ta.value.trim() === '';
    });
}

var username = null;
function getUserInfo() {
    if (usernameGotten) {
        return;
    }
    userLogged = localStorage.getItem('userLogged');
    if (userLogged) {
        username = userInfoDiv.innerText;
        if (username) {
            if (username.includes("getting user info…")) {
                setTimeout(getUserInfo, 500);
                return;
            } else if (username === " ") {
                localStorage.removeItem("username");
                localStorage.removeItem("userLogged")
                userLogged = false;
                usernameGotten = true;
                return;
            } else {
                username = username.match(/User:\s*(.*)/)[1] || username;
                localStorage.setItem("username", username);
                usernameGotten = true;
                clearHistoryHtml();
            }
        }
    }
}

function toggleUserInfoVisibility(shouldHide) {
    if (userInfoDiv) {
        if (shouldHide) {
            userInfoDiv.classList.add("hideK");
        } else {
            userInfoDiv.classList.remove("hideK");
        }
    }
}
function showOrHideUserInfo() {
    // Bind mouse/touch events to show/hide user info
    appTitleDiv.addEventListener("mouseenter", function () {
        toggleUserInfoVisibility(false);
    });
    userInfoDiv.addEventListener("mouseenter", function () {
        toggleUserInfoVisibility(false);
    });
    sendBtn.addEventListener("mouseenter", function () {
        toggleUserInfoVisibility(false);
    });

    appTitleDiv.addEventListener("mouseleave", function () {
        toggleUserInfoVisibility(true);
    });
    userInfoDiv.addEventListener("mouseleave", function () {
        toggleUserInfoVisibility(true);
    });
    sendBtn.addEventListener("mouseleave", function () {
        toggleUserInfoVisibility(true);
    });

    appTitleDiv.ontouchstart = function () {
        toggleUserInfoVisibility(false);
    };
    userInfoDiv.ontouchstart = function () {
        toggleUserInfoVisibility(false);
    };
    sendBtn.ontouchstart = function () {
        toggleUserInfoVisibility(false);
    };

    appTitleDiv.ontouchend = function () {
        setTimeout(function () {
            toggleUserInfoVisibility(true);
        }, 3000);
    };
    userInfoDiv.ontouchend = function () {
        setTimeout(function () {
            toggleUserInfoVisibility(true);
        }, 3000);
    };
    sendBtn.ontouchend = function () {
        setTimeout(function () {
            toggleUserInfoVisibility(true);
        }, 3000); // Delay 1 second to hide user info
    };

    // Hide user info after 2 second
    setTimeout(function () {
        toggleUserInfoVisibility(true);
    }, 2000);
}

function toggleDarkMode(isEnabled) {
    if (isEnabled) {
        document.body.classList.add("dark");
        document.body.style.setProperty("background-color", "var(--neutral-950)", "important");
    } else {
        document.body.classList.remove("dark");
        document.body.style.backgroundColor = "";
    }
}
function adjustDarkMode() {
    const darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)");

    // 根据当前颜色模式设置初始状态
    apSwitch.checked = darkModeQuery.matches;
    toggleDarkMode(darkModeQuery.matches);
    // 监听颜色模式变化
    darkModeQuery.addEventListener("change", (e) => {
        apSwitch.checked = e.matches;
        toggleDarkMode(e.matches);
    });
    // apSwitch = document.querySelector('.apSwitch input[type="checkbox"]');
    apSwitch.addEventListener("change", (e) => {
        toggleDarkMode(e.target.checked);
    });
}

function setChatbotHeight() {
    const screenWidth = window.innerWidth;
    const statusDisplay = document.querySelector('#status_display');
    const statusDisplayHeight = statusDisplay ? statusDisplay.offsetHeight : 0;
    const vh = window.innerHeight * 0.01;
    document.documentElement.style.setProperty('--vh', `${vh}px`);
    if (isInIframe) {
        chatbot.style.height = `700px`;
        chatbotWrap.style.maxHeight = `calc(700px - var(--line-sm) * 1rem - 2 * var(--block-label-margin))`
    } else {
        if (screenWidth <= 320) {
            chatbot.style.height = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 150}px)`;
            chatbotWrap.style.maxHeight = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 150}px - var(--line-sm) * 1rem - 2 * var(--block-label-margin))`;
        } else if (screenWidth <= 499) {
            chatbot.style.height = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 100}px)`;
            chatbotWrap.style.maxHeight = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 100}px - var(--line-sm) * 1rem - 2 * var(--block-label-margin))`;
        } else {
            chatbot.style.height = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 160}px)`;
            chatbotWrap.style.maxHeight = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 160}px - var(--line-sm) * 1rem - 2 * var(--block-label-margin))`;
        }
    }
}
function setChatbotScroll() {
    var scrollHeight = chatbotWrap.scrollHeight;
    chatbotWrap.scrollTo(0,scrollHeight)
}
var rangeInputs = null;
var numberInputs = null;
function setSlider() {
    rangeInputs = document.querySelectorAll('input[type="range"]');
    numberInputs = document.querySelectorAll('input[type="number"]')
    setSliderRange();
    rangeInputs.forEach(rangeInput => {
        rangeInput.addEventListener('input', setSliderRange);
    });
    numberInputs.forEach(numberInput => {
        numberInput.addEventListener('input', setSliderRange);
    })
}
function setSliderRange() {
    var range = document.querySelectorAll('input[type="range"]');
    range.forEach(range => {
        range.style.backgroundSize = (range.value - range.min) / (range.max - range.min) * 100 + '% 100%';
    });
}

function addChuanhuButton(botElement) {
    var rawMessage = null;
    var mdMessage = null;
    rawMessage = botElement.querySelector('.raw-message');
    mdMessage = botElement.querySelector('.md-message');
    if (!rawMessage) {
        var buttons = botElement.querySelectorAll('button.chuanhu-btn');
        for (var i = 0; i < buttons.length; i++) {
            buttons[i].parentNode.removeChild(buttons[i]);
        }
        return;
    }
    var oldCopyButton = null;
    var oldToggleButton = null;
    oldCopyButton = botElement.querySelector('button.copy-bot-btn');
    oldToggleButton = botElement.querySelector('button.toggle-md-btn');
    if (oldCopyButton) oldCopyButton.remove();
    if (oldToggleButton) oldToggleButton.remove();

    // Copy bot button
    var copyButton = document.createElement('button');
    copyButton.classList.add('chuanhu-btn');
    copyButton.classList.add('copy-bot-btn');
    copyButton.setAttribute('aria-label', 'Copy');
    copyButton.innerHTML = copyIcon;
    copyButton.addEventListener('click', async () => {
        const textToCopy = rawMessage.innerText;
        try {
            if ("clipboard" in navigator) {
                await navigator.clipboard.writeText(textToCopy);
                copyButton.innerHTML = copiedIcon;
                setTimeout(() => {
                    copyButton.innerHTML = copyIcon;
                }, 1500);
            } else {
                const textArea = document.createElement("textarea");
                textArea.value = textToCopy;
                document.body.appendChild(textArea);
                textArea.select();
                try {
                    document.execCommand('copy');
                    copyButton.innerHTML = copiedIcon;
                    setTimeout(() => {
                        copyButton.innerHTML = copyIcon;
                    }, 1500);
                } catch (error) {
                    console.error("Copy failed: ", error);
                }
                document.body.removeChild(textArea);
            }
        } catch (error) {
            console.error("Copy failed: ", error);
        }
    });
    botElement.appendChild(copyButton);

    // Toggle button
    var toggleButton = document.createElement('button');
    toggleButton.classList.add('chuanhu-btn');
    toggleButton.classList.add('toggle-md-btn');
    toggleButton.setAttribute('aria-label', 'Toggle');
    var renderMarkdown = mdMessage.classList.contains('hideM');
    toggleButton.innerHTML = renderMarkdown ? mdIcon : rawIcon;
    toggleButton.addEventListener('click', () => {
        renderMarkdown = mdMessage.classList.contains('hideM');
        if (renderMarkdown){
            renderMarkdownText(botElement);
            toggleButton.innerHTML=rawIcon;
        } else {
            removeMarkdownText(botElement);
            toggleButton.innerHTML=mdIcon;
        }
    });
    botElement.insertBefore(toggleButton, copyButton);
}

function renderMarkdownText(message) {
    var mdDiv = message.querySelector('.md-message');
    if (mdDiv) mdDiv.classList.remove('hideM');
    var rawDiv = message.querySelector('.raw-message');
    if (rawDiv) rawDiv.classList.add('hideM');
}
function removeMarkdownText(message) {
    var rawDiv = message.querySelector('.raw-message');
    if (rawDiv) rawDiv.classList.remove('hideM');
    var mdDiv = message.querySelector('.md-message');
    if (mdDiv) mdDiv.classList.add('hideM');
}

let timeoutId;
let isThrottled = false;
var mmutation
// 监听chatWrap元素的变化,为 bot 消息添加复制按钮。
var mObserver = new MutationObserver(function (mutationsList) {
    for (mmutation of mutationsList) {
        if (mmutation.type === 'childList') {
            for (var node of mmutation.addedNodes) {
                if (node.nodeType === 1 && node.classList.contains('message')) {
                    saveHistoryHtml();
                    disableSendBtn();
                    document.querySelectorAll('#chuanhu_chatbot .message-wrap .message.bot').forEach(addChuanhuButton);
                }
            }
            for (var node of mmutation.removedNodes) {
                if (node.nodeType === 1 && node.classList.contains('message')) {
                    saveHistoryHtml();
                    disableSendBtn();
                    document.querySelectorAll('#chuanhu_chatbot .message-wrap .message.bot').forEach(addChuanhuButton);
                }
            }
        } else if (mmutation.type === 'attributes') {
            if (isThrottled) break; // 为了防止重复不断疯狂渲染,加上等待_(:з」∠)_
            isThrottled = true;
            clearTimeout(timeoutId);
            timeoutId = setTimeout(() => {
                isThrottled = false;
                document.querySelectorAll('#chuanhu_chatbot .message-wrap .message.bot').forEach(addChuanhuButton);
                saveHistoryHtml();
                disableSendBtn();
            }, 1500);
        }
    }
});
// mObserver.observe(targetNode, { attributes: true, childList: true, subtree: true, characterData: true});

var submitObserver = new MutationObserver(function (mutationsList) {
    document.querySelectorAll('#chuanhu_chatbot .message-wrap .message.bot').forEach(addChuanhuButton);
    saveHistoryHtml();
});

var statusObserver = new MutationObserver(function (mutationsList) {
    for (const mutation of mutationsList) {
        if (mutation.type === 'attributes' || mutation.type === 'childList') {
            if (statusDisplay.innerHTML.includes('<span id="update-status"')) {
                if (getUpdateStatus() === "success") {
                    releaseNoteElement.innerHTML = updateSuccess_i18n.hasOwnProperty(language) ? updateSuccess_i18n[language] : updateSuccess_i18n['en'];
                    noUpdateHtml();
                    localStorage.setItem('isLatestVersion', 'true');
                    isLatestVersion = true;
                } else if (getUpdateStatus() === "failure") {
                    releaseNoteElement.innerHTML = updateFailure_i18n.hasOwnProperty(language) ? updateFailure_i18n[language] : updateFailure_i18n['en'];
                } else {
                    releaseNoteElement.innerHTML = getUpdateStatus();
                }
            }
        }
    }
});

var loadhistorytime = 0; // for debugging
function saveHistoryHtml() {
    var historyHtml = document.querySelector('#chuanhu_chatbot>.wrapper>.wrap');
    if (!historyHtml) return;   // no history, do nothing
    localStorage.setItem('chatHistory', historyHtml.innerHTML);
    // console.log("History Saved")
    historyLoaded = false;
}
function loadHistoryHtml() {
    var historyHtml = localStorage.getItem('chatHistory');
    if (!historyHtml) {
        historyLoaded = true;
        return; // no history, do nothing
    }
    userLogged = localStorage.getItem('userLogged');
    if (userLogged){
        historyLoaded = true;
        return; // logged in, do nothing
    }
    if (!historyLoaded) {
        var tempDiv = document.createElement('div');
        tempDiv.innerHTML = historyHtml;
        var buttons = tempDiv.querySelectorAll('button.chuanhu-btn');
        var gradioCopyButtons = tempDiv.querySelectorAll('button.copy_code_button');
        for (var i = 0; i < buttons.length; i++) {
            buttons[i].parentNode.removeChild(buttons[i]);
        }
        for (var i = 0; i < gradioCopyButtons.length; i++) {
            gradioCopyButtons[i].parentNode.removeChild(gradioCopyButtons[i]);
        }
        var fakeHistory = document.createElement('div');
        fakeHistory.classList.add('history-message');
        fakeHistory.innerHTML = tempDiv.innerHTML;
        webLocale();
        chatbotWrap.insertBefore(fakeHistory, chatbotWrap.firstChild);
        // var fakeHistory = document.createElement('div');
        // fakeHistory.classList.add('history-message');
        // fakeHistory.innerHTML = historyHtml;
        // chatbotWrap.insertBefore(fakeHistory, chatbotWrap.firstChild);
        historyLoaded = true;
        console.log("History Loaded");
        loadhistorytime += 1; // for debugging
    } else {
        historyLoaded = false;
    }
}
function clearHistoryHtml() {
    localStorage.removeItem("chatHistory");
    historyMessages = chatbotWrap.querySelector('.history-message');
    if (historyMessages) {
        chatbotWrap.removeChild(historyMessages);
        console.log("History Cleared");
    }
}

var showingUpdateInfo = false;
async function getLatestRelease() {
    try {
        const response = await fetch('https://api.github.com/repos/gaizhenbiao/chuanhuchatgpt/releases/latest');
        if (!response.ok) {
            console.log(`Error: ${response.status} - ${response.statusText}`);
            updateInfoGotten = true;
            return null;
        }
        const data = await response.json();
        updateInfoGotten = true;
        return data;
    } catch (error) {
        console.log(`Error: ${error}`);
        updateInfoGotten = true;
        return null;
    }
}

var releaseNoteElement = document.getElementById('release-note-content');
async function updateLatestVersion() {
    const currentVersionElement = document.getElementById('current-version');
    const latestVersionElement = document.getElementById('latest-version-title');
    releaseNoteElement = document.getElementById('release-note-content');
    const currentVersion = currentVersionElement.textContent;
    const versionTime = document.getElementById('version-time').innerText;
    const localVersionTime = versionTime !== "unknown" ? (new Date(versionTime)).getTime() : 0;
    updateInfoGotten = true; //无论成功与否都只执行一次,否则容易api超限...
    try {
        const data = await getLatestRelease();
        const releaseNote = data.body;
        if (releaseNote) {
            releaseNoteElement.innerHTML = marked.parse(releaseNote, {mangle: false, headerIds: false});
        }
        const latestVersion = data.tag_name;
        const latestVersionTime = (new Date(data.created_at)).getTime();
        if (latestVersionTime) {
            if (localVersionTime < latestVersionTime) {
                latestVersionElement.textContent = latestVersion;
                console.log(`New version ${latestVersion} found!`);
                if (!isInIframe) {openUpdateToast();}      
            } else {
                noUpdate();
            }
            currentTime = new Date().getTime();
            localStorage.setItem('lastCheckTime', currentTime);
        }
    } catch (error) {
        console.error(error);
    }
}
function getUpdateInfo() {
    window.open('https://github.com/gaizhenbiao/chuanhuchatgpt/releases/latest', '_blank');
    closeUpdateToast();
}
function bgUpdateChuanhu() {
    updateChuanhuBtn.click();
    releaseNoteElement = document.getElementById('release-note-content');
    releaseNoteElement.innerHTML = '<p>正在尝试更新...</p>';
    statusObserver.observe(statusDisplay, { childList: true, subtree: true, characterData: true});
}
function cancelUpdate() {
    closeUpdateToast();
}
function openUpdateToast() {
    showingUpdateInfo = true;
    setUpdateWindowHeight();
}
function closeUpdateToast() {
    updateToast.style.setProperty('top', '-500px');
    showingUpdateInfo = false;
}
function manualCheckUpdate() {
    openUpdateToast();
    updateLatestVersion();
    currentTime = new Date().getTime();
    localStorage.setItem('lastCheckTime', currentTime);
}
function noUpdate() {
    localStorage.setItem('isLatestVersion', 'true');
    isLatestVersion = true;
    const releaseNoteWrap = document.getElementById('release-note-wrap');
    releaseNoteWrap.style.setProperty('display', 'none');
    noUpdateHtml();
}
function noUpdateHtml() {
    const versionInfoElement = document.getElementById('version-info-title');
    const gotoUpdateBtn = document.getElementById('goto-update-btn');
    const closeUpdateBtn = document.getElementById('close-update-btn');
    versionInfoElement.textContent = usingLatest_i18n.hasOwnProperty(language) ? usingLatest_i18n[language] : usingLatest_i18n['en'];
    gotoUpdateBtn.classList.add('hideK');
    closeUpdateBtn.classList.remove('hideK');
}
function getUpdateStatus() {
    const updateStatus = statusDisplay.querySelector("#update-status")
    if (updateStatus) {
        return updateStatus.innerText
    } else {
        return "unknown"
    }
}

function setUpdateWindowHeight() {
    if (!showingUpdateInfo) {return;}
    const scrollPosition = window.scrollY;
    // const originalTop = updateToast.style.getPropertyValue('top');
    const resultTop = scrollPosition - 20 + 'px';
    updateToast.style.setProperty('top', resultTop);
}
    
// 监视页面内部 DOM 变动
var observer = new MutationObserver(function (mutations) {
    gradioLoaded(mutations);
});
observer.observe(targetNode, { childList: true, subtree: true });

// 监视页面变化
window.addEventListener("DOMContentLoaded", function () {
    isInIframe = (window.self !== window.top);
    historyLoaded = false;
});
window.addEventListener('resize', setChatbotHeight);
window.addEventListener('scroll', function(){setChatbotHeight();setUpdateWindowHeight();});
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", adjustDarkMode);

// console suprise
var styleTitle1 = `
font-size: 16px;
font-family: ui-monospace, monospace;
color: #06AE56;
`
var styleDesc1 = `
font-size: 12px;
font-family: ui-monospace, monospace;
`
function makeML(str) {
    let l = new String(str)
    l = l.substring(l.indexOf("/*") + 3, l.lastIndexOf("*/"))
    return l
}
let ChuanhuInfo = function () {
    /* 
   ________                      __             ________          __ 
  / ____/ /_  __  ______ _____  / /_  __  __   / ____/ /_  ____ _/ /_
 / /   / __ \/ / / / __ `/ __ \/ __ \/ / / /  / /   / __ \/ __ `/ __/
/ /___/ / / / /_/ / /_/ / / / / / / / /_/ /  / /___/ / / / /_/ / /_  
\____/_/ /_/\__,_/\__,_/_/ /_/_/ /_/\__,_/   \____/_/ /_/\__,_/\__/  
                                                                     
   川虎Chat (Chuanhu Chat) - GUI for ChatGPT API and many LLMs
 */
}
let description = `
© 2023 Chuanhu, MZhao, Keldos
GitHub repository: [https://github.com/GaiZhenbiao/ChuanhuChatGPT]\n
Enjoy our project!\n
`
console.log(`%c${makeML(ChuanhuInfo)}`,styleTitle1)
console.log(`%c${description}`, styleDesc1)

// button svg code
const copyIcon   = '<span><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height=".8em" width=".8em" xmlns="http://www.w3.org/2000/svg"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg></span>';
const copiedIcon = '<span><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height=".8em" width=".8em" xmlns="http://www.w3.org/2000/svg"><polyline points="20 6 9 17 4 12"></polyline></svg></span>';
const mdIcon     = '<span><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 14 18" stroke-linecap="round" stroke-linejoin="round" height=".8em" width=".8em" xmlns="http://www.w3.org/2000/svg"><g transform-origin="center" transform="scale(0.85)"><path d="M1.5,0 L12.5,0 C13.3284271,-1.52179594e-16 14,0.671572875 14,1.5 L14,16.5 C14,17.3284271 13.3284271,18 12.5,18 L1.5,18 C0.671572875,18 1.01453063e-16,17.3284271 0,16.5 L0,1.5 C-1.01453063e-16,0.671572875 0.671572875,1.52179594e-16 1.5,0 Z" stroke-width="1.8"></path><line x1="3.5" y1="3.5" x2="10.5" y2="3.5"></line><line x1="3.5" y1="6.5" x2="8" y2="6.5"></line></g><path d="M4,9 L10,9 C10.5522847,9 11,9.44771525 11,10 L11,13.5 C11,14.0522847 10.5522847,14.5 10,14.5 L4,14.5 C3.44771525,14.5 3,14.0522847 3,13.5 L3,10 C3,9.44771525 3.44771525,9 4,9 Z" stroke="none" fill="currentColor"></path></svg></span>';
const rawIcon    = '<span><svg stroke="currentColor" fill="none" stroke-width="1.8" viewBox="0 0 18 14" stroke-linecap="round" stroke-linejoin="round" height=".8em" width=".8em" xmlns="http://www.w3.org/2000/svg"><g transform-origin="center" transform="scale(0.85)"><polyline points="4 3 0 7 4 11"></polyline><polyline points="14 3 18 7 14 11"></polyline><line x1="12" y1="0" x2="6" y2="14"></line></g></svg></span>';