various updates.

This commit is contained in:
Khyretos 2023-12-28 16:25:52 +01:00
parent 914cf831c4
commit 525cb6116e
45 changed files with 5022 additions and 5277 deletions

9
.editorconfig Normal file
View file

@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

5
.eslintignore Normal file
View file

@ -0,0 +1,5 @@
node_modules
docs
dist
out
build

16
.eslintrc.js Normal file
View file

@ -0,0 +1,16 @@
module.exports = {
root: true,
env: {
es6: true,
node: true
},
parserOptions: {
sourceType: 'module',
ecmaVersion: 2021
},
extends: ['eslint:recommended', '@electron-internal', '@electron-toolkit'],
rules: {
'space-before-function-paren': 'off',
vendorPrefix: 'off'
}
};

5
.prettierrc.yaml Normal file
View file

@ -0,0 +1,5 @@
singleQuote: true
semi: true
printWidth: 140
trailingComma: none
arrowParens: avoid

View file

@ -19,6 +19,7 @@
"dependencies": {
"axios": "^1.4.0",
"electron-squirrel-startup": "^1.0.0",
"emoji-picker-element": "^1.21.0",
"express": "^4.18.2",
"ini": "^2.0.0",
"kill-process-by-name": "^1.0.5",
@ -38,6 +39,12 @@
"@electron-forge/maker-squirrel": "^6.2.1",
"@electron-forge/maker-zip": "^6.2.1",
"@electron-forge/plugin-auto-unpack-natives": "^6.2.1",
"electron": "^25.9.8"
"@electron-internal/eslint-config": "^1.0.1",
"@electron-toolkit/eslint-config": "^1.0.2",
"electron": "^25.9.8",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.2",
"prettier": "^3.1.1"
}
}

View file

@ -1,7 +1,15 @@
# from wsgiref.simple_server import WSGIServer
from flask import Flask, Response, jsonify, request
import gevent
import re
import gevent.monkey
import json
from waitress import serve
import logging
logger = logging.getLogger("waitress")
logger.setLevel(logging.INFO)
gevent.monkey.patch_all()
import gevent.queue
@ -40,6 +48,7 @@ q = queue.Queue()
# gobal functions
# classes
class LanguageDetection:
def __init__(self):
@ -55,10 +64,8 @@ class LanguageDetection:
language_detection_model = os.path.join(
resources_folder, "language_detection_model", f"lid.176.bin"
)
language_detection_model = (
rf"{language_detection_model}"
)
language_detection_model = rf"{language_detection_model}"
self.model = fasttext.load_model(language_detection_model)
def predict_lang(self, text):
@ -69,6 +76,7 @@ class LanguageDetection:
return language_codes
class STT:
samplerate = None
args = ""
@ -92,9 +100,7 @@ class STT:
resources_folder, "speech_to_text_models", settings["STT"]["LANGUAGE"]
)
self.model = Model(
rf"{vosk_model}"
)
self.model = Model(rf"{vosk_model}")
self.dump_fn = None
self.q = gevent.queue.Queue()
@ -180,6 +186,7 @@ text_to_speech_service = TTS()
# endpoints
@app.route("/stream", methods=["GET"])
def stream_recognition():
def generate():
@ -239,9 +246,14 @@ def trigger_backend_event():
try:
request_data = request.json
message = request_data.get("message", "")
filteredMessage = re.sub(
r"https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)",
"a link",
message,
)
voice = request_data.get("voice")
count = request_data.get("count")
text_to_speech_service.say(message, voice, count)
text_to_speech_service.say(filteredMessage, voice, count)
except Exception as e:
return jsonify({"error": "An error occurred"}), 500
return jsonify({"message": "Audio triggered"}), 200
@ -257,14 +269,14 @@ def get_voices():
if __name__ == "__main__":
LANGUAGE = LanguageDetection()
lang = LANGUAGE.predict_lang("hola cómo estás")
print(lang)
text = "Keep it up. You are awesome"
translated = MyMemoryTranslator(
source="english", target="spanish latin america"
).translate(text)
print(translated)
# LANGUAGE = LanguageDetection()
# lang = LANGUAGE.predict_lang("hola cómo estás")
# print(lang)
# text = "Keep it up. You are awesome"
# translated = MyMemoryTranslator(
# source="english", target="spanish latin america"
# ).translate(text)
# print(translated)
if len(sys.argv) > 1:
settings.read(settingsPath)
port = int(settings["GENERAL"]["PORT"])
@ -273,5 +285,4 @@ if __name__ == "__main__":
port = 9000
stream_recognition()
app.run(host="127.0.0.1", port=port)
app.terminate()
serve(app, host="0.0.0.0", port=port)

View file

@ -1,462 +1,469 @@
@font-face {
font-family: 'FRAMDCN';
font-family: 'FRAMDCN';
src: url(../fonts/FRAMCDN/FRAMDCN.woff);
}
h1 {
font-family: 'FRAMDCN';
font-family: 'FRAMDCN';
}
.message-window {
height: calc(100% - 60px);
overflow: hidden;
overflow-y: auto;
display: flex;
align-items: center;
flex-direction: column;
background-color: var(--mid-section);
padding-left: 50px;
padding-right: 50px;
font-family: 'FRAMDCN';
position: relative;
z-index: 1;
height: calc(100% - 60px);
overflow: hidden;
overflow-y: auto;
display: flex;
align-items: center;
flex-direction: column;
background-color: var(--mid-section);
padding-left: 50px;
padding-right: 50px;
font-family: 'FRAMDCN';
position: relative;
z-index: 1;
}
.input-box {
display: flex;
border: none;
width: 100%;
height: 30px;
font-size: 16px;
display: flex;
border: none;
width: 100%;
height: 30px;
font-size: 16px;
}
.userText {
color: var(--chat-bubble-message);
font-family: Helvetica;
font-size: 16px;
text-align: right;
clear: both;
color: var(--chat-bubble-message);
font-family: Helvetica;
font-size: 16px;
text-align: right;
clear: both;
}
.userText span {
line-height: 1.5em;
display: inline-block;
background: #5ca6fa;
padding: 10px;
border-radius: 8px;
border-bottom-right-radius: 2px;
max-width: 80%;
margin-right: 10px;
animation: floatup 0.5s forwards;
line-height: 1.5em;
display: inline-block;
background: #5ca6fa;
padding: 10px;
border-radius: 8px;
border-bottom-right-radius: 2px;
max-width: 80%;
margin-right: 10px;
animation: floatup 0.5s forwards;
}
.botText {
color: #000;
font-family: Helvetica;
font-weight: normal;
font-size: 16px;
text-align: left;
color: #000;
font-family: Helvetica;
font-weight: normal;
font-size: 16px;
text-align: left;
}
.botText span {
line-height: 1.5em;
display: inline-block;
background: #e0e0e0;
padding: 10px;
border-radius: 8px;
border-bottom-left-radius: 2px;
max-width: 80%;
margin-left: 10px;
animation: floatup 0.5s forwards;
line-height: 1.5em;
display: inline-block;
background: #e0e0e0;
padding: 10px;
border-radius: 8px;
border-bottom-left-radius: 2px;
max-width: 80%;
margin-left: 10px;
animation: floatup 0.5s forwards;
}
@keyframes floatup {
from {
transform: translateY(14px);
opacity: 0;
}
from {
transform: translateY(14px);
opacity: 0;
}
to {
transform: translateY(0px);
opacity: 1;
}
to {
transform: translateY(0px);
opacity: 1;
}
}
@media screen and (max-width: 600px) {
.full-chat-block {
width: 100%;
border-radius: 0px;
}
.full-chat-block {
width: 100%;
border-radius: 0px;
}
.chat-bar-collapsible {
position: fixed;
bottom: 0;
right: 0;
width: 100%;
}
.chat-bar-collapsible {
position: fixed;
bottom: 0;
right: 0;
width: 100%;
}
.collapsible {
width: 100%;
border: 0px;
border-radius: 0px;
}
.collapsible {
width: 100%;
border: 0px;
border-radius: 0px;
}
}
::-webkit-scrollbar {
width: 4px;
width: 4px;
}
::-webkit-scrollbar-thumb {
background-color: #4c4c6a;
border-radius: 2px;
background-color: #4c4c6a;
border-radius: 2px;
}
.chatBox {
width: 300px;
height: 400px;
max-height: 400px;
display: flex;
flex-direction: column;
overflow: hidden;
box-shadow: 0 0 4px var(--main-color4);
width: 300px;
height: 400px;
max-height: 400px;
display: flex;
flex-direction: column;
overflow: hidden;
box-shadow: 0 0 4px var(--main-color4);
}
.chat-window {
flex: auto;
max-height: calc(100% - 60px);
background: #2f323b;
overflow: auto;
flex: auto;
max-height: calc(100% - 60px);
background: #2f323b;
overflow: auto;
}
.chat-input {
height: 30px;
display: flex;
flex: 0 0 auto;
height: 60px;
background: var(--main-color3);
height: 30px;
display: flex;
flex: 0 0 auto;
height: 60px;
background: var(--main-color3);
}
.chat-input input {
height: 59px;
line-height: 60px;
outline: 0 none;
border: none;
width: calc(100% - 60px);
color: var(--chat-bubble-message);
text-indent: 10px;
font-size: 12pt;
padding: 0;
background: var(--main-color3);
height: 59px;
line-height: 60px;
outline: 0 none;
border: none;
width: calc(100% - 60px);
color: var(--chat-bubble-message);
text-indent: 10px;
font-size: 12pt;
padding: 0;
background: var(--main-color3);
}
.chat-input button {
float: right;
outline: 0 none;
border: none;
background: var(--main-color3);
height: 40px;
width: 40px;
border-radius: 50%;
padding: 2px 0 0 0;
margin: 10px;
float: right;
outline: 0 none;
border: none;
background: var(--main-color3);
height: 40px;
width: 40px;
border-radius: 50%;
padding: 2px 0 0 0;
margin: 10px;
}
.chat-input input[good] + button {
box-shadow: 0 0 2px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.24);
box-shadow:
0 0 2px rgba(0, 0, 0, 0.12),
0 2px 4px rgba(0, 0, 0, 0.24);
}
.chat-input input[good] + button:hover {
box-shadow: 0 8px 17px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
/* filter: brightness(150%); */
box-shadow:
0 8px 17px 0 rgba(0, 0, 0, 0.2),
0 6px 20px 0 rgba(0, 0, 0, 0.19);
/* filter: brightness(150%); */
}
.chat-input input[good] + button path {
fill: var(--chat-bubble-message);
fill: var(--chat-bubble-message);
}
.msg-container {
direction: ltr;
position: static;
width: 100%;
padding: 10px 0px 0px 0px;
display: grid;
grid-template: 1fr / 1fr;
align-self: start;
direction: ltr;
position: static;
width: 100%;
padding: 10px 0px 0px 0px;
display: grid;
grid-template: 1fr / 1fr;
align-self: start;
}
.msg-container > * {
grid-column: 1 / 1;
grid-row: 1 / 1;
grid-column: 1 / 1;
grid-row: 1 / 1;
}
.msg-container.sender {
place-items: self-start;
place-items: self-start;
}
.msg-container.user {
place-items: self-end;
place-items: self-end;
}
.msg-box {
background: var(--chat-bubble);
color: white;
min-width: 100px;
border-radius: 5px;
padding: 18px 5px 5px 5px;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.24);
width: fit-content;
position: relative;
align-self: start;
background: var(--chat-bubble);
color: white;
min-width: 100px;
border-radius: 5px;
padding: 18px 5px 5px 5px;
box-shadow:
0 0 2px rgba(0, 0, 0, 0.12),
0 2px 4px rgba(0, 0, 0, 0.24);
width: fit-content;
position: relative;
align-self: start;
}
.msg-box.sender {
margin: 25px 25px 0px 35px;
margin: 25px 25px 0px 35px;
}
.msg-box.user {
text-align: left;
margin: 25px 35px 0px 0px;
text-align: left;
margin: 25px 35px 0px 0px;
}
.msg-box-user-temp {
background: var(--chat-bubble-temp);
background: var(--chat-bubble-temp);
}
.user-img {
display: inline-block;
position: relative;
border-radius: 50%;
height: 50px;
width: 50px;
z-index: 5;
align-self: start;
display: inline-block;
position: relative;
border-radius: 50%;
height: 50px;
width: 50px;
z-index: 5;
align-self: start;
}
.messages.user {
margin-right: 20px;
margin-right: 20px;
}
.msg {
font-size: 12pt;
color: var(--chat-bubble-message);
margin: 0 0 0 0;
font-size: 12pt;
color: var(--chat-bubble-message);
margin: 0 0 0 0;
}
.msg-temp {
color: var(--chat-bubble-message-temp);
color: var(--chat-bubble-message-temp);
}
.timestamp {
color: var(--chat-bubble-header);
font-size: 10pt;
align-items: center;
font-family: 'xxii_avenmedium';
color: var(--chat-bubble-header);
font-size: 10pt;
align-items: center;
font-family: 'xxii_avenmedium';
}
.timestamp-temp {
color: var(--chat-bubble-header-temp);
color: var(--chat-bubble-header-temp);
}
.username {
background-color: var(--main-color4);
color: white;
position: relative;
border-radius: 5px;
z-index: 3;
align-self: start;
background-color: var(--main-color4);
color: white;
position: relative;
border-radius: 5px;
z-index: 3;
align-self: start;
}
.username.sender {
padding: 0px 5px 5px 30px;
margin: 20px 5px 5px 30px;
padding: 0px 5px 5px 30px;
margin: 20px 5px 5px 30px;
}
.username.user {
padding: 0px 30px 5px 5px;
margin: 20px 30px 5px 5px;
padding: 0px 30px 5px 5px;
margin: 20px 30px 5px 5px;
}
.username-temp {
color: var(--chat-bubble-header-temp);
color: var(--chat-bubble-header-temp);
}
.post-time {
font-size: 8pt;
color: white;
display: inline-block;
background-color: var(--main-color4);
position: relative;
z-index: 2;
border-radius: 5px;
align-self: start;
font-size: 8pt;
color: white;
display: inline-block;
background-color: var(--main-color4);
position: relative;
z-index: 2;
border-radius: 5px;
align-self: start;
}
.post-time.sender {
padding: 5px 5px 0px 15px;
margin: 0px 0px 0px 50px;
padding: 5px 5px 0px 15px;
margin: 0px 0px 0px 50px;
}
.post-time.user {
padding: 5px 15px 0px 5px;
margin: 0px 50px 0px 0px;
padding: 5px 15px 0px 5px;
margin: 0px 50px 0px 0px;
}
.mmg {
display: flex;
display: flex;
}
.img {
height: 100%;
width: 100%;
border-radius: 50%;
height: 100%;
width: 100%;
border-radius: 50%;
}
.status-circle {
width: 20px;
border-radius: 50%;
z-index: 6;
position: relative;
align-self: start;
width: 20px;
border-radius: 50%;
z-index: 6;
position: relative;
align-self: start;
}
.status-circle.sender {
margin-left: 40px;
margin-left: 40px;
}
.status-circle.user {
margin-right: 40px;
margin-right: 40px;
}
.menu-select {
font-size: 0.9rem;
height: 40px;
border-radius: 20px;
background-color: var(--main-color3);
color: var(--main-color2);
align-items: center;
border: 0px;
padding-left: 10px;
width: 300px;
font-size: 100%;
padding: 10px;
padding-right: 25px;
outline: none;
-webkit-appearance: none;
-moz-appearance: none;
background-image: url("data:image/svg+xml;utf8,<svg fill='white' height='34' viewBox='0 0 24 24' width='32' xmlns='http://www.w3.org/2000/svg'><path d='M7 10l5 5 5-5z'/><path d='M0 0h24v24H0z' fill='none'/></svg>");
background-repeat: no-repeat;
background-position-x: 100%;
background-position-y: 5px;
font-size: 0.9rem;
height: 40px;
border-radius: 20px;
background-color: var(--main-color3);
color: var(--main-color2);
align-items: center;
border: 0px;
padding-left: 10px;
width: 300px;
font-size: 100%;
padding: 10px;
padding-right: 25px;
outline: none;
-webkit-appearance: none;
-moz-appearance: none;
background-image: url("data:image/svg+xml;utf8,<svg fill='white' height='34' viewBox='0 0 24 24' width='32' xmlns='http://www.w3.org/2000/svg'><path d='M7 10l5 5 5-5z'/><path d='M0 0h24v24H0z' fill='none'/></svg>");
background-repeat: no-repeat;
background-position-x: 100%;
background-position-y: 5px;
}
.top-select {
width: auto;
height: 24px;
padding: 0px;
margin: 0px;
background-color: transparent;
color: white;
-webkit-appearance: none;
-moz-appearance: none;
border: none;
width: auto;
height: 24px;
padding: 0px;
margin: 0px;
background-color: transparent;
color: white;
-webkit-appearance: none;
-moz-appearance: none;
border: none;
}
.info-image {
width: 50px;
height: 50px;
width: 50px;
height: 50px;
}
.top-select option {
margin: 40px;
background: rgba(0, 0, 0, 0.3);
color: #fff;
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.4);
background-color: var(--top-bar);
margin: 40px;
background: rgba(0, 0, 0, 0.3);
color: #fff;
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.4);
background-color: var(--top-bar);
}
.AdvancedMenu {
border: 1px var(--main-color2) solid;
margin-top: 10px;
min-width: 555px;
border-radius: 5px;
border-radius: 5px;
border: 1px var(--main-color2) solid;
margin-top: 10px;
min-width: 555px;
border-radius: 5px;
border-radius: 5px;
}
.legendStyle {
margin-left: 1em;
padding: 0.2em 0.8em;
display: flex;
align-items: center;
margin-left: 1em;
padding: 0.2em 0.8em;
display: flex;
align-items: center;
}
.AdvancedMenuRow {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
justify-content: left;
margin-bottom: 10px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
justify-content: left;
margin-bottom: 10px;
}
.AdvancedMenuLabel {
font-size: 10pt;
padding-right: 5px;
margin-left: 10px;
width: 125px;
font-size: 10pt;
padding-right: 5px;
margin-left: 10px;
width: 125px;
}
.AdvancedMenuLabel2 {
font-size: 10pt;
padding-right: 5px;
margin-left: 10px;
font-size: 10pt;
padding-right: 5px;
margin-left: 10px;
}
.AdvancedMenuLabel3 {
font-size: 12pt;
padding-right: 5px;
margin-left: 10px;
font-size: 12pt;
padding-right: 5px;
margin-left: 10px;
}
#SaveAdvancedSettingsButton {
margin-left: 10px;
margin-left: 10px;
}
.toggle {
position: relative;
display: inline-block;
width: 60px;
height: 40px;
background-color: var(--main-color3);
border-radius: 20px;
position: relative;
display: inline-block;
width: 60px;
height: 40px;
background-color: var(--main-color3);
border-radius: 20px;
}
/* After slide changes */
.toggle:after {
content: '';
position: absolute;
width: 30px;
height: 30px;
border-radius: 50%;
background-color: var(--main-color2);
left: 5px;
top: 5px;
content: '';
position: absolute;
width: 30px;
height: 30px;
border-radius: 50%;
background-color: var(--main-color2);
left: 5px;
top: 5px;
}
/* Checkbox checked effect */
.checkbox:checked + .toggle::after {
left: 25px;
left: 25px;
}
/* Checkbox checked toggle label bg color */
.checkbox:checked + .toggle {
background-color: var(--main-color1);
background-color: var(--main-color1);
}
/* Checkbox vanished */
.checkbox {
display: none;
display: none;
}
/* Small toggle */
@ -464,36 +471,45 @@ h1 {
/* toggle in label designing */
.toggle-small {
position: relative;
display: inline-block;
width: 30px;
height: 20px;
background-color: var(--main-color3);
border-radius: 10px;
margin-left: 10px;
position: relative;
display: inline-block;
width: 30px;
height: 20px;
background-color: var(--main-color3);
border-radius: 10px;
margin-left: 10px;
}
/* After slide changes */
.toggle-small:after {
content: '';
position: absolute;
width: 15px;
height: 15px;
border-radius: 50%;
background-color: white;
left: 2px;
top: 2px;
content: '';
position: absolute;
width: 15px;
height: 15px;
border-radius: 50%;
background-color: white;
left: 2px;
top: 2px;
}
/* Checkbox checked effect */
.checkbox:checked + .toggle-small::after {
left: 13px;
left: 13px;
}
/* Checkbox checked toggle label bg color */
.checkbox:checked + .toggle-small {
background-color: var(--main-color1);
background-color: var(--main-color1);
}
.emoji-picker {
margin-left: auto;
visibility: hidden;
bottom: 50px;
left: 0px;
position: absolute;
z-index: 1;
}

View file

@ -1,62 +1,62 @@
input[type="radio"]:checked {
visibility: hidden;
position: absolute;
input[type='radio']:checked {
visibility: hidden;
position: absolute;
}
input[type="radio"] {
visibility: hidden;
position: absolute;
input[type='radio'] {
visibility: hidden;
position: absolute;
}
label.btn span {
font-size: 1.5em;
font-size: 1.5em;
}
label input[type="radio"]~i.fa.fa-square {
color: var(--main-color3);
display: inline;
label input[type='radio'] ~ i.fa.fa-square {
color: var(--main-color3);
display: inline;
}
label input[type="radio"]~i.fa.fa-check-square {
display: none;
label input[type='radio'] ~ i.fa.fa-check-square {
display: none;
}
label input[type="radio"]:checked~i.fa.fa-square {
display: none;
label input[type='radio']:checked ~ i.fa.fa-square {
display: none;
}
label input[type="radio"]:checked~i.fa.fa-check-square {
display: inline;
color: var(--main-color2);
label input[type='radio']:checked ~ i.fa.fa-check-square {
display: inline;
color: var(--main-color2);
}
label:hover input[type="radio"]~i.fa {
color: var(--main-color1);
/* filter: brightness(150%); */
label:hover input[type='radio'] ~ i.fa {
color: var(--main-color1);
/* filter: brightness(150%); */
}
div[data-toggle="buttons"] label {
display: inline-block;
padding: 3px 12px;
margin-bottom: 0;
font-size: 20px;
font-weight: normal;
line-height: 2em;
text-align: left;
white-space: nowrap;
vertical-align: top;
cursor: pointer;
background-color: none;
border-radius: 3px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
div[data-toggle='buttons'] label {
display: inline-block;
padding: 3px 12px;
margin-bottom: 0;
font-size: 20px;
font-weight: normal;
line-height: 2em;
text-align: left;
white-space: nowrap;
vertical-align: top;
cursor: pointer;
background-color: none;
border-radius: 3px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}
div[data-toggle="buttons"] label:active,
div[data-toggle="buttons"] label.active {
-webkit-box-shadow: none;
box-shadow: none;
}
div[data-toggle='buttons'] label:active,
div[data-toggle='buttons'] label.active {
-webkit-box-shadow: none;
box-shadow: none;
}

View file

@ -1,253 +1,273 @@
/* Basic styling */
:root {
overflow: hidden;
--main-color1: #6e2c8c;
--main-color1-temp: #6e2c8c;
/*Left bar and top right bar*/
--main-color2: white;
--main-color2-temp: white;
/*Icons and text*/
--main-color3: #211e1e;
--main-color3-temp: #211e1e;
/*Buttons and input*/
--main-color4: #2f2c34;
--main-color4-temp: #2f2c34;
--top-bar: #100b12;
--top-bar-temp: #100b12;
--mid-section: #352d3d;
--mid-section-temp: #352d3d;
--chat-bubble: #7a6d7f;
--chat-bubble-header: #141414;
--chat-bubble-username: white;
--chat-bubble-message: white;
--chat-bubble-temp: #7a6d7f;
--chat-bubble-header-temp: #141414;
--chat-bubble-message-temp: white;
overflow: hidden;
--main-color1: #6e2c8c;
--main-color1-temp: #6e2c8c;
/*Left bar and top right bar*/
--main-color2: white;
--main-color2-temp: white;
/*Icons and text*/
--main-color3: #211e1e;
--main-color3-temp: #211e1e;
/*Buttons and input*/
--main-color4: #2f2c34;
--main-color4-temp: #2f2c34;
--top-bar: #100b12;
--top-bar-temp: #100b12;
--mid-section: #352d3d;
--mid-section-temp: #352d3d;
--chat-bubble: #7a6d7f;
--chat-bubble-header: #141414;
--chat-bubble-username: white;
--chat-bubble-message: white;
--chat-bubble-temp: #7a6d7f;
--chat-bubble-header-temp: #141414;
--chat-bubble-message-temp: white;
}
html {
box-sizing: border-box;
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
box-sizing: inherit;
}
html,
body {
height: 100%;
margin: 0;
/* border-top-left-radius: 20px; */
/* border-top-right-radius: 20px; */
overflow-x: hidden;
height: 100%;
margin: 0;
/* border-top-left-radius: 20px; */
/* border-top-right-radius: 20px; */
overflow-x: hidden;
}
body {
font-family: 'Segoe UI', sans-serif;
background: transparent;
font-family: 'Segoe UI', sans-serif;
background: transparent;
}
/* Styling of window frame and titlebar */
body {
/* border: 1px solid #48545c; */
overflow-y: hidden;
/* border: 1px solid #48545c; */
overflow-y: hidden;
}
#titlebar {
display: block;
/* position: fixed; */
height: 32px;
width: calc(100%);
background-color: var(--top-bar);
/* border-top-left-radius: 20px;
display: block;
/* position: fixed; */
height: 32px;
width: calc(100%);
background-color: var(--top-bar);
/* border-top-left-radius: 20px;
border-top-right-radius: 20px; */
}
.maximized #titlebar {
width: 100%;
padding: 0;
width: 100%;
padding: 0;
}
#main {
height: calc(100% - 32px);
margin-top: 32px;
padding: 20px;
overflow-y: auto;
display: flex;
height: calc(100% - 32px);
margin-top: 32px;
padding: 20px;
overflow-y: auto;
display: flex;
}
#titlebar {
padding: 4px;
padding: 4px;
}
#titlebar #drag-region {
width: 100%;
height: 100%;
-webkit-app-region: drag;
display: inline-flex;
width: 100%;
height: 100%;
-webkit-app-region: drag;
display: inline-flex;
}
#titlebar {
color: var(--main-color2);
color: var(--main-color2);
}
#window-title {
grid-column: 1;
display: flex;
align-items: center;
margin-left: 8px;
overflow: hidden;
font-family: 'Segoe UI', sans-serif;
font-size: 12px;
grid-column: 1;
display: flex;
align-items: center;
margin-left: 8px;
overflow: hidden;
font-family: 'Segoe UI', sans-serif;
font-size: 12px;
}
.maximized #window-title {
margin-left: 12px;
margin-left: 12px;
}
#window-title span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: 1.5;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: 1.5;
}
#window-controls {
display: grid;
grid-template-columns: repeat(3, 46px);
position: absolute;
top: 0;
right: 0;
height: 32px;
-webkit-app-region: no-drag;
display: grid;
grid-template-columns: repeat(3, 46px);
position: absolute;
top: 0;
right: 0;
height: 32px;
-webkit-app-region: no-drag;
}
#window-controls .button {
grid-row: 1 / span 1;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 32px;
grid-row: 1 / span 1;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 32px;
}
@media (-webkit-device-pixel-ratio: 1.5),
(device-pixel-ratio: 1.5),
(-webkit-device-pixel-ratio: 2),
(device-pixel-ratio: 2),
(-webkit-device-pixel-ratio: 3),
(device-pixel-ratio: 3) {
#window-controls .icon {
width: 10px;
height: 10px;
}
(device-pixel-ratio: 1.5),
(-webkit-device-pixel-ratio: 2),
(device-pixel-ratio: 2),
(-webkit-device-pixel-ratio: 3),
(device-pixel-ratio: 3) {
#window-controls .icon {
width: 10px;
height: 10px;
}
}
#window-controls .button {
user-select: none;
user-select: none;
}
#window-controls .button:hover {
background: rgba(255, 255, 255, 0.1);
/* filter: brightness(150%); */
background: rgba(255, 255, 255, 0.1);
/* filter: brightness(150%); */
}
#window-controls .button:active {
background: rgba(255, 255, 255, 0.2);
background: rgba(255, 255, 255, 0.2);
}
#close-button:hover {
background: rgba(255, 255, 255, 0.1);
/* border-top-right-radius: 20px; */
background: #f1707a !important;
background: rgba(255, 255, 255, 0.1);
/* border-top-right-radius: 20px; */
background: #f1707a !important;
}
#close-button:active {
background: #f1707a !important;
background: #f1707a !important;
}
#close-button:active .icon {
filter: invert(1);
background: #f1707a !important;
filter: invert(1);
background: #f1707a !important;
}
#min-button {
grid-column: 1;
grid-column: 1;
}
#max-button,
#restore-button {
grid-column: 2;
grid-column: 2;
}
#close-button {
grid-column: 3;
grid-column: 3;
}
#restore-button {
display: none !important;
display: none !important;
}
.maximized #restore-button {
display: flex !important;
display: flex !important;
}
.maximized #max-button {
display: none;
display: none;
}
.active-mic {
position: absolute;
bottom: 0;
position: absolute;
bottom: 0;
}
.about {
-webkit-app-region: no-drag;
position: absolute;
left: 0;
width: 32px;
text-align: -webkit-center;
-webkit-app-region: no-drag;
position: absolute;
left: 0;
width: 32px;
text-align: -webkit-center;
}
.language-selector {
position: absolute;
-webkit-app-region: no-drag;
display: inline-block;
background-color: transparent;
cursor: pointer;
font-family: 'NotoColorEmojiLimited', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
left: 50%;
transform: translateX(-50%);
position: absolute;
-webkit-app-region: no-drag;
display: inline-block;
background-color: transparent;
cursor: pointer;
font-family:
'NotoColorEmojiLimited',
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
Helvetica,
Arial,
sans-serif,
'Apple Color Emoji',
'Segoe UI Emoji',
'Segoe UI Symbol';
left: 50%;
transform: translateX(-50%);
}
.language-dropdown {
display: none;
position: absolute;
background-color: #fff;
width: 55px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 2;
font-family: 'NotoColorEmojiLimited', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
display: none;
position: absolute;
background-color: #fff;
width: 55px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 2;
font-family:
'NotoColorEmojiLimited',
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
Helvetica,
Arial,
sans-serif,
'Apple Color Emoji',
'Segoe UI Emoji',
'Segoe UI Symbol';
}
.language-item {
padding: 5px;
cursor: pointer;
background-color: var(--top-bar);
padding: 5px;
cursor: pointer;
background-color: var(--top-bar);
}
.language-item:hover {
/* filter: brightness(150%); */
}
/* .language-item:hover {
filter: brightness(150%);
} */
@font-face {
font-family: NotoColorEmojiLimited;
unicode-range: U+1F1E6-1F1FF;
src: url(https://raw.githack.com/googlefonts/noto-emoji/main/fonts/NotoColorEmoji.ttf);
font-family: NotoColorEmojiLimited;
unicode-range: U+1F1E6-1F1FF;
src: url(https://raw.githack.com/googlefonts/noto-emoji/main/fonts/NotoColorEmoji.ttf);
}

View file

@ -1,34 +1,34 @@
table {
margin-left: 10px;
background-color: white;
border-collapse: collapse;
width: 100%;
margin-top: 60px;
margin-left: 10px;
background-color: white;
border-collapse: collapse;
width: 100%;
margin-top: 60px;
}
th,
td {
border: 1px solid black;
padding: 8px;
text-align: left;
border: 1px solid black;
padding: 8px;
text-align: left;
}
.info {
background-color: lightblue !important;
background-color: lightblue !important;
}
.warn {
background-color: #f39c12 !important;
background-color: #f39c12 !important;
}
.error {
background-color: #e74c3c !important;
background-color: #e74c3c !important;
}
#logTable {
width: 95%;
width: 95%;
}
#Logs {
overflow: auto;
}
overflow: auto;
}

View file

@ -1,226 +1,230 @@
.container {
display: flex;
height: calc(100vh - 32px);
width: 100%;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
/* will contain if #first is longer than #second */
display: flex;
height: calc(100vh - 32px);
width: 100%;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
/* will contain if #first is longer than #second */
}
.mid {
flex: 3;
display: block;
position: relative;
flex: 3;
display: block;
position: relative;
}
.OptionPanel {
/* visibility: hidden; */
background-color: var(--mid-section);
flex: 3;
display: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
/* visibility: hidden; */
background-color: var(--mid-section);
flex: 3;
display: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.OptionPanel.show {
display: block;
overflow: auto;
display: block;
overflow: auto;
}
.menu {
height: 100%;
display: block;
width: 100%;
background: var(--main-color1);
z-index: 1;
position: relative;
height: 100%;
display: block;
width: 100%;
background: var(--main-color1);
z-index: 1;
position: relative;
}
.menu .items {
list-style: none;
margin: auto;
padding: 0;
list-style: none;
margin: auto;
padding: 0;
}
#rpe {
font-size: 8pt;
margin: 2px 0px 0px 0px;
font-size: 8pt;
margin: 2px 0px 0px 0px;
}
.menu .items .item {
width: 100%;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
background: var(--main-color1);
color: var(--main-color2);
width: 100%;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
background: var(--main-color1);
color: var(--main-color2);
}
.hdp:hover {
position: fixed;
/* filter: brightness(150%); */
position: fixed;
/* filter: brightness(150%); */
}
.menu .items .item-active {
background: -webkit-linear-gradient(left, var(--main-color2) 10%, var(--main-color2), var(--main-color1) 10%, var(--main-color1) 10%);
color: var(--main-color2);
filter: brightness(90%);
background: -webkit-linear-gradient(left, var(--main-color2) 10%, var(--main-color2), var(--main-color1) 10%, var(--main-color1) 10%);
color: var(--main-color2);
filter: brightness(90%);
}
.menu .items .item:hover {
cursor: pointer;
color: var(--main-color2);
filter: brightness(120%);
cursor: pointer;
color: var(--main-color2);
filter: brightness(120%);
}
.sidepanel-left {
position: relative;
width: 50px;
font-size: 1.5em;
line-height: 1.5em;
font-family: Helvetica;
text-align: center;
box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px;
transition: 0.3s ease-in-out;
position: relative;
width: 50px;
font-size: 1.5em;
line-height: 1.5em;
font-family: Helvetica;
text-align: center;
box-shadow:
rgba(0, 0, 0, 0.16) 0px 3px 6px,
rgba(0, 0, 0, 0.23) 0px 3px 6px;
transition: 0.3s ease-in-out;
}
.sidepanel-right {
position: relative;
width: 200px;
font-size: 1.5em;
line-height: 1.5em;
font-family: Helvetica;
text-align: center;
box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px;
transition: 0.3s ease-in-out;
position: relative;
width: 200px;
font-size: 1.5em;
line-height: 1.5em;
font-family: Helvetica;
text-align: center;
box-shadow:
rgba(0, 0, 0, 0.16) 0px 3px 6px,
rgba(0, 0, 0, 0.23) 0px 3px 6px;
transition: 0.3s ease-in-out;
}
.collapse-menu-left {
margin-left: -50px;
margin-left: -50px;
}
.collapse-menu-right {
margin-right: -200px;
margin-right: -200px;
}
.sidepanel-left span {
position: relative;
display: block;
position: relative;
display: block;
}
.sidepanel-right span {
position: relative;
display: block;
position: relative;
display: block;
}
.circle-left {
position: absolute;
width: 50px;
height: 50px;
-webkit-clip-path: polygon(100% 0, 0 0, 0 100%);
clip-path: ellipse(68% 50% at 6% 50%);
font-size: 20px;
background-color: var(--main-color3);
color: var(--main-color2);
justify-content: left;
top: 32px;
left: 50px;
cursor: pointer;
display: flex;
z-index: 2;
transition: 0.3s ease-in-out;
position: absolute;
width: 50px;
height: 50px;
-webkit-clip-path: polygon(100% 0, 0 0, 0 100%);
clip-path: ellipse(68% 50% at 6% 50%);
font-size: 20px;
background-color: var(--main-color3);
color: var(--main-color2);
justify-content: left;
top: 32px;
left: 50px;
cursor: pointer;
display: flex;
z-index: 2;
transition: 0.3s ease-in-out;
}
.collapse-circle-left {
left: 0px;
left: 0px;
}
.circle-right {
position: absolute;
width: 50px;
height: 50px;
-webkit-clip-path: polygon(100% 100%, 0 0, 100% 0);
clip-path: ellipse(68% 50% at 94% 50%);
font-size: 20px;
background-color: var(--main-color3);
color: var(--main-color2);
justify-content: right;
top: 32px;
right: 199px;
cursor: pointer;
display: flex;
z-index: 1;
transition: 0.3s ease-in-out;
position: absolute;
width: 50px;
height: 50px;
-webkit-clip-path: polygon(100% 100%, 0 0, 100% 0);
clip-path: ellipse(68% 50% at 94% 50%);
font-size: 20px;
background-color: var(--main-color3);
color: var(--main-color2);
justify-content: right;
top: 32px;
right: 199px;
cursor: pointer;
display: flex;
z-index: 1;
transition: 0.3s ease-in-out;
}
.collapse-circle-right {
right: 0px;
right: 0px;
}
.fa-cog {
align-items: center;
align-self: center;
margin-left: 7px;
align-items: center;
align-self: center;
margin-left: 7px;
}
.fa-eye {
align-self: center;
margin-right: 7px;
align-self: center;
margin-right: 7px;
}
#min-button {
grid-column: 1;
grid-column: 1;
}
#max-button,
#restore-button {
grid-column: 2;
grid-column: 2;
}
#close-button {
grid-column: 3;
grid-column: 3;
}
#mini-container {
width: 100%;
height: 300px;
display: flex;
flex-direction: column;
margin-bottom: 25px;
width: 100%;
height: 300px;
display: flex;
flex-direction: column;
margin-bottom: 25px;
}
#mini-main {
display: flex;
height: 100%;
flex-direction: row;
display: flex;
height: 100%;
flex-direction: row;
}
#mini-top-bar {
height: 30px;
background-color: var(--top-bar-temp);
/* border-top-left-radius: 20px;
height: 30px;
background-color: var(--top-bar-temp);
/* border-top-left-radius: 20px;
border-top-right-radius: 20px; */
}
#mini-left {
flex: 1;
background-color: var(--main-color1-temp);
height: 100%;
flex: 1;
background-color: var(--main-color1-temp);
height: 100%;
}
#mini-mid {
flex: 8;
background-color: var(--mid-section-temp);
height: 100%;
flex: 8;
background-color: var(--mid-section-temp);
height: 100%;
}
#mini-right {
flex: 2;
background-color: var(--main-color4-temp);
height: 100%;
flex: 2;
background-color: var(--main-color4-temp);
height: 100%;
}

View file

@ -1,179 +1,195 @@
/*generated with Input range slider CSS style generator (version 20211225)
https://toughengineer.github.io/demo/slider-styler*/
input[type='range'].styled-slider {
/* height: 500px; */
background: transparent;
-webkit-appearance: none;
width: 300px;
/* height: 500px; */
background: transparent;
-webkit-appearance: none;
width: 300px;
}
/*progress support*/
input[type='range'].styled-slider.slider-progress1 {
--range: calc(var(--max) - var(--min));
--ratio: calc((var(--tiempotemporal) - var(--min)) / var(--range));
--sx: calc(0.5 * 2em + var(--ratio) * (100% - 2em));
--range: calc(var(--max) - var(--min));
--ratio: calc((var(--tiempotemporal) - var(--min)) / var(--range));
--sx: calc(0.5 * 2em + var(--ratio) * (100% - 2em));
}
input[type='range'].styled-slider.slider-progress2 {
--range: calc(var(--max) - var(--min));
--ratio: calc((var(--tiempotemporal) - var(--min)) / var(--range));
--sx: calc(0.5 * 2em + var(--ratio) * (100% - 2em));
--range: calc(var(--max) - var(--min));
--ratio: calc((var(--tiempotemporal) - var(--min)) / var(--range));
--sx: calc(0.5 * 2em + var(--ratio) * (100% - 2em));
}
input[type='range'].styled-slider.slider-progress3 {
--range: calc(var(--max) - var(--min));
--ratio: calc((var(--tiempotemporal) - var(--min)) / var(--range));
--sx: calc(0.5 * 2em + var(--ratio) * (100% - 2em));
--range: calc(var(--max) - var(--min));
--ratio: calc((var(--tiempotemporal) - var(--min)) / var(--range));
--sx: calc(0.5 * 2em + var(--ratio) * (100% - 2em));
}
input[type='range'].styled-slider.slider-progress4 {
--range: calc(var(--max) - var(--min));
--ratio: calc((var(--tiempotemporal) - var(--min)) / var(--range));
--sx: calc(0.5 * 2em + var(--ratio) * (100% - 2em));
--range: calc(var(--max) - var(--min));
--ratio: calc((var(--tiempotemporal) - var(--min)) / var(--range));
--sx: calc(0.5 * 2em + var(--ratio) * (100% - 2em));
}
/*webkit*/
input[type='range'].styled-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 2em;
height: 40px;
border-radius: 20px;
background: #ffffff;
border: none;
box-shadow: 0 0 2px black;
margin-top: calc(2em * 0.5 - 2em * 0.5);
-webkit-appearance: none;
width: 2em;
height: 40px;
border-radius: 20px;
background: #ffffff;
border: none;
box-shadow: 0 0 2px black;
margin-top: calc(2em * 0.5 - 2em * 0.5);
}
input[type='range'].styled-slider::-webkit-slider-runnable-track {
height: 40px;
border: none;
border-radius: 20px;
background: #1a1a1a;
box-shadow: none;
height: 40px;
border: none;
border-radius: 20px;
background: #1a1a1a;
box-shadow: none;
}
input[type='range'].styled-slider.slider-progress1::-webkit-slider-runnable-track {
background: linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat, #1a1a1a;
background:
linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat,
#1a1a1a;
}
input[type='range'].styled-slider.slider-progress2::-webkit-slider-runnable-track {
background: linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat, #1a1a1a;
background:
linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat,
#1a1a1a;
}
input[type='range'].styled-slider.slider-progress3::-webkit-slider-runnable-track {
background: linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat, #1a1a1a;
background:
linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat,
#1a1a1a;
}
input[type='range'].styled-slider.slider-progress4::-webkit-slider-runnable-track {
background: linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat, #1a1a1a;
background:
linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat,
#1a1a1a;
}
/*mozilla*/
input[type='range'].styled-slider::-moz-range-thumb {
width: 2em;
height: 40px;
border-radius: 20px;
background: #ffffff;
border: none;
box-shadow: 0 0 2px black;
width: 2em;
height: 40px;
border-radius: 20px;
background: #ffffff;
border: none;
box-shadow: 0 0 2px black;
}
input[type='range'].styled-slider::-moz-range-track {
height: 40px;
border: none;
border-radius: 20px;
background: var(--main-color3);
box-shadow: none;
height: 40px;
border: none;
border-radius: 20px;
background: var(--main-color3);
box-shadow: none;
}
input[type='range'].styled-slider.slider-progress1::-moz-range-track {
background: linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat, #464646;
background:
linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat,
#464646;
}
input[type='range'].styled-slider.slider-progress2::-moz-range-track {
background: linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat, #464646;
background:
linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat,
#464646;
}
input[type='range'].styled-slider.slider-progress3::-moz-range-track {
background: linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat, #464646;
background:
linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat,
#464646;
}
input[type='range'].styled-slider.slider-progress4::-moz-range-track {
background: linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat, #464646;
background:
linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat,
#464646;
}
/*ms*/
input[type='range'].styled-slider::-ms-fill-upper {
background: transparent;
border-color: transparent;
background: transparent;
border-color: transparent;
}
input[type='range'].styled-slider::-ms-fill-lower {
background: transparent;
border-color: transparent;
background: transparent;
border-color: transparent;
}
input[type='range'].styled-slider::-ms-thumb {
width: 2em;
height: 40px;
border-radius: 20px;
background: #ffffff;
border: none;
box-shadow: 0 0 2px black;
margin-top: 0;
box-sizing: border-box;
width: 2em;
height: 40px;
border-radius: 20px;
background: #ffffff;
border: none;
box-shadow: 0 0 2px black;
margin-top: 0;
box-sizing: border-box;
}
input[type='range'].styled-slider::-ms-track {
height: 40px;
border-radius: 20px;
background: var(--main-color3);
border: none;
box-shadow: none;
box-sizing: border-box;
height: 40px;
border-radius: 20px;
background: var(--main-color3);
border: none;
box-shadow: none;
box-sizing: border-box;
}
input[type='range'].styled-slider.slider-progress1::-ms-fill-lower {
height: 40px;
border-radius: 1em 0 0 1em;
margin: -undefined 0 -undefined -undefined;
background: var(--main-color1);
border: none;
border-right-width: 0;
height: 40px;
border-radius: 1em 0 0 1em;
margin: -undefined 0 -undefined -undefined;
background: var(--main-color1);
border: none;
border-right-width: 0;
}
input[type='range'].styled-slider.slider-progress2::-ms-fill-lower {
height: 40px;
border-radius: 1em 0 0 1em;
margin: -undefined 0 -undefined -undefined;
background: var(--main-color1);
border: none;
border-right-width: 0;
height: 40px;
border-radius: 1em 0 0 1em;
margin: -undefined 0 -undefined -undefined;
background: var(--main-color1);
border: none;
border-right-width: 0;
}
input[type='range'].styled-slider.slider-progress3::-ms-fill-lower {
height: 40px;
border-radius: 1em 0 0 1em;
margin: -undefined 0 -undefined -undefined;
background: var(--main-color1);
border: none;
border-right-width: 0;
height: 40px;
border-radius: 1em 0 0 1em;
margin: -undefined 0 -undefined -undefined;
background: var(--main-color1);
border: none;
border-right-width: 0;
}
input[type='range'].styled-slider.slider-progress4::-ms-fill-lower {
height: 40px;
border-radius: 1em 0 0 1em;
margin: -undefined 0 -undefined -undefined;
background: var(--main-color1);
border: none;
border-right-width: 0;
height: 40px;
border-radius: 1em 0 0 1em;
margin: -undefined 0 -undefined -undefined;
background: var(--main-color1);
border: none;
border-right-width: 0;
}
.inputBox {
border: none;
width: 38px;
border-radius: 10px;
text-align: center;
font-size: 14pt;
font-weight: bold;
border: none;
width: 38px;
border-radius: 10px;
text-align: center;
font-size: 14pt;
font-weight: bold;
}

View file

@ -1,394 +1,398 @@
.viewer-list {
background-color: var(--main-color4);
height: 100%;
flex: 1;
font-size: 14px;
height: 100%;
display: block;
background-color: var(--main-color4);
height: 100%;
flex: 1;
font-size: 14px;
height: 100%;
display: block;
}
ul.no-bullets {
list-style-type: none;
list-style-position: outside;
margin: 0%;
padding: 0%;
padding-left: 5px;
text-align: left;
list-style-type: none;
list-style-position: outside;
margin: 0%;
padding: 0%;
padding-left: 5px;
text-align: left;
}
li {
line-height: 1em;
line-height: 1em;
}
h1.titles {
margin: 0px;
padding: 0%;
text-align: left;
padding-left: 5px;
margin: 0px;
padding: 0%;
text-align: left;
padding-left: 5px;
}
main {
background: var(--main-color1);
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.2);
display: flex;
justify-content: space-between;
height: 32px;
background: var(--main-color1);
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.2);
display: flex;
justify-content: space-between;
height: 32px;
}
main label {
text-align: center;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
}
p {
font-size: 13px;
font-size: 13px;
}
main input {
display: none;
display: none;
}
section {
clear: both;
padding-top: 10px;
display: none;
height: calc(100% - 60px);
clear: both;
padding-top: 10px;
display: none;
height: calc(100% - 60px);
}
.Basiclabel {
font-weight: bold;
font-size: 14px;
display: block;
float: left;
flex: auto;
font-size: 20px;
padding: 0px 10px;
width: 50px;
align-self: baseline;
text-align: center;
font-weight: bold;
font-size: 14px;
display: block;
float: left;
flex: auto;
font-size: 20px;
padding: 0px 10px;
width: 50px;
align-self: baseline;
text-align: center;
}
.scale {
height: 2em;
width: 2em;
vertical-align: bottom;
height: 2em;
width: 2em;
vertical-align: bottom;
}
.tab {
/* filter: invert(100%) sepia(100%) saturate(0%) hue-rotate(350deg) brightness(104%) contrast(101%); */
align-items: flex-start;
margin: auto;
cursor: pointer;
/* filter: invert(100%) sepia(100%) saturate(0%) hue-rotate(350deg) brightness(104%) contrast(101%); */
align-items: flex-start;
margin: auto;
cursor: pointer;
}
input:checked + label {
border-top-color: #ffb03d;
border-right-color: #ddd;
border-left-color: #ddd;
border-bottom-color: transparent;
text-decoration: none;
border-top-color: #ffb03d;
border-right-color: #ddd;
border-left-color: #ddd;
border-bottom-color: transparent;
text-decoration: none;
}
/* --------------------------------- */
.radius {
border-radius: 5px;
border-radius: 5px;
}
.radius .tabx:active::before {
border-radius: 5px !important;
border-radius: 5px !important;
}
.tabx-bar {
background-color: var(--main-color1);
/* padding: 5px; */
box-shadow: 1px 4px 20px rgba(0, 0, 0, 0.2);
display: flex;
/* margin: 10px; */
color: black;
height: 50px;
background-color: var(--main-color1);
/* padding: 5px; */
box-shadow: 1px 4px 20px rgba(0, 0, 0, 0.2);
display: flex;
/* margin: 10px; */
color: black;
height: 50px;
}
.tabx-bar .tabx {
box-sizing: border-box;
text-decoration: none;
color: inherit;
width: 70px;
height: 50px;
background: inherit;
display: inherit;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
transition: all 0.3s;
box-sizing: border-box;
text-decoration: none;
color: inherit;
width: 70px;
height: 50px;
background: inherit;
display: inherit;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
transition: all 0.3s;
}
.tabx-bar .tabx::before {
position: absolute;
content: '';
width: 26%;
height: 13%;
border-top-left-radius: 200px;
border-top-right-radius: 200px;
border-bottom: none;
background-color: #607d8b;
/* bottom: -8px; */
opacity: 0;
transition: all 0.3s ease-in-out;
position: absolute;
content: '';
width: 26%;
height: 13%;
border-top-left-radius: 200px;
border-top-right-radius: 200px;
border-bottom: none;
background-color: #607d8b;
/* bottom: -8px; */
opacity: 0;
transition: all 0.3s ease-in-out;
}
.tabx-bar .tabx:active::before {
width: 100%;
height: 100%;
background-color: #5fef8d;
border-radius: 0;
width: 100%;
height: 100%;
background-color: #5fef8d;
border-radius: 0;
}
.tabx-bar .tabx:hover::before {
opacity: 1;
bottom: 0px;
opacity: 1;
bottom: 0px;
}
.tabx-bar .tabx::after {
}
/* .tabx-bar .tabx::after {
} */
.tabx-bar .tabx:hover {
padding-bottom: 10px;
padding-bottom: 10px;
}
.tabx-bar .tabx:hover::after {
opacity: 1;
top: 6px;
opacity: 1;
top: 6px;
}
.tabx-bar .tabx.selected {
background-color: rgba(0, 0, 0, 0.1);
border-radius: inherit;
padding-top: 0px;
background-color: rgba(0, 0, 0, 0.1);
border-radius: inherit;
padding-top: 0px;
}
.tabx-bar .tabx.selected::after {
opacity: 1;
top: -10px;
opacity: 1;
top: -10px;
}
.tabx-bar .tabx.selected::before {
opacity: 1;
bottom: 0px;
opacity: 1;
bottom: 0px;
}
.tabx-bar .tabx .icon {
color: inherit;
font-size: 24px;
display: inherit;
color: inherit;
font-size: 24px;
display: inherit;
}
.tabx-bar .tabx .icon img {
margin: auto;
height: 32 px;
margin: auto;
height: 32 px;
}
.language {
width: 80px;
text-align: center;
width: 80px;
text-align: center;
}
#AdvancedMenu_mask {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin-top: 0px;
width: 100%;
height: 100%;
visibility: hidden;
overflow: auto;
overflow-x: hidden;
background-color: var(--mid-section);
font-family: 'xxii_avenmedium';
padding-left: 50px;
padding-right: 50px;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin-top: 0px;
width: 100%;
height: 100%;
visibility: hidden;
overflow: auto;
overflow-x: hidden;
background-color: var(--mid-section);
font-family: 'xxii_avenmedium';
padding-left: 50px;
padding-right: 50px;
}
#ThemeCreator_mask {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin-top: 0px;
width: 100%;
height: 100%;
visibility: hidden;
flex-direction: column;
justify-content: center;
overflow: auto;
overflow-x: hidden;
background-color: var(--mid-section);
font-family: 'xxii_avenmedium';
padding-left: 50px;
padding-right: 50px;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin-top: 0px;
width: 100%;
height: 100%;
visibility: hidden;
flex-direction: column;
justify-content: center;
overflow: auto;
overflow-x: hidden;
background-color: var(--mid-section);
font-family: 'xxii_avenmedium';
padding-left: 50px;
padding-right: 50px;
}
.fname {
background: var(--main-color3);
border: none;
height: 40px;
border-radius: 40px;
width: 300px;
outline: none;
color: var(--main-color2);
font-size: 10pt;
padding-left: 10px;
background: var(--main-color3);
border: none;
height: 40px;
border-radius: 40px;
width: 300px;
outline: none;
color: var(--main-color2);
font-size: 10pt;
padding-left: 10px;
}
input[type='password'] {
background: var(--main-color3);
border: none;
height: 40px;
border-radius: 40px;
width: 300px;
outline: none;
color: var(--main-color2);
font-size: 10pt;
padding-left: 10px;
padding-right: 40px;
/* To make space for the reveal button */
background: var(--main-color3);
border: none;
height: 40px;
border-radius: 40px;
width: 300px;
outline: none;
color: var(--main-color2);
font-size: 10pt;
padding-left: 10px;
padding-right: 40px;
/* To make space for the reveal button */
}
input[type='url'] {
background: var(--main-color3);
border: none;
height: 40px;
border-radius: 40px;
width: 260px;
outline: none;
color: var(--main-color2);
font-size: 10pt;
margin-left: 10px;
background: var(--main-color3);
border: none;
height: 40px;
border-radius: 40px;
width: 260px;
outline: none;
color: var(--main-color2);
font-size: 10pt;
margin-left: 10px;
}
input[type='lol'] {
background: var(--main-color3);
border: none;
height: 40px;
border-radius: 40px;
width: 300px;
outline: none;
color: var(--main-color2);
font-size: 10pt;
padding-left: 10px;
padding-right: 40px;
/* To make space for the reveal button */
background: var(--main-color3);
border: none;
height: 40px;
border-radius: 40px;
width: 300px;
outline: none;
color: var(--main-color2);
font-size: 10pt;
padding-left: 10px;
padding-right: 40px;
/* To make space for the reveal button */
}
/* Style for the reveal button */
.password-toggle-btn {
position: absolute;
background-color: transparent;
border: none;
cursor: pointer;
left: 450px;
position: absolute;
background-color: transparent;
border: none;
cursor: pointer;
left: 450px;
}
/* Hide the default appearance of the button */
.password-toggle-btn::-moz-focus-inner {
border: 0;
border: 0;
}
/* Style the reveal icon (you can use your preferred icon or font) */
.password-toggle-icon {
font-size: 16px;
color: var(--main-color2);
font-size: 16px;
color: var(--main-color2);
}
#toasts {
position: absolute;
bottom: 20px;
/* Adjust the distance from the bottom of the screen */
right: 0%;
/* Center the toasts horizontally */
display: flex;
flex-direction: column;
z-index: 999;
position: absolute;
bottom: 20px;
/* Adjust the distance from the bottom of the screen */
right: 0%;
/* Center the toasts horizontally */
display: flex;
flex-direction: column;
z-index: 999;
}
.toast {
background-color: #333;
color: white;
border-radius: 5px;
padding: 1rem 2rem;
margin: 0.5rem;
opacity: 0;
transform: translateY(100%);
animation: toastAnimation 0.5s ease-in-out forwards, toastDisappear 0.5s ease-in-out 9s forwards;
background-color: #333;
color: white;
border-radius: 5px;
padding: 1rem 2rem;
margin: 0.5rem;
opacity: 0;
transform: translateY(100%);
animation:
toastAnimation 0.5s ease-in-out forwards,
toastDisappear 0.5s ease-in-out 9s forwards;
}
/* Apply different colors based on the toast type */
.info {
background-color: lightblue !important;
background-color: lightblue !important;
}
.success {
background-color: #2ecc71 !important;
background-color: #2ecc71 !important;
}
.warning {
background-color: #f39c12 !important;
background-color: #f39c12 !important;
}
.error {
background-color: #e74c3c !important;
background-color: #e74c3c !important;
}
/* CSS animation for the toast appearance */
@keyframes toastAnimation {
from {
opacity: 0;
transform: translateY(100%);
}
from {
opacity: 0;
transform: translateY(100%);
}
to {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* CSS animation for the toast disappearance */
@keyframes toastDisappear {
from {
opacity: 1;
}
from {
opacity: 1;
}
to {
opacity: 0;
}
to {
opacity: 0;
}
}
.menu-icon {
font-size: 17pt;
font-size: 17pt;
}
.tooltip {
position: absolute;
display: inline-block;
visibility: hidden;
font-size: 12px;
line-height: 20px;
padding: 5px;
background: var(--main-color3);
border-radius: 5px;
visibility: hidden;
opacity: 1;
box-shadow: -2px 2px 5px rgba(0, 0, 0, 0.2);
transition: opacity 0.3s, visibility 0s;
color: var(--main-color2);
font-family: 'xxii_avenmedium';
z-index: 999;
position: absolute;
display: inline-block;
visibility: hidden;
font-size: 12px;
line-height: 20px;
padding: 5px;
background: var(--main-color3);
border-radius: 5px;
visibility: hidden;
opacity: 1;
box-shadow: -2px 2px 5px rgba(0, 0, 0, 0.2);
transition:
opacity 0.3s,
visibility 0s;
color: var(--main-color2);
font-family: 'xxii_avenmedium';
z-index: 999;
}
/* .tooltip .tooltiptext {
@ -413,29 +417,29 @@ input[type='lol'] {
} */
div[type='text']:disabled {
background: #4b4b4b;
display: none;
background: #4b4b4b;
display: none;
}
input[type='text']:disabled {
background: #4b4b4b;
background: #4b4b4b;
}
button[type='text']:disabled {
background: #4b4b4b;
display: none;
background: #4b4b4b;
display: none;
}
input[type2='text']:disabled {
background: #4b4b4b;
background: #4b4b4b;
}
div:disabled {
background: #4b4b4b;
filter: brightness(200%);
background: #4b4b4b;
filter: brightness(200%);
}
div:disabled {
background: #4b4b4b;
filter: brightness(200%);
background: #4b4b4b;
filter: brightness(200%);
}

View file

@ -1,161 +1,169 @@
#tstx {
display: flex;
flex-direction: row;
margin-left: 40px;
display: flex;
flex-direction: row;
margin-left: 40px;
}
.optionrow {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
justify-content: left;
margin-bottom: 10px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
justify-content: left;
margin-bottom: 10px;
}
.LabelText {
margin-bottom: 5px;
color: var(--main-color2);
margin: 0px 0px 5px 0px;
margin-bottom: 5px;
color: var(--main-color2);
margin: 0px 0px 5px 0px;
}
#volume-icon {
color: var(--main-color2);
scale: 0.75;
cursor: pointer;
text-align: center;
align-self: center;
color: var(--main-color2);
scale: 0.75;
cursor: pointer;
text-align: center;
align-self: center;
}
#image {
width: 100px;
height: 100px;
margin-bottom: 10px;
width: 100px;
height: 100px;
margin-bottom: 10px;
}
.TTSVolume {
color: var(--main-color2);
font-size: 12pt;
text-align: center;
color: var(--main-color2);
font-size: 12pt;
text-align: center;
}
#SoundVolume {
color: var(--main-color2);
font-size: 12pt;
color: var(--main-color2);
font-size: 12pt;
}
.testLabel {
color: var(--main-color2);
font-size: 12pt;
color: var(--main-color2);
font-size: 12pt;
}
textarea {
height: 60px;
padding: 5px;
width: 300px;
resize: none;
border-radius: 5px;
background: var(--main-color3);
color: var(--main-color2);
font-family: 'xxii_avenmedium';
border: none;
height: 60px;
padding: 5px;
width: 300px;
resize: none;
border-radius: 5px;
background: var(--main-color3);
color: var(--main-color2);
font-family: 'xxii_avenmedium';
border: none;
}
.SaveConfig {
align-content: center;
display: flex;
flex-direction: column;
justify-content: center;
color: var(--main-color2);
margin-bottom: 10px;
align-content: center;
display: flex;
flex-direction: column;
justify-content: center;
color: var(--main-color2);
margin-bottom: 10px;
}
.SmallButton {
color: var(--main-color2);
width: 50px;
cursor: pointer;
text-shadow: 0 0 5px #070607, 0 0 5px #070607, 0 0 5px #070607;
/* transition: all 0.15s ease-in-out; */
text-align: center;
color: var(--main-color2);
width: 50px;
cursor: pointer;
text-shadow:
0 0 5px #070607,
0 0 5px #070607,
0 0 5px #070607;
/* transition: all 0.15s ease-in-out; */
text-align: center;
}
.SmallButton:hover {
/* color: var(--main-color1); */
width: 50px;
cursor: pointer;
/* filter: brightness(150%); */
/* color: var(--main-color1); */
width: 50px;
cursor: pointer;
/* filter: brightness(150%); */
}
.SmallButton:active {
color: var(--main-color1);
transform: translateY(4px);
text-shadow: 0 0 5px #000, 0 0 5px #000, 0 0 5px #000;
color: var(--main-color1);
transform: translateY(4px);
text-shadow:
0 0 5px #000,
0 0 5px #000,
0 0 5px #000;
}
.AdvancedMenuButton {
width: 300px;
height: 40px;
border-radius: 20px;
background-color: var(--main-color3);
color: var(--main-color2);
padding: 0;
border: none;
font-family: 'xxii_avenmedium';
font-size: 14pt;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
transition: box-shadow 0.3s ease, background-color 0.3s ease;
/* Add a smooth transition for box-shadow and background-color */
width: 300px;
height: 40px;
border-radius: 20px;
background-color: var(--main-color3);
color: var(--main-color2);
padding: 0;
border: none;
font-family: 'xxii_avenmedium';
font-size: 14pt;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
transition:
box-shadow 0.3s ease,
background-color 0.3s ease;
/* Add a smooth transition for box-shadow and background-color */
}
.AdvancedMenuButton:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
background-color: var(--main-color3);
filter: brightness(150%);
/* Darken the background color on hover */
cursor: pointer;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
background-color: var(--main-color3);
filter: brightness(150%);
/* Darken the background color on hover */
cursor: pointer;
}
.AdvancedMenuButton:active {
transform: translateY(2px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
background-color: var(--main-color3);
/* Reset the background color on click */
transform: translateY(2px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
background-color: var(--main-color3);
/* Reset the background color on click */
}
.AdvancedMenuIcon {
filter: invert(100%) sepia(100%) saturate(0%) hue-rotate(350deg) brightness(104%) contrast(101%);
align-items: flex-start;
margin: auto;
height: 24px;
width: 24px;
filter: invert(100%) sepia(100%) saturate(0%) hue-rotate(350deg) brightness(104%) contrast(101%);
align-items: flex-start;
margin: auto;
height: 24px;
width: 24px;
}
.AdvancedMenuIcon2 {
align-items: flex-start;
margin: auto;
height: 24px;
width: 24px;
align-items: flex-start;
margin: auto;
height: 24px;
width: 24px;
}
input:hover {
filter: brightness(120%);
filter: brightness(120%);
}
select:hover {
filter: brightness(120%);
filter: brightness(120%);
}
textarea:hover {
filter: brightness(120%);
filter: brightness(120%);
}
label:hover {
filter: brightness(120%);
filter: brightness(120%);
}
.circle-right:hover {
filter: brightness(120%);
filter: brightness(120%);
}
.circle-left:hover {
filter: brightness(120%);
filter: brightness(120%);
}

View file

@ -1,87 +1,87 @@
.middle {
width: 100%;
height: 50px;
border-radius: 10px;
display: flex;
align-items: center;
width: 100%;
height: 50px;
border-radius: 10px;
display: flex;
align-items: center;
}
.slider-container {
width: 300px;
height: 100%;
position: relative;
width: 300px;
height: 100%;
position: relative;
}
.slider-container .bar {
display: block;
width: 100%;
height: 100%;
border-radius: 10px;
background-color: var(--main-color3);
display: block;
width: 100%;
height: 100%;
border-radius: 10px;
background-color: var(--main-color3);
}
.slider-container .bar .fill {
display: block;
width: 50%;
height: 100%;
border-radius: inherit;
background-color: var(--main-color1);
display: block;
width: 50%;
height: 100%;
border-radius: inherit;
background-color: var(--main-color1);
}
.slider-container .slider {
position: absolute;
top: 50%;
-webkit-appearance: none;
margin: 0;
width: 100%;
height: 0;
border-radius: 5px;
outline: none;
background-color: transparent;
position: absolute;
top: 50%;
-webkit-appearance: none;
margin: 0;
width: 100%;
height: 0;
border-radius: 5px;
outline: none;
background-color: transparent;
}
.slider-container .slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 30px;
height: 30px;
background-color: var(--main-color2);
border-top-left-radius: 50%;
border-top-right-radius: 50%;
border-bottom-right-radius: 50%;
border-bottom-left-radius: 50%;
transform: rotate(-45deg) translate(0%, 0%);
cursor: pointer;
outline: none;
/* box-shadow: 0 0 0 0 rgba(255, 255, 255, 0); */
box-shadow: 0 0 3px rgb(0 0 0 / 10%);
/* transition: .3s ease-in-out; */
-webkit-appearance: none;
width: 30px;
height: 30px;
background-color: var(--main-color2);
border-top-left-radius: 50%;
border-top-right-radius: 50%;
border-bottom-right-radius: 50%;
border-bottom-left-radius: 50%;
transform: rotate(-45deg) translate(0%, 0%);
cursor: pointer;
outline: none;
/* box-shadow: 0 0 0 0 rgba(255, 255, 255, 0); */
box-shadow: 0 0 3px rgb(0 0 0 / 10%);
/* transition: .3s ease-in-out; */
}
.slider-container .slider:active::-webkit-slider-thumb,
.slider-container .slider::-webkit-slider-thumb:hover {
border-bottom-left-radius: 0;
transform: rotate(-45deg) translate(50%, -50%);
border-bottom-left-radius: 0;
transform: rotate(-45deg) translate(50%, -50%);
}
.slider-container .slider:active::-webkit-slider-thumb {
box-shadow: 0 0 0 3px rgba(255, 255, 255, 1);
box-shadow: 0 0 0 3px rgba(255, 255, 255, 1);
}
.option-icon-container {
display: flex;
flex-direction: column;
/* justify-content: center; */
align-items: center;
/* width: 78px; */
/* float: right; */
flex-grow: 1;
display: flex;
flex-direction: column;
/* justify-content: center; */
align-items: center;
/* width: 78px; */
/* float: right; */
flex-grow: 1;
}
.brush-icon-container {
margin-left: -20px;
font-size: 20pt;
margin-left: -20px;
font-size: 20pt;
}
.icon {
fill: var(--main-color2);
fill: var(--main-color2);
}

File diff suppressed because it is too large Load diff

View file

@ -1,118 +1,118 @@
/* global settings, callback, addVoiceService, amazonVoices, */
const https = require('https');
const querystring = require('querystring');
const aws4 = require('aws4');
function getAmazonVoices() {
if (!settings.AMAZON.USE_AMAZON) {
callback();
return;
}
if (!settings.AMAZON.USE_AMAZON) {
callback();
return;
}
addVoiceService('Amazon');
addVoiceService('Amazon');
let primaryVoice = document.querySelector('#primaryAmazonVoice');
let secondaryVoice = document.querySelector('#secondaryAmazonVoice');
const primaryVoice = document.querySelector('#primaryAmazonVoice');
const secondaryVoice = document.querySelector('#secondaryAmazonVoice');
function setVoicesinSelect(voiceSelect) {
const voices = Object.values(amazonVoices);
voices.forEach((voice) => {
const option = document.createElement('option');
option.classList.add('option');
function setVoicesinSelect(voiceSelect) {
const voices = Object.values(amazonVoices);
voices.forEach(voice => {
const option = document.createElement('option');
option.classList.add('option');
option.value = voice;
option.innerHTML = voice;
option.value = voice;
option.innerHTML = voice;
voiceSelect.appendChild(option);
});
}
setVoicesinSelect(primaryVoice);
primaryVoice.value = settings.AMAZON.PRIMARY_VOICE;
setVoicesinSelect(secondaryVoice);
secondaryVoice.value = settings.AMAZON.SECONDARY_VOICE;
voiceSelect.appendChild(option);
});
}
setVoicesinSelect(primaryVoice);
primaryVoice.value = settings.AMAZON.PRIMARY_VOICE;
setVoicesinSelect(secondaryVoice);
secondaryVoice.value = settings.AMAZON.SECONDARY_VOICE;
}
if (settings.AMAZON.USE_AMAZON) {
getAmazonVoices();
getAmazonVoices();
}
class PollyTTS {
constructor() {}
textToSpeech(options, callback) {
if (!options) {
return callback(new Error('Options are missing'));
}
textToSpeech(options, callback) {
if (!options) {
return callback(new Error('Options are missing'));
const qs = {
Text: options.text,
TextType: options.textType || 'text',
VoiceId: options.voiceId || 'Mia',
SampleRate: options.sampleRate || 22050,
OutputFormat: options.outputFormat || 'mp3',
Engine: options.engine || 'neural'
};
const opts = {
service: 'polly',
region: options.region || 'us-east-1',
path: `/v1/speech?${querystring.stringify(qs)}`,
signQuery: true
};
// you can also pass AWS credentials in explicitly (otherwise taken from process.env)
aws4.sign(opts, this.credentials);
https
.get(opts, res => {
if (res.statusCode !== 200) {
return callback(new Error(`Request Failed. Status Code: ${res.statusCode}`));
}
callback(null, res);
return true;
})
.on('error', e => {
callback(e);
});
return null;
}
describeVoices(callback, credentials) {
this.credentials = credentials;
const qs = {
Engine: 'neural'
};
const opts = {
service: 'polly',
region: 'us-east-1',
path: `/v1/voices?${querystring.stringify(qs)}`,
signQuery: true
};
// you can also pass AWS credentials in explicitly (otherwise taken from process.env)
aws4.sign(opts, this.credentials);
https
.get(opts, res => {
if (res.statusCode !== 200) {
return callback(new Error(`Request Failed. Status Code: ${res.statusCode}`));
}
const qs = {
Text: options.text,
TextType: options.textType || 'text',
VoiceId: options.voiceId || 'Mia',
SampleRate: options.sampleRate || 22050,
OutputFormat: options.outputFormat || 'mp3',
Engine: options.engine || 'neural',
};
let body = '';
res.on('readable', () => {
body += res.read();
});
res.on('end', () => {
callback(null, body);
});
const opts = {
service: 'polly',
region: options.region || 'us-east-1',
path: `/v1/speech?${querystring.stringify(qs)}`,
signQuery: true,
};
return undefined;
})
.on('error', e => {
callback(e);
});
// you can also pass AWS credentials in explicitly (otherwise taken from process.env)
aws4.sign(opts, this.credentials);
https
.get(opts, (res) => {
if (res.statusCode !== 200) {
return callback(new Error(`Request Failed. Status Code: ${res.statusCode}`));
}
callback(null, res);
return true;
})
.on('error', (e) => {
callback(e);
});
return null;
}
describeVoices(callback, credentials) {
this.credentials = credentials;
const qs = {
Engine: 'neural',
};
const opts = {
service: 'polly',
region: 'us-east-1',
path: `/v1/voices?${querystring.stringify(qs)}`,
signQuery: true,
};
// you can also pass AWS credentials in explicitly (otherwise taken from process.env)
aws4.sign(opts, this.credentials);
https
.get(opts, (res) => {
if (res.statusCode !== 200) {
return callback(new Error(`Request Failed. Status Code: ${res.statusCode}`));
}
let body = '';
res.on('readable', () => {
body += res.read();
});
res.on('end', () => {
callback(null, body);
});
return undefined;
})
.on('error', (e) => {
callback(e);
});
return null;
}
return null;
}
}
const pollyTTS = new PollyTTS();

View file

@ -1,103 +1,217 @@
/* global settings, fs, settingsPath, ini, shell, options, axios */
const twitchAuthentication = () =>
new Promise((resolve) => {
const http = require('http');
const redirectUri = 'http://localhost:1989/auth';
const scopes = ['chat:edit', 'chat:read'];
new Promise(resolve => {
const http = require('http');
const redirectUri = 'http://localhost:1989/auth';
const scopes = ['chat:edit', 'chat:read'];
const express = require('express');
let tempAuthServer = express();
const port = 1989;
const express = require('express');
const tempAuthServer = express();
const port = 1989;
const { parse: parseQueryString } = require('querystring');
const { parse: parseQueryString } = require('querystring');
tempAuthServer.use(function (req, res, next) {
if (req.url !== '/auth') {
let token = parseQueryString(req.query.auth);
settings.TWITCH.OAUTH_TOKEN = token['#access_token'];
fs.writeFileSync(settingsPath, ini.stringify(settings));
tempAuthServer.use(function (req, res, next) {
if (req.url !== '/auth') {
const token = parseQueryString(req.query.auth);
settings.TWITCH.OAUTH_TOKEN = token['#access_token'];
fs.writeFileSync(settingsPath, ini.stringify(settings));
resolve(token['#access_token']);
stopServer();
}
next();
});
function stopServer() {
tempAuthServer.close();
}
const htmlString = `
<!DOCTYPE html>
<html>
<head>
<title>Authentication</title>
</head>
<body>
<h1>Authentication successful! You can close this window now.</h1>
<form name="auth" "action="auth" method="get" >
<input type="text" id="auth" name="auth"/>
</form>
</body>
</html>
<script>
function onSubmitComplete() {
close();
}
document.querySelector("#auth").style.display="none";
document.querySelector("#auth").value = document.location.hash;
document.auth.submit();
setTimeout(onSubmitComplete, 500);
</script>
`;
tempAuthServer.get('/auth', (req, res) => {
res.send(htmlString);
});
tempAuthServer.post('/auth', (req, res) => {
res.render('authentication', { name: req.body.name });
});
const server = http.createServer(tempAuthServer);
server.listen(port, () => {
const authURL = `https://id.twitch.tv/oauth2/authorize?client_id=${settings.TWITCH.CLIENT_ID}&redirect_uri=${encodeURIComponent(
redirectUri,
)}&response_type=token&scope=${scopes.join(' ')}`;
shell.openExternal(authURL);
});
function stopServer() {
server.close(() => {});
}
resolve(token['#access_token']);
stopServer();
}
next();
});
// function stopServer() {
// tempAuthServer.close();
// }
const htmlString = `
<!DOCTYPE html>
<html>
<head>
<title>Authentication</title>
</head>
<body>
<h1>Authentication successful! You can close this window now.</h1>
<form name="auth" "action="auth" method="get" >
<input type="text" id="auth" name="auth"/>
</form>
</body>
</html>
<script>
function onSubmitComplete() {
close();
}
document.querySelector("#auth").style.display="none";
document.querySelector("#auth").value = document.location.hash;
document.auth.submit();
setTimeout(onSubmitComplete, 500);
</script>
`;
tempAuthServer.get('/auth', (req, res) => {
res.send(htmlString);
});
tempAuthServer.post('/auth', (req, res) => {
res.render('authentication', { name: req.body.name });
});
const server = http.createServer(tempAuthServer);
server.listen(port, () => {
const authURL = `https://id.twitch.tv/oauth2/authorize?client_id=${settings.TWITCH.CLIENT_ID}&redirect_uri=${encodeURIComponent(
redirectUri
)}&response_type=token&scope=${scopes.join(' ')}`;
shell.openExternal(authURL);
});
function stopServer() {
server.close(() => {});
}
});
function getTwitchUserId() {
// Get user Logo with access token
options = {
method: 'GET',
url: `https://api.twitch.tv/helix/users`,
headers: { 'Client-ID': settings.TWITCH.CLIENT_ID, Authorization: `Bearer ${settings.TWITCH.OAUTH_TOKEN}` },
};
// Get user Logo with access token
options = {
method: 'GET',
url: 'https://api.twitch.tv/helix/users',
headers: {
'Client-ID': settings.TWITCH.CLIENT_ID,
Authorization: `Bearer ${settings.TWITCH.OAUTH_TOKEN}`
}
};
axios
.request(options)
.then((responseLogoUrl) => {
console.log(responseLogoUrl.data.data[0]);
settings.TWITCH.USERNAME = responseLogoUrl.data.data[0].display_name;
settings.TWITCH.USER_LOGO_URL = responseLogoUrl.data.data[0].profile_image_url;
settings.TWITCH.USER_ID = responseLogoUrl.data.data[0].id;
fs.writeFileSync(settingsPath, ini.stringify(settings));
})
.catch((error) => {
console.error(error);
});
}
function getTwitchOauthToken() {
return twitchAuthentication().then((res) => {
getTwitchUserId();
return res;
axios
.request(options)
.then(responseLogoUrl => {
console.log(responseLogoUrl.data.data[0]);
settings.TWITCH.USERNAME = responseLogoUrl.data.data[0].display_name;
settings.TWITCH.USER_LOGO_URL = responseLogoUrl.data.data[0].profile_image_url;
settings.TWITCH.USER_ID = responseLogoUrl.data.data[0].id;
fs.writeFileSync(settingsPath, ini.stringify(settings));
})
.catch(error => {
console.error(error);
});
}
function getTwitchSubscriptions(channelId) {
// Get user Logo with access token
options = {
method: 'GET',
url: `https://api.twitch.tv/helix/chat/emotes?broadcaster_id=${channelId}`,
headers: {
'Client-ID': settings.TWITCH.CLIENT_ID,
Authorization: `Bearer ${settings.TWITCH.OAUTH_TOKEN}`
}
};
axios
.request(options)
.then(responseLogoUrl => {
console.log(responseLogoUrl);
})
.catch(error => {
console.error(error);
});
}
function getTwitchChannelEmotes(channelId) {
// Get user Logo with access token
options = {
method: 'GET',
url: `https://api.twitch.tv/helix/chat/emotes?broadcaster_id=${channelId}`,
headers: {
'Client-ID': settings.TWITCH.CLIENT_ID,
Authorization: `Bearer ${settings.TWITCH.OAUTH_TOKEN}`
}
};
axios
.request(options)
.then(responseLogoUrl => {
console.log(responseLogoUrl);
})
.catch(error => {
console.error(error);
});
}
function getTwitchChannelFollows(channelId) {
// Get user Logo with access token
options = {
method: 'GET',
url: 'https://api.twitch.tv/helix/streams/followed',
headers: {
'Client-ID': settings.TWITCH.CLIENT_ID,
Authorization: `Bearer ${settings.TWITCH.OAUTH_TOKEN}`
}
};
axios
.request(options)
.then(responseLogoUrl => {
console.log(responseLogoUrl);
})
.catch(error => {
console.error(error);
});
}
function getTwitchChannelSubscriptions(channelId) {
// Get user Logo with access token
options = {
method: 'GET',
url: `https://api.twitch.tv/helix/chat/emotes?broadcaster_id=${channelId}`,
headers: {
'Client-ID': settings.TWITCH.CLIENT_ID,
Authorization: `Bearer ${settings.TWITCH.OAUTH_TOKEN}`
}
};
axios
.request(options)
.then(responseLogoUrl => {
console.log(responseLogoUrl);
})
.catch(error => {
console.error(error);
});
}
function getTwitchChannelId() {
// Get user Logo with access token
options = {
method: 'GET',
url: 'https://api.twitch.tv/helix/users?login=MelvyCoral',
headers: {
'Client-ID': settings.TWITCH.CLIENT_ID,
Authorization: `Bearer ${settings.TWITCH.OAUTH_TOKEN}`
}
};
axios
.request(options)
.then(responseLogoUrl => {
// console.log(responseLogoUrl);
getTwitchChannelEmotes(responseLogoUrl.data.data[0].id);
})
.catch(error => {
console.error(error);
});
}
function getTwitchOauthToken() {
// getTwitchChannelId();
getTwitchGLobalEmotes();
// return twitchAuthentication().then(res => {
// getTwitchUserId();
// return res;
// });
}
module.exports = { getTwitchOauthToken };

View file

@ -1,167 +1,178 @@
/* global settings, addVoiceService, internalVoices, ttsRequestCount, main, path, pythonPath, settingsPath, ipcRenderer */
const spawn = require('child_process').spawn;
var kill = require('kill-process-by-name');
const kill = require('kill-process-by-name');
let python;
async function getInstalledVoices() {
if (!settings.TTS.USE_TTS) {
return;
if (!settings.TTS.USE_TTS) {
return;
}
addVoiceService('Internal');
try {
const response = await fetch(`http://127.0.0.1:${settings.GENERAL.PORT}/voices`, { method: 'GET' });
if (response.ok) {
const responseData = await response.json();
console.log('Response:', responseData);
internalVoices = responseData;
} else {
console.error('Failed to send termination signal to Flask server.');
}
addVoiceService('Internal');
} catch (error) {
console.error('Error sending termination signal:', error);
}
try {
const response = await fetch(`http://127.0.0.1:${settings.GENERAL.PORT}/voices`, { method: 'GET' });
if (response.ok) {
const responseData = await response.json();
console.log('Response:', responseData);
internalVoices = responseData;
} else {
console.error('Failed to send termination signal to Flask server.');
}
} catch (error) {
console.error('Error sending termination signal:', error);
}
const primaryVoice = document.querySelector('#primaryVoice');
const secondaryVoice = document.querySelector('#secondaryVoice');
let primaryVoice = document.querySelector('#primaryVoice');
let secondaryVoice = document.querySelector('#secondaryVoice');
function setVoicesinSelect(voiceSelect) {
const voices = Object.values(internalVoices.voices);
voices.forEach(voice => {
const option = document.createElement('option');
option.classList.add('option');
function setVoicesinSelect(voiceSelect) {
const voices = Object.values(internalVoices.voices);
voices.forEach((voice) => {
const option = document.createElement('option');
option.classList.add('option');
option.value = voice;
option.innerHTML = voice;
option.value = voice;
option.innerHTML = voice;
voiceSelect.appendChild(option);
});
}
setVoicesinSelect(primaryVoice);
primaryVoice.value = settings.TTS.PRIMARY_VOICE;
setVoicesinSelect(secondaryVoice);
secondaryVoice.value = settings.TTS.SECONDARY_VOICE;
voiceSelect.appendChild(option);
});
}
setVoicesinSelect(primaryVoice);
primaryVoice.value = settings.TTS.PRIMARY_VOICE;
setVoicesinSelect(secondaryVoice);
secondaryVoice.value = settings.TTS.SECONDARY_VOICE;
}
async function getBackendServerStatus() {
try {
const response = await fetch(`http://127.0.0.1:${settings.GENERAL.PORT}/status`, { method: 'GET' });
if (response.ok) {
const responseData = await response.json();
console.log('Response:', responseData);
} else {
console.error('Failed to send termination signal to Flask server.');
}
} catch (error) {
console.error('Error sending termination signal:', error);
console.log('getting status');
try {
const response = await fetch(`http://127.0.0.1:${settings.GENERAL.PORT}/status`, { method: 'GET' });
if (response.ok) {
const responseData = await response.json();
console.log('Response:', responseData);
} else {
console.error('Failed to send termination signal to Flask server.');
}
} catch (error) {
console.error('Error sending termination signal:', error);
}
}
function startSTT() {
const eventSource = new EventSource('http://127.0.0.1:9000/stream');
const eventSource = new EventSource('http://127.0.0.1:9000/stream');
eventSource.addEventListener('message', (event) => {
const result = event.data;
console.log(result); // Log the received data
});
eventSource.addEventListener('message', event => {
const result = event.data;
console.log(result); // Log the received data
});
eventSource.addEventListener('error', (event) => {
console.error('EventSource failed:', event);
eventSource.addEventListener('error', event => {
console.error('EventSource failed:', event);
eventSource.close();
});
eventSource.close();
});
window.addEventListener('beforeunload', () => {
eventSource.close();
});
window.addEventListener('beforeunload', () => {
eventSource.close();
});
}
async function getInternalTTSAudio(requestData) {
ttsRequestCount++;
requestData.count = ttsRequestCount;
const requestOptions = {
method: 'POST', // HTTP method
headers: {
'Content-Type': 'application/json', // Specify the content type
},
body: JSON.stringify(requestData), // Convert the data to JSON and include it in the request body
};
ttsRequestCount++;
requestData.count = ttsRequestCount;
const requestOptions = {
method: 'POST', // HTTP method
headers: {
'Content-Type': 'application/json' // Specify the content type
},
body: JSON.stringify(requestData) // Convert the data to JSON and include it in the request body
};
try {
const response = await fetch(`http://127.0.0.1:${settings.GENERAL.PORT}/audio`, requestOptions);
if (response.ok) {
const responseData = await response.json();
console.log('Response:', responseData);
return ttsRequestCount;
} else {
console.error('Failed to send termination signal to Flask server.');
}
} catch (error) {
console.error('Error sending termination signal:', error);
try {
const response = await fetch(`http://127.0.0.1:${settings.GENERAL.PORT}/audio`, requestOptions);
if (response.ok) {
const responseData = await response.json();
console.log('Response:', responseData);
return ttsRequestCount;
} else {
console.error('Failed to send termination signal to Flask server.');
}
} catch (error) {
console.error('Error sending termination signal:', error);
}
}
const createBackendServer = () =>
new Promise((resolve) => {
if (main.isPackaged) {
python = spawn(path.join(pythonPath, './loquendoBot_backend.exe'), [settingsPath, 'prod']);
} else {
python = spawn('python', ['-u', path.join(pythonPath, './loquendoBot_backend.py'), settingsPath, 'dev']);
}
// Capture the stdout of the Python process
python.stdout.on('data', (data) => {
console.info(`${data}`);
});
// Capture the stderr of the Python process
python.stderr.on('data', (data) => {
console.error(`${data}`);
resolve('finished'); // cannot get it to resolve with stdout
});
// Listen for the Python process to exit
python.on('close', (code) => {
console.log(`Python process exited with code ${code}`);
});
if (typeof python.pid !== 'number') {
console.log('failed');
} else {
console.log(`Spawned subprocess correctly!, PID = ${python.pid}`);
}
new Promise(resolve => {
if (main.isPackaged) {
python = spawn(path.join(pythonPath, './loquendoBot_backend.exe'), [settingsPath, 'prod']);
} else {
python = spawn('python', ['-u', path.join(pythonPath, './loquendoBot_backend.py'), settingsPath, 'dev']);
}
// Capture the stdout of the Python process
python.stdout.on('data', data => {
console.info(`${data}`);
if (data.toString().startsWith('kees')) {
console.log('yess');
// getBackendServerStatus();
}
});
async function initiateBackend() {
try {
createBackendServer().then(() => {
getBackendServerStatus();
getInstalledVoices();
if (settings.STT.USE_STT) {
startSTT();
}
});
} catch (error) {
console.error('Error during backend initialization:', error);
// Capture the stderr of the Python process
python.stderr.on('data', data => {
// console.error(`${data}`);
if (data.toString().startsWith('INFO:waitress:Serving on')) {
resolve('finished');
} else {
console.error(`${data}`);
}
});
// Listen for the Python process to exit
python.on('close', code => {
console.log(`Python process exited with code ${code}`);
});
if (typeof python.pid !== 'number') {
console.log('failed');
} else {
// console.log(`Spawned subprocess correctly!, PID = ${python.pid}`);
}
});
async function initiateBackend() {
try {
createBackendServer().then(() => {
getBackendServerStatus();
getInstalledVoices();
if (settings.STT.USE_STT) {
startSTT();
}
});
} catch (error) {
console.error('Error during backend initialization:', error);
}
}
initiateBackend();
//TODO: convert to restartServer function
// TODO: convert to restartServer function
ipcRenderer.on('quit-event', async () => {
try {
const response = await fetch(`http://127.0.0.1:${settings.GENERAL.PORT}/terminate`, { method: 'GET' });
if (response.ok) {
const responseData = await response.json();
console.log('Response:', responseData);
kill('loquendoBot_backend');
} else {
console.error('Failed to send termination signal to Flask server.');
kill('loquendoBot_backend');
}
} catch (error) {
console.error('Error sending termination signal:', error);
kill('loquendoBot_backend');
try {
const response = await fetch(`http://127.0.0.1:${settings.GENERAL.PORT}/terminate`, { method: 'GET' });
if (response.ok) {
const responseData = await response.json();
console.log('Response:', responseData);
kill('loquendoBot_backend');
} else {
console.error('Failed to send termination signal to Flask server.');
kill('loquendoBot_backend');
}
} catch (error) {
console.error('Error sending termination signal:', error);
kill('loquendoBot_backend');
}
});
module.exports = { getInternalTTSAudio };

View file

@ -1,54 +1,76 @@
function getResponse() {
const userText = document.querySelector('#textInput').value;
/* global messageTemplates, emojiPicker, settings, getPostTime, showChatMessage, twitch */
// If nothing is written don't do anything
if (userText === '') {
return;
}
async function getResponse() {
const userText = document.querySelector('#textInput').value;
// Create chat message from received data
const article = document.createElement('article');
article.className = 'msg-container user';
// If nothing is written don't do anything
if (userText === '') {
return;
}
article.innerHTML = messageTemplates.userTemplate;
// Create chat message from received data
const article = document.createElement('article');
article.className = 'msg-container user';
const userImg = article.querySelector('.user-img');
if (userImg) {
userImg.src = settings.TWITCH.USER_LOGO_URL;
}
article.innerHTML = messageTemplates.userTemplate;
const postTime = article.querySelector('.post-time');
const userImg = article.querySelector('.user-img');
if (userImg) {
userImg.src = settings.TWITCH.USER_LOGO_URL;
}
if (postTime) {
postTime.innerText = getPostTime();
}
const postTime = article.querySelector('.post-time');
article.appendChild(postTime);
if (postTime) {
postTime.innerText = getPostTime();
}
const msg = article.querySelector('.msg-box');
if (msg) {
msg.innerText = userText;
}
article.appendChild(postTime);
// Appends the message to the main chat box (shows the message)
showChatMessage(article, true);
const msg = article.querySelector('.msg-box');
if (msg) {
console.log(0);
await replaceChatMessageWithCustomEmojis(userText).then(data => {
// console.log(data);
msg.innerHTML = data;
twitch.sendMessage(userText);
// Appends the message to the main chat box (shows the message)
showChatMessage(article);
// Empty input box after sending message
document.body.querySelector('#textInput').value = '';
twitch.sendMessage(userText);
// Empty input box after sending message
document.body.querySelector('#textInput').value = '';
});
}
}
const replaceChatMessageWithCustomEmojis = message =>
new Promise(resolve => {
const words = message.split(' ');
words.forEach(async word => {
if (word !== '') {
await emojiPicker.database.getEmojiByUnicodeOrName(word).then(data => {
if (data && data.name === word) {
const url = `<img src="${data.url}">`;
message = message.replace(word, url);
}
});
resolve(message);
}
});
});
// Function that will execute when you press 'enter' in the message box
document.body.querySelector('#textInput').addEventListener('keydown', (e) => {
if (e.which === 13) {
getResponse();
}
document.body.querySelector('#textInput').addEventListener('keydown', e => {
if (e.which === 13) {
getResponse();
}
});
// Function that will execute when you click the 'send' button
document.body.querySelector('#SendButton').addEventListener('click', () => {
getResponse();
getResponse();
});
// #endregion
@ -57,21 +79,21 @@ document.body.querySelector('#SendButton').addEventListener('click', () => {
// Left panel
document.body.querySelector('.circle-left').addEventListener('click', () => {
const menu = document.body.querySelector('.sidepanel-left');
const menu = document.body.querySelector('.sidepanel-left');
if (menu.classList.contains('collapse-menu-left')) {
menu.classList.remove('collapse-menu-left');
} else {
menu.classList.add('collapse-menu-left');
}
if (menu.classList.contains('collapse-menu-left')) {
menu.classList.remove('collapse-menu-left');
} else {
menu.classList.add('collapse-menu-left');
}
const leftCircle = document.body.querySelector('.circle-left');
const leftCircle = document.body.querySelector('.circle-left');
if (leftCircle.classList.contains('collapse-circle-left')) {
leftCircle.classList.remove('collapse-circle-left');
} else {
leftCircle.classList.add('collapse-circle-left');
}
if (leftCircle.classList.contains('collapse-circle-left')) {
leftCircle.classList.remove('collapse-circle-left');
} else {
leftCircle.classList.add('collapse-circle-left');
}
});
// #region Show panels
@ -80,27 +102,26 @@ document.body.querySelector('.circle-left').addEventListener('click', () => {
// TODO : optimize show panels
// Function that shows and hides the option panels. (TTS, Configuration, Commands)
const displayPanel = (panelSelectorClass, panelSelectorID, btnSelectorID) => {
const btn = document.querySelector(btnSelectorID);
const panel = document.querySelector(panelSelectorID);
const panels = document.querySelectorAll(panelSelectorClass);
const btn = document.querySelector(btnSelectorID);
const panel = document.querySelector(panelSelectorID);
const panels = document.querySelectorAll(panelSelectorClass);
btn.addEventListener(
'click',
(event) => {
event.stopPropagation();
panels.forEach((el) => {
if (el === panel) return;
el.classList.remove('show');
});
if (panel.classList.contains('show')) {
} else {
panel.classList.add('show');
}
},
{
capture: true,
},
);
btn.addEventListener(
'click',
event => {
event.stopPropagation();
panels.forEach(el => {
if (el === panel) return;
el.classList.remove('show');
});
if (!panel.classList.contains('show')) {
panel.classList.add('show');
}
},
{
capture: true
}
);
};
displayPanel('.OptionPanel', '#Configuration', '#btnConfiguration');
@ -113,27 +134,26 @@ displayPanel('.OptionPanel', '#ChatCreator', '#btnChatCreator');
// #endregion
const displayPanelX = (panelSelectorClass, panelSelectorID, btnSelectorID) => {
const btn = document.querySelector(btnSelectorID);
const panel = document.querySelector(panelSelectorID);
const panels = document.querySelectorAll(panelSelectorClass);
const btn = document.querySelector(btnSelectorID);
const panel = document.querySelector(panelSelectorID);
const panels = document.querySelectorAll(panelSelectorClass);
btn.addEventListener(
'click',
(event) => {
event.stopPropagation();
panels.forEach((el) => {
if (el === panel) return;
el.classList.remove('item-active');
});
if (panel.classList.contains('item-active')) {
} else {
panel.classList.add('item-active');
}
},
{
capture: true,
},
);
btn.addEventListener(
'click',
event => {
event.stopPropagation();
panels.forEach(el => {
if (el === panel) return;
el.classList.remove('item-active');
});
if (!panel.classList.contains('item-active')) {
panel.classList.add('item-active');
}
},
{
capture: true
}
);
};
displayPanelX('.item', '#btnChat', '#btnChat');

9
src/js/customEmojis.js Normal file
View file

@ -0,0 +1,9 @@
const customEmojis = [
{
name: 'sakuraestaKleefeliz',
shortcodes: ['sakuraestaKleefeliz'],
url: 'https://static-cdn.jtvnw.net/emoticons/v2/emotesv2_0cb536ddb6e143ab87ffeccb160a4d45/default/dark/1.0'
}
];
module.exports = { customEmojis };

View file

@ -1,31 +1,33 @@
/* global settings, addVoiceService, googleVoices */
function getGoogleVoices() {
if (!settings.GOOGLE.USE_GOOGLE) {
return;
}
if (!settings.GOOGLE.USE_GOOGLE) {
return;
}
addVoiceService('Google');
addVoiceService('Google');
let primaryVoice = document.querySelector('#primaryGoogleVoice');
let secondaryVoice = document.querySelector('#secondaryGoogleVoice');
const primaryVoice = document.querySelector('#primaryGoogleVoice');
const secondaryVoice = document.querySelector('#secondaryGoogleVoice');
function setVoicesinSelect(voiceSelect) {
const voices = Object.values(googleVoices);
voices.forEach((voice) => {
const option = document.createElement('option');
option.classList.add('option');
function setVoicesinSelect(voiceSelect) {
const voices = Object.values(googleVoices);
voices.forEach(voice => {
const option = document.createElement('option');
option.classList.add('option');
option.value = voice;
option.innerHTML = voice;
option.value = voice;
option.innerHTML = voice;
voiceSelect.appendChild(option);
});
}
setVoicesinSelect(primaryVoice);
primaryVoice.value = settings.GOOGLE.PRIMARY_VOICE;
setVoicesinSelect(secondaryVoice);
secondaryVoice.value = settings.GOOGLE.SECONDARY_VOICE;
voiceSelect.appendChild(option);
});
}
setVoicesinSelect(primaryVoice);
primaryVoice.value = settings.GOOGLE.PRIMARY_VOICE;
setVoicesinSelect(secondaryVoice);
secondaryVoice.value = settings.GOOGLE.SECONDARY_VOICE;
}
if (settings.GOOGLE.USE_GOOGLE) {
getGoogleVoices();
getGoogleVoices();
}

View file

@ -1,331 +1,332 @@
// TODO: Enable STT:
// Output STT to TTS? *TTS service selection* (for now, later add the option to choose a specific voice with mega dropdowns)
// Output STT to TTS? *TTS service selection* (for now, later add the option to choose a specific voice with mega dropdowns)
// *automatic translation: make an translation.js and add ALL the texts and have it translated if user chooses a language in top bar
// *info page with credits, version and more info
const languages = {
acehnese: { IETF: 'ace-ID', 'ISO-639': 'ace' },
afrikaans: { IETF: 'af-ZA', 'ISO-639': 'af' },
akan: { IETF: 'ak-GH', 'ISO-639': 'ak' },
albanian: { IETF: 'sq-AL', 'ISO-639': 'sq' },
amharic: { IETF: 'am-ET', 'ISO-639': 'am' },
'antigua and barbuda creole english': { IETF: 'aig-AG', 'ISO-639': 'aig' },
arabic: { IETF: 'ar-SA', 'ISO-639': 'ar' },
'arabic egyptian': { IETF: 'ar-EG', 'ISO-639': 'ar' },
aragonese: { IETF: 'an-ES', 'ISO-639': 'an' },
armenian: { IETF: 'hy-AM', 'ISO-639': 'hy' },
assamese: { IETF: 'as-IN', 'ISO-639': 'as' },
asturian: { IETF: 'ast-ES', 'ISO-639': 'ast' },
'austrian german': { IETF: 'de-AT', 'ISO-639': 'de' },
awadhi: { IETF: 'awa-IN', 'ISO-639': 'awa' },
'ayacucho quechua': { IETF: 'quy-PE', 'ISO-639': 'quy' },
azerbaijani: { IETF: 'az-AZ', 'ISO-639': 'az' },
'bahamas creole english': { IETF: 'bah-BS', 'ISO-639': 'bah' },
bajan: { IETF: 'bjs-BB', 'ISO-639': 'bjs' },
balinese: { IETF: 'ban-ID', 'ISO-639': 'ban' },
'balkan gipsy': { IETF: 'rm-RO', 'ISO-639': 'rm' },
bambara: { IETF: 'bm-ML', 'ISO-639': 'bm' },
banjar: { IETF: 'bjn-ID', 'ISO-639': 'bjn' },
bashkir: { IETF: 'ba-RU', 'ISO-639': 'ba' },
basque: { IETF: 'eu-ES', 'ISO-639': 'eu' },
belarusian: { IETF: 'be-BY', 'ISO-639': 'be' },
'belgian french': { IETF: 'fr-BE', 'ISO-639': 'fr' },
bemba: { IETF: 'bem-ZM', 'ISO-639': 'bem' },
bengali: { IETF: 'bn-IN', 'ISO-639': 'bn' },
bhojpuri: { IETF: 'bho-IN', 'ISO-639': 'bho' },
bihari: { IETF: 'bh-IN', 'ISO-639': 'bh' },
bislama: { IETF: 'bi-VU', 'ISO-639': 'bi' },
borana: { IETF: 'gax-KE', 'ISO-639': 'gax' },
bosnian: { IETF: 'bs-BA', 'ISO-639': 'bs' },
'bosnian (cyrillic)': { IETF: 'bs-Cyrl-BA', 'ISO-639': 'bs' },
breton: { IETF: 'br-FR', 'ISO-639': 'br' },
buginese: { IETF: 'bug-ID', 'ISO-639': 'bug' },
bulgarian: { IETF: 'bg-BG', 'ISO-639': 'bg' },
burmese: { IETF: 'my-MM', 'ISO-639': 'my' },
catalan: { IETF: 'ca-ES', 'ISO-639': 'ca' },
'catalan valencian': { IETF: 'cav-ES', 'ISO-639': 'cav' },
cebuano: { IETF: 'ceb-PH', 'ISO-639': 'ceb' },
'central atlas tamazight': { IETF: 'tzm-MA', 'ISO-639': 'tzm' },
'central aymara': { IETF: 'ayr-BO', 'ISO-639': 'ayr' },
'central kanuri (latin script)': { IETF: 'knc-NG', 'ISO-639': 'knc' },
'chadian arabic': { IETF: 'shu-TD', 'ISO-639': 'shu' },
chamorro: { IETF: 'ch-GU', 'ISO-639': 'ch' },
cherokee: { IETF: 'chr-US', 'ISO-639': 'chr' },
chhattisgarhi: { IETF: 'hne-IN', 'ISO-639': 'hne' },
'chinese simplified': { IETF: 'zh-CN', 'ISO-639': 'zh' },
'chinese trad. (hong kong)': { IETF: 'zh-HK', 'ISO-639': 'zh' },
'chinese traditional': { IETF: 'zh-TW', 'ISO-639': 'zh' },
'chinese traditional macau': { IETF: 'zh-MO', 'ISO-639': 'zh' },
chittagonian: { IETF: 'ctg-BD', 'ISO-639': 'ctg' },
chokwe: { IETF: 'cjk-AO', 'ISO-639': 'cjk' },
'classical greek': { IETF: 'grc-GR', 'ISO-639': 'grc' },
'comorian ngazidja': { IETF: 'zdj-KM', 'ISO-639': 'zdj' },
coptic: { IETF: 'cop-EG', 'ISO-639': 'cop' },
'crimean tatar': { IETF: 'crh-RU', 'ISO-639': 'crh' },
'crioulo upper guinea': { IETF: 'pov-GW', 'ISO-639': 'pov' },
croatian: { IETF: 'hr-HR', 'ISO-639': 'hr' },
czech: { IETF: 'cs-CZ', 'ISO-639': 'cs' },
danish: { IETF: 'da-DK', 'ISO-639': 'da' },
dari: { IETF: 'prs-AF', 'ISO-639': 'prs' },
dimli: { IETF: 'diq-TR', 'ISO-639': 'diq' },
dutch: { IETF: 'nl-NL', 'ISO-639': 'nl' },
dyula: { IETF: 'dyu-CI', 'ISO-639': 'dyu' },
dzongkha: { IETF: 'dz-BT', 'ISO-639': 'dz' },
'eastern yiddish': { IETF: 'ydd-US', 'ISO-639': 'ydd' },
emakhuwa: { IETF: 'vmw-MZ', 'ISO-639': 'vmw' },
english: { IETF: 'en-GB', 'ISO-639': 'en' },
'english australia': { IETF: 'en-AU', 'ISO-639': 'en' },
'english canada': { IETF: 'en-CA', 'ISO-639': 'en' },
'english india': { IETF: 'en-IN', 'ISO-639': 'en' },
'english ireland': { IETF: 'en-IE', 'ISO-639': 'en' },
'english new zealand': { IETF: 'en-NZ', 'ISO-639': 'en' },
'english singapore': { IETF: 'en-SG', 'ISO-639': 'en' },
'english south africa': { IETF: 'en-ZA', 'ISO-639': 'en' },
'english us': { IETF: 'en-US', 'ISO-639': 'en' },
esperanto: { IETF: 'eo-EU', 'ISO-639': 'eo' },
estonian: { IETF: 'et-EE', 'ISO-639': 'et' },
ewe: { IETF: 'ee-GH', 'ISO-639': 'ee' },
fanagalo: { IETF: 'fn-FNG', 'ISO-639': 'fn' },
faroese: { IETF: 'fo-FO', 'ISO-639': 'fo' },
fijian: { IETF: 'fj-FJ', 'ISO-639': 'fj' },
filipino: { IETF: 'fil-PH', 'ISO-639': 'fil' },
finnish: { IETF: 'fi-FI', 'ISO-639': 'fi' },
flemish: { IETF: 'nl-BE', 'ISO-639': 'nl' },
fon: { IETF: 'fon-BJ', 'ISO-639': 'fon' },
french: { IETF: 'fr-FR', 'ISO-639': 'fr' },
'french canada': { IETF: 'fr-CA', 'ISO-639': 'fr' },
'french swiss': { IETF: 'fr-CH', 'ISO-639': 'fr' },
friulian: { IETF: 'fur-IT', 'ISO-639': 'fur' },
fula: { IETF: 'ff-FUL', 'ISO-639': 'ff' },
galician: { IETF: 'gl-ES', 'ISO-639': 'gl' },
gamargu: { IETF: 'mfi-NG', 'ISO-639': 'mfi' },
garo: { IETF: 'grt-IN', 'ISO-639': 'grt' },
georgian: { IETF: 'ka-GE', 'ISO-639': 'ka' },
german: { IETF: 'de-DE', 'ISO-639': 'de' },
gilbertese: { IETF: 'gil-KI', 'ISO-639': 'gil' },
glavda: { IETF: 'glw-NG', 'ISO-639': 'glw' },
greek: { IETF: 'el-GR', 'ISO-639': 'el' },
'grenadian creole english': { IETF: 'gcl-GD', 'ISO-639': 'gcl' },
guarani: { IETF: 'gn-PY', 'ISO-639': 'gn' },
gujarati: { IETF: 'gu-IN', 'ISO-639': 'gu' },
'guyanese creole english': { IETF: 'gyn-GY', 'ISO-639': 'gyn' },
'haitian creole french': { IETF: 'ht-HT', 'ISO-639': 'ht' },
'halh mongolian': { IETF: 'khk-MN', 'ISO-639': 'khk' },
hausa: { IETF: 'ha-NE', 'ISO-639': 'ha' },
hawaiian: { IETF: 'haw-US', 'ISO-639': 'haw' },
hebrew: { IETF: 'he-IL', 'ISO-639': 'he' },
higi: { IETF: 'hig-NG', 'ISO-639': 'hig' },
hiligaynon: { IETF: 'hil-PH', 'ISO-639': 'hil' },
'hill mari': { IETF: 'mrj-RU', 'ISO-639': 'mrj' },
hindi: { IETF: 'hi-IN', 'ISO-639': 'hi' },
hmong: { IETF: 'hmn-CN', 'ISO-639': 'hmn' },
hungarian: { IETF: 'hu-HU', 'ISO-639': 'hu' },
icelandic: { IETF: 'is-IS', 'ISO-639': 'is' },
'igbo ibo': { IETF: 'ibo-NG', 'ISO-639': 'ibo' },
'igbo ig': { IETF: 'ig-NG', 'ISO-639': 'ig' },
ilocano: { IETF: 'ilo-PH', 'ISO-639': 'ilo' },
indonesian: { IETF: 'id-ID', 'ISO-639': 'id' },
'inuktitut greenlandic': { IETF: 'kl-GL', 'ISO-639': 'kl' },
'irish gaelic': { IETF: 'ga-IE', 'ISO-639': 'ga' },
italian: { IETF: 'it-IT', 'ISO-639': 'it' },
'italian swiss': { IETF: 'it-CH', 'ISO-639': 'it' },
'jamaican creole english': { IETF: 'jam-JM', 'ISO-639': 'jam' },
japanese: { IETF: 'ja-JP', 'ISO-639': 'ja' },
javanese: { IETF: 'jv-ID', 'ISO-639': 'jv' },
jingpho: { IETF: 'kac-MM', 'ISO-639': 'kac' },
"k'iche'": { IETF: 'quc-GT', 'ISO-639': 'quc' },
'kabiy<69>': { IETF: 'kbp-TG', 'ISO-639': 'kbp' },
kabuverdianu: { IETF: 'kea-CV', 'ISO-639': 'kea' },
kabylian: { IETF: 'kab-DZ', 'ISO-639': 'kab' },
kalenjin: { IETF: 'kln-KE', 'ISO-639': 'kln' },
kamba: { IETF: 'kam-KE', 'ISO-639': 'kam' },
kannada: { IETF: 'kn-IN', 'ISO-639': 'kn' },
kanuri: { IETF: 'kr-KAU', 'ISO-639': 'kr' },
karen: { IETF: 'kar-MM', 'ISO-639': 'kar' },
'kashmiri (devanagari script)': { IETF: 'ks-IN', 'ISO-639': 'ks' },
'kashmiri (arabic script)': { IETF: 'kas-IN', 'ISO-639': 'kas' },
kazakh: { IETF: 'kk-KZ', 'ISO-639': 'kk' },
khasi: { IETF: 'kha-IN', 'ISO-639': 'kha' },
khmer: { IETF: 'km-KH', 'ISO-639': 'km' },
'kikuyu kik': { IETF: 'kik-KE', 'ISO-639': 'kik' },
'kikuyu ki': { IETF: 'ki-KE', 'ISO-639': 'ki' },
kimbundu: { IETF: 'kmb-AO', 'ISO-639': 'kmb' },
kinyarwanda: { IETF: 'rw-RW', 'ISO-639': 'rw' },
kirundi: { IETF: 'rn-BI', 'ISO-639': 'rn' },
kisii: { IETF: 'guz-KE', 'ISO-639': 'guz' },
kongo: { IETF: 'kg-CG', 'ISO-639': 'kg' },
konkani: { IETF: 'kok-IN', 'ISO-639': 'kok' },
korean: { IETF: 'ko-KR', 'ISO-639': 'ko' },
'northern kurdish': { IETF: 'kmr-TR', 'ISO-639': 'kmr' },
'kurdish sorani': { IETF: 'ckb-IQ', 'ISO-639': 'ckb' },
kyrgyz: { IETF: 'ky-KG', 'ISO-639': 'ky' },
lao: { IETF: 'lo-LA', 'ISO-639': 'lo' },
latgalian: { IETF: 'ltg-LV', 'ISO-639': 'ltg' },
latin: { IETF: 'la-XN', 'ISO-639': 'la' },
latvian: { IETF: 'lv-LV', 'ISO-639': 'lv' },
ligurian: { IETF: 'lij-IT', 'ISO-639': 'lij' },
limburgish: { IETF: 'li-NL', 'ISO-639': 'li' },
lingala: { IETF: 'ln-LIN', 'ISO-639': 'ln' },
lithuanian: { IETF: 'lt-LT', 'ISO-639': 'lt' },
lombard: { IETF: 'lmo-IT', 'ISO-639': 'lmo' },
'luba-kasai': { IETF: 'lua-CD', 'ISO-639': 'lua' },
luganda: { IETF: 'lg-UG', 'ISO-639': 'lg' },
luhya: { IETF: 'luy-KE', 'ISO-639': 'luy' },
luo: { IETF: 'luo-KE', 'ISO-639': 'luo' },
luxembourgish: { IETF: 'lb-LU', 'ISO-639': 'lb' },
maa: { IETF: 'mas-KE', 'ISO-639': 'mas' },
macedonian: { IETF: 'mk-MK', 'ISO-639': 'mk' },
magahi: { IETF: 'mag-IN', 'ISO-639': 'mag' },
maithili: { IETF: 'mai-IN', 'ISO-639': 'mai' },
malagasy: { IETF: 'mg-MG', 'ISO-639': 'mg' },
malay: { IETF: 'ms-MY', 'ISO-639': 'ms' },
malayalam: { IETF: 'ml-IN', 'ISO-639': 'ml' },
maldivian: { IETF: 'dv-MV', 'ISO-639': 'dv' },
maltese: { IETF: 'mt-MT', 'ISO-639': 'mt' },
mandara: { IETF: 'mfi-CM', 'ISO-639': 'mfi' },
manipuri: { IETF: 'mni-IN', 'ISO-639': 'mni' },
'manx gaelic': { IETF: 'gv-IM', 'ISO-639': 'gv' },
maori: { IETF: 'mi-NZ', 'ISO-639': 'mi' },
marathi: { IETF: 'mr-IN', 'ISO-639': 'mr' },
margi: { IETF: 'mrt-NG', 'ISO-639': 'mrt' },
mari: { IETF: 'mhr-RU', 'ISO-639': 'mhr' },
marshallese: { IETF: 'mh-MH', 'ISO-639': 'mh' },
mende: { IETF: 'men-SL', 'ISO-639': 'men' },
meru: { IETF: 'mer-KE', 'ISO-639': 'mer' },
mijikenda: { IETF: 'nyf-KE', 'ISO-639': 'nyf' },
minangkabau: { IETF: 'min-ID', 'ISO-639': 'min' },
mizo: { IETF: 'lus-IN', 'ISO-639': 'lus' },
mongolian: { IETF: 'mn-MN', 'ISO-639': 'mn' },
montenegrin: { IETF: 'sr-ME', 'ISO-639': 'sr' },
morisyen: { IETF: 'mfe-MU', 'ISO-639': 'mfe' },
'moroccan arabic': { IETF: 'ar-MA', 'ISO-639': 'ar' },
mossi: { IETF: 'mos-BF', 'ISO-639': 'mos' },
ndau: { IETF: 'ndc-MZ', 'ISO-639': 'ndc' },
ndebele: { IETF: 'nr-ZA', 'ISO-639': 'nr' },
nepali: { IETF: 'ne-NP', 'ISO-639': 'ne' },
'nigerian fulfulde': { IETF: 'fuv-NG', 'ISO-639': 'fuv' },
niuean: { IETF: 'niu-NU', 'ISO-639': 'niu' },
'north azerbaijani': { IETF: 'azj-AZ', 'ISO-639': 'azj' },
sesotho: { IETF: 'nso-ZA', 'ISO-639': 'nso' },
'northern uzbek': { IETF: 'uzn-UZ', 'ISO-639': 'uzn' },
'norwegian bokm<6B>l': { IETF: 'nb-NO', 'ISO-639': 'nb' },
'norwegian nynorsk': { IETF: 'nn-NO', 'ISO-639': 'nn' },
nuer: { IETF: 'nus-SS', 'ISO-639': 'nus' },
nyanja: { IETF: 'ny-MW', 'ISO-639': 'ny' },
occitan: { IETF: 'oc-FR', 'ISO-639': 'oc' },
'occitan aran': { IETF: 'oc-ES', 'ISO-639': 'oc' },
odia: { IETF: 'or-IN', 'ISO-639': 'or' },
oriya: { IETF: 'ory-IN', 'ISO-639': 'ory' },
urdu: { IETF: 'ur-PK', 'ISO-639': 'ur' },
palauan: { IETF: 'pau-PW', 'ISO-639': 'pau' },
pali: { IETF: 'pi-IN', 'ISO-639': 'pi' },
pangasinan: { IETF: 'pag-PH', 'ISO-639': 'pag' },
papiamentu: { IETF: 'pap-CW', 'ISO-639': 'pap' },
pashto: { IETF: 'ps-PK', 'ISO-639': 'ps' },
persian: { IETF: 'fa-IR', 'ISO-639': 'fa' },
pijin: { IETF: 'pis-SB', 'ISO-639': 'pis' },
'plateau malagasy': { IETF: 'plt-MG', 'ISO-639': 'plt' },
polish: { IETF: 'pl-PL', 'ISO-639': 'pl' },
portuguese: { IETF: 'pt-PT', 'ISO-639': 'pt' },
'portuguese brazil': { IETF: 'pt-BR', 'ISO-639': 'pt' },
potawatomi: { IETF: 'pot-US', 'ISO-639': 'pot' },
punjabi: { IETF: 'pa-IN', 'ISO-639': 'pa' },
'punjabi (pakistan)': { IETF: 'pnb-PK', 'ISO-639': 'pnb' },
quechua: { IETF: 'qu-PE', 'ISO-639': 'qu' },
rohingya: { IETF: 'rhg-MM', 'ISO-639': 'rhg' },
rohingyalish: { IETF: 'rhl-MM', 'ISO-639': 'rhl' },
romanian: { IETF: 'ro-RO', 'ISO-639': 'ro' },
romansh: { IETF: 'roh-CH', 'ISO-639': 'roh' },
rundi: { IETF: 'run-BI', 'ISO-639': 'run' },
russian: { IETF: 'ru-RU', 'ISO-639': 'ru' },
'saint lucian creole french': { IETF: 'acf-LC', 'ISO-639': 'acf' },
samoan: { IETF: 'sm-WS', 'ISO-639': 'sm' },
sango: { IETF: 'sg-CF', 'ISO-639': 'sg' },
sanskrit: { IETF: 'sa-IN', 'ISO-639': 'sa' },
santali: { IETF: 'sat-IN', 'ISO-639': 'sat' },
sardinian: { IETF: 'sc-IT', 'ISO-639': 'sc' },
'scots gaelic': { IETF: 'gd-GB', 'ISO-639': 'gd' },
sena: { IETF: 'seh-ZW', 'ISO-639': 'seh' },
'serbian cyrillic': { IETF: 'sr-Cyrl-RS', 'ISO-639': 'sr' },
'serbian latin': { IETF: 'sr-Latn-RS', 'ISO-639': 'sr' },
'seselwa creole french': { IETF: 'crs-SC', 'ISO-639': 'crs' },
'setswana (south africa)': { IETF: 'tn-ZA', 'ISO-639': 'tn' },
shan: { IETF: 'shn-MM', 'ISO-639': 'shn' },
shona: { IETF: 'sn-ZW', 'ISO-639': 'sn' },
sicilian: { IETF: 'scn-IT', 'ISO-639': 'scn' },
silesian: { IETF: 'szl-PL', 'ISO-639': 'szl' },
'sindhi snd': { IETF: 'snd-PK', 'ISO-639': 'snd' },
'sindhi sd': { IETF: 'sd-PK', 'ISO-639': 'sd' },
sinhala: { IETF: 'si-LK', 'ISO-639': 'si' },
slovak: { IETF: 'sk-SK', 'ISO-639': 'sk' },
slovenian: { IETF: 'sl-SI', 'ISO-639': 'sl' },
somali: { IETF: 'so-SO', 'ISO-639': 'so' },
'sotho southern': { IETF: 'st-LS', 'ISO-639': 'st' },
'south azerbaijani': { IETF: 'azb-AZ', 'ISO-639': 'azb' },
'southern pashto': { IETF: 'pbt-PK', 'ISO-639': 'pbt' },
'southwestern dinka': { IETF: 'dik-SS', 'ISO-639': 'dik' },
spanish: { IETF: 'es-ES', 'ISO-639': 'es' },
'spanish argentina': { IETF: 'es-AR', 'ISO-639': 'es' },
'spanish colombia': { IETF: 'es-CO', 'ISO-639': 'es' },
'spanish latin america': { IETF: 'es-419', 'ISO-639': 'es' },
'spanish mexico': { IETF: 'es-MX', 'ISO-639': 'es' },
'spanish united states': { IETF: 'es-US', 'ISO-639': 'es' },
'sranan tongo': { IETF: 'srn-SR', 'ISO-639': 'srn' },
'standard latvian': { IETF: 'lvs-LV', 'ISO-639': 'lvs' },
'standard malay': { IETF: 'zsm-MY', 'ISO-639': 'zsm' },
sundanese: { IETF: 'su-ID', 'ISO-639': 'su' },
swahili: { IETF: 'sw-KE', 'ISO-639': 'sw' },
swati: { IETF: 'ss-SZ', 'ISO-639': 'ss' },
swedish: { IETF: 'sv-SE', 'ISO-639': 'sv' },
'swiss german': { IETF: 'de-CH', 'ISO-639': 'de' },
'syriac (aramaic)': { IETF: 'syc-TR', 'ISO-639': 'syc' },
tagalog: { IETF: 'tl-PH', 'ISO-639': 'tl' },
tahitian: { IETF: 'ty-PF', 'ISO-639': 'ty' },
tajik: { IETF: 'tg-TJ', 'ISO-639': 'tg' },
'tamashek (tuareg)': { IETF: 'tmh-DZ', 'ISO-639': 'tmh' },
tamasheq: { IETF: 'taq-ML', 'ISO-639': 'taq' },
'tamil india': { IETF: 'ta-IN', 'ISO-639': 'ta' },
'tamil sri lanka': { IETF: 'ta-LK', 'ISO-639': 'ta' },
taroko: { IETF: 'trv-TW', 'ISO-639': 'trv' },
tatar: { IETF: 'tt-RU', 'ISO-639': 'tt' },
telugu: { IETF: 'te-IN', 'ISO-639': 'te' },
tetum: { IETF: 'tet-TL', 'ISO-639': 'tet' },
thai: { IETF: 'th-TH', 'ISO-639': 'th' },
tibetan: { IETF: 'bo-CN', 'ISO-639': 'bo' },
tigrinya: { IETF: 'ti-ET', 'ISO-639': 'ti' },
'tok pisin': { IETF: 'tpi-PG', 'ISO-639': 'tpi' },
tokelauan: { IETF: 'tkl-TK', 'ISO-639': 'tkl' },
tongan: { IETF: 'to-TO', 'ISO-639': 'to' },
'tosk albanian': { IETF: 'als-AL', 'ISO-639': 'als' },
tsonga: { IETF: 'ts-ZA', 'ISO-639': 'ts' },
tswa: { IETF: 'tsc-MZ', 'ISO-639': 'tsc' },
tswana: { IETF: 'tn-BW', 'ISO-639': 'tn' },
tumbuka: { IETF: 'tum-MW', 'ISO-639': 'tum' },
turkish: { IETF: 'tr-TR', 'ISO-639': 'tr' },
turkmen: { IETF: 'tk-TM', 'ISO-639': 'tk' },
tuvaluan: { IETF: 'tvl-TV', 'ISO-639': 'tvl' },
twi: { IETF: 'tw-GH', 'ISO-639': 'tw' },
udmurt: { IETF: 'udm-RU', 'ISO-639': 'udm' },
ukrainian: { IETF: 'uk-UA', 'ISO-639': 'uk' },
uma: { IETF: 'ppk-ID', 'ISO-639': 'ppk' },
umbundu: { IETF: 'umb-AO', 'ISO-639': 'umb' },
'uyghur uig': { IETF: 'uig-CN', 'ISO-639': 'uig' },
'uyghur ug': { IETF: 'ug-CN', 'ISO-639': 'ug' },
uzbek: { IETF: 'uz-UZ', 'ISO-639': 'uz' },
venetian: { IETF: 'vec-IT', 'ISO-639': 'vec' },
vietnamese: { IETF: 'vi-VN', 'ISO-639': 'vi' },
'vincentian creole english': { IETF: 'svc-VC', 'ISO-639': 'svc' },
'virgin islands creole english': { IETF: 'vic-US', 'ISO-639': 'vic' },
wallisian: { IETF: 'wls-WF', 'ISO-639': 'wls' },
'waray (philippines)': { IETF: 'war-PH', 'ISO-639': 'war' },
welsh: { IETF: 'cy-GB', 'ISO-639': 'cy' },
'west central oromo': { IETF: 'gaz-ET', 'ISO-639': 'gaz' },
'western persian': { IETF: 'pes-IR', 'ISO-639': 'pes' },
wolof: { IETF: 'wo-SN', 'ISO-639': 'wo' },
xhosa: { IETF: 'xh-ZA', 'ISO-639': 'xh' },
yiddish: { IETF: 'yi-YD', 'ISO-639': 'yi' },
yoruba: { IETF: 'yo-NG', 'ISO-639': 'yo' },
zulu: { IETF: 'zu-ZA', 'ISO-639': 'zu' },
none: { IETF: 'none', 'ISO-639': 'none' },
acehnese: { IETF: 'ace-ID', 'ISO-639': 'ace' },
afrikaans: { IETF: 'af-ZA', 'ISO-639': 'af' },
akan: { IETF: 'ak-GH', 'ISO-639': 'ak' },
albanian: { IETF: 'sq-AL', 'ISO-639': 'sq' },
amharic: { IETF: 'am-ET', 'ISO-639': 'am' },
'antigua and barbuda creole english': { IETF: 'aig-AG', 'ISO-639': 'aig' },
arabic: { IETF: 'ar-SA', 'ISO-639': 'ar' },
'arabic egyptian': { IETF: 'ar-EG', 'ISO-639': 'ar' },
aragonese: { IETF: 'an-ES', 'ISO-639': 'an' },
armenian: { IETF: 'hy-AM', 'ISO-639': 'hy' },
assamese: { IETF: 'as-IN', 'ISO-639': 'as' },
asturian: { IETF: 'ast-ES', 'ISO-639': 'ast' },
'austrian german': { IETF: 'de-AT', 'ISO-639': 'de' },
awadhi: { IETF: 'awa-IN', 'ISO-639': 'awa' },
'ayacucho quechua': { IETF: 'quy-PE', 'ISO-639': 'quy' },
azerbaijani: { IETF: 'az-AZ', 'ISO-639': 'az' },
'bahamas creole english': { IETF: 'bah-BS', 'ISO-639': 'bah' },
bajan: { IETF: 'bjs-BB', 'ISO-639': 'bjs' },
balinese: { IETF: 'ban-ID', 'ISO-639': 'ban' },
'balkan gipsy': { IETF: 'rm-RO', 'ISO-639': 'rm' },
bambara: { IETF: 'bm-ML', 'ISO-639': 'bm' },
banjar: { IETF: 'bjn-ID', 'ISO-639': 'bjn' },
bashkir: { IETF: 'ba-RU', 'ISO-639': 'ba' },
basque: { IETF: 'eu-ES', 'ISO-639': 'eu' },
belarusian: { IETF: 'be-BY', 'ISO-639': 'be' },
'belgian french': { IETF: 'fr-BE', 'ISO-639': 'fr' },
bemba: { IETF: 'bem-ZM', 'ISO-639': 'bem' },
bengali: { IETF: 'bn-IN', 'ISO-639': 'bn' },
bhojpuri: { IETF: 'bho-IN', 'ISO-639': 'bho' },
bihari: { IETF: 'bh-IN', 'ISO-639': 'bh' },
bislama: { IETF: 'bi-VU', 'ISO-639': 'bi' },
borana: { IETF: 'gax-KE', 'ISO-639': 'gax' },
bosnian: { IETF: 'bs-BA', 'ISO-639': 'bs' },
'bosnian (cyrillic)': { IETF: 'bs-Cyrl-BA', 'ISO-639': 'bs' },
breton: { IETF: 'br-FR', 'ISO-639': 'br' },
buginese: { IETF: 'bug-ID', 'ISO-639': 'bug' },
bulgarian: { IETF: 'bg-BG', 'ISO-639': 'bg' },
burmese: { IETF: 'my-MM', 'ISO-639': 'my' },
catalan: { IETF: 'ca-ES', 'ISO-639': 'ca' },
'catalan valencian': { IETF: 'cav-ES', 'ISO-639': 'cav' },
cebuano: { IETF: 'ceb-PH', 'ISO-639': 'ceb' },
'central atlas tamazight': { IETF: 'tzm-MA', 'ISO-639': 'tzm' },
'central aymara': { IETF: 'ayr-BO', 'ISO-639': 'ayr' },
'central kanuri (latin script)': { IETF: 'knc-NG', 'ISO-639': 'knc' },
'chadian arabic': { IETF: 'shu-TD', 'ISO-639': 'shu' },
chamorro: { IETF: 'ch-GU', 'ISO-639': 'ch' },
cherokee: { IETF: 'chr-US', 'ISO-639': 'chr' },
chhattisgarhi: { IETF: 'hne-IN', 'ISO-639': 'hne' },
'chinese simplified': { IETF: 'zh-CN', 'ISO-639': 'zh' },
'chinese trad. (hong kong)': { IETF: 'zh-HK', 'ISO-639': 'zh' },
'chinese traditional': { IETF: 'zh-TW', 'ISO-639': 'zh' },
'chinese traditional macau': { IETF: 'zh-MO', 'ISO-639': 'zh' },
chittagonian: { IETF: 'ctg-BD', 'ISO-639': 'ctg' },
chokwe: { IETF: 'cjk-AO', 'ISO-639': 'cjk' },
'classical greek': { IETF: 'grc-GR', 'ISO-639': 'grc' },
'comorian ngazidja': { IETF: 'zdj-KM', 'ISO-639': 'zdj' },
coptic: { IETF: 'cop-EG', 'ISO-639': 'cop' },
'crimean tatar': { IETF: 'crh-RU', 'ISO-639': 'crh' },
'crioulo upper guinea': { IETF: 'pov-GW', 'ISO-639': 'pov' },
croatian: { IETF: 'hr-HR', 'ISO-639': 'hr' },
czech: { IETF: 'cs-CZ', 'ISO-639': 'cs' },
danish: { IETF: 'da-DK', 'ISO-639': 'da' },
dari: { IETF: 'prs-AF', 'ISO-639': 'prs' },
dimli: { IETF: 'diq-TR', 'ISO-639': 'diq' },
dutch: { IETF: 'nl-NL', 'ISO-639': 'nl' },
dyula: { IETF: 'dyu-CI', 'ISO-639': 'dyu' },
dzongkha: { IETF: 'dz-BT', 'ISO-639': 'dz' },
'eastern yiddish': { IETF: 'ydd-US', 'ISO-639': 'ydd' },
emakhuwa: { IETF: 'vmw-MZ', 'ISO-639': 'vmw' },
english: { IETF: 'en-GB', 'ISO-639': 'en' },
'english australia': { IETF: 'en-AU', 'ISO-639': 'en' },
'english canada': { IETF: 'en-CA', 'ISO-639': 'en' },
'english india': { IETF: 'en-IN', 'ISO-639': 'en' },
'english ireland': { IETF: 'en-IE', 'ISO-639': 'en' },
'english new zealand': { IETF: 'en-NZ', 'ISO-639': 'en' },
'english singapore': { IETF: 'en-SG', 'ISO-639': 'en' },
'english south africa': { IETF: 'en-ZA', 'ISO-639': 'en' },
'english us': { IETF: 'en-US', 'ISO-639': 'en' },
esperanto: { IETF: 'eo-EU', 'ISO-639': 'eo' },
estonian: { IETF: 'et-EE', 'ISO-639': 'et' },
ewe: { IETF: 'ee-GH', 'ISO-639': 'ee' },
fanagalo: { IETF: 'fn-FNG', 'ISO-639': 'fn' },
faroese: { IETF: 'fo-FO', 'ISO-639': 'fo' },
fijian: { IETF: 'fj-FJ', 'ISO-639': 'fj' },
filipino: { IETF: 'fil-PH', 'ISO-639': 'fil' },
finnish: { IETF: 'fi-FI', 'ISO-639': 'fi' },
flemish: { IETF: 'nl-BE', 'ISO-639': 'nl' },
fon: { IETF: 'fon-BJ', 'ISO-639': 'fon' },
french: { IETF: 'fr-FR', 'ISO-639': 'fr' },
'french canada': { IETF: 'fr-CA', 'ISO-639': 'fr' },
'french swiss': { IETF: 'fr-CH', 'ISO-639': 'fr' },
friulian: { IETF: 'fur-IT', 'ISO-639': 'fur' },
fula: { IETF: 'ff-FUL', 'ISO-639': 'ff' },
galician: { IETF: 'gl-ES', 'ISO-639': 'gl' },
gamargu: { IETF: 'mfi-NG', 'ISO-639': 'mfi' },
garo: { IETF: 'grt-IN', 'ISO-639': 'grt' },
georgian: { IETF: 'ka-GE', 'ISO-639': 'ka' },
german: { IETF: 'de-DE', 'ISO-639': 'de' },
gilbertese: { IETF: 'gil-KI', 'ISO-639': 'gil' },
glavda: { IETF: 'glw-NG', 'ISO-639': 'glw' },
greek: { IETF: 'el-GR', 'ISO-639': 'el' },
'grenadian creole english': { IETF: 'gcl-GD', 'ISO-639': 'gcl' },
guarani: { IETF: 'gn-PY', 'ISO-639': 'gn' },
gujarati: { IETF: 'gu-IN', 'ISO-639': 'gu' },
'guyanese creole english': { IETF: 'gyn-GY', 'ISO-639': 'gyn' },
'haitian creole french': { IETF: 'ht-HT', 'ISO-639': 'ht' },
'halh mongolian': { IETF: 'khk-MN', 'ISO-639': 'khk' },
hausa: { IETF: 'ha-NE', 'ISO-639': 'ha' },
hawaiian: { IETF: 'haw-US', 'ISO-639': 'haw' },
hebrew: { IETF: 'he-IL', 'ISO-639': 'he' },
higi: { IETF: 'hig-NG', 'ISO-639': 'hig' },
hiligaynon: { IETF: 'hil-PH', 'ISO-639': 'hil' },
'hill mari': { IETF: 'mrj-RU', 'ISO-639': 'mrj' },
hindi: { IETF: 'hi-IN', 'ISO-639': 'hi' },
hmong: { IETF: 'hmn-CN', 'ISO-639': 'hmn' },
hungarian: { IETF: 'hu-HU', 'ISO-639': 'hu' },
icelandic: { IETF: 'is-IS', 'ISO-639': 'is' },
'igbo ibo': { IETF: 'ibo-NG', 'ISO-639': 'ibo' },
'igbo ig': { IETF: 'ig-NG', 'ISO-639': 'ig' },
ilocano: { IETF: 'ilo-PH', 'ISO-639': 'ilo' },
indonesian: { IETF: 'id-ID', 'ISO-639': 'id' },
'inuktitut greenlandic': { IETF: 'kl-GL', 'ISO-639': 'kl' },
'irish gaelic': { IETF: 'ga-IE', 'ISO-639': 'ga' },
italian: { IETF: 'it-IT', 'ISO-639': 'it' },
'italian swiss': { IETF: 'it-CH', 'ISO-639': 'it' },
'jamaican creole english': { IETF: 'jam-JM', 'ISO-639': 'jam' },
japanese: { IETF: 'ja-JP', 'ISO-639': 'ja' },
javanese: { IETF: 'jv-ID', 'ISO-639': 'jv' },
jingpho: { IETF: 'kac-MM', 'ISO-639': 'kac' },
"k'iche'": { IETF: 'quc-GT', 'ISO-639': 'quc' },
'kabiy<69>': { IETF: 'kbp-TG', 'ISO-639': 'kbp' },
kabuverdianu: { IETF: 'kea-CV', 'ISO-639': 'kea' },
kabylian: { IETF: 'kab-DZ', 'ISO-639': 'kab' },
kalenjin: { IETF: 'kln-KE', 'ISO-639': 'kln' },
kamba: { IETF: 'kam-KE', 'ISO-639': 'kam' },
kannada: { IETF: 'kn-IN', 'ISO-639': 'kn' },
kanuri: { IETF: 'kr-KAU', 'ISO-639': 'kr' },
karen: { IETF: 'kar-MM', 'ISO-639': 'kar' },
'kashmiri (devanagari script)': { IETF: 'ks-IN', 'ISO-639': 'ks' },
'kashmiri (arabic script)': { IETF: 'kas-IN', 'ISO-639': 'kas' },
kazakh: { IETF: 'kk-KZ', 'ISO-639': 'kk' },
khasi: { IETF: 'kha-IN', 'ISO-639': 'kha' },
khmer: { IETF: 'km-KH', 'ISO-639': 'km' },
'kikuyu kik': { IETF: 'kik-KE', 'ISO-639': 'kik' },
'kikuyu ki': { IETF: 'ki-KE', 'ISO-639': 'ki' },
kimbundu: { IETF: 'kmb-AO', 'ISO-639': 'kmb' },
kinyarwanda: { IETF: 'rw-RW', 'ISO-639': 'rw' },
kirundi: { IETF: 'rn-BI', 'ISO-639': 'rn' },
kisii: { IETF: 'guz-KE', 'ISO-639': 'guz' },
kongo: { IETF: 'kg-CG', 'ISO-639': 'kg' },
konkani: { IETF: 'kok-IN', 'ISO-639': 'kok' },
korean: { IETF: 'ko-KR', 'ISO-639': 'ko' },
'northern kurdish': { IETF: 'kmr-TR', 'ISO-639': 'kmr' },
'kurdish sorani': { IETF: 'ckb-IQ', 'ISO-639': 'ckb' },
kyrgyz: { IETF: 'ky-KG', 'ISO-639': 'ky' },
lao: { IETF: 'lo-LA', 'ISO-639': 'lo' },
latgalian: { IETF: 'ltg-LV', 'ISO-639': 'ltg' },
latin: { IETF: 'la-XN', 'ISO-639': 'la' },
latvian: { IETF: 'lv-LV', 'ISO-639': 'lv' },
ligurian: { IETF: 'lij-IT', 'ISO-639': 'lij' },
limburgish: { IETF: 'li-NL', 'ISO-639': 'li' },
lingala: { IETF: 'ln-LIN', 'ISO-639': 'ln' },
lithuanian: { IETF: 'lt-LT', 'ISO-639': 'lt' },
lombard: { IETF: 'lmo-IT', 'ISO-639': 'lmo' },
'luba-kasai': { IETF: 'lua-CD', 'ISO-639': 'lua' },
luganda: { IETF: 'lg-UG', 'ISO-639': 'lg' },
luhya: { IETF: 'luy-KE', 'ISO-639': 'luy' },
luo: { IETF: 'luo-KE', 'ISO-639': 'luo' },
luxembourgish: { IETF: 'lb-LU', 'ISO-639': 'lb' },
maa: { IETF: 'mas-KE', 'ISO-639': 'mas' },
macedonian: { IETF: 'mk-MK', 'ISO-639': 'mk' },
magahi: { IETF: 'mag-IN', 'ISO-639': 'mag' },
maithili: { IETF: 'mai-IN', 'ISO-639': 'mai' },
malagasy: { IETF: 'mg-MG', 'ISO-639': 'mg' },
malay: { IETF: 'ms-MY', 'ISO-639': 'ms' },
malayalam: { IETF: 'ml-IN', 'ISO-639': 'ml' },
maldivian: { IETF: 'dv-MV', 'ISO-639': 'dv' },
maltese: { IETF: 'mt-MT', 'ISO-639': 'mt' },
mandara: { IETF: 'mfi-CM', 'ISO-639': 'mfi' },
manipuri: { IETF: 'mni-IN', 'ISO-639': 'mni' },
'manx gaelic': { IETF: 'gv-IM', 'ISO-639': 'gv' },
maori: { IETF: 'mi-NZ', 'ISO-639': 'mi' },
marathi: { IETF: 'mr-IN', 'ISO-639': 'mr' },
margi: { IETF: 'mrt-NG', 'ISO-639': 'mrt' },
mari: { IETF: 'mhr-RU', 'ISO-639': 'mhr' },
marshallese: { IETF: 'mh-MH', 'ISO-639': 'mh' },
mende: { IETF: 'men-SL', 'ISO-639': 'men' },
meru: { IETF: 'mer-KE', 'ISO-639': 'mer' },
mijikenda: { IETF: 'nyf-KE', 'ISO-639': 'nyf' },
minangkabau: { IETF: 'min-ID', 'ISO-639': 'min' },
mizo: { IETF: 'lus-IN', 'ISO-639': 'lus' },
mongolian: { IETF: 'mn-MN', 'ISO-639': 'mn' },
montenegrin: { IETF: 'sr-ME', 'ISO-639': 'sr' },
morisyen: { IETF: 'mfe-MU', 'ISO-639': 'mfe' },
'moroccan arabic': { IETF: 'ar-MA', 'ISO-639': 'ar' },
mossi: { IETF: 'mos-BF', 'ISO-639': 'mos' },
ndau: { IETF: 'ndc-MZ', 'ISO-639': 'ndc' },
ndebele: { IETF: 'nr-ZA', 'ISO-639': 'nr' },
nepali: { IETF: 'ne-NP', 'ISO-639': 'ne' },
'nigerian fulfulde': { IETF: 'fuv-NG', 'ISO-639': 'fuv' },
niuean: { IETF: 'niu-NU', 'ISO-639': 'niu' },
'north azerbaijani': { IETF: 'azj-AZ', 'ISO-639': 'azj' },
sesotho: { IETF: 'nso-ZA', 'ISO-639': 'nso' },
'northern uzbek': { IETF: 'uzn-UZ', 'ISO-639': 'uzn' },
'norwegian bokm<6B>l': { IETF: 'nb-NO', 'ISO-639': 'nb' },
'norwegian nynorsk': { IETF: 'nn-NO', 'ISO-639': 'nn' },
nuer: { IETF: 'nus-SS', 'ISO-639': 'nus' },
nyanja: { IETF: 'ny-MW', 'ISO-639': 'ny' },
occitan: { IETF: 'oc-FR', 'ISO-639': 'oc' },
'occitan aran': { IETF: 'oc-ES', 'ISO-639': 'oc' },
odia: { IETF: 'or-IN', 'ISO-639': 'or' },
oriya: { IETF: 'ory-IN', 'ISO-639': 'ory' },
urdu: { IETF: 'ur-PK', 'ISO-639': 'ur' },
palauan: { IETF: 'pau-PW', 'ISO-639': 'pau' },
pali: { IETF: 'pi-IN', 'ISO-639': 'pi' },
pangasinan: { IETF: 'pag-PH', 'ISO-639': 'pag' },
papiamentu: { IETF: 'pap-CW', 'ISO-639': 'pap' },
pashto: { IETF: 'ps-PK', 'ISO-639': 'ps' },
persian: { IETF: 'fa-IR', 'ISO-639': 'fa' },
pijin: { IETF: 'pis-SB', 'ISO-639': 'pis' },
'plateau malagasy': { IETF: 'plt-MG', 'ISO-639': 'plt' },
polish: { IETF: 'pl-PL', 'ISO-639': 'pl' },
portuguese: { IETF: 'pt-PT', 'ISO-639': 'pt' },
'portuguese brazil': { IETF: 'pt-BR', 'ISO-639': 'pt' },
potawatomi: { IETF: 'pot-US', 'ISO-639': 'pot' },
punjabi: { IETF: 'pa-IN', 'ISO-639': 'pa' },
'punjabi (pakistan)': { IETF: 'pnb-PK', 'ISO-639': 'pnb' },
quechua: { IETF: 'qu-PE', 'ISO-639': 'qu' },
rohingya: { IETF: 'rhg-MM', 'ISO-639': 'rhg' },
rohingyalish: { IETF: 'rhl-MM', 'ISO-639': 'rhl' },
romanian: { IETF: 'ro-RO', 'ISO-639': 'ro' },
romansh: { IETF: 'roh-CH', 'ISO-639': 'roh' },
rundi: { IETF: 'run-BI', 'ISO-639': 'run' },
russian: { IETF: 'ru-RU', 'ISO-639': 'ru' },
'saint lucian creole french': { IETF: 'acf-LC', 'ISO-639': 'acf' },
samoan: { IETF: 'sm-WS', 'ISO-639': 'sm' },
sango: { IETF: 'sg-CF', 'ISO-639': 'sg' },
sanskrit: { IETF: 'sa-IN', 'ISO-639': 'sa' },
santali: { IETF: 'sat-IN', 'ISO-639': 'sat' },
sardinian: { IETF: 'sc-IT', 'ISO-639': 'sc' },
'scots gaelic': { IETF: 'gd-GB', 'ISO-639': 'gd' },
sena: { IETF: 'seh-ZW', 'ISO-639': 'seh' },
'serbian cyrillic': { IETF: 'sr-Cyrl-RS', 'ISO-639': 'sr' },
'serbian latin': { IETF: 'sr-Latn-RS', 'ISO-639': 'sr' },
'seselwa creole french': { IETF: 'crs-SC', 'ISO-639': 'crs' },
'setswana (south africa)': { IETF: 'tn-ZA', 'ISO-639': 'tn' },
shan: { IETF: 'shn-MM', 'ISO-639': 'shn' },
shona: { IETF: 'sn-ZW', 'ISO-639': 'sn' },
sicilian: { IETF: 'scn-IT', 'ISO-639': 'scn' },
silesian: { IETF: 'szl-PL', 'ISO-639': 'szl' },
'sindhi snd': { IETF: 'snd-PK', 'ISO-639': 'snd' },
'sindhi sd': { IETF: 'sd-PK', 'ISO-639': 'sd' },
sinhala: { IETF: 'si-LK', 'ISO-639': 'si' },
slovak: { IETF: 'sk-SK', 'ISO-639': 'sk' },
slovenian: { IETF: 'sl-SI', 'ISO-639': 'sl' },
somali: { IETF: 'so-SO', 'ISO-639': 'so' },
'sotho southern': { IETF: 'st-LS', 'ISO-639': 'st' },
'south azerbaijani': { IETF: 'azb-AZ', 'ISO-639': 'azb' },
'southern pashto': { IETF: 'pbt-PK', 'ISO-639': 'pbt' },
'southwestern dinka': { IETF: 'dik-SS', 'ISO-639': 'dik' },
spanish: { IETF: 'es-ES', 'ISO-639': 'es' },
'spanish argentina': { IETF: 'es-AR', 'ISO-639': 'es' },
'spanish colombia': { IETF: 'es-CO', 'ISO-639': 'es' },
'spanish latin america': { IETF: 'es-419', 'ISO-639': 'es' },
'spanish mexico': { IETF: 'es-MX', 'ISO-639': 'es' },
'spanish united states': { IETF: 'es-US', 'ISO-639': 'es' },
'sranan tongo': { IETF: 'srn-SR', 'ISO-639': 'srn' },
'standard latvian': { IETF: 'lvs-LV', 'ISO-639': 'lvs' },
'standard malay': { IETF: 'zsm-MY', 'ISO-639': 'zsm' },
sundanese: { IETF: 'su-ID', 'ISO-639': 'su' },
swahili: { IETF: 'sw-KE', 'ISO-639': 'sw' },
swati: { IETF: 'ss-SZ', 'ISO-639': 'ss' },
swedish: { IETF: 'sv-SE', 'ISO-639': 'sv' },
'swiss german': { IETF: 'de-CH', 'ISO-639': 'de' },
'syriac (aramaic)': { IETF: 'syc-TR', 'ISO-639': 'syc' },
tagalog: { IETF: 'tl-PH', 'ISO-639': 'tl' },
tahitian: { IETF: 'ty-PF', 'ISO-639': 'ty' },
tajik: { IETF: 'tg-TJ', 'ISO-639': 'tg' },
'tamashek (tuareg)': { IETF: 'tmh-DZ', 'ISO-639': 'tmh' },
tamasheq: { IETF: 'taq-ML', 'ISO-639': 'taq' },
'tamil india': { IETF: 'ta-IN', 'ISO-639': 'ta' },
'tamil sri lanka': { IETF: 'ta-LK', 'ISO-639': 'ta' },
taroko: { IETF: 'trv-TW', 'ISO-639': 'trv' },
tatar: { IETF: 'tt-RU', 'ISO-639': 'tt' },
telugu: { IETF: 'te-IN', 'ISO-639': 'te' },
tetum: { IETF: 'tet-TL', 'ISO-639': 'tet' },
thai: { IETF: 'th-TH', 'ISO-639': 'th' },
tibetan: { IETF: 'bo-CN', 'ISO-639': 'bo' },
tigrinya: { IETF: 'ti-ET', 'ISO-639': 'ti' },
'tok pisin': { IETF: 'tpi-PG', 'ISO-639': 'tpi' },
tokelauan: { IETF: 'tkl-TK', 'ISO-639': 'tkl' },
tongan: { IETF: 'to-TO', 'ISO-639': 'to' },
'tosk albanian': { IETF: 'als-AL', 'ISO-639': 'als' },
tsonga: { IETF: 'ts-ZA', 'ISO-639': 'ts' },
tswa: { IETF: 'tsc-MZ', 'ISO-639': 'tsc' },
tswana: { IETF: 'tn-BW', 'ISO-639': 'tn' },
tumbuka: { IETF: 'tum-MW', 'ISO-639': 'tum' },
turkish: { IETF: 'tr-TR', 'ISO-639': 'tr' },
turkmen: { IETF: 'tk-TM', 'ISO-639': 'tk' },
tuvaluan: { IETF: 'tvl-TV', 'ISO-639': 'tvl' },
twi: { IETF: 'tw-GH', 'ISO-639': 'tw' },
udmurt: { IETF: 'udm-RU', 'ISO-639': 'udm' },
ukrainian: { IETF: 'uk-UA', 'ISO-639': 'uk' },
uma: { IETF: 'ppk-ID', 'ISO-639': 'ppk' },
umbundu: { IETF: 'umb-AO', 'ISO-639': 'umb' },
'uyghur uig': { IETF: 'uig-CN', 'ISO-639': 'uig' },
'uyghur ug': { IETF: 'ug-CN', 'ISO-639': 'ug' },
uzbek: { IETF: 'uz-UZ', 'ISO-639': 'uz' },
venetian: { IETF: 'vec-IT', 'ISO-639': 'vec' },
vietnamese: { IETF: 'vi-VN', 'ISO-639': 'vi' },
'vincentian creole english': { IETF: 'svc-VC', 'ISO-639': 'svc' },
'virgin islands creole english': { IETF: 'vic-US', 'ISO-639': 'vic' },
wallisian: { IETF: 'wls-WF', 'ISO-639': 'wls' },
'waray (philippines)': { IETF: 'war-PH', 'ISO-639': 'war' },
welsh: { IETF: 'cy-GB', 'ISO-639': 'cy' },
'west central oromo': { IETF: 'gaz-ET', 'ISO-639': 'gaz' },
'western persian': { IETF: 'pes-IR', 'ISO-639': 'pes' },
wolof: { IETF: 'wo-SN', 'ISO-639': 'wo' },
xhosa: { IETF: 'xh-ZA', 'ISO-639': 'xh' },
yiddish: { IETF: 'yi-YD', 'ISO-639': 'yi' },
yoruba: { IETF: 'yo-NG', 'ISO-639': 'yo' },
zulu: { IETF: 'zu-ZA', 'ISO-639': 'zu' }
};
module.exports = { languages };

View file

@ -6,73 +6,75 @@ const path = require('path');
const consoleloggerLevel = process.env.WINSTON_LOGGER_LEVEL || 'info';
const consoleFormat = format.combine(
format.colorize(),
format.timestamp(),
format.align(),
format.printf((info) => `${info.timestamp} - ${info.level}: ${info.message} ${JSON.stringify(info.metadata)}`),
format.colorize(),
format.timestamp(),
format.align(),
format.printf(info => `${info.timestamp} - ${info.level}: ${info.message} ${JSON.stringify(info.metadata)}`)
);
const fileFormat = format.combine(
format.timestamp(),
format.metadata({ fillExcept: ['message', 'level', 'timestamp', 'label'] }),
format.json(),
format.timestamp(),
format.metadata({ fillExcept: ['message', 'level', 'timestamp', 'label'] }),
format.json()
);
const logger = createLogger({
level: 'info',
format: fileFormat,
transports: [
new transports.File({
filename: path.join(__dirname, '../logs/error.log'),
level: 'error',
}),
new transports.File({
filename: path.join(__dirname, '../logs/activity.log'),
maxsize: 5242880,
maxFiles: 5,
}),
],
level: 'info',
format: fileFormat,
transports: [
new transports.File({
filename: path.join(__dirname, '../logs/error.log'),
level: 'error'
}),
new transports.File({
filename: path.join(__dirname, '../logs/activity.log'),
maxsize: 5242880,
maxFiles: 5
})
]
});
if (process.env.NODE_ENV !== 'production') {
logger.add(
new transports.Console({
level: consoleloggerLevel,
format: consoleFormat,
}),
);
logger.add(
new transports.Console({
level: consoleloggerLevel,
format: consoleFormat
})
);
}
fetch(path.join(__dirname, '../logs/activity.log'))
.then((response) => response.text())
.then((logData) => {
const logLines = logData.trim().split('\n');
const tableBody = document.getElementById('logContent');
.then(response => response.text())
.then(logData => {
const logLines = logData.trim().split('\n');
const tableBody = document.getElementById('logContent');
logLines.forEach((logLine) => {
const logObject = JSON.parse(logLine);
const row = document.createElement('tr');
logLines.forEach(logLine => {
const logObject = JSON.parse(logLine);
const row = document.createElement('tr');
const levelCell = document.createElement('td');
levelCell.textContent = logObject.level;
levelCell.classList.add(logObject.level); // Add class for styling
row.appendChild(levelCell);
const levelCell = document.createElement('td');
levelCell.textContent = logObject.level;
levelCell.classList.add(logObject.level); // Add class for styling
row.appendChild(levelCell);
const messageCell = document.createElement('td');
messageCell.textContent = logObject.message;
row.appendChild(messageCell);
const messageCell = document.createElement('td');
messageCell.textContent = logObject.message;
row.appendChild(messageCell);
const metadataCell = document.createElement('td');
metadataCell.textContent = JSON.stringify(logObject.metadata, null, 2);
row.appendChild(metadataCell);
const metadataCell = document.createElement('td');
metadataCell.textContent = JSON.stringify(logObject.metadata, null, 2);
row.appendChild(metadataCell);
const timestampCell = document.createElement('td');
timestampCell.textContent = logObject.timestamp;
row.appendChild(timestampCell);
const timestampCell = document.createElement('td');
timestampCell.textContent = logObject.timestamp;
row.appendChild(timestampCell);
tableBody.appendChild(row);
});
})
.catch((error) => {});
tableBody.appendChild(row);
});
})
.catch(error => {
console.error(error);
});
module.exports = logger;

View file

@ -1,49 +1,51 @@
let micSelect = document.querySelector('#microphone');
/* global settings, */
const micSelect = document.querySelector('#microphone');
let selectedMic;
function getAvailableMediaDevices(type) {
return new Promise((resolve, reject) => {
navigator.mediaDevices
.enumerateDevices()
.then((devices) => {
const microphones = devices.filter((device) => device.kind === type);
resolve(microphones);
})
.catch((error) => {
reject(error);
});
});
return new Promise((resolve, reject) => {
navigator.mediaDevices
.enumerateDevices()
.then(devices => {
const microphones = devices.filter(device => device.kind === type);
resolve(microphones);
})
.catch(error => {
reject(error);
});
});
}
// Microphones
getAvailableMediaDevices('audioinput')
.then((microphones) => {
let i = 0;
let tempname = '';
for (let mic of microphones) {
if (mic.deviceId === 'default') {
tempname = mic.label.slice(10); // remove "default -" from the label to get the default device name.
}
.then(microphones => {
let i = 0;
let tempname = '';
for (const mic of microphones) {
if (mic.deviceId === 'default') {
tempname = mic.label.slice(10); // remove "default -" from the label to get the default device name.
}
if (mic.deviceId === 'communications' || mic.label === tempname) {
continue;
}
if (mic.deviceId === 'communications' || mic.label === tempname) {
continue;
}
const option = document.createElement('option');
const option = document.createElement('option');
// Set the options value and text.
option.value = i;
option.innerHTML = `${mic.label}`;
// Set the options value and text.
option.value = i;
option.innerHTML = `${mic.label}`;
// Add the option to the voice selector.
micSelect.appendChild(option);
// Add the option to the voice selector.
micSelect.appendChild(option);
if (i === microphones.length - 1) {
document.getElementById('microphone').value = settings.STT.SELECTED_MICROPHONE;
}
i++;
}
})
.catch((error) => {
console.error('Error retrieving microphones:', error);
});
if (i === microphones.length - 1) {
document.getElementById('microphone').value = settings.STT.SELECTED_MICROPHONE;
}
i++;
}
})
.catch(error => {
console.error('Error retrieving microphones:', error);
});

View file

@ -1,26 +1,26 @@
const twitchTemplate = `
<img class="user-img" src="" />
<img class="status-circle sender" src="./images/twitch-icon.png" />
<span class="post-time sender"></span>
<span class="username sender"></span>
<div class="msg-box sender"></div>
<img class="user-img" src="" />
<img class="status-circle sender" src="./images/twitch-icon.png" />
<span class="post-time sender"></span>
<span class="username sender"></span>
<div class="msg-box sender"></div>
`.trim();
const userTemplate = `
<img class="user-img" src="https://gravatar.com/avatar/56234674574535734573000000000001?d=retro" />
<img class="status-circle user" src="./images/twitch-icon.png" />
<span class="post-time user"></span>
<span class="username user">You</span>
<div class="msg-box user"></div>
<img class="user-img" src="https://gravatar.com/avatar/56234674574535734573000000000001?d=retro" />
<img class="status-circle user" src="./images/twitch-icon.png" />
<span class="post-time user"></span>
<span class="username user">You</span>
<div class="msg-box user"></div>
`.trim();
const messageTemplate = `
<article class="msg-container user" id="msg-0">
<img class="user-img" src="https://gravatar.com/avatar/56234674574535734573000000000001?d=retro" />
<img class="status-circle user" src="./images/twitch-icon.png" />
<span class="post-time user"> 12:00 PM</span>
<span class="username user">You</span>
<div class="msg-box user">Hello there</div>
<img class="user-img" src="https://gravatar.com/avatar/56234674574535734573000000000001?d=retro" />
<img class="status-circle user" src="./images/twitch-icon.png" />
<span class="post-time user"> 12:00 PM</span>
<span class="username user">You</span>
<div class="msg-box user">Hello there</div>
</article>
`.trim();

View file

@ -3,7 +3,7 @@ const ini = require('ini');
const path = require('path'); // get directory path
const axios = require('axios');
const { ipcRenderer, shell } = require('electron'); // necessary electron libraries to send data to the app
const { webFrame, ipcRenderer, shell } = require('electron'); // necessary electron libraries to send data to the app
const io = require('socket.io-client');
const util = require('util');
@ -17,8 +17,8 @@ const { Socket } = require('socket.io-client');
const main = ipcRenderer.sendSync('environment');
const resourcesPath = main.resourcesPath;
let settingsPath = main.settingsPath.toString();
let pythonPath = main.pythonPath.toString();
const settingsPath = main.settingsPath.toString();
const pythonPath = main.pythonPath.toString();
const settings = main.settings;
// TODO: remove gooogle voices txt and use api instead
@ -35,6 +35,8 @@ const devicesDropdown = document.querySelector('#devicesDropdown');
const notificationSound = document.querySelector('#notification'); // obtain the html reference of the sound comboBox
const sttModel = document.querySelector('#sttModel'); // obtain the html reference of the sound comboBox
const ttsAudioDevices = document.querySelector('#ttsAudioDevice'); // obtain the html reference of the installedTTS comboBox
const notificationSoundAudioDevices = document.querySelector('#notificationSoundAudioDevice'); // obtain the html reference of the installedTTS comboBox
const emojiPicker = document.body.querySelector('emoji-picker');
// laod local javascript files
const chat = require(path.join(__dirname, './js/chat'));
@ -47,18 +49,18 @@ const config = require(path.join(__dirname, './js/settings'));
const mediaDevices = require(path.join(__dirname, './js/mediaDevices'));
let notificationSounds = path.join(__dirname, './sounds/notifications');
let sttModels = path.join(__dirname, '../speech_to_text_models');
const notificationSounds = path.join(__dirname, './sounds/notifications');
const sttModels = path.join(__dirname, '../speech_to_text_models');
function reset() {
ipcRenderer.send('restart');
ipcRenderer.send('restart');
}
let server = require(path.join(__dirname, './js/server'));
const server = require(path.join(__dirname, './js/server'));
const backend = require(path.join(__dirname, './js/backend'));
let socket = io(`http://localhost:${settings.GENERAL.PORT}`); // Connect to your Socket.IO server
const socket = io(`http://localhost:${settings.GENERAL.PORT}`); // Connect to your Socket.IO server
let twitch = settings.TWITCH.USE_TWITCH ? require(path.join(__dirname, './js/twitch')) : '';
const twitch = settings.TWITCH.USE_TWITCH ? require(path.join(__dirname, './js/twitch')) : '';
const Polly = settings.AMAZON.USE_AMAZON ? require(path.join(__dirname, './js/amazon')) : '';
const google = settings.GOOGLE.USE_GOOGLE ? require(path.join(__dirname, './js/google')) : '';
@ -66,6 +68,9 @@ const theme = require(path.join(__dirname, './js/theme'));
const auth = require(path.join(__dirname, './js/auth'));
let ttsRequestCount = 0;
ttsRequestCount = 0;
let customEmojis = [];
customEmojis = [];
// initialize values
config.getGeneralSettings();
@ -77,83 +82,93 @@ const StartDateAndTime = Date.now();
const speakButton = document.querySelector('#speakBtn');
const amazonCredentials = {
accessKeyId: settings.AMAZON.ACCESS_KEY,
secretAccessKey: settings.AMAZON.ACCESS_SECRET,
accessKeyId: settings.AMAZON.ACCESS_KEY,
secretAccessKey: settings.AMAZON.ACCESS_SECRET
};
// Check for installed sounds
fs.readdir(notificationSounds, (err, files) => {
files.forEach((file, i) => {
// Create a new option element.
const option = document.createElement('option');
if (err) {
console.error(err);
}
// Set the options value and text.
option.value = i;
option.innerHTML = file;
files.forEach((file, i) => {
// Create a new option element.
const option = document.createElement('option');
// Add the option to the sound selector.
notificationSound.appendChild(option);
});
// Set the options value and text.
option.value = i;
option.innerHTML = file;
// set the saved notification sound
notificationSound.selectedIndex = settings.AUDIO.NOTIFICATION_SOUND;
// Add the option to the sound selector.
notificationSound.appendChild(option);
});
// set the saved notification sound
notificationSound.selectedIndex = settings.AUDIO.NOTIFICATION_SOUND;
});
// Check for installed stt models
fs.readdir(sttModels, (err, files) => {
for (let file of files) {
if (file.includes('.txt')) {
continue;
}
// Create a new option element.
const option = document.createElement('option');
if (err) {
console.error(err);
}
// Set the options value and text.
option.value = file;
option.innerHTML = file;
// Add the option to the sound selector.
sttModel.appendChild(option);
for (const file of files) {
if (file.includes('.txt')) {
continue;
}
// Create a new option element.
const option = document.createElement('option');
// set the saved notification sound
sttModel.value = settings.STT.LANGUAGE;
// Set the options value and text.
option.value = file;
option.innerHTML = file;
// Add the option to the sound selector.
sttModel.appendChild(option);
}
// set the saved notification sound
sttModel.value = settings.STT.LANGUAGE;
});
async function getAudioDevices() {
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
return;
}
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
return;
}
const devices = await navigator.mediaDevices.enumerateDevices();
const audioOutputDevices = devices.filter((device) => device.kind === 'audiooutput');
const devices = await navigator.mediaDevices.enumerateDevices();
const audioOutputDevices = devices.filter(device => device.kind === 'audiooutput');
audioOutputDevices.forEach((device) => {
const option = document.createElement('option');
option.text = device.label || `Output ${device.deviceId}`;
option.value = device.deviceId;
ttsAudioDevices.appendChild(option);
});
audioOutputDevices.forEach(device => {
const option = document.createElement('option');
option.text = device.label || `Output ${device.deviceId}`;
option.value = device.deviceId;
ttsAudioDevices.appendChild(option);
notificationSoundAudioDevices.appendChild(option);
});
ttsAudioDevices.selectedIndex = settings.AUDIO.SELECTED_TTS_AUDIO_DEVICE;
ttsAudioDevices.selectedIndex = settings.AUDIO.SELECTED_TTS_AUDIO_DEVICE;
notificationSoundAudioDevices.selectedIndex = settings.AUDIO.SELECTED_NOTIFICATION_AUDIO_DEVICE;
}
getAudioDevices();
function setLanguagesinSelect(languageSelector, setting) {
let languageSelect = document.querySelector(languageSelector); // obtain the html reference of the google voices comboBox
const languageSelect = document.querySelector(languageSelector); // obtain the html reference of the google voices comboBox
for (const language in languageObject.languages) {
if (languageObject.languages.hasOwnProperty(language)) {
const iso639 = languageObject.languages[language]['ISO-639'];
const option = document.createElement('option');
option.value = iso639;
option.innerHTML = `${iso639} - ${language}`;
languageSelect.appendChild(option);
}
for (const language in languageObject.languages) {
if (Object.prototype.hasOwnProperty.call(languageObject.languages, language)) {
const iso639 = languageObject.languages[language]['ISO-639'];
const option = document.createElement('option');
option.value = iso639;
option.innerHTML = `${iso639} - ${language}`;
languageSelect.appendChild(option);
}
}
languageSelect.selectedIndex = setting;
languageSelect.selectedIndex = setting;
}
setLanguagesinSelect('#language', settings.GENERAL.LANGUAGE);
@ -161,95 +176,122 @@ setLanguagesinSelect('#defaultLanguage', settings.TTS.PRIMARY_TTS_LANGUAGE_INDEX
setLanguagesinSelect('#secondaryLanguage', settings.TTS.SECONDARY_TTS_LANGUAGE_INDEX);
function addVoiceService(name) {
function addToselect(select) {
let ttsService = document.querySelector(select);
const option = document.createElement('option');
ttsService.appendChild(option);
function addToselect(select) {
const ttsService = document.querySelector(select);
const option = document.createElement('option');
ttsService.appendChild(option);
option.value = name;
option.innerHTML = name;
}
addToselect('#primaryTTSService');
addToselect('#secondaryTTSService');
option.value = name;
option.innerHTML = name;
}
addToselect('#primaryTTSService');
addToselect('#secondaryTTSService');
}
// Small tooltip
Array.from(document.body.querySelectorAll('[tip]')).forEach((el) => {
const tip = document.createElement('div');
const body = document.querySelector('.container');
const element = el;
tip.classList.add('tooltip');
tip.classList.add('tooltiptext');
tip.innerText = el.getAttribute('tip');
tip.style.transform = `translate(${el.hasAttribute('tip-left') ? 'calc(-100% - 5px)' : '15px'}, ${
el.hasAttribute('tip-top') ? '-100%' : '15px'
})`;
body.appendChild(tip);
element.onmousemove = (e) => {
tip.style.left = `${e.x}px`;
tip.style.top = `${e.y}px`;
tip.style.zIndex = 1;
tip.style.visibility = 'visible';
};
element.onmouseleave = (e) => {
tip.style.visibility = 'hidden';
};
Array.from(document.body.querySelectorAll('[tip]')).forEach(el => {
const tip = document.createElement('div');
const body = document.querySelector('.container');
const element = el;
tip.classList.add('tooltip');
tip.classList.add('tooltiptext');
tip.innerText = el.getAttribute('tip');
tip.style.transform = `translate(${el.hasAttribute('tip-left') ? 'calc(-100% - 5px)' : '15px'}, ${
el.hasAttribute('tip-top') ? '-100%' : '15px'
})`;
body.appendChild(tip);
element.onmousemove = e => {
tip.style.left = `${e.x}px`;
tip.style.top = `${e.y}px`;
tip.style.zIndex = 1;
tip.style.visibility = 'visible';
};
element.onmouseleave = e => {
tip.style.visibility = 'hidden';
};
});
function showChatMessage(article, isUser) {
document.querySelector('#chatBox').appendChild(article);
let usernameHtml;
let msg;
let messages = Array.from(document.body.querySelectorAll('.msg-container'));
function showChatMessage(article) {
document.querySelector('#chatBox').appendChild(article);
if (isUser) {
usernameHtml = article.querySelector('.username-user');
msg = article.querySelector('.msg-box-user');
} else {
usernameHtml = article.querySelector('.username');
msg = article.querySelector('.msg-box');
}
const messages = Array.from(document.body.querySelectorAll('.msg-container'));
// var style = getComputedStyle(usernameHtml);
// var style2 = getComputedStyle(usernameHtml);
const lastMessage = messages[messages.length - 1];
lastMessage.scrollIntoView({ behavior: 'smooth' });
const lastMessage = messages[messages.length - 1];
lastMessage.scrollIntoView({ behavior: 'smooth' });
}
function getPostTime() {
const date = new Date();
document.body.querySelectorAll('.container').innerHTML = date.getHours();
const hours = date.getHours();
var ampm = hours >= 12 ? 'PM' : 'AM';
const minutes = (date.getMinutes() < 10 ? '0' : '') + date.getMinutes();
const time = `${hours}:${minutes} ${ampm}`;
const date = new Date();
document.body.querySelectorAll('.container').innerHTML = date.getHours();
const hours = date.getHours();
const ampm = hours >= 12 ? 'PM' : 'AM';
const minutes = (date.getMinutes() < 10 ? '0' : '') + date.getMinutes();
const time = `${hours}:${minutes} ${ampm}`;
return time;
return time;
}
function showPreviewChatMessage() {
const message = messageTemplates.messageTemplate;
document.querySelector('#mini-mid').innerHTML += message;
const messages = Array.from(document.body.querySelectorAll('#mini-mid'));
const lastMessage = messages[messages.length - 1];
lastMessage.scrollIntoView({ behavior: 'smooth' });
const message = messageTemplates.messageTemplate;
document.querySelector('#mini-mid').innerHTML += message;
const messages = Array.from(document.body.querySelectorAll('#mini-mid'));
const lastMessage = messages[messages.length - 1];
lastMessage.scrollIntoView({ behavior: 'smooth' });
}
showPreviewChatMessage();
function hideText(button, field) {
document.body.querySelector(button).addEventListener('click', () => {
const passwordInput = document.querySelector(field);
if (passwordInput.type === 'password') {
passwordInput.type = 'lol';
} else {
passwordInput.type = 'password';
}
});
document.body.querySelector(button).addEventListener('click', () => {
const passwordInput = document.querySelector(field);
if (passwordInput.type === 'password') {
passwordInput.type = 'lol';
} else {
passwordInput.type = 'password';
}
});
}
hideText('.password-toggle-btn1', '#TWITCH_OAUTH_TOKEN');
hideText('.password-toggle-btn4', '#AMAZON_ACCESS_KEY');
hideText('.password-toggle-btn5', '#AMAZON_ACCESS_SECRET');
hideText('.password-toggle-btn6', '#GOOGLE_API_KEY');
function setZoomLevel(currentZoom, zoomIn) {
let newZoom = currentZoom.toFixed(2);
if (zoomIn === true && currentZoom < 4.95) {
newZoom = (currentZoom + 0.05).toFixed(2);
}
if (zoomIn === false && currentZoom > 0.25) {
newZoom = (currentZoom - 0.05).toFixed(2);
}
webFrame.setZoomFactor(parseFloat(newZoom));
settings.GENERAL.ZOOMLEVEL = newZoom;
fs.writeFileSync(settingsPath, ini.stringify(settings));
document.body.querySelector('#ZOOMLEVEL').value = (settings.GENERAL.ZOOMLEVEL * 100).toFixed(0);
}
// const customEmojix = [
// {
// name: 'sakuraestaKleefeliz',
// shortcodes: ['sakuraestaKleefeliz'],
// url: 'https://static-cdn.jtvnw.net/emoticons/v2/emotesv2_0cb536ddb6e143ab87ffeccb160a4d45/default/dark/1.0',
// category: 'Sakura'
// }
// ];
// const customEmojiy = [
// {
// name: 'sakuraestaKleefeliz',
// shortcodes: ['sakuraestaKleefeliz'],
// url: 'https://static-cdn.jtvnw.net/emoticons/v2/emotesv2_0cb536ddb6e143ab87ffeccb160a4d45/default/dark/1.0',
// category: 'Sakurax'
// }
// ];
// emojiPicker.customEmoji = customEmojix;
// emojiPicker.customEmoji = customEmojiy;
// console.log(emojiPicker.database.getEmojiBySearchQuery('Kappa'));

View file

@ -1,18 +1,21 @@
function getBotResponse(input) {
// rock paper scissors
if (input === 'rock') {
return 'paper';
} if (input === 'paper') {
return 'scissors';
} if (input === 'scissors') {
return 'rock';
}
// rock paper scissors
if (input === 'rock') {
return 'paper';
}
if (input === 'paper') {
return 'scissors';
}
if (input === 'scissors') {
return 'rock';
}
// Simple responses
if (input === 'hello') {
return 'Hello there!';
} if (input === 'goodbye') {
return 'Talk to you later!';
}
return 'Try asking something else!';
// Simple responses
if (input === 'hello') {
return 'Hello there!';
}
if (input === 'goodbye') {
return 'Talk to you later!';
}
return 'Try asking something else!';
}

View file

@ -1,3 +1,5 @@
/* global settings */
const express = require('express');
const app = express();
const path = require('path');
@ -5,42 +7,42 @@ const http = require('http');
const localServer = http.createServer(app);
const io = require('socket.io')(localServer);
let requestCount = 0;
const requestCount = 0;
function startVtuberModule() {
if (!settings.MODULES.USE_VTUBER) {
return;
}
if (!settings.MODULES.USE_VTUBER) {
return;
}
app.use('/vtuber', express.static(path.join(__dirname, '../modules/vtuber/')));
app.use('/vtuber', express.static(path.join(__dirname, '../modules/vtuber/')));
let vtuber = document.body.querySelector('#BrowsersourceVtuber');
let vtuberframe = document.createElement('iframe');
vtuberframe.class = 'frame';
vtuberframe.src = `http://localhost:${settings.GENERAL.PORT}/vtuber`;
vtuberframe.style.width = '100%';
vtuberframe.style.height = '100%';
vtuberframe.frameBorder = 0;
vtuber.appendChild(vtuberframe);
const vtuber = document.body.querySelector('#BrowsersourceVtuber');
const vtuberframe = document.createElement('iframe');
vtuberframe.class = 'frame';
vtuberframe.src = `http://localhost:${settings.GENERAL.PORT}/vtuber`;
vtuberframe.style.width = '100%';
vtuberframe.style.height = '100%';
vtuberframe.frameBorder = 0;
vtuber.appendChild(vtuberframe);
}
startVtuberModule();
function startChatBubbleModule() {
if (!settings.MODULES.USE_CHATBUBBLE) {
return;
}
if (!settings.MODULES.USE_CHATBUBBLE) {
return;
}
app.use('/chat', express.static(path.join(__dirname, '../modules/chat')));
app.use('/chat', express.static(path.join(__dirname, '../modules/chat')));
let chat = document.body.querySelector('#BrowsersourceChat');
let chatframe = document.createElement('iframe');
chatframe.class = 'frame';
chatframe.src = `http://localhost:${settings.GENERAL.PORT}/chat`;
chatframe.style.width = '100%';
chatframe.style.height = '100%';
chatframe.frameBorder = 0;
chat.appendChild(chatframe);
const chat = document.body.querySelector('#BrowsersourceChat');
const chatframe = document.createElement('iframe');
chatframe.class = 'frame';
chatframe.src = `http://localhost:${settings.GENERAL.PORT}/chat`;
chatframe.style.width = '100%';
chatframe.style.height = '100%';
chatframe.frameBorder = 0;
chat.appendChild(chatframe);
}
startChatBubbleModule();
@ -49,34 +51,31 @@ function startSTT() {}
// Middleware to conditionally serve routes
app.use((req, res, next) => {
if (!settings.MODULES.USE_VTUBER && req.path === '/vtuber') {
res.sendStatus(404); // Return a 404 status for /vtuber when it's disabled
} else if (!settings.MODULES.USE_CHATBUBBLE && req.path === '/chat') {
res.sendStatus(404); // Return a 404 status for /chat when it's disabled
} else {
next(); // Proceed to the next middleware or route handler
}
if (!settings.MODULES.USE_VTUBER && req.path === '/vtuber') {
res.sendStatus(404); // Return a 404 status for /vtuber when it's disabled
} else if (!settings.MODULES.USE_CHATBUBBLE && req.path === '/chat') {
res.sendStatus(404); // Return a 404 status for /chat when it's disabled
} else {
next(); // Proceed to the next middleware or route handler
}
});
localServer.listen(settings.GENERAL.PORT, () => {
startVtuberModule();
startChatBubbleModule();
if (settings.TTS.USE_TTS) {
}
startVtuberModule();
startChatBubbleModule();
});
// Handle socket connections
io.on('connection', (socket) => {
// Receive data from the client
socket.on('message', (data) => {});
io.on('connection', socket => {
// Receive data from the client
socket.on('message', data => {});
// Receive data from the client
socket.on('xxx', (logoUrl, username, message) => {
socket.broadcast.emit('message', logoUrl, username, message);
});
// Receive data from the client
socket.on('xxx', (logoUrl, username, message) => {
socket.broadcast.emit('message', logoUrl, username, message);
});
socket.on('disconnect', () => {});
socket.on('disconnect', () => {});
});
module.exports = { startVtuberModule, startChatBubbleModule };

File diff suppressed because it is too large Load diff

View file

@ -1,120 +1,139 @@
/* global ttsAudioFile, path, resourcesPath, settings, fs, notificationSound, backend, socket, requestData */
let trueMessage = '';
let currentLogoUrl = '';
let currentUsername = '';
let voiceSoundArray = [];
const voiceSoundArray = [];
let status = 0;
let counter = 0;
const counter = 0;
const playTTS = (data) =>
new Promise((resolve) => {
ttsAudioFile = path.join(resourcesPath, `./sounds/tts/${data.service}_${data.count}.mp3`);
const tts = new Audio(ttsAudioFile);
console.log(settings.AUDIO.TTS_AUDIO_DEVICE);
tts.setSinkId(settings.AUDIO.TTS_AUDIO_DEVICE);
const playTTS = data =>
new Promise(resolve => {
ttsAudioFile = path.join(resourcesPath, `./sounds/tts/${data.service}_${data.count}.mp3`);
const tts = new Audio(ttsAudioFile);
// console.log(settings.AUDIO.TTS_AUDIO_DEVICE);
tts.setSinkId(settings.AUDIO.TTS_AUDIO_DEVICE);
tts.addEventListener('ended', () => {
console.log('ended');
fs.unlink(ttsAudioFile, (err) => {
if (err) {
console.error('TEST');
resolve('finished');
return;
}
resolve('finished');
});
});
tts.setSinkId(settings.AUDIO.TTS_AUDIO_DEVICE)
.then(() => {
console.log('playing');
tts.volume = settings.AUDIO.TTS_VOLUME / 100;
tts.play().catch((error) => {
resolve('finished');
});
})
.catch((error) => {
console.error('Failed to set audio output device:', error);
resolve('finished');
});
tts.addEventListener('ended', () => {
// console.log('ended');
fs.unlink(ttsAudioFile, err => {
if (err) {
console.error(err);
resolve('finished');
return;
}
resolve('finished');
});
});
tts
.setSinkId(settings.AUDIO.TTS_AUDIO_DEVICE)
.then(() => {
// console.log('playing');
tts.volume = settings.AUDIO.TTS_VOLUME / 100;
tts.play().catch(error => {
if (error) {
console.error(error);
}
resolve('finished');
});
})
.catch(error => {
console.error('Failed to set audio output device:', error);
resolve('finished');
});
});
async function shiftVoice() {
status = 1;
while (voiceSoundArray.length > 0) {
await playTTS(voiceSoundArray.shift());
}
status = 0;
status = 1;
while (voiceSoundArray.length > 0) {
await playTTS(voiceSoundArray.shift());
}
status = 0;
}
function add(data) {
voiceSoundArray.push(data);
if (status === 0) {
shiftVoice();
}
voiceSoundArray.push(data);
if (status === 0) {
shiftVoice();
}
}
function playNotificationSound() {
if (settings.AUDIO.USE_NOTIFICATION_SOUNDS) {
let notfication = new Audio(
path.join(resourcesPath, `./sounds/notifications/${notificationSound.options[settings.AUDIO.NOTIFICATION_SOUND].text}`),
);
if (settings.AUDIO.USE_NOTIFICATION_SOUNDS) {
const notfication = new Audio(
path.join(resourcesPath, `./sounds/notifications/${notificationSound.options[settings.AUDIO.NOTIFICATION_SOUND].text}`)
);
notfication
.setSinkId(settings.AUDIO.SELECTED_NOTIFICATION_AUDIO_DEVICE)
.then(() => {
// console.log('playing');
notfication.volume = settings.AUDIO.NOTIFICATION_VOLUME / 100;
notfication.play();
}
notfication.play().catch(error => {
if (error) {
console.error(error);
}
});
})
.catch(error => {
console.error('Failed to set audio output device:', error);
});
}
}
// Play sound function
function playAudio(data) {
if (data.service !== '') {
add(data);
}
if (data.service !== '') {
add(data);
}
}
async function playVoice(filteredMessage, logoUrl, username, message) {
trueMessage = filteredMessage;
currentLogoUrl = logoUrl;
currentUsername = username;
let textObject = { filtered: filteredMessage, formatted: message };
let voice;
textObject.filtered = `${username}: ${filteredMessage}`;
trueMessage = filteredMessage;
currentLogoUrl = logoUrl;
currentUsername = username;
const textObject = { filtered: filteredMessage, formatted: message };
let voice;
textObject.filtered = `${username}: ${filteredMessage}`;
// if (
// settings.TTS.PRIMARY_TTS_LANGUAGE.toLowerCase() !== settings.TTS.SECONDARY_TTS_LANGUAGE.toLowerCase() &&
// language[0].lang === settings.TTS.SECONDARY_TTS_LANGUAGE.toLowerCase()
// ) {
// voice = settings.TTS.SECONDARY_TTS_NAME;
// textObject.filtered = `${username}: ${filteredMessage}`;
// } else {
// voice = settings.TTS.PRIMARY_TTS_NAME;
// textObject.filtered = `${username}: ${filteredMessage}`;
// }
// if (
// settings.TTS.PRIMARY_TTS_LANGUAGE.toLowerCase() !== settings.TTS.SECONDARY_TTS_LANGUAGE.toLowerCase() &&
// language[0].lang === settings.TTS.SECONDARY_TTS_LANGUAGE.toLowerCase()
// ) {
// voice = settings.TTS.SECONDARY_TTS_NAME;
// textObject.filtered = `${username}: ${filteredMessage}`;
// } else {
// voice = settings.TTS.PRIMARY_TTS_NAME;
// textObject.filtered = `${username}: ${filteredMessage}`;
// }
const service = document.getElementById('primaryTTSService').value;
const service = document.getElementById('primaryTTSService').value;
switch (service) {
case 'Internal':
const requestData = {
message: textObject.filtered,
voice: settings.TTS.PRIMARY_VOICE,
};
switch (service) {
case 'Internal': {
const requestData = {
message: textObject.filtered,
voice: settings.TTS.PRIMARY_VOICE
};
let count = await backend.getInternalTTSAudio(requestData);
playAudio({ service, message: textObject, count });
break;
case 'Amazon':
// playAudio({ service: 'Amazon', message: textObject, count });
break;
case 'Google':
// playAudio({ service: 'Google', message: textObject, count });
break;
const count = await backend.getInternalTTSAudio(requestData);
playAudio({ service, message: textObject, count });
break;
}
case 'Amazon':
// playAudio({ service: 'Amazon', message: textObject, count });
break;
case 'Google':
// playAudio({ service: 'Google', message: textObject, count });
break;
}
if (settings.MODULES.USE_CHATBUBBLE) {
socket.emit('xxx', currentLogoUrl, currentUsername, textObject);
}
if (settings.MODULES.USE_CHATBUBBLE) {
socket.emit('xxx', currentLogoUrl, currentUsername, textObject);
}
playNotificationSound();
playNotificationSound();
}
module.exports = { playAudio, playVoice, playNotificationSound };

View file

@ -1,145 +1,143 @@
/* global settings, root, fs, settingsPath, ini */
function changeColor(section, setting, tempSection) {
document.querySelector(section).value = setting;
const value = document.querySelector(section).value;
root.style.setProperty(tempSection, value);
document.querySelector(section).value = setting;
const value = document.querySelector(section).value;
root.style.setProperty(tempSection, value);
}
function setCurrentTheme(adjustTemp = false) {
changeColor('#MAIN_COLOR_1', settings.THEME.MAIN_COLOR_1, adjustTemp ? '--main-color1-temp' : '--main-color1');
changeColor('#MAIN_COLOR_2', settings.THEME.MAIN_COLOR_2, adjustTemp ? '--main-color2-temp' : '--main-color2');
changeColor('#MAIN_COLOR_3', settings.THEME.MAIN_COLOR_3, adjustTemp ? '--main-color3-temp' : '--main-color3');
changeColor('#MAIN_COLOR_4', settings.THEME.MAIN_COLOR_4, adjustTemp ? '--main-color4-temp' : '--main-color4');
changeColor('#TOP_BAR', settings.THEME.TOP_BAR, adjustTemp ? '--top-bar-temp' : '--top-bar');
changeColor('#MID_SECTION', settings.THEME.MID_SECTION, adjustTemp ? '--mid-section-temp' : '--mid-section');
changeColor('#CHAT_BUBBLE_BG', settings.THEME.CHAT_BUBBLE_BG, adjustTemp ? '--chat-bubble-temp' : '--chat-bubble');
changeColor(
'#CHAT_BUBBLE_HEADER',
settings.THEME.CHAT_BUBBLE_HEADER,
adjustTemp ? '--chat-bubble-header-temp' : '--chat-bubble-header',
);
changeColor(
'#CHAT_BUBBLE_MESSAGE',
settings.THEME.CHAT_BUBBLE_MESSAGE,
adjustTemp ? '--chat-bubble-message-temp' : '--chat-bubble-message',
);
changeColor('#MAIN_COLOR_1', settings.THEME.MAIN_COLOR_1, adjustTemp ? '--main-color1-temp' : '--main-color1');
changeColor('#MAIN_COLOR_2', settings.THEME.MAIN_COLOR_2, adjustTemp ? '--main-color2-temp' : '--main-color2');
changeColor('#MAIN_COLOR_3', settings.THEME.MAIN_COLOR_3, adjustTemp ? '--main-color3-temp' : '--main-color3');
changeColor('#MAIN_COLOR_4', settings.THEME.MAIN_COLOR_4, adjustTemp ? '--main-color4-temp' : '--main-color4');
changeColor('#TOP_BAR', settings.THEME.TOP_BAR, adjustTemp ? '--top-bar-temp' : '--top-bar');
changeColor('#MID_SECTION', settings.THEME.MID_SECTION, adjustTemp ? '--mid-section-temp' : '--mid-section');
changeColor('#CHAT_BUBBLE_BG', settings.THEME.CHAT_BUBBLE_BG, adjustTemp ? '--chat-bubble-temp' : '--chat-bubble');
changeColor('#CHAT_BUBBLE_HEADER', settings.THEME.CHAT_BUBBLE_HEADER, adjustTemp ? '--chat-bubble-header-temp' : '--chat-bubble-header');
changeColor(
'#CHAT_BUBBLE_MESSAGE',
settings.THEME.CHAT_BUBBLE_MESSAGE,
adjustTemp ? '--chat-bubble-message-temp' : '--chat-bubble-message'
);
}
setCurrentTheme(true);
function setTheme() {
if (settings.THEME.USE_CUSTOM_THEME) {
setCurrentTheme();
} else {
root.style.setProperty('--main-color1', '#6e2c8c');
root.style.setProperty('--main-color2', 'white');
root.style.setProperty('--main-color3', '#211E1E');
root.style.setProperty('--main-color4', '#2f2c34');
root.style.setProperty('--top-bar', '#100B12');
root.style.setProperty('--mid-section', '#352d3d');
root.style.setProperty('--chat-bubble', ' #7A6D7F');
root.style.setProperty('--chat-bubble-header', '#141414');
root.style.setProperty('--chat-bubble-message', 'white');
}
if (settings.THEME.USE_CUSTOM_THEME) {
setCurrentTheme();
} else {
root.style.setProperty('--main-color1', '#6e2c8c');
root.style.setProperty('--main-color2', 'white');
root.style.setProperty('--main-color3', '#211E1E');
root.style.setProperty('--main-color4', '#2f2c34');
root.style.setProperty('--top-bar', '#100B12');
root.style.setProperty('--mid-section', '#352d3d');
root.style.setProperty('--chat-bubble', ' #7A6D7F');
root.style.setProperty('--chat-bubble-header', '#141414');
root.style.setProperty('--chat-bubble-message', 'white');
}
}
document.body.querySelector('#MAIN_COLOR_1').addEventListener('input', () => {
const x = document.getElementById('MAIN_COLOR_1').value;
root.style.setProperty('--main-color1-temp', x);
console.log(x);
const x = document.getElementById('MAIN_COLOR_1').value;
root.style.setProperty('--main-color1-temp', x);
console.log(x);
});
document.body.querySelector('#MAIN_COLOR_1').addEventListener('change', () => {
settings.THEME.MAIN_COLOR_1 = document.getElementById('MAIN_COLOR_1').value;
fs.writeFileSync(settingsPath, ini.stringify(settings));
changeColor('#MAIN_COLOR_1', settings.THEME.MAIN_COLOR_1, '--main-color1');
settings.THEME.MAIN_COLOR_1 = document.getElementById('MAIN_COLOR_1').value;
fs.writeFileSync(settingsPath, ini.stringify(settings));
changeColor('#MAIN_COLOR_1', settings.THEME.MAIN_COLOR_1, '--main-color1');
});
document.body.querySelector('#MAIN_COLOR_2').addEventListener('input', () => {
const x = document.getElementById('MAIN_COLOR_2').value;
root.style.setProperty('--main-color2-temp', x);
const x = document.getElementById('MAIN_COLOR_2').value;
root.style.setProperty('--main-color2-temp', x);
});
document.body.querySelector('#MAIN_COLOR_2').addEventListener('change', () => {
settings.THEME.MAIN_COLOR_2 = document.getElementById('MAIN_COLOR_2').value;
fs.writeFileSync(settingsPath, ini.stringify(settings));
changeColor('#MAIN_COLOR_2', settings.THEME.MAIN_COLOR_2, '--main-color2');
settings.THEME.MAIN_COLOR_2 = document.getElementById('MAIN_COLOR_2').value;
fs.writeFileSync(settingsPath, ini.stringify(settings));
changeColor('#MAIN_COLOR_2', settings.THEME.MAIN_COLOR_2, '--main-color2');
});
document.body.querySelector('#MAIN_COLOR_3').addEventListener('input', () => {
const x = document.getElementById('MAIN_COLOR_3').value;
root.style.setProperty('--main-color3-temp', x);
const x = document.getElementById('MAIN_COLOR_3').value;
root.style.setProperty('--main-color3-temp', x);
});
document.body.querySelector('#MAIN_COLOR_3').addEventListener('change', () => {
settings.THEME.MAIN_COLOR_3 = document.getElementById('MAIN_COLOR_3').value;
fs.writeFileSync(settingsPath, ini.stringify(settings));
changeColor('#MAIN_COLOR_3', settings.THEME.MAIN_COLOR_3, '--main-color3');
settings.THEME.MAIN_COLOR_3 = document.getElementById('MAIN_COLOR_3').value;
fs.writeFileSync(settingsPath, ini.stringify(settings));
changeColor('#MAIN_COLOR_3', settings.THEME.MAIN_COLOR_3, '--main-color3');
});
document.body.querySelector('#MAIN_COLOR_4').addEventListener('input', () => {
const x = document.getElementById('MAIN_COLOR_4').value;
root.style.setProperty('--main-color4-temp', x);
const x = document.getElementById('MAIN_COLOR_4').value;
root.style.setProperty('--main-color4-temp', x);
});
document.body.querySelector('#MAIN_COLOR_4').addEventListener('change', () => {
settings.THEME.MAIN_COLOR_4 = document.getElementById('MAIN_COLOR_4').value;
fs.writeFileSync(settingsPath, ini.stringify(settings));
changeColor('#MAIN_COLOR_4', settings.THEME.MAIN_COLOR_4, '--main-color4');
settings.THEME.MAIN_COLOR_4 = document.getElementById('MAIN_COLOR_4').value;
fs.writeFileSync(settingsPath, ini.stringify(settings));
changeColor('#MAIN_COLOR_4', settings.THEME.MAIN_COLOR_4, '--main-color4');
});
document.body.querySelector('#TOP_BAR').addEventListener('input', () => {
const x = document.getElementById('TOP_BAR').value;
root.style.setProperty('--top-bar-temp', x);
const x = document.getElementById('TOP_BAR').value;
root.style.setProperty('--top-bar-temp', x);
});
document.body.querySelector('#TOP_BAR').addEventListener('change', () => {
settings.THEME.TOP_BAR = document.getElementById('TOP_BAR').value;
fs.writeFileSync(settingsPath, ini.stringify(settings));
changeColor('#TOP_BAR', settings.THEME.TOP_BAR, '--top-bar');
settings.THEME.TOP_BAR = document.getElementById('TOP_BAR').value;
fs.writeFileSync(settingsPath, ini.stringify(settings));
changeColor('#TOP_BAR', settings.THEME.TOP_BAR, '--top-bar');
});
document.body.querySelector('#MID_SECTION').addEventListener('input', () => {
const x = document.getElementById('MID_SECTION').value;
root.style.setProperty('--mid-section-temp', x);
const x = document.getElementById('MID_SECTION').value;
root.style.setProperty('--mid-section-temp', x);
});
document.body.querySelector('#MID_SECTION').addEventListener('change', () => {
settings.THEME.MID_SECTION = document.getElementById('MID_SECTION').value;
fs.writeFileSync(settingsPath, ini.stringify(settings));
changeColor('#MID_SECTION', settings.THEME.MID_SECTION, '--mid-section');
settings.THEME.MID_SECTION = document.getElementById('MID_SECTION').value;
fs.writeFileSync(settingsPath, ini.stringify(settings));
changeColor('#MID_SECTION', settings.THEME.MID_SECTION, '--mid-section');
});
document.body.querySelector('#CHAT_BUBBLE_BG').addEventListener('input', () => {
const x = document.getElementById('CHAT_BUBBLE_BG').value;
root.style.setProperty('--chat-bubble-temp', x);
const x = document.getElementById('CHAT_BUBBLE_BG').value;
root.style.setProperty('--chat-bubble-temp', x);
});
document.body.querySelector('#CHAT_BUBBLE_BG').addEventListener('change', () => {
settings.THEME.CHAT_BUBBLE_BG = document.getElementById('CHAT_BUBBLE_BG').value;
fs.writeFileSync(settingsPath, ini.stringify(settings));
changeColor('#CHAT_BUBBLE_BG', settings.THEME.CHAT_BUBBLE_BG, '--chat-bubble');
settings.THEME.CHAT_BUBBLE_BG = document.getElementById('CHAT_BUBBLE_BG').value;
fs.writeFileSync(settingsPath, ini.stringify(settings));
changeColor('#CHAT_BUBBLE_BG', settings.THEME.CHAT_BUBBLE_BG, '--chat-bubble');
});
document.body.querySelector('#CHAT_BUBBLE_HEADER').addEventListener('input', () => {
const x = document.getElementById('CHAT_BUBBLE_HEADER').value;
root.style.setProperty('--chat-bubble-header-temp', x);
const x = document.getElementById('CHAT_BUBBLE_HEADER').value;
root.style.setProperty('--chat-bubble-header-temp', x);
});
document.body.querySelector('#CHAT_BUBBLE_HEADER').addEventListener('change', () => {
settings.THEME.CHAT_BUBBLE_HEADER = document.getElementById('CHAT_BUBBLE_HEADER').value;
fs.writeFileSync(settingsPath, ini.stringify(settings));
changeColor('#CHAT_BUBBLE_HEADER', settings.THEME.CHAT_BUBBLE_HEADER, '--chat-bubble-header');
settings.THEME.CHAT_BUBBLE_HEADER = document.getElementById('CHAT_BUBBLE_HEADER').value;
fs.writeFileSync(settingsPath, ini.stringify(settings));
changeColor('#CHAT_BUBBLE_HEADER', settings.THEME.CHAT_BUBBLE_HEADER, '--chat-bubble-header');
});
document.body.querySelector('#CHAT_BUBBLE_MESSAGE').addEventListener('input', () => {
const x = document.getElementById('CHAT_BUBBLE_MESSAGE').value;
root.style.setProperty('--chat-bubble-message-temp', x);
const x = document.getElementById('CHAT_BUBBLE_MESSAGE').value;
root.style.setProperty('--chat-bubble-message-temp', x);
});
document.body.querySelector('#CHAT_BUBBLE_MESSAGE').addEventListener('change', () => {
settings.THEME.CHAT_BUBBLE_MESSAGE = document.getElementById('CHAT_BUBBLE_MESSAGE').value;
fs.writeFileSync(settingsPath, ini.stringify(settings));
changeColor('#CHAT_BUBBLE_MESSAGE', settings.THEME.CHAT_BUBBLE_MESSAGE, '--chat-bubble-message');
settings.THEME.CHAT_BUBBLE_MESSAGE = document.getElementById('CHAT_BUBBLE_MESSAGE').value;
fs.writeFileSync(settingsPath, ini.stringify(settings));
changeColor('#CHAT_BUBBLE_MESSAGE', settings.THEME.CHAT_BUBBLE_MESSAGE, '--chat-bubble-message');
});
module.exports = { setTheme };

View file

@ -1,144 +1,186 @@
/* global client, customEmojis, emojiPicker, settings, options, sound, showChatMessage, messageTemplates, getPostTime */
const tmi = require('tmi.js');
const axios = require('axios');
let client;
let client = null;
let logoUrl = null;
function sendMessage(message) {
client.say(settings.TWITCH.CHANNEL_NAME, message).catch(console.error);
client.say(settings.TWITCH.CHANNEL_NAME, message).catch(console.error);
}
client = new tmi.Client({
options: {
skipUpdatingEmotesets: true,
},
identity: {
username: settings.TWITCH.USERNAME,
password: settings.TWITCH.OAUTH_TOKEN,
},
channels: [settings.TWITCH.CHANNEL_NAME],
options: {
skipUpdatingEmotesets: true
},
identity: {
username: settings.TWITCH.USERNAME,
password: settings.TWITCH.OAUTH_TOKEN
},
channels: [settings.TWITCH.CHANNEL_NAME]
});
client
.connect()
.then((data) => {})
.catch(console.error);
.connect()
.then(data => {})
.catch(console.error);
function ping(element) {
let value = document.body.querySelector(element);
const value = document.body.querySelector(element);
client
.ping()
.then((data) => {
value.classList.add('success');
value.innerText = 'Success!';
})
.catch((e) => {
value.classList.add('error');
value.innerText = 'Failed!';
});
client
.ping()
.then(data => {
value.classList.add('success');
value.innerText = 'Success!';
})
.catch(e => {
value.classList.add('error');
value.innerText = 'Failed!';
});
}
function displayTwitchMessage(logoUrl, username, messageObject, fileteredMessage) {
const article = document.createElement('article');
article.className = 'msg-container sender';
const article = document.createElement('article');
article.className = 'msg-container sender';
article.innerHTML = messageTemplates.twitchTemplate;
article.innerHTML = messageTemplates.twitchTemplate;
const userImg = article.querySelector('.user-img');
if (userImg) {
userImg.src = logoUrl;
}
const userImg = article.querySelector('.user-img');
if (userImg) {
userImg.src = logoUrl;
}
const usernameHtml = article.querySelector('.username');
if (usernameHtml) {
usernameHtml.innerText = username;
}
const usernameHtml = article.querySelector('.username');
if (usernameHtml) {
usernameHtml.innerText = username;
}
const postTime = article.querySelector('.post-time');
const postTime = article.querySelector('.post-time');
if (postTime) {
postTime.innerText = getPostTime();
}
if (postTime) {
postTime.innerText = getPostTime();
}
article.appendChild(postTime);
article.appendChild(postTime);
const msg = article.querySelector('.msg-box');
if (msg) {
messageObject.forEach((entry) => {
if (entry.text) {
msg.innerHTML += entry.text;
} else {
msg.innerHTML += entry.html;
}
});
}
const msg = article.querySelector('.msg-box');
if (msg) {
messageObject.forEach(entry => {
if (entry.text) {
msg.innerHTML += entry.text;
} else {
msg.innerHTML += entry.html;
}
});
}
// Appends the message to the main chat box (shows the message)
showChatMessage(article, false);
// Appends the message to the main chat box (shows the message)
showChatMessage(article);
if (fileteredMessage) {
sound.playVoice(fileteredMessage, logoUrl, username, msg);
}
if (fileteredMessage) {
sound.playVoice(fileteredMessage, logoUrl, username, msg);
}
window.article = article;
window.article = article;
}
function getProfileImage(userid, username, message, fileteredMessage) {
// Get user Logo with access token
options = {
method: 'GET',
url: `https://api.twitch.tv/helix/users?id=${userid}`,
headers: { 'Client-ID': settings.TWITCH.CLIENT_ID, Authorization: `Bearer ${settings.TWITCH.OAUTH_TOKEN}` },
};
// Get user Logo with access token
options = {
method: 'GET',
url: `https://api.twitch.tv/helix/users?id=${userid}`,
headers: { 'Client-ID': settings.TWITCH.CLIENT_ID, Authorization: `Bearer ${settings.TWITCH.OAUTH_TOKEN}` }
};
axios
.request(options)
.then((responseLogoUrl) => {
const logoUrl = responseLogoUrl.data.data[0].profile_image_url;
displayTwitchMessage(logoUrl, username, message, fileteredMessage);
})
.catch((error) => {
console.error(error);
});
axios
.request(options)
.then(responseLogoUrl => {
logoUrl = responseLogoUrl.data.data[0].profile_image_url;
displayTwitchMessage(logoUrl, username, message, fileteredMessage);
})
.catch(error => {
console.error(error);
});
}
function parseString(inputString) {
const regex = /(<img.*?\/>)|([^<]+)/g;
const matches = inputString.match(regex) || [];
const result = [];
const regex = /(<img.*?\/>)|([^<]+)/g;
const matches = inputString.match(regex) || [];
const result = [];
for (let i = 0; i < matches.length; i++) {
const match = matches[i].trim();
if (match.startsWith('<img')) {
result.push({ html: match });
}
if (match !== '' && !match.startsWith('<img')) {
result.push({ text: match });
}
for (let i = 0; i < matches.length; i++) {
const match = matches[i].trim();
if (match.startsWith('<img')) {
result.push({ html: match });
}
return result;
if (match !== '' && !match.startsWith('<img')) {
result.push({ text: match });
}
}
return result;
}
client.on('message', (channel, tags, message, self) => {
if (self) {
return;
}
const emotes = tags.emotes || {};
const emoteValues = Object.entries(emotes);
let fileteredMessage = message;
let emoteMessage = message;
if (self) {
return;
}
const emotes = tags.emotes || {};
const emoteValues = Object.entries(emotes);
let filteredMessage = message;
let emoteMessage = message;
emoteValues.forEach((entry) => {
entry[1].forEach((lol) => {
const [start, end] = lol.split('-');
let emote = `<img src="https://static-cdn.jtvnw.net/emoticons/v2/${entry[0]}/default/dark/1.0"/>`;
emoteMessage = emoteMessage.replaceAll(message.slice(parseInt(start), parseInt(end) + 1), emote);
fileteredMessage = fileteredMessage.replaceAll(message.slice(parseInt(start), parseInt(end) + 1), '');
});
emoteValues.forEach(entry => {
entry[1].forEach(lol => {
const [start, end] = lol.split('-');
const emote = `<img src="https://static-cdn.jtvnw.net/emoticons/v2/${entry[0]}/default/dark/1.0"/>`;
emoteMessage = emoteMessage.replaceAll(message.slice(parseInt(start), parseInt(end) + 1), emote);
filteredMessage = filteredMessage.replaceAll(message.slice(parseInt(start), parseInt(end) + 1), '');
});
});
let messageObject = parseString(emoteMessage);
getProfileImage(tags['user-id'], tags['display-name'], messageObject, fileteredMessage);
const messageObject = parseString(emoteMessage);
getProfileImage(tags['user-id'], tags['display-name'], messageObject, filteredMessage);
});
function formatTwitchEmojis(emojis, name) {
emojis.forEach(emoji => {
const emojiToBeAdded = {
name: emoji.name,
shortcodes: [emoji.name],
url: emoji.images.url_1x,
category: name
};
customEmojis.push(emojiToBeAdded);
});
emojiPicker.customEmoji = customEmojis;
}
function getTwitchGLobalEmotes() {
// Get user Logo with access token
options = {
method: 'GET',
url: 'https://api.twitch.tv/helix/chat/emotes/global',
headers: {
'Client-ID': settings.TWITCH.CLIENT_ID,
Authorization: `Bearer ${settings.TWITCH.OAUTH_TOKEN}`
}
};
axios
.request(options)
.then(responseLogoUrl => {
formatTwitchEmojis(responseLogoUrl.data.data, 'Twitch Global');
// console.log(responseLogoUrl);
})
.catch(error => {
console.error(error);
});
}
if (settings.TWITCH.OAUTH_TOKEN) {
getTwitchGLobalEmotes();
}
module.exports = { sendMessage, ping, client };

View file

@ -1,3 +1,5 @@
/* global pythonPath, a */
const { app, BrowserWindow, ipcMain } = require('electron');
const { writeIniFile } = require('write-ini-file');
const path = require('path');
@ -14,197 +16,199 @@ let settings;
let window;
if (app.isPackaged) {
settingsPath = path.join(process.resourcesPath, './settings.ini');
pythonPath = path.join(process.resourcesPath, './backend');
resourcesPath = process.resourcesPath;
settingsPath = path.join(process.resourcesPath, './settings.ini');
pythonPath = path.join(process.resourcesPath, './backend');
resourcesPath = process.resourcesPath;
} else {
settingsPath = path.join(resourcesPath, './config/settings.ini');
pythonPath = path.join(resourcesPath, './backend');
settingsPath = path.join(resourcesPath, './config/settings.ini');
pythonPath = path.join(resourcesPath, './backend');
}
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) {
app.quit();
app.quit();
}
async function createWindow() {
if (!fs.existsSync(settingsPath)) {
console.log(resourcesPath);
await createIniFile();
} else {
settings = ini.parse(fs.readFileSync(settingsPath, 'utf-8'));
if (!fs.existsSync(settingsPath)) {
console.log(resourcesPath);
await createIniFile();
} else {
settings = ini.parse(fs.readFileSync(settingsPath, 'utf-8'));
}
window = new BrowserWindow({
icon: path.join(__dirname, '/images/icon-512.png'),
width: parseInt(settings.GENERAL.WIDTH),
height: parseInt(settings.GENERAL.HEIGHT),
x: parseInt(settings.GENERAL.POSITION_X),
y: parseInt(settings.GENERAL.POSITION_Y),
frame: false,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true
}
});
window = new BrowserWindow({
icon: path.join(__dirname, '/images/icon-512.png'),
width: parseInt(settings.GENERAL.WIDTH),
height: parseInt(settings.GENERAL.HEIGHT),
x: parseInt(settings.GENERAL.POSITION_X),
y: parseInt(settings.GENERAL.POSITION_Y),
frame: false,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true,
},
});
window.loadFile(path.join(__dirname, 'index.html'));
window.loadFile(path.join(__dirname, 'index.html'));
if (!app.isPackaged) {
window.webContents.openDevTools();
}
if (!app.isPackaged) {
window.webContents.openDevTools();
}
window.on('close', e => {
settings = ini.parse(fs.readFileSync(settingsPath, 'utf-8')); // load newest settings in case anything changed after starting the program
const bounds = window.getBounds();
window.on('close', (e) => {
settings = ini.parse(fs.readFileSync(settingsPath, 'utf-8')); // load newest settings in case anything changed after starting the program
const bounds = window.getBounds();
settings.GENERAL.WIDTH = bounds.width;
settings.GENERAL.HEIGHT = bounds.height;
settings.GENERAL.POSITION_X = bounds.x;
settings.GENERAL.POSITION_Y = bounds.y;
settings.GENERAL.WIDTH = bounds.width;
settings.GENERAL.HEIGHT = bounds.height;
settings.GENERAL.POSITION_X = bounds.x;
settings.GENERAL.POSITION_Y = bounds.y;
fs.writeFileSync(settingsPath, ini.stringify(settings));
});
fs.writeFileSync(settingsPath, ini.stringify(settings));
});
}
app.whenReady().then(() => {
createWindow();
createWindow();
});
app.on('window-all-closed', (event) => {
if (process.platform !== 'darwin') {
app.quit();
}
app.on('window-all-closed', event => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
app.on('before-quit', () => {
window.webContents.send('quit-event');
window.webContents.send('quit-event');
});
ipcMain.on('resize-window', (event, width, height) => {
const browserWindow = BrowserWindow.fromWebContents(event.sender);
browserWindow.setSize(width, height);
const browserWindow = BrowserWindow.fromWebContents(event.sender);
browserWindow.setSize(width, height);
});
ipcMain.on('minimize-window', (event) => {
const browserWindow = BrowserWindow.fromWebContents(event.sender);
browserWindow.minimize();
ipcMain.on('minimize-window', event => {
const browserWindow = BrowserWindow.fromWebContents(event.sender);
browserWindow.minimize();
});
ipcMain.on('maximize-window', (event) => {
const browserWindow = BrowserWindow.fromWebContents(event.sender);
ipcMain.on('maximize-window', event => {
const browserWindow = BrowserWindow.fromWebContents(event.sender);
if (!browserWindow.isMaximized()) {
browserWindow.maximize();
} else {
browserWindow.unmaximize();
}
if (!browserWindow.isMaximized()) {
browserWindow.maximize();
} else {
browserWindow.unmaximize();
}
});
ipcMain.on('close-window', (event) => {
const browserWindow = BrowserWindow.fromWebContents(event.sender);
kill('loquendoBot_backend');
browserWindow.close();
app.quit();
ipcMain.on('close-window', event => {
const browserWindow = BrowserWindow.fromWebContents(event.sender);
kill('loquendoBot_backend');
browserWindow.close();
app.quit();
});
ipcMain.on('restart', (event) => {
app.relaunch();
ipcMain.on('restart', event => {
app.relaunch();
});
ipcMain.on('environment', (event) => {
event.returnValue = { resourcesPath, pythonPath, settingsPath, settings, isPackaged: app.isPackaged };
ipcMain.on('environment', event => {
event.returnValue = { resourcesPath, pythonPath, settingsPath, settings, isPackaged: app.isPackaged };
});
async function createIniFile() {
await writeIniFile(settingsPath, {
GENERAL: {
VOICE_ENABLED: true,
NOTIFICATION_ENABLED: true,
POSITION_X: 0,
POSITION_Y: 0,
WIDTH: 1024,
HEIGHT: 768,
LANGUAGE: 'EN',
PORT: 9000,
VIEWERS_PANEL: false,
LOCATION: pythonPath,
},
LANGUAGE: {
USE_DETECTION: false,
},
TTS: {
USE_TTS: true,
PRIMARY_VOICE: '',
PRIMARY_TTS_LANGUAGE: 'EN',
SECONDARY_VOICE: '',
SECONDARY_TTS_LANGUAGE: 'EN',
},
STT: {
USE_STT: false,
MICROPHONE_ID: 'default',
SELECTED_MICROPHONE: 'default',
MICROPHONE: 0,
LANGUAGE: 'vosk-model-small-es-0.42',
},
AUDIO: {
USE_NOTIFICATION_SOUNDS: true,
NOTIFICATION_AUDIO_DEVICE: 0,
NOTIFICATION_SOUND: 0,
NOTIFICATION_VOLUME: 50,
SELECTED_TTS_AUDIO_DEVICE: 0,
TTS_AUDIO_DEVICE: 'default',
TTS_VOLUME: 50,
},
THEME: {
USE_CUSTOM_THEME: false,
MAIN_COLOR_1: '#cdc1c1',
MAIN_COLOR_2: '#b12020',
MAIN_COLOR_3: '#6c4104',
MAIN_COLOR_4: '#532d2d',
TOP_BAR: '#c8ff00',
MID_SECTION: '#6b8578',
CHAT_BUBBLE_BG: '#447466',
CHAT_BUBBLE_HEADER: '#ffffff',
CHAT_BUBBLE_MESSAGE: '#b5b5b5',
},
TWITCH: {
USE_TWITCH: false,
CHANNEL_NAME: '',
USERNAME: '',
USER_ID: '',
USER_LOGO_URL: '',
OAUTH_TOKEN: '',
CLIENT_ID: '2t206sj7rvtr1rutob3p627d13jch9',
},
MODULES: {
USE_MODULES: false,
USE_VTUBER: false,
USE_CHATBUBBLE: false,
},
AMAZON: {
USE_AMAZON: false,
ACCESS_KEY: '',
ACCESS_SECRET: '',
PRIMARY_VOICE: '',
SECONDARY_VOICE: '',
CHARACTERS_USED: 0,
},
GOOGLE: {
USE_GOOGLE: false,
API_KEY: '',
PRIMARY_VOICE: '',
SECONDARY_VOICE: '',
CHARACTERS_USED: 0,
},
}).then(() => {
settings = ini.parse(fs.readFileSync(settingsPath, 'utf-8'));
});
await writeIniFile(settingsPath, {
GENERAL: {
VOICE_ENABLED: true,
NOTIFICATION_ENABLED: true,
POSITION_X: 0,
POSITION_Y: 0,
WIDTH: 1024,
HEIGHT: 768,
LANGUAGE: 'EN',
PORT: 9000,
VIEWERS_PANEL: false,
LOCATION: pythonPath,
ZOOMLEVEL: 1
},
LANGUAGE: {
USE_DETECTION: false
},
TTS: {
USE_TTS: true,
PRIMARY_VOICE: '',
PRIMARY_TTS_LANGUAGE: 'EN',
SECONDARY_VOICE: '',
SECONDARY_TTS_LANGUAGE: 'EN'
},
STT: {
USE_STT: false,
MICROPHONE_ID: 'default',
SELECTED_MICROPHONE: 'default',
MICROPHONE: 0,
LANGUAGE: 'vosk-model-small-es-0.42'
},
AUDIO: {
USE_NOTIFICATION_SOUNDS: true,
SELECTED_NOTIFICATION_AUDIO_DEVICE: 'default',
NOTIFICATION_AUDIO_DEVICE: 0,
NOTIFICATION_SOUND: 0,
NOTIFICATION_VOLUME: 50,
SELECTED_TTS_AUDIO_DEVICE: 0,
TTS_AUDIO_DEVICE: 'default',
TTS_VOLUME: 50
},
THEME: {
USE_CUSTOM_THEME: false,
MAIN_COLOR_1: '#cdc1c1',
MAIN_COLOR_2: '#b12020',
MAIN_COLOR_3: '#6c4104',
MAIN_COLOR_4: '#532d2d',
TOP_BAR: '#c8ff00',
MID_SECTION: '#6b8578',
CHAT_BUBBLE_BG: '#447466',
CHAT_BUBBLE_HEADER: '#ffffff',
CHAT_BUBBLE_MESSAGE: '#b5b5b5'
},
TWITCH: {
USE_TWITCH: false,
CHANNEL_NAME: '',
USERNAME: '',
USER_ID: '',
USER_LOGO_URL: '',
OAUTH_TOKEN: '',
CLIENT_ID: '2t206sj7rvtr1rutob3p627d13jch9'
},
MODULES: {
USE_MODULES: false,
USE_VTUBER: false,
USE_CHATBUBBLE: false
},
AMAZON: {
USE_AMAZON: false,
ACCESS_KEY: '',
ACCESS_SECRET: '',
PRIMARY_VOICE: '',
SECONDARY_VOICE: '',
CHARACTERS_USED: 0
},
GOOGLE: {
USE_GOOGLE: false,
API_KEY: '',
PRIMARY_VOICE: '',
SECONDARY_VOICE: '',
CHARACTERS_USED: 0
}
}).then(() => {
settings = ini.parse(fs.readFileSync(settingsPath, 'utf-8'));
});
}

View file

@ -1,23 +1,23 @@
<!DOCTYPE html>
<!doctype html>
<html>
<head>
<title>Chat</title>
<script
src="https://cdn.socket.io/4.6.0/socket.io.min.js"
integrity="sha384-c79GN5VsunZvi+Q/WObgk2in0CbZsHnjEqvFxC5DxHn9lTfNce2WW6h2pH6u/kF+"
crossorigin="anonymous"
></script>
<link rel="stylesheet" href="./fonts/FRAMCDN/font.css" />
<link href="main.css" rel="stylesheet" />
</head>
<body>
<!-- #region Main chat box-->
<div class="OptionPanel show" id="Chat">
<div id="chatBox" class="message-window">
<div class="texts"></div>
</div>
</div>
<script src="main.js"></script>
<video id="camera" autoplay></video>
</body>
<head>
<title>Chat</title>
<script
src="https://cdn.socket.io/4.6.0/socket.io.min.js"
integrity="sha384-c79GN5VsunZvi+Q/WObgk2in0CbZsHnjEqvFxC5DxHn9lTfNce2WW6h2pH6u/kF+"
crossorigin="anonymous"
></script>
<link rel="stylesheet" href="./fonts/FRAMCDN/font.css" />
<link href="main.css" rel="stylesheet" />
</head>
<body>
<!-- #region Main chat box-->
<div class="OptionPanel show" id="Chat">
<div id="chatBox" class="message-window">
<div class="texts"></div>
</div>
</div>
<script src="main.js"></script>
<video id="camera" autoplay></video>
</body>
</html>

View file

@ -1,171 +1,171 @@
body {
background-color: transparent;
font-family: 'FRAMDCN';
background-color: transparent;
font-family: 'FRAMDCN';
}
:root {
--variable: 2s;
--buttonBackground: #bf2c2c;
--variable: 2s;
--buttonBackground: #bf2c2c;
}
.thomas {
position: relative;
float: center;
display: inline-block;
position: relative;
float: center;
/* display: inline-block; */
}
.speechbubble {
display: block;
bottom: 0;
position: absolute;
z-index: -1;
display: block;
bottom: 0;
position: absolute;
z-index: -1;
}
.fade-outx {
animation: fade-outx var(--variable) linear;
animation: fade-outx var(--variable) linear;
}
@keyframes fade-outx {
from {
opacity: 1;
}
from {
opacity: 1;
}
to {
opacity: 0;
}
to {
opacity: 0;
}
}
.fade-outxx {
animation: fade-outxx var(--variable) linear;
animation: fade-outxx var(--variable) linear;
}
@keyframes fade-outxx {
from {
opacity: 1;
}
from {
opacity: 1;
}
to {
opacity: 0;
}
to {
opacity: 0;
}
}
.bounce-in {
animation: bounce-in 1s ease;
animation: bounce-in 1s ease;
}
@keyframes bounce-in {
0% {
opacity: 0;
transform: scale(0.3);
}
0% {
opacity: 0;
transform: scale(0.3);
}
50% {
opacity: 1;
transform: scale(1.05);
}
50% {
opacity: 1;
transform: scale(1.05);
}
70% {
transform: scale(0.9);
}
70% {
transform: scale(0.9);
}
100% {
transform: scale(1);
}
100% {
transform: scale(1);
}
}
.bounce-inx {
animation: bounce-inx 1s ease;
animation: bounce-inx 1s ease;
}
@keyframes bounce-inx {
0% {
opacity: 0;
}
0% {
opacity: 0;
}
50% {
opacity: 1;
}
50% {
opacity: 1;
}
}
.msg-container {
position: static;
display: inline-block;
width: 100%;
padding-top: 10px;
position: static;
display: inline-block;
width: 100%;
padding-top: 10px;
}
.message-window {
height: calc(100% - 50px);
overflow: hidden;
overflow-y: hidden;
display: flex;
flex-direction: column;
width: 80%;
margin: auto;
background: transparent;
height: calc(100% - 50px);
overflow: hidden;
overflow-y: hidden;
display: flex;
flex-direction: column;
width: 80%;
margin: auto;
background: transparent;
}
.message-window::before {
content: '';
flex: 1 0 0px;
content: '';
flex: 1 0 0px;
}
.OptionPanel {
flex: 3;
display: none;
position: absolute;
top: 10px;
left: 0;
width: 100%;
height: calc(100% - 25px);
background: transparent;
flex: 3;
display: none;
position: absolute;
top: 10px;
left: 0;
width: 100%;
height: calc(100% - 25px);
background: transparent;
}
.OptionPanel.show {
display: block;
display: block;
}
.message {
text-align: left;
max-width: 100%;
height: auto;
min-width: 125px;
hyphens: auto;
bottom: 0;
right: 0;
float: right;
overflow-wrap: break-word;
text-align: left;
max-width: 100%;
height: auto;
min-width: 125px;
hyphens: auto;
bottom: 0;
right: 0;
float: right;
overflow-wrap: break-word;
}
.message {
position: relative;
border: 2px solid #ff80e1;
box-shadow: 0 2px 10px rgba(255, 128, 225, 0.5);
background: linear-gradient(45deg, rgb(15, 12, 41, 0.7), rgb(48, 43, 99, 0.7));
/* background: linear-gradient(45deg, rgba(72, 0, 154, 0.7), rgba(138, 43, 226, 0.7)); */
position: relative;
border: 2px solid #ff80e1;
box-shadow: 0 2px 10px rgba(255, 128, 225, 0.5);
background: linear-gradient(45deg, rgb(15, 12, 41, 0.7), rgb(48, 43, 99, 0.7));
/* background: linear-gradient(45deg, rgba(72, 0, 154, 0.7), rgba(138, 43, 226, 0.7)); */
color: white;
padding: 15px;
border-radius: 20px;
color: white;
padding: 15px;
border-radius: 20px;
}
/*
.message::after {
}
} */
.arrow {
content: '';
border: 2px solid #ff80e1;
position: absolute;
left: 50%;
top: 100%;
transform: translateX(-50%) rotate(180deg);
border-width: 10px;
border-style: solid;
border-color: transparent transparent rgb(255, 128, 225, 0.7) transparent;
color: #ff80e1;
content: '';
border: 2px solid #ff80e1;
position: absolute;
left: 50%;
top: 100%;
transform: translateX(-50%) rotate(180deg);
border-width: 10px;
border-style: solid;
border-color: transparent transparent rgb(255, 128, 225, 0.7) transparent;
color: #ff80e1;
}
.sender {
color: #ff80e1;
font-size: 14pt;
color: #ff80e1;
font-size: 14pt;
}

View file

@ -1,3 +1,5 @@
/* global io */
// Connect to the Socket.IO server
const socket = io();
@ -5,18 +7,18 @@ const socket = io();
socket.emit('message', 'Hello, Server!');
function getPostTime() {
const d = new Date();
document.body.querySelectorAll('.container').innerHTML = d.getHours();
const hours = d.getHours();
const minutes = (d.getMinutes() < 10 ? '0' : '') + d.getMinutes();
const time = `${hours}:${minutes}`;
return time;
const d = new Date();
document.body.querySelectorAll('.container').innerHTML = d.getHours();
const hours = d.getHours();
const minutes = (d.getMinutes() < 10 ? '0' : '') + d.getMinutes();
const time = `${hours}:${minutes}`;
return time;
}
function showChatMessage(article) {
const main = document.querySelector('#chatBox');
main.appendChild(article);
main.scrollTop = main.scrollHeight;
const main = document.querySelector('#chatBox');
main.appendChild(article);
main.scrollTop = main.scrollHeight;
}
let textStreamContainer;
@ -28,96 +30,96 @@ let x;
let currentIndex = 0;
let messageStream = '';
let tempMessageObject = '';
let fullMessageLength = 0;
const tempMessageObject = '';
const fullMessageLength = 0;
function getFullMessageLength(text) {
let fullMessageLength = 0;
text.forEach((element) => {
if (element.text) {
fullMessageLength += element.text.length;
}
element.html;
fullMessageLength += 1;
});
let fullMessageLength = 0;
text.forEach(element => {
if (element.text) {
fullMessageLength += element.text.length;
}
// element.html;
fullMessageLength += 1;
});
return fullMessageLength;
return fullMessageLength;
}
function streamText() {
// if (currentIndex < fullMessageLength) {
// if (currentIndex < fullMessageLength) {
// textStreamContainer.innerHTML += messageStream.filtered.charAt(currentIndex);
// currentIndex++;
// setTimeout(streamText, 50);
// }
if (currentIndex < messageStream.length) {
textStreamContainer.innerHTML += messageStream.charAt(currentIndex);
currentIndex++;
setTimeout(streamText, 50);
} else {
currentIndex = 0;
x.classList.add('fade-outx');
}
// textStreamContainer.innerHTML += messageStream.filtered.charAt(currentIndex);
// currentIndex++;
// setTimeout(streamText, 50);
// }
if (currentIndex < messageStream.length) {
textStreamContainer.innerHTML += messageStream.charAt(currentIndex);
currentIndex++;
setTimeout(streamText, 50);
} else {
currentIndex = 0;
x.classList.add('fade-outx');
}
}
function displayTwitchMessage(logoUrl, username, messageObject) {
if (!messageObject) {
return;
if (!messageObject) {
return;
}
const root = document.querySelector(':root');
root.style.setProperty('--variable', '5s');
const article = document.createElement('article');
x = article;
article.className = 'msg-container';
const placeMessage = `
<div class="thomas bounce-in">
<div class="message"></div>
<div class="sender"></div>
<div class="speechbubble"></div>
<div class="arrow"></div>
</div>
`.trim();
article.innerHTML = placeMessage;
const msg = article.querySelector('.message');
msg.innerHTML = `<div class="sender">${username}</div>`; // \n${message}`;
msg.style.fontSize = '12pt';
showChatMessage(article);
const elements = document.getElementsByClassName('msg-container');
if (elements.length > 1) {
elements[0].remove();
}
article.addEventListener('animationend', e => {
if (e.animationName === 'fade-outx') {
article.remove();
}
});
const root = document.querySelector(':root');
root.style.setProperty('--variable', '5s');
const article = document.createElement('article');
x = article;
article.className = 'msg-container';
const placeMessage = `
<div class="thomas bounce-in">
<div class="message"></div>
<div class="sender"></div>
<div class="speechbubble"></div>
<div class="arrow"></div>
</div>
`.trim();
article.innerHTML = placeMessage;
const msg = article.querySelector('.message');
msg.innerHTML = `<div class="sender">${username}</div>`; //\n${message}`;
msg.style.fontSize = '12pt';
showChatMessage(article);
const elements = document.getElementsByClassName('msg-container');
if (elements.length > 1) {
if (elements.length > 1) {
elements[0].classList.add('fade-outxx');
elements[0].addEventListener('animationend', e => {
if (e.animationName === 'fade-outxx') {
elements[0].remove();
}
article.addEventListener('animationend', (e) => {
if (e.animationName == 'fade-outx') {
article.remove();
}
}
});
if (elements.length > 1) {
elements[0].classList.add('fade-outxx');
elements[0].addEventListener('animationend', (e) => {
if (e.animationName == 'fade-outxx') {
elements[0].remove();
}
});
}
// fullMessageLength = getFullMessageLength(messageObject);
messageStream = messageObject.filtered;
textStreamContainer = document.querySelector('.message');
streamText();
}
// fullMessageLength = getFullMessageLength(messageObject);
messageStream = messageObject.filtered;
textStreamContainer = document.querySelector('.message');
streamText();
}
// // Receive a message from the server
socket.on('message', (logoUrl, username, message, messageDuration) => {
displayTwitchMessage(logoUrl, username, message);
displayTwitchMessage(logoUrl, username, message);
});

View file

@ -20,8 +20,8 @@ body {
padding-left: 40dip;
padding-right: 10dip;
vertical-align: top;
foreground-repeat: no-repeat;
foreground-position: 16dip 50%;
/* foreground-repeat: no-repeat;
foreground-position: 16dip 50%; */
background-repeat: no-repeat;
background-position: 10dip 50%;
background-size: 64dip 64dip;
@ -37,7 +37,7 @@ body {
.title {
font-weight: bold !important;
flow: column !important;
/* flow: column !important; */
text-align: left !important;
margin: auto !important;
width: min-content !important;
@ -86,12 +86,12 @@ img {
}
#button-bar {
flow: horizontal;
/* flow: horizontal; */
padding: 10dip;
border-spacing: 10dip;
margin: 0;
flow: horizontal;
horizontal-align: right;
/* flow: horizontal; */
/* horizontal-align: right; */
}
label {
@ -103,7 +103,7 @@ label {
text-shadow: #fff 0px 1px;
min-width: 4em;
line-height: 2em;
vertical-align: middle;
/* vertical-align: middle; */
width: min-intrinsic;
text-align: center;
}
}

View file

@ -1,386 +1,380 @@
* {
box-sizing: border-box;
image-rendering: optimize-quality;
box-sizing: border-box;
image-rendering: optimize-quality;
}
:disabled {
display: none;
display: none;
}
html {
overflow: hidden;
overflow: hidden;
}
body {
margin: 0;
padding: 16dip;
position: relative;
background-image: url('../png/grid.png');
background-size: 70px;
background-repeat: repeat;
margin: 0;
padding: 16dip;
position: relative;
background-image: url('../png/grid.png');
background-size: 70px;
background-repeat: repeat;
}
#controls {
height: 100%;
width: 100%;
/* flow: horizontal; */
overflow: hidden;
display: flex;
height: 100%;
width: 100%;
/* flow: horizontal; */
overflow: hidden;
display: flex;
}
#controls-left {
order: 2px solid cyan;
width: 7%;
height: 10%;
/* flow: vertical; */
border-spacing: 1%;
order: 2px solid cyan;
width: 7%;
height: 10%;
/* flow: vertical; */
border-spacing: 1%;
}
#controls-top {
height: 10%;
width: 100%;
/* flow: horizontal; */
border-spacing: 1%;
margin-left: 10px;
height: 10%;
width: 100%;
/* flow: horizontal; */
border-spacing: 1%;
margin-left: 10px;
}
#meters {
display: flex;
height: 50vh;
/* flow: horizontal; */
/* border-spacing: *; */
position: relative;
display: flex;
height: 50vh;
/* flow: horizontal; */
/* border-spacing: *; */
position: relative;
}
#meter-microphone {
margin-left: 10%;
margin-right: 10%;
margin-left: 10%;
margin-right: 10%;
}
#meter-delay {
margin-right: 20%;
margin-right: 20%;
}
#meter-microphone,
#meter-delay {
height: 45vh;
width: 25%;
background-repeat: no-repeat;
height: 45vh;
width: 25%;
background-repeat: no-repeat;
}
input[type='vslider']>button.slider {
background-size: 100% auto;
border-radius: 0;
width: 275%;
height: 5%;
background-position: 0 50%;
border-width: 0;
input[type='vslider'] > button.slider {
background-size: 100% auto;
border-radius: 0;
width: 275%;
height: 5%;
background-position: 0 50%;
border-width: 0;
}
button {
height: 50px;
width: 50px;
background-size: cover;
overflow: hidden;
border-style: none;
background-color: transparent;
height: 50px;
width: 50px;
background-size: cover;
overflow: hidden;
border-style: none;
background-color: transparent;
}
#meter-microphone>button.slider {
background-image: url('../png/controls/meters/left.png');
#meter-microphone > button.slider {
background-image: url('../png/controls/meters/left.png');
}
#meter-delay>button.slider {
background-image: url('../png/controls/meters/right.png');
#meter-delay > button.slider {
background-image: url('../png/controls/meters/right.png');
}
#meter-microphone {
color: #2ebe38;
color: #2ebe38;
}
#meter-delay {
color: #453be2;
color: #453be2;
}
#slider-left {
left: -25%;
left: -25%;
}
#slider-right {
right: -25%;
right: -25%;
}
#buttons-left {
width: 100%;
height: 100%;
box-shadow: none;
width: 100%;
height: 100%;
box-shadow: none;
}
#buttons-top {
height: 50px;
width: 100%;
display: flex;
height: 50px;
width: 100%;
display: flex;
}
#buttons-left button {
width: 50;
height: 50px;
display: flex;
background-size: cover;
overflow: hidden;
width: 50;
height: 50px;
display: flex;
background-size: cover;
overflow: hidden;
}
#buttons-top button {
background-size: auto 100%;
background-position: 50% 50%;
border-width: 0;
border-radius: 0;
background-size: auto 100%;
background-position: 50% 50%;
border-width: 0;
border-radius: 0;
}
#buttons-left button:active,
#about:active,
#close:active {
filter: brightness(-10%);
filter: brightness(-10%);
}
popup[role='tooltip'] {
color: white;
background: rgba(0, 0, 0, 0.666);
border-width: 0;
padding: 0.333em;
font-size: 1.25em;
font-family: Calibri;
color: white;
background: rgba(0, 0, 0, 0.666);
border-width: 0;
padding: 0.333em;
font-size: 1.25em;
font-family: Calibri;
}
#microphone-device {
background-image: url('../png/controls/buttons/left/mic.png');
background-image: url('../png/controls/buttons/left/mic.png');
}
#background-color {
background-image: url('../png/controls/buttons/left/bg.png');
background-image: url('../png/controls/buttons/left/bg.png');
}
#open-file {
background-image: url('../png/controls/buttons/left/open.png');
background-image: url('../png/controls/buttons/left/open.png');
}
#save-file {
background-image: url('../png/controls/buttons/left/save.png');
background-image: url('../png/controls/buttons/left/save.png');
}
/* TOP ROW BUTTONS */
button.mouth-image.border-default {
background-image: url('../png/controls/buttons/top/avatar-change/border/default.png');
position: absolute;
width: 50px;
height: 50px;
top: 0;
bottom: 0;
background-image: url('../png/controls/buttons/top/avatar-change/border/default.png');
position: absolute;
width: 50px;
height: 50px;
top: 0;
bottom: 0;
}
button.mouth-image.border-add {
background-image: url('../png/controls/buttons/top/avatar-change/border/add.png');
background-image: url('../png/controls/buttons/top/avatar-change/border/add.png');
}
button.mouth-image:active {
filter: brightness(-10%);
filter: brightness(-10%);
}
button.mouth-image::before {
position: absolute;
top: 5%;
right: 10%;
width: 50px;
height: 66%;
background-size: 100% 100%;
z-index: -1;
position: absolute;
top: 5%;
right: 10%;
width: 50px;
height: 66%;
background-size: 100% 100%;
z-index: -1;
}
button.mouth-image::after {
position: absolute;
top: 69%;
left: 37%;
width: 50px;
height: 35%;
transform: translate(-50%, -50%);
background-size: 100% 100%;
position: absolute;
top: 69%;
left: 37%;
width: 50px;
height: 35%;
transform: translate(-50%, -50%);
background-size: 100% 100%;
}
#closed-mouth-image.border-default::before {
background-image: url('../png/avatars/crab/closed.png');
background-image: url('../png/avatars/crab/closed.png');
}
#closed-mouth-image::after {
background-image: url('../png/controls/buttons/top/avatar-change/closed.png');
background-image: url('../png/controls/buttons/top/avatar-change/closed.png');
}
#open-mouth-image.border-default::before {
background-image: url('../png/avatars/crab/open.png');
background-image: url('../png/avatars/crab/open.png');
}
#open-mouth-image::after {
background-image: url('../png/controls/buttons/top/avatar-change/open.png');
background-image: url('../png/controls/buttons/top/avatar-change/open.png');
}
#closed-mouth-blinking-image.border-default::before {
background-image: url('../png/avatars/crab/closed-blink.png');
background-image: url('../png/avatars/crab/closed-blink.png');
}
#closed-mouth-blinking-image::after {
background-image: url('../png/controls/buttons/top/avatar-change/closed-blink.png');
background-image: url('../png/controls/buttons/top/avatar-change/closed-blink.png');
}
#open-mouth-blinking-image.border-default::before {
background-image: url('../png/avatars/crab/open-blink.png');
background-image: url('../png/avatars/crab/open-blink.png');
}
#open-mouth-blinking-image::after {
background-image: url('../png/controls/buttons/top/avatar-change/open-blink.png');
background-image: url('../png/controls/buttons/top/avatar-change/open-blink.png');
}
button.motion {
/* background-image: url('../png/controls/buttons/top/motion/template.png'); */
position: relative;
/* var(x): 0dip;
/* background-image: url('../png/controls/buttons/top/motion/template.png'); */
position: relative;
/* var(x): 0dip;
var(y): height(33%); */
height: 50px;
width: 50px;
height: 50px;
width: 50px;
}
.closed-mouth-motion {
background-image: url('../png/controls/buttons/top/motion/closed.png');
box-sizing: border-box;
image-rendering: optimize-quality;
height: 50px;
width: 50px;
background-color: #9bccd4;
background-image: url('../png/controls/buttons/top/motion/closed.png');
box-sizing: border-box;
image-rendering: optimize-quality;
height: 50px;
width: 50px;
background-color: #9bccd4;
}
.test {
width: 50px;
height: 50px;
background-size: cover;
overflow: hidden;
width: 50px;
height: 50px;
background-size: cover;
overflow: hidden;
}
.avatar-change {
background-image: url('../png/controls/buttons/top/avatar-change/border/default.png');
background-size: cover;
width: 50px;
height: 50px;
position: relative;
left: -5px;
background-image: url('../png/controls/buttons/top/avatar-change/border/default.png');
background-size: cover;
width: 50px;
height: 50px;
position: relative;
left: -5px;
}
.border {
background-image: url('../png/controls/buttons/top/motion/border.png');
background-size: cover;
width: 50px;
height: 50px;
position: relative;
top: -50px;
background-image: url('../png/controls/buttons/top/motion/border.png');
background-size: cover;
width: 50px;
height: 50px;
position: relative;
top: -50px;
}
.open-mouth-motion {
background-image: url('../png/controls/buttons/top/motion/open.png');
box-sizing: border-box;
image-rendering: optimize-quality;
height: 50px;
width: 50px;
background-image: url('../png/controls/buttons/top/motion/open.png');
box-sizing: border-box;
image-rendering: optimize-quality;
height: 50px;
width: 50px;
}
.mouth-transition {
background-image: url('../png/controls/buttons/top/avatar-change/border/default.png');
box-sizing: border-box;
image-rendering: optimize-quality;
height: 50px;
width: 50px;
background-position: 50% 50%;
background-color: yellow;
background-image: url('../png/controls/buttons/top/avatar-change/border/default.png');
box-sizing: border-box;
image-rendering: optimize-quality;
height: 50px;
width: 50px;
background-position: 50% 50%;
background-color: yellow;
}
.mouth-transitionx {
background-image: url('../png/controls/buttons/top/avatar-change/border/default.png');
box-sizing: border-box;
image-rendering: optimize-quality;
height: 50px;
width: 50px;
background-position: 50% 50%;
background-color: red;
background-image: url('../png/controls/buttons/top/avatar-change/border/default.png');
box-sizing: border-box;
image-rendering: optimize-quality;
height: 50px;
width: 50px;
background-position: 50% 50%;
background-color: red;
}
#mouth-transition::before {
background-color: red;
background-image: url('../png/controls/buttons/top/avatar-change/border/default.png');
background-color: red;
background-image: url('../png/controls/buttons/top/avatar-change/border/default.png');
}
button.motion::before {
position: absolute;
width: 50px;
height: 90%;
background-size: 105% 105%;
transform: translate(-11%, -11%);
/* background-position: -50% -50%; */
overflow: hidden;
z-index: -1;
/* hit-margin: -999px; */
position: absolute;
width: 50px;
height: 90%;
background-size: 105% 105%;
transform: translate(-11%, -11%);
/* background-position: -50% -50%; */
overflow: hidden;
z-index: -1;
/* hit-margin: -999px; */
}
#closed-mouth-motion::before {
background-color: #2ebe38;
background-color: #2ebe38;
}
#open-mouth-motion::before {
background-image: url('../png/controls/buttons/top/motion/open.png');
height: 50px;
width: 50px;
pointer-events: none;
background-color: yellow;
background-image: url('../png/controls/buttons/top/motion/open.png');
height: 50px;
width: 50px;
pointer-events: none;
background-color: yellow;
}
button.motion::after {
position: absolute;
width: 80%;
height: 80%;
background-color: red;
overflow: hidden;
z-index: -1;
transform: translate(11%, 11%);
/* hit-margin: -999px; */
position: absolute;
width: 80%;
height: 80%;
background-color: red;
overflow: hidden;
z-index: -1;
transform: translate(11%, 11%);
/* hit-margin: -999px; */
}
#set-hotkey {
background-image: url('../png/controls/buttons/top/hotkey/set/default.png');
background-image: url('../png/controls/buttons/top/hotkey/set/default.png');
}
#hotkey-mode {
background-image: url('../png/controls/buttons/top/hotkey/mode/0.png');
background-image: url('../png/controls/buttons/top/hotkey/mode/0.png');
}
#delete-state {
background-image: url('../png/controls/buttons/right/delete.png');
background-image: url('../png/controls/buttons/right/delete.png');
}
#about {
background-image: url('../png/controls/buttons/top/info.png');
background-image: url('../png/controls/buttons/top/info.png');
}
#close {
background-image: url('../png/controls/buttons/top/close.png');
background-image: url('../png/controls/buttons/top/close.png');
}
#avatar {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 40%;
height: width(100%);
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 40%;
height: width(100%);
}
menu.popup {
/* box-shadow: 3px 3px 1px rgba(0, 0, 0, 0.692); */
}

View file

@ -38,4 +38,4 @@
<script src="../js/about.js"></script>
</body>
</html>
</html>

View file

@ -1,76 +1,76 @@
<!DOCTYPE html>
<!doctype html>
<html window-frame="transparent">
<head>
<title>TransTube</title>
<script
src="https://code.jquery.com/jquery-3.7.0.min.js"
integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g="
crossorigin="anonymous"
></script>
<link href="./css/main.css" rel="stylesheet" />
<script src="./js/main.js" type="module"></script>
</head>
<head>
<title>TransTube</title>
<script
src="https://code.jquery.com/jquery-3.7.0.min.js"
integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g="
crossorigin="anonymous"
></script>
<link href="./css/main.css" rel="stylesheet" />
<script src="./js/main.js" type="module"></script>
</head>
<body>
<img id="avatar" src="./png/avatars/crab/closed.png" width="900" height="900" />
<div id="controls">
<div id="controls-left">
<div id="meters">
<input id="meter-microphone" value="50" min="0" max="100" title="microphone volume sensitivity" />
<input id="meter-delay" value="95" min="0" max="100" title="microphone delay sensitivity" />
</div>
<div id="buttons-left">
<button id="microphone-device" title="microphone device"></button>
<button id="background-color" title="background color"></button>
<button id="open-file" title="open file"></button>
<button id="save-file" title="save file"></button>
</div>
</div>
<div id="controls-top">
<div id="buttons-top">
<div>
<button class="mouth-image.border-default" id="closed-mouth-image" title="closed mouth image">
<div class="avatar-change"></div>
</button>
</div>
<div>
<button class="mouth-image.border-default" id="open-mouth-image" title="open mouth image">
<div class="avatar-change"></div>
</button>
</div>
<div>
<button class="mouth-image.border-default" id="closed-mouth-blinking-image" title="closed mouth blinking image">
<div class="avatar-change"></div>
</button>
</div>
<div>
<button class="mouth-image.border-default" id="open-mouth-blinking-image" title="open mouth blinking image">
<div class="avatar-change"></div>
</button>
</div>
<div>
<button class="motion closed-mouth-motion" id="closed-mouth-motion" title="closed mouth motion" counter="1"></button>
<div class="border"></div>
</div>
<div>
<button class="motion open-mouth-motion" id="open-mouth-motion" title="open mouth motion" counter="4"></button>
<div class="border"></div>
</div>
<div>
<button class="motion mouth-transition" id="mouth-transition" title="mouth transition"></button>
<div class="border"></div>
</div>
<button id="set-hotkey" title="set hotkey"></button>
<button id="hotkey-mode" title="hotkey mode"></button>
<button id="delete-state" title="delete state"></button>
</div>
</div>
<body>
<img id="avatar" src="./png/avatars/crab/closed.png" width="900" height="900" />
<div id="controls">
<div id="controls-left">
<div id="meters">
<input id="meter-microphone" value="50" min="0" max="100" title="microphone volume sensitivity" />
<input id="meter-delay" value="95" min="0" max="100" title="microphone delay sensitivity" />
</div>
<div id="buttons-left">
<button id="microphone-device" title="microphone device"></button>
<button id="background-color" title="background color"></button>
<button id="open-file" title="open file"></button>
<button id="save-file" title="save file"></button>
</div>
</div>
<div id="controls-top">
<div id="buttons-top">
<div>
<button class="mouth-image.border-default" id="closed-mouth-image" title="closed mouth image">
<div class="avatar-change"></div>
</button>
</div>
<div>
<button class="mouth-image.border-default" id="open-mouth-image" title="open mouth image">
<div class="avatar-change"></div>
</button>
</div>
<div>
<button class="mouth-image.border-default" id="closed-mouth-blinking-image" title="closed mouth blinking image">
<div class="avatar-change"></div>
</button>
</div>
<div>
<button class="mouth-image.border-default" id="open-mouth-blinking-image" title="open mouth blinking image">
<div class="avatar-change"></div>
</button>
</div>
<div>
<button class="motion closed-mouth-motion" id="closed-mouth-motion" title="closed mouth motion" counter="1"></button>
<div class="border"></div>
</div>
<div>
<button class="motion open-mouth-motion" id="open-mouth-motion" title="open mouth motion" counter="4"></button>
<div class="border"></div>
</div>
<div>
<button class="motion mouth-transition" id="mouth-transition" title="mouth transition"></button>
<div class="border"></div>
</div>
<menu class="popup">
<li id="change-image">change image</li>
<li id="remove-image">remove image</li>
</menu>
</body>
<button id="set-hotkey" title="set hotkey"></button>
<button id="hotkey-mode" title="hotkey mode"></button>
<button id="delete-state" title="delete state"></button>
</div>
</div>
</div>
<menu class="popup">
<li id="change-image">change image</li>
<li id="remove-image">remove image</li>
</menu>
</body>
</html>

View file

@ -10,311 +10,270 @@ globalThis.MOUTH_IS_OPEN = false;
globalThis.BLINKING = false;
function avatarBlink() {
const state = globalThis.BLINKING ? './png/avatars/crab/closed-blink.png' : './png/avatars/crab/closed.png';
return state;
const state = globalThis.BLINKING ? './png/avatars/crab/closed-blink.png' : './png/avatars/crab/closed.png';
return state;
}
function avatarTalk() {
let state = globalThis.MOUTH_IS_OPEN ? './png/avatars/crab/open.png' : './png/avatars/crab/closed.png';
if (globalThis.BLINK) {
state = './png/avatars/crab/open-blink.png';
}
let state = globalThis.MOUTH_IS_OPEN ? './png/avatars/crab/open.png' : './png/avatars/crab/closed.png';
if (globalThis.BLINK) {
state = './png/avatars/crab/open-blink.png';
}
return state;
return state;
}
function adjustWindow() {
// const [width, height] = Window.this.screenBox('frame', 'dimension');
// const w = width * 0.666;
// const h = height * 0.666;
// Window.this.move(width / 2 - w / 2, height / 2 - h / 2, w, h, true);
// const [width, height] = Window.this.screenBox('frame', 'dimension');
// const w = width * 0.666;
// const h = height * 0.666;
// Window.this.move(width / 2 - w / 2, height / 2 - h / 2, w, h, true);
}
adjustWindow();
async function monitorDelay() {
setInterval(() => { });
setInterval(() => {});
}
monitorDelay();
function animate() {
let avatarCurr = $('#avatar').src;
let avatarCurr = $('#avatar').src;
const animation = globalThis.MOUTH_IS_OPEN
? globalThis.CHOSEN_OPEN_MOUTH_ANIMATION
: globalThis.CHOSEN_CLOSED_MOUTH_ANIMATION;
const animation = globalThis.MOUTH_IS_OPEN ? globalThis.CHOSEN_OPEN_MOUTH_ANIMATION : globalThis.CHOSEN_CLOSED_MOUTH_ANIMATION;
globalThis.ANIMATION = animation;
globalThis.ANIMATION = animation;
clearInterval(globalThis.ANIMATION_INTERVAL);
clearInterval(globalThis.ANIMATION_INTERVAL);
globalThis.ANIMATION_INTERVAL = setInterval(() => {
// console.log(globalThis.ANIMATION_INTERVAL);
const t = Date.now() / 1000;
const { x, y } = MOTION[animation](t);
const left = -50 - x * 5;
const top = -50 - y * 5;
document.body.querySelector('#avatar').style.transform = `translate(${left}%, ${top}%)`;
globalThis.ANIMATION_INTERVAL = setInterval(() => {
// console.log(globalThis.ANIMATION_INTERVAL);
const t = Date.now() / 1000;
const { x, y } = MOTION[animation](t);
const left = -50 - x * 5;
const top = -50 - y * 5;
document.body.querySelector('#avatar').style.transform = `translate(${left}%, ${top}%)`;
avatarCurr = $('#avatar').src;
avatarCurr = $('#avatar').src;
if (globalThis.MOUTH_IS_OPEN === false) {
// const avatarNew = avatarCurr.replace('open', 'closed');
if (globalThis.MOUTH_IS_OPEN === false) {
// const avatarNew = avatarCurr.replace('open', 'closed');
const avatarNew = avatarTalk();
const avatarNew = avatarTalk();
if (avatarNew !== avatarCurr) {
$('#avatar').src = avatarNew;
avatarCurr = avatarNew;
}
} else {
// const avatarNew = avatarCurr.replace('closed', 'open');
if (avatarNew !== avatarCurr) {
$('#avatar').src = avatarNew;
avatarCurr = avatarNew;
}
} else {
// const avatarNew = avatarCurr.replace('closed', 'open');
const avatarNew = avatarBlink();
const avatarNew = avatarBlink();
if (avatarNew !== avatarCurr) {
$('#avatar').src = avatarNew;
avatarCurr = avatarNew;
}
}
});
if (avatarNew !== avatarCurr) {
$('#avatar').src = avatarNew;
avatarCurr = avatarNew;
}
}
});
}
animate();
function updateMeter() {
setInterval(() => {
// let volume = 50//Window.this.xcall('get_volume');
const volume = 100;
document.body.querySelector('#meter-microphone').value = volume;
setInterval(() => {
// let volume = 50//Window.this.xcall('get_volume');
const volume = 100;
document.body.querySelector('#meter-microphone').value = volume;
const delayVolume = document.body.querySelector('#meter-delay').value;
const delaySlider = document.body.querySelector('#meter-delay').value;
const delayVolume = document.body.querySelector('#meter-delay').value;
const delaySlider = document.body.querySelector('#meter-delay').value;
const microphoneVolume = document.body.querySelector('#meter-microphone').value;
const microphoneSlider = document.body.querySelector('#meter-microphone').value;
const microphoneVolume = document.body.querySelector('#meter-microphone').value;
const microphoneSlider = document.body.querySelector('#meter-microphone').value;
if (microphoneVolume > microphoneSlider) {
document.body.querySelector('#meter-delay').value = 100;
} else {
document.body.querySelector('#meter-delay').value = Math.max(0, delayVolume - 1);
}
if (microphoneVolume > microphoneSlider) {
document.body.querySelector('#meter-delay').value = 100;
} else {
document.body.querySelector('#meter-delay').value = Math.max(0, delayVolume - 1);
}
if (delayVolume > delaySlider) {
if (globalThis.MOUTH_IS_OPEN === false) {
globalThis.MOUTH_IS_OPEN = true;
// console.log('monitor delay => delayVolume > delaySlider');
$('#avatar').src = avatarTalk();
if (delayVolume > delaySlider) {
if (globalThis.MOUTH_IS_OPEN === false) {
globalThis.MOUTH_IS_OPEN = true;
// console.log('monitor delay => delayVolume > delaySlider');
$('#avatar').src = avatarTalk();
animate();
}
} else if (globalThis.MOUTH_IS_OPEN === true) {
globalThis.MOUTH_IS_OPEN = false;
// console.log('monitor delay => else');
$('#avatar').src = avatarTalk();
animate();
}
});
animate();
}
} else if (globalThis.MOUTH_IS_OPEN === true) {
globalThis.MOUTH_IS_OPEN = false;
// console.log('monitor delay => else');
$('#avatar').src = avatarTalk();
animate();
}
});
}
updateMeter();
function animateButton(button, animation = 'motionless') {
clearInterval(button.BUTTON_INTERVAL);
const animatedButton = document.body.querySelector(button);
animatedButton.BUTTON_INTERVAL = setInterval(() => {
const t = Date.now() / 1000;
const { x, y } = MOTION[animation](t);
const left = 50 + x * 50;
const top = 50 - y * 50;
clearInterval(button.BUTTON_INTERVAL);
const animatedButton = document.body.querySelector(button);
animatedButton.BUTTON_INTERVAL = setInterval(() => {
const t = Date.now() / 1000;
const { x, y } = MOTION[animation](t);
const left = 50 + x * 50;
const top = 50 - y * 50;
// animatedButton
// animatedButton
// animatedButton.style.x = left;
// animatedButton.style.y = (top + 500) * -1;
// animatedButton.style.transform = `translate(${left}%, -${top + 500}%)`;
});
// animatedButton.style.x = left;
// animatedButton.style.y = (top + 500) * -1;
// animatedButton.style.transform = `translate(${left}%, -${top + 500}%)`;
});
}
animateButton(
'#closed-mouth-motion',
globalThis.CHOSEN_CLOSED_MOUTH_ANIMATION,
);
animateButton('#closed-mouth-motion', globalThis.CHOSEN_CLOSED_MOUTH_ANIMATION);
animateButton('#open-mouth-motion', globalThis.CHOSEN_OPEN_MOUTH_ANIMATION);
function blink() {
let avatarCurr = $('#avatar').src;
setInterval(() => {
avatarCurr = $('#avatar').src;
const n = Date.now() % 3200;
if (n > 3000) {
globalThis.BLINKING = true;
const avatarNew = avatarBlink();
if (avatarCurr !== avatarNew) {
document.body.querySelector('#avatar').src = avatarNew;
avatarCurr = avatarNew;
}
} else {
globalThis.BLINKING = false;
// const avatarNew = avatarCurr.replace('-blink', '');
const avatarNew = avatarBlink();
if (avatarCurr !== avatarNew) {
document.body.querySelector('#avatar').src = avatarNew;
avatarCurr = avatarNew;
}
}
});
let avatarCurr = $('#avatar').src;
setInterval(() => {
avatarCurr = $('#avatar').src;
const n = Date.now() % 3200;
if (n > 3000) {
globalThis.BLINKING = true;
const avatarNew = avatarBlink();
if (avatarCurr !== avatarNew) {
document.body.querySelector('#avatar').src = avatarNew;
avatarCurr = avatarNew;
}
} else {
globalThis.BLINKING = false;
// const avatarNew = avatarCurr.replace('-blink', '');
const avatarNew = avatarBlink();
if (avatarCurr !== avatarNew) {
document.body.querySelector('#avatar').src = avatarNew;
avatarCurr = avatarNew;
}
}
});
}
blink();
async function _cycleAnimations() {
const animations = Object.keys(MOTION);
let i = 0;
while (true) {
const key = animations[i % animations.length];
animate(key);
Window.this.caption = key;
i++;
await new Promise((r) => setTimeout(r, 2000));
}
const animations = Object.keys(MOTION);
let i = 0;
while (true) {
const key = animations[i % animations.length];
animate(key);
Window.this.caption = key;
i++;
await new Promise(resolve => setTimeout(resolve, 2000));
}
}
$(document).on(
'~mousedown',
'#closed-mouth-motion, #open-mouth-motion',
motionButtonEvent,
);
$(document).on('~mousedown', '#closed-mouth-motion, #open-mouth-motion', motionButtonEvent);
$(document).on(
'~doubleclick',
'#closed-mouth-motion, #open-mouth-motion',
motionButtonEvent,
);
$(document).on('~doubleclick', '#closed-mouth-motion, #open-mouth-motion', motionButtonEvent);
function motionButtonEvent(evt) {
const { target: button } = evt;
button.attributes.counter = button.attributes.counter || 0;
const { target: button } = evt;
button.attributes.counter = button.attributes.counter || 0;
if (evt.button === 1) {
button.attributes.counter++;
} else if (evt.button === 2) {
button.attributes.counter--;
}
if (evt.button === 1) {
button.attributes.counter++;
} else if (evt.button === 2) {
button.attributes.counter--;
}
const color = [
'white',
'#9BCCD4',
'#8087D6',
'#AB65CF',
'#E7FD5B',
'#EC9F45',
'#E24555',
][mod(button.attributes.counter, 7)];
button.style.variable('color', color);
const color = ['white', '#9BCCD4', '#8087D6', '#AB65CF', '#E7FD5B', '#EC9F45', '#E24555'][mod(button.attributes.counter, 7)];
button.style.variable('color', color);
const animation = [
'motionless',
'vibing',
'shaking',
'shakingMore',
'bouncy',
'excited',
'nervous',
][mod(button.attributes.counter, 7)];
const animation = ['motionless', 'vibing', 'shaking', 'shakingMore', 'bouncy', 'excited', 'nervous'][mod(button.attributes.counter, 7)];
animateButton(button, animation);
animateButton(button, animation);
if (button.id === 'closed-mouth-motion') {
globalThis.CHOSEN_CLOSED_MOUTH_ANIMATION = animation;
animate();
} else if (button.id === 'open-mouth-motion') {
globalThis.CHOSEN_OPEN_MOUTH_ANIMATION = animation;
animate();
}
if (button.id === 'closed-mouth-motion') {
globalThis.CHOSEN_CLOSED_MOUTH_ANIMATION = animation;
animate();
} else if (button.id === 'open-mouth-motion') {
globalThis.CHOSEN_OPEN_MOUTH_ANIMATION = animation;
animate();
}
}
function animateMouthButton() {
setInterval(() => {
const n = Date.now() % 1200;
document.body.querySelector('.mouth-transition').style.backgroundImage = `url('../vtuber/png/controls/buttons/top/motion/${n > 600 ? 'open' : 'closed'
}.png')`;
});
setInterval(() => {
const n = Date.now() % 1200;
document.body.querySelector('.mouth-transition').style.backgroundImage = `url('../vtuber/png/controls/buttons/top/motion/${
n > 600 ? 'open' : 'closed'
}.png')`;
});
}
animateMouthButton();
function mod(n, m) {
return ((n % m) + m) % m;
return ((n % m) + m) % m;
}
globalThis.CURRENT_BUTTON = null;
$(document).on(
'click',
'.mouth-image.border-default:not(:first-of-type)',
(evt, el) => {
globalThis.CURRENT_BUTTON = el;
el.popup($('menu'));
},
);
$(document).on('click', '.mouth-image.border-default:not(:first-of-type)', (evt, el) => {
globalThis.CURRENT_BUTTON = el;
el.popup($('menu'));
});
function loadImage() {
const filename = Window.this.selectFile({
mode: 'open',
filter:
'image files (*.bmp,*.dib,*.gif,*.png,*.apng,*.jpg,*.jpeg,*.jiff)|*.bmp;*.dib;*.gif;*.png;*.apng;*.jpg;*.jpeg;*.jiff',
caption: 'select image for closed mouth...',
});
return filename;
const filename = Window.this.selectFile({
mode: 'open',
filter: 'image files (*.bmp,*.dib,*.gif,*.png,*.apng,*.jpg,*.jpeg,*.jiff)|*.bmp;*.dib;*.gif;*.png;*.apng;*.jpg;*.jpeg;*.jiff',
caption: 'select image for closed mouth...'
});
return filename;
}
function changeImage(evt, el) {
// setTimeout to avoid context menu popping up
setTimeout(() => {
if (el.matches('.mouth-image:first-of-type, .mouth-image.border-add')) {
globalThis.CURRENT_BUTTON = el;
}
// setTimeout to avoid context menu popping up
setTimeout(() => {
if (el.matches('.mouth-image:first-of-type, .mouth-image.border-add')) {
globalThis.CURRENT_BUTTON = el;
}
const filename = loadImage();
if (!filename) return;
const which = globalThis.CURRENT_BUTTON.id
.match(/closed|open|blink/g)
.join('-');
document.style.variable(which, `url('${filename}')`);
const filename = loadImage();
if (!filename) return;
const which = globalThis.CURRENT_BUTTON.id.match(/closed|open|blink/g).join('-');
document.style.variable(which, `url('${filename}')`);
globalThis.CURRENT_BUTTON.classList.add('border-default');
globalThis.CURRENT_BUTTON.classList.remove('border-add');
});
globalThis.CURRENT_BUTTON.classList.add('border-default');
globalThis.CURRENT_BUTTON.classList.remove('border-add');
});
}
$(document).on(
'click',
'.mouth-image:first-of-type, .mouth-image.border-add',
changeImage,
);
$(document).on('click', '.mouth-image:first-of-type, .mouth-image.border-add', changeImage);
$(document).on('click', '#change-image', changeImage);
$(document).on('click', '#remove-image', (evt, el) => {
globalThis.CURRENT_BUTTON.classList.remove('border-default');
globalThis.CURRENT_BUTTON.classList.add('border-add');
globalThis.CURRENT_BUTTON.classList.remove('border-default');
globalThis.CURRENT_BUTTON.classList.add('border-add');
const which = globalThis.CURRENT_BUTTON.id
.match(/closed|open|blink/g)
.join('-');
document.style.variable(which, null);
const which = globalThis.CURRENT_BUTTON.id.match(/closed|open|blink/g).join('-');
document.style.variable(which, null);
});
movableView('body');
// Window.this.on('activate', (evt) => {
// if (evt.reason === 0) {
// document.classList.add('transparent');
// } else {
// document.classList.remove('transparent');
// }
// if (evt.reason === 0) {
// document.classList.add('transparent');
// } else {
// document.classList.remove('transparent');
// }
// });
// setInterval(() => (Window.this.isTopmost = true));

View file

@ -1,65 +1,65 @@
import { pnoise1 } from './perlin_noise.js';
export default {
motionless,
vibing,
shaking,
shakingMore,
bouncy,
excited,
nervous,
motionless,
vibing,
shaking,
shakingMore,
bouncy,
excited,
nervous
};
function motionless(t) {
return { x: 0, y: 0 };
return { x: 0, y: 0 };
}
function clamp01(number) {
return Math.max(0, Math.min(number, 1));
return Math.max(0, Math.min(number, 1));
}
function _shake(t, amount, velocity) {
let num = clamp01(pnoise1(t * velocity, 0));
let num2 = clamp01(pnoise1(t * velocity, 50));
num = num * 2 - 1;
num2 = num2 * 2 - 1;
return {
x: amount * (num * Math.sqrt(1 - (num2 * num2) / 2)),
y: amount * (num2 * Math.sqrt(1 - (num * num) / 2)),
};
let num = clamp01(pnoise1(t * velocity, 0));
let num2 = clamp01(pnoise1(t * velocity, 50));
num = num * 2 - 1;
num2 = num2 * 2 - 1;
return {
x: amount * (num * Math.sqrt(1 - (num2 * num2) / 2)),
y: amount * (num2 * Math.sqrt(1 - (num * num) / 2))
};
}
function _jumpy(t, amountX, amountY, velocity) {
t *= velocity;
const num = t % 1;
const num2 = -8 * num * (num - 1) - 1;
let num3 = t % 2;
if (num3 > 1) {
num3 = 2 - num3;
}
return { x: (num3 * 2 - 1) * amountX, y: num2 * amountY };
t *= velocity;
const num = t % 1;
const num2 = -8 * num * (num - 1) - 1;
let num3 = t % 2;
if (num3 > 1) {
num3 = 2 - num3;
}
return { x: (num3 * 2 - 1) * amountX, y: num2 * amountY };
}
function vibing(t) {
return _shake(t, 0.7, 0.5);
return _shake(t, 0.7, 0.5);
}
function shaking(t) {
return _shake(t, 0.3, 10);
return _shake(t, 0.3, 10);
}
function shakingMore(t) {
return _shake(t, 1, 8);
return _shake(t, 1, 8);
}
function bouncy(t) {
return _jumpy(t, 0, 0.5, 1);
return _jumpy(t, 0, 0.5, 1);
}
function excited(t) {
return _jumpy(t, 0.5, 0.5, 2);
return _jumpy(t, 0.5, 0.5, 2);
}
function nervous(t) {
return _jumpy(t, 1, 1, 4);
}
return _jumpy(t, 1, 1, 4);
}

View file

@ -1,64 +1,65 @@
if (!Number.prototype.limit) {
Number.prototype.limit = function (min, max) {
if (this < min) return min;
if (this > max) return max;
return this;
};
// eslint-disable-next-line no-extend-native
Number.prototype.limit = function (min, max) {
if (this < min) return min;
if (this > max) return max;
return this;
};
}
function movableView(s, screenBound = false) {
let xoff; let yoff; let minXY; let maxX; let
maxY;
let dragging = false;
let xoff;
let yoff;
let minXY;
let maxX;
let maxY;
let dragging = false;
function screenBounds() {
if (screenBound) {
[maxX, maxY] = Window.this.screenBox('workarea', 'dimension');
const [w, h] = Window.this.box('dimension', 'border');
maxX -= w;
maxY -= h;
minXY = 0;
} else {
maxX = Number.MAX_SAFE_INTEGER;
maxY = Number.MAX_SAFE_INTEGER;
minXY = Number.MIN_SAFE_INTEGER;
}
}
function screenBounds() {
if (screenBound) {
[maxX, maxY] = Window.this.screenBox('workarea', 'dimension');
const [w, h] = Window.this.box('dimension', 'border');
maxX -= w;
maxY -= h;
minXY = 0;
} else {
maxX = Number.MAX_SAFE_INTEGER;
maxY = Number.MAX_SAFE_INTEGER;
minXY = Number.MIN_SAFE_INTEGER;
}
}
function onMouseDown(e) {
screenBounds();
e.target.state.capture(true);
function onMouseDown(e) {
screenBounds();
e.target.state.capture(true);
const [x, y] = Window.this.box('position', 'border', 'screen');
xoff = e.screenX - x;
yoff = e.screenY - y;
const [x, y] = Window.this.box('position', 'border', 'screen');
xoff = e.screenX - x;
yoff = e.screenY - y;
dragging = true;
}
dragging = true;
}
function onMouseMove(e) {
if (dragging) {
Window.this.move(
(e.screenX - xoff).limit(minXY, maxX),
(e.screenY - yoff).limit(minXY, maxY),
);
}
}
function onMouseMove(e) {
if (dragging) {
Window.this.move((e.screenX - xoff).limit(minXY, maxX), (e.screenY - yoff).limit(minXY, maxY));
}
}
function onMouseUp(e) {
if (dragging) {
dragging = false;
e.target.state.capture(false);
}
}
function onMouseUp(e) {
if (dragging) {
dragging = false;
e.target.state.capture(false);
}
}
const elements = document.querySelectorAll(s);
for (let i = 0; i < elements.length; ++i) {
// elements[i].on('mousedown', onMouseDown);
// elements[i].on('mousemove', onMouseMove);
// elements[i].on('mouseup', onMouseUp);
}
return !!elements.length;
const elements = document.querySelectorAll(s);
for (let i = 0; i < elements.length; ++i) {
// elements[i].on('mousedown', onMouseDown);
// elements[i].on('mousemove', onMouseMove);
// elements[i].on('mouseup', onMouseUp);
}
return !!elements.length;
}
// | Module export (uncomment bellow)

File diff suppressed because it is too large Load diff