diff --git a/Cargo.lock b/Cargo.lock
index 65ed3c4..1cbd499 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3123,6 +3123,7 @@ dependencies = [
name = "jarvis-core"
version = "0.1.0"
dependencies = [
+ "chrono",
"fluent",
"fluent-bundle",
"futures-util",
diff --git a/Cargo.toml b/Cargo.toml
index 4f6eb5c..eaa710e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -39,4 +39,5 @@ tokio-tungstenite = "0.28.0"
futures-util = "0.3"
fluent = "0.17.0"
fluent-bundle = "0.16.0"
-unic-langid = "0.9"
\ No newline at end of file
+unic-langid = "0.9"
+chrono = "0.4"
\ No newline at end of file
diff --git a/crates/jarvis-app/src/app.rs b/crates/jarvis-app/src/app.rs
index 53db821..ea2db38 100644
--- a/crates/jarvis-app/src/app.rs
+++ b/crates/jarvis-app/src/app.rs
@@ -1,26 +1,27 @@
+use std::sync::mpsc::Receiver;
use std::time::SystemTime;
-use jarvis_core::{audio, audio_processing, commands, config, listener, recorder, stt, COMMANDS_LIST, intent, ipc::{self, IpcEvent}};
+use jarvis_core::{audio, audio_processing, commands, config, listener, recorder, stt, COMMANDS_LIST, intent, voices, ipc::{self, IpcEvent}};
use rand::prelude::*;
use crate::should_stop;
-pub fn start() -> Result<(), ()> {
+pub fn start(text_cmd_rx: Receiver) -> Result<(), ()> {
// start the loop
- main_loop()
+ main_loop(text_cmd_rx)
}
-fn main_loop() -> Result<(), ()> {
+fn main_loop(text_cmd_rx: Receiver) -> Result<(), ()> {
let rt = tokio::runtime::Runtime::new().expect("Failed to create tokio runtime");
let mut start: SystemTime;
- let sounds_directory = audio::get_sound_directory().unwrap();
+ // let sounds_directory = audio::get_sound_directory().unwrap();
let frame_length: usize = 512; // default for every wake-word engine
let mut frame_buffer: Vec = vec![0; frame_length];
let mut silence_frames: u32 = 0;
- // play some run phrase
- // @TODO. Different sounds? Or better make it via commands or upcoming events system.
- audio::play_sound(&sounds_directory.join("run.wav"));
+ // play some startup phrase
+ // audio::play_sound(&sounds_directory.join("run.wav"));
+ voices::play_greet();
// start recording
match recorder::start_recording() {
@@ -39,10 +40,17 @@ fn main_loop() -> Result<(), ()> {
// check for stop signal
if should_stop() {
info!("Stop signal received, shutting down...");
+ voices::play_goodbye();
ipc::send(IpcEvent::Stopping);
break;
}
+ // check for text commands
+ if let Ok(text) = text_cmd_rx.try_recv() {
+ process_text_command(&text, &rt);
+ continue 'wake_word;
+ }
+
// read from microphone
recorder::read_microphone(&mut frame_buffer);
@@ -70,14 +78,10 @@ fn main_loop() -> Result<(), ()> {
start = SystemTime::now();
silence_frames = 0;
- // play some greet phrase
+ // play some reply phrase
// @TODO. Make it via commands or upcoming events system.
- audio::play_sound(&sounds_directory.join(format!(
- "{}.wav",
- config::ASSISTANT_GREET_PHRASES
- .choose(&mut rand::thread_rng())
- .unwrap()
- )));
+ voices::play_reply();
+
// notify GUI we're listening
ipc::send(IpcEvent::Listening);
@@ -125,12 +129,13 @@ fn main_loop() -> Result<(), ()> {
info!("Wake word detected during chaining, reactivating...");
// play greet sound
- audio::play_sound(&sounds_directory.join(format!(
- "{}.wav",
- config::ASSISTANT_GREET_PHRASES
- .choose(&mut rand::thread_rng())
- .unwrap()
- )));
+ // audio::play_sound(&sounds_directory.join(format!(
+ // "{}.wav",
+ // config::ASSISTANT_GREET_PHRASES
+ // .choose(&mut rand::thread_rng())
+ // .unwrap()
+ // )));
+ voices::play_reply();
// reset timer and continue listening
start = SystemTime::now();
@@ -152,59 +157,8 @@ fn main_loop() -> Result<(), ()> {
continue 'voice_recognition;
}
- // infer command (try intent recognition first, fallback to levenshtein)
- let cmd_result = if let Some((intent_id, confidence)) =
- rt.block_on(intent::classify(&recognized_voice))
- {
- info!("Intent recognized: {} (confidence: {:.2})", intent_id, confidence);
- intent::get_command_by_intent(COMMANDS_LIST.get().unwrap(), &intent_id)
- } else {
- info!("Intent not recognized, trying levenshtein fallback ...");
- commands::fetch_command(&recognized_voice, COMMANDS_LIST.get().unwrap())
- };
-
- if let Some((cmd_path, cmd_config)) = cmd_result {
- info!("Command found: {:?}", cmd_path);
- info!("Executing!");
-
- // execute the command
- match commands::execute_command(&cmd_path, &cmd_config) {
- Ok(chain) => {
- // success
- info!("Command executed successfully.");
-
- // notify GUI
- ipc::send(IpcEvent::CommandExecuted {
- id: cmd_config.id.clone(),
- success: true,
- });
-
- if chain {
- // chain commands
- start = SystemTime::now();
- } else {
- // skip, if chaining is not required
- start = start
- .checked_sub(core::time::Duration::from_secs(1000))
- .unwrap();
- }
-
- continue 'voice_recognition; // continue voice recognition
- }
- Err(msg) => {
- // fail
- error!("Error executing command: {}", msg);
-
- ipc::send(IpcEvent::CommandExecuted {
- id: cmd_config.id.clone(),
- success: false,
- });
- ipc::send(IpcEvent::Error {
- message: msg.to_string(),
- });
- }
- }
- }
+ // execute command (shared executor)
+ execute_command(&recognized_voice, &rt);
// return to wake-word listening after command execution (no matter successful or not)
break 'voice_recognition;
@@ -236,10 +190,93 @@ fn main_loop() -> Result<(), ()> {
Ok(())
}
+
+// process text command from GUI
+fn process_text_command(text: &str, rt: &tokio::runtime::Runtime) {
+ info!("Processing text command: {}", text);
+
+ ipc::send(IpcEvent::SpeechRecognized { text: text.to_string() });
+
+ // filter text same as voice
+ let mut filtered = text.to_lowercase();
+ for tbr in config::ASSISTANT_PHRASES_TBR {
+ filtered = filtered.replace(tbr, "");
+ }
+ let filtered = filtered.trim();
+
+ if filtered.is_empty() {
+ ipc::send(IpcEvent::Idle);
+ return;
+ }
+
+ execute_command(filtered, rt);
+}
+
+// shared command execution logic (manual & voice)
+fn execute_command(text: &str, rt: &tokio::runtime::Runtime) {
+ let commands_list = match COMMANDS_LIST.get() {
+ Some(c) => c,
+ None => {
+ ipc::send(IpcEvent::Error { message: "Commands not loaded".to_string() });
+ ipc::send(IpcEvent::Idle);
+ return;
+ }
+ };
+
+ // let sounds_directory = audio::get_sound_directory().unwrap();
+
+ // try intent recognition first, fallback to levenshtein
+ let cmd_result = if let Some((intent_id, confidence)) =
+ rt.block_on(intent::classify(text))
+ {
+ info!("Intent recognized: {} (confidence: {:.2})", intent_id, confidence);
+ intent::get_command_by_intent(commands_list, &intent_id)
+ } else {
+ info!("Intent not recognized, trying levenshtein fallback...");
+ commands::fetch_command(text, commands_list)
+ };
+
+ if let Some((cmd_path, cmd_config)) = cmd_result {
+ info!("Command found: {:?}", cmd_path);
+
+ match commands::execute_command(&cmd_path, &cmd_config) {
+ Ok(_) => {
+ info!("Command executed successfully");
+ voices::play_ok(); // command executed sound
+ ipc::send(IpcEvent::CommandExecuted {
+ id: cmd_config.id.clone(),
+ success: true,
+ });
+ }
+ Err(msg) => {
+ error!("Error executing command: {}", msg);
+ voices::play_error();
+ ipc::send(IpcEvent::CommandExecuted {
+ id: cmd_config.id.clone(),
+ success: false,
+ });
+ ipc::send(IpcEvent::Error { message: msg.to_string() });
+ }
+ }
+ } else {
+ info!("No command found for: {}", text);
+ // play "not understood" sound
+ // audio::play_sound(&sounds_directory.join("not_understand.wav"));
+ voices::play_not_found();
+ ipc::send(IpcEvent::Error {
+ message: format!("Command not found: {}", text)
+ });
+ }
+
+ ipc::send(IpcEvent::Idle);
+}
+
+
fn keyword_callback(keyword_index: i32) {}
pub fn close(code: i32) {
info!("Closing application.");
+ voices::play_goodbye();
ipc::send(IpcEvent::Stopping);
std::process::exit(code);
}
diff --git a/crates/jarvis-app/src/main.rs b/crates/jarvis-app/src/main.rs
index 30d45c2..3c72bcb 100644
--- a/crates/jarvis-app/src/main.rs
+++ b/crates/jarvis-app/src/main.rs
@@ -1,12 +1,13 @@
use parking_lot::RwLock;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::mpsc;
// include core
use jarvis_core::{
audio, audio_processing, commands, config, db, listener, recorder, stt, intent,
ipc::{self, IpcAction},
- i18n,
+ i18n, voices,
APP_CONFIG_DIR, APP_LOG_DIR, COMMANDS_LIST, DB,
};
@@ -41,6 +42,12 @@ fn main() -> Result<(), String> {
DB.set(Arc::new(RwLock::new(db::init_settings())))
.expect("DB already initialized");
+ // init voices
+ let voice_id = DB.get().unwrap().read().voice.clone();
+ if let Err(e) = voices::init(&voice_id) {
+ warn!("Failed to init voices: {}", e);
+ }
+
// init i18n
i18n::init(&DB.get().unwrap().read().language);
@@ -108,7 +115,10 @@ fn main() -> Result<(), String> {
info!("Initializing IPC...");
ipc::init();
- ipc::set_action_handler(|action| {
+ // channel for text commands (manually written in the GUI)
+ let (text_cmd_tx, text_cmd_rx) = mpsc::channel::();
+
+ ipc::set_action_handler(move |action| {
match action {
IpcAction::Stop => {
info!("Received stop command from GUI");
@@ -122,6 +132,15 @@ fn main() -> Result<(), String> {
info!("Received mute request: {}", muted);
// TODO: implement mute
}
+ IpcAction::TextCommand { text } => {
+ info!("Received text command: {}", text);
+ if let Err(e) = text_cmd_tx.send(text) {
+ error!("Failed to send text command to app: {}", e);
+ }
+ }
+ IpcAction::Ping => {
+ // handled internally by server
+ }
_ => {}
}
});
@@ -134,7 +153,7 @@ fn main() -> Result<(), String> {
// start the app (in the background thread)
std::thread::spawn(|| {
- let _ = app::start();
+ let _ = app::start(text_cmd_rx);
});
tray::init_blocking();
diff --git a/crates/jarvis-app/src/tray.rs b/crates/jarvis-app/src/tray.rs
index 3686477..3f85d61 100644
--- a/crates/jarvis-app/src/tray.rs
+++ b/crates/jarvis-app/src/tray.rs
@@ -6,11 +6,12 @@ use tray_icon::{
};
use winit::event_loop::{ControlFlow, EventLoopBuilder};
use image;
+use std::process::Command;
#[cfg(target_os="windows")]
use winit::platform::windows::EventLoopBuilderExtWindows;
-use jarvis_core::{config, i18n};
+use jarvis_core::{config, i18n, ipc::{self, IpcEvent}};
const TRAY_ICON_BYTES: &[u8] = include_bytes!("../../../resources/icons/32x32.png");
@@ -103,8 +104,14 @@ pub fn init_blocking() {
fn handle_menu_event(event: &MenuEvent) {
match event.id.0.as_str() {
"exit" => std::process::exit(0),
- "restart" => { /* restart logic */ }
- "settings" => { /* open settings */ }
+ "restart" => {
+ info!("Restarting from tray menu...");
+ restart_app();
+ }
+ "settings" => {
+ info!("Opening settings from tray menu...");
+ open_settings();
+ }
_ => {}
}
}
@@ -129,3 +136,74 @@ fn load_icon(path: &std::path::Path) -> tray_icon::Icon {
};
tray_icon::Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to open icon")
}
+
+fn restart_app() {
+ // get current executable path
+ let exe_path = match std::env::current_exe() {
+ Ok(path) => path,
+ Err(e) => {
+ error!("Failed to get executable path: {}", e);
+ return;
+ }
+ };
+
+ // spawn new instance
+ match Command::new(&exe_path).spawn() {
+ Ok(_) => {
+ info!("Spawned new instance, exiting current...");
+ std::process::exit(0);
+ }
+ Err(e) => {
+ error!("Failed to restart: {}", e);
+ }
+ }
+}
+
+fn open_settings() {
+ // check if jarvis-gui is connected via IPC
+ if ipc::has_clients() {
+ // gui is running, send reveal event
+ info!("GUI is connected, sending reveal event");
+ ipc::send(IpcEvent::RevealWindow);
+ } else {
+ // gui not running, launch it
+ info!("GUI not connected, launching jarvis-gui");
+ launch_gui();
+ }
+}
+
+fn launch_gui() {
+ let exe_path = match std::env::current_exe() {
+ Ok(path) => path,
+ Err(e) => {
+ error!("Failed to get executable path: {}", e);
+ return;
+ }
+ };
+
+ // jarvis-gui should be in same directory as jarvis-app
+ let gui_path = exe_path.parent()
+ .map(|p| p.join(get_gui_executable_name()))
+ .unwrap_or_else(|| get_gui_executable_name().into());
+
+ info!("Launching GUI: {:?}", gui_path);
+
+ match Command::new(&gui_path).spawn() {
+ Ok(_) => {
+ info!("Launched jarvis-gui");
+ }
+ Err(e) => {
+ error!("Failed to launch jarvis-gui: {}", e);
+ }
+ }
+}
+
+#[cfg(target_os = "windows")]
+fn get_gui_executable_name() -> &'static str {
+ "jarvis-gui.exe"
+}
+
+#[cfg(not(target_os = "windows"))]
+fn get_gui_executable_name() -> &'static str {
+ "jarvis-gui"
+}
\ No newline at end of file
diff --git a/crates/jarvis-core/Cargo.toml b/crates/jarvis-core/Cargo.toml
index fa7469b..ac90755 100644
--- a/crates/jarvis-core/Cargo.toml
+++ b/crates/jarvis-core/Cargo.toml
@@ -29,6 +29,7 @@ futures-util = { workspace = true, optional = true }
fluent.workspace = true
fluent-bundle.workspace = true
unic-langid.workspace = true
+chrono.workspace = true
# pv_recorder = { workspace = true, optional = true }
vosk = { version = "0.3.1", optional = true }
diff --git a/crates/jarvis-core/src/audio.rs b/crates/jarvis-core/src/audio.rs
index 3f0a39d..05a6547 100644
--- a/crates/jarvis-core/src/audio.rs
+++ b/crates/jarvis-core/src/audio.rs
@@ -57,9 +57,17 @@ pub fn init() -> Result<(), ()> {
}
pub fn play_sound(filename: &PathBuf) {
+ let audio_type = match AUDIO_TYPE.get() {
+ Some(t) => t,
+ None => {
+ warn!("Audio not initialized, cannot play: {}", filename.display());
+ return;
+ }
+ };
+
info!("Playing {}", filename.display());
- match AUDIO_TYPE.get().unwrap() {
+ match audio_type {
AudioType::Rodio => {
rodio::play_sound(filename, true);
}
@@ -75,18 +83,11 @@ pub fn get_sound_directory() -> Option {
SOUND_DIR.join(&s.voice)
};
- match voice_path.exists() && voice_path.cmp(&SOUND_DIR) != Ordering::Equal {
+ match voice_path.exists() {
true => Some(voice_path),
_ => {
- let default_voice_path = SOUND_DIR.join(config::DEFAULT_VOICE);
-
- match default_voice_path.exists() {
- true => Some(default_voice_path),
- _ => {
- error!("No sounds found. Search path - {:?}", voice_path);
- None
- }
- }
+ error!("No sounds folder found. Search path - {:?}", voice_path);
+ None
}
}
}
diff --git a/crates/jarvis-core/src/commands.rs b/crates/jarvis-core/src/commands.rs
index c5a230b..9e7cd57 100644
--- a/crates/jarvis-core/src/commands.rs
+++ b/crates/jarvis-core/src/commands.rs
@@ -1,6 +1,7 @@
+use crate::APP_DIR;
use rand::prelude::*;
use seqdiff::ratio;
-use serde_yaml;
+// use serde_yaml;
use std::path::Path;
use std::{fs, fs::File};
@@ -14,15 +15,16 @@ pub use structs::*;
use std::collections::HashMap;
-use crate::{audio, config};
+use crate::{config};
// @TODO. Allow commands both in yaml and json format.
pub fn parse_commands() -> Result, String> {
// collect commands
let mut commands: Vec = Vec::new();
- let cmd_dirs = fs::read_dir(config::COMMANDS_PATH)
- .map_err(|e| format!("Error reading commands directory: {}", e))?;
+ let commands_path = APP_DIR.join(config::COMMANDS_PATH);
+ let cmd_dirs = fs::read_dir(&commands_path)
+ .map_err(|e| format!("Error reading commands directory {:?}: {}", commands_path, e))?;
for entry in cmd_dirs {
let entry = match entry {
@@ -204,7 +206,7 @@ pub fn execute_command(
cmd_config: &JCommand,
// app_handle: &tauri::AppHandle,
) -> Result {
- let sounds_directory = audio::get_sound_directory().unwrap();
+ // let sounds_directory = audio::get_sound_directory().unwrap();
match cmd_config.action.as_str() {
"voice" => {
@@ -217,7 +219,7 @@ pub fn execute_command(
.unwrap()
);
// events::play(random_cmd_sound, app_handle);
- audio::play_sound(&sounds_directory.join(random_cmd_sound));
+ // audio::play_sound(&sounds_directory.join(random_cmd_sound));
Ok(true)
}
@@ -242,7 +244,7 @@ pub fn execute_command(
.unwrap()
);
// events::play(random_cmd_sound, app_handle);
- audio::play_sound(&sounds_directory.join(random_cmd_sound));
+ // audio::play_sound(&sounds_directory.join(random_cmd_sound));
Ok(true)
} else {
@@ -264,7 +266,7 @@ pub fn execute_command(
.unwrap()
);
// events::play(random_cmd_sound, app_handle);
- audio::play_sound(&sounds_directory.join(random_cmd_sound));
+ // audio::play_sound(&sounds_directory.join(random_cmd_sound));
Ok(true)
}
@@ -284,7 +286,7 @@ pub fn execute_command(
.unwrap()
);
// events::play(random_cmd_sound, app_handle);
- audio::play_sound(&sounds_directory.join(random_cmd_sound));
+ // audio::play_sound(&sounds_directory.join(random_cmd_sound));
std::thread::sleep(Duration::from_secs(2));
std::process::exit(0);
@@ -299,7 +301,7 @@ pub fn execute_command(
.unwrap()
);
// events::play(random_cmd_sound, app_handle);
- audio::play_sound(&sounds_directory.join(random_cmd_sound));
+ // audio::play_sound(&sounds_directory.join(random_cmd_sound));
Ok(false)
}
diff --git a/crates/jarvis-core/src/commands/structs.rs b/crates/jarvis-core/src/commands/structs.rs
index f2bda6b..d403d85 100644
--- a/crates/jarvis-core/src/commands/structs.rs
+++ b/crates/jarvis-core/src/commands/structs.rs
@@ -1,7 +1,7 @@
use std::path::PathBuf;
-use serde::Deserialize;
+use serde::{Serialize, Deserialize};
-#[derive(Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug)]
pub struct JCommandsList {
#[serde(skip)]
pub path: PathBuf,
@@ -11,7 +11,7 @@ pub struct JCommandsList {
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct JCommand {
pub id: String,
pub action: String,
diff --git a/crates/jarvis-core/src/config.rs b/crates/jarvis-core/src/config.rs
index 12303d7..b9c8cc4 100644
--- a/crates/jarvis-core/src/config.rs
+++ b/crates/jarvis-core/src/config.rs
@@ -70,7 +70,9 @@ pub const DEFAULT_WAKE_WORD_ENGINE: WakeWordEngine = WakeWordEngine::Vosk;
pub const DEFAULT_INTENT_RECOGNITION_ENGINE: IntentRecognitionEngine = IntentRecognitionEngine::IntentClassifier;
pub const DEFAULT_SPEECH_TO_TEXT_ENGINE: SpeechToTextEngine = SpeechToTextEngine::Vosk;
-pub const DEFAULT_VOICE: &str = "jarvis-og";
+pub const DEFAULT_VOICE: &str = "jarvis-remaster";
+pub const SOUND_PATH: &str = "resources/sound"; // extended from SOUND_DIR (resources/sound)
+pub const VOICES_PATH: &str = "voices"; // extended from SOUND_PATH (resources/sound)
pub const BUNDLE_IDENTIFIER: &str = "com.priler.jarvis";
pub const DB_FILE_NAME: &str = "app.db";
@@ -81,7 +83,7 @@ pub const REPOSITORY_LINK: Option<&str> = option_env!("CARGO_PKG_REPOSITORY");
pub const TG_OFFICIAL_LINK: Option<&str> = Some("https://t.me/howdyho_official");
pub const FEEDBACK_LINK: Option<&str> = Some("https://t.me/jarvis_feedback_bot");
pub const SUPPORT_BOOSTY_LINK: Option<&str> = Some("https://boosty.to/howdyho");
-pub const SUPPORT_PATREON_LINK: Option<&str> = Some("https://www.patreon.com/user?u=22843414");
+pub const SUPPORT_PATREON_LINK: Option<&str> = Some("https://www.patreon.com/c/priler");
/*
Tray.
diff --git a/crates/jarvis-core/src/i18n/locales/en.ftl b/crates/jarvis-core/src/i18n/locales/en.ftl
index 10f293d..f4adde8 100644
--- a/crates/jarvis-core/src/i18n/locales/en.ftl
+++ b/crates/jarvis-core/src/i18n/locales/en.ftl
@@ -54,7 +54,9 @@ settings-microphone = Microphone
settings-microphone-desc = The assistant will listen to this microphone.
settings-mic-default = Default (System)
settings-voice = Assistant voice
-settings-voice-desc = Not all commands work with all sound packs.
+settings-voice-desc =
+ Not all commands work with all sound packs.
+ Click to listen the preview of sound.
settings-wake-word-engine = Wake word engine
settings-wake-word-desc = Choose the engine for wake word recognition.
settings-stt-engine = Speech recognition
@@ -117,3 +119,8 @@ notification-saved = Settings saved!
notification-error = Error
notification-assistant-started = Assistant started
notification-assistant-stopped = Assistant stopped
+
+# ETC
+search-error-not-running = Assistant is not running
+search-error-failed = Failed to execute command
+settings-no-voices = No voices found
\ No newline at end of file
diff --git a/crates/jarvis-core/src/i18n/locales/ru.ftl b/crates/jarvis-core/src/i18n/locales/ru.ftl
index a883c66..9ece828 100644
--- a/crates/jarvis-core/src/i18n/locales/ru.ftl
+++ b/crates/jarvis-core/src/i18n/locales/ru.ftl
@@ -54,7 +54,9 @@ settings-microphone = Микрофон
settings-microphone-desc = Его будет слушать ассистент.
settings-mic-default = По умолчанию (Система)
settings-voice = Голос ассистента
-settings-voice-desc = Не все команды работают со всеми звуковыми пакетами.
+settings-voice-desc =
+ Не все команды работают со всеми звуковыми пакетами.
+ Кликните, чтобы прослушать как звучит голос.
settings-wake-word-engine = Движок активации
settings-wake-word-desc = Выберите нейросеть для распознавания активационной фразы.
settings-stt-engine = Распознавание речи
@@ -117,3 +119,8 @@ notification-saved = Настройки сохранены!
notification-error = Ошибка
notification-assistant-started = Ассистент запущен
notification-assistant-stopped = Ассистент остановлен
+
+# ETC
+search-error-not-running = Ассистент не запущен
+search-error-failed = Не удалось выполнить команду
+settings-no-voices = Голоса не найдены
\ No newline at end of file
diff --git a/crates/jarvis-core/src/i18n/locales/ua.ftl b/crates/jarvis-core/src/i18n/locales/ua.ftl
index 3f39b2c..3e3e145 100644
--- a/crates/jarvis-core/src/i18n/locales/ua.ftl
+++ b/crates/jarvis-core/src/i18n/locales/ua.ftl
@@ -54,7 +54,9 @@ settings-microphone = Мікрофон
settings-microphone-desc = Його буде слухати асистент.
settings-mic-default = За замовчуванням (Система)
settings-voice = Голос асистента
-settings-voice-desc = Не всі команди працюють з усіма звуковими пакетами.
+settings-voice-desc =
+ Не всі команди працюють з усіма звуковими пакетами.
+ Натисніть, щоб прослухати як звучить голос.
settings-wake-word-engine = Рушій активації
settings-wake-word-desc = Виберіть нейромережу для розпізнавання активаційної фрази.
settings-stt-engine = Розпізнавання мовлення
@@ -117,3 +119,9 @@ notification-saved = Налаштування збережено!
notification-error = Помилка
notification-assistant-started = Асистент запущено
notification-assistant-stopped = Асистент зупинено
+
+# ETC
+
+search-error-not-running = Асистент не запущено
+search-error-failed = Не вдалося виконати команду
+settings-no-voices = Голоси не знайдено
\ No newline at end of file
diff --git a/crates/jarvis-core/src/ipc.rs b/crates/jarvis-core/src/ipc.rs
index 11cac5c..1768c1d 100644
--- a/crates/jarvis-core/src/ipc.rs
+++ b/crates/jarvis-core/src/ipc.rs
@@ -2,4 +2,4 @@ mod events;
mod server;
pub use events::{IpcAction, IpcEvent};
-pub use server::{init, send, set_action_handler, start_server, IPC_ADDR, IPC_PORT};
\ No newline at end of file
+pub use server::{init, send, set_action_handler, start_server, has_clients, IPC_ADDR, IPC_PORT};
\ No newline at end of file
diff --git a/crates/jarvis-core/src/ipc/events.rs b/crates/jarvis-core/src/ipc/events.rs
index e555b04..e372d40 100644
--- a/crates/jarvis-core/src/ipc/events.rs
+++ b/crates/jarvis-core/src/ipc/events.rs
@@ -30,6 +30,9 @@ pub enum IpcEvent {
// Pong response
Pong,
+
+ // request GUI to reveal/focus window
+ RevealWindow,
}
// Actions sent from GUI to jarvis-app
@@ -47,4 +50,7 @@ pub enum IpcAction {
// Mute/unmute listening
SetMuted { muted: bool },
+
+ // Execute text command
+ TextCommand { text: String },
}
\ No newline at end of file
diff --git a/crates/jarvis-core/src/ipc/server.rs b/crates/jarvis-core/src/ipc/server.rs
index 53d1f7f..6e9072c 100644
--- a/crates/jarvis-core/src/ipc/server.rs
+++ b/crates/jarvis-core/src/ipc/server.rs
@@ -187,4 +187,12 @@ async fn handle_client(
}
info!("IPC: Client disconnected: {}", peer_addr);
+}
+
+pub fn has_clients() -> bool {
+ if let Some(tx) = BROADCAST_TX.get() {
+ tx.receiver_count() > 0
+ } else {
+ false
+ }
}
\ No newline at end of file
diff --git a/crates/jarvis-core/src/lib.rs b/crates/jarvis-core/src/lib.rs
index 00cec74..d9dbebf 100644
--- a/crates/jarvis-core/src/lib.rs
+++ b/crates/jarvis-core/src/lib.rs
@@ -7,6 +7,8 @@ use std::path::PathBuf;
#[macro_use]
extern crate log;
+pub mod time;
+
pub mod audio;
pub mod commands;
pub mod config;
@@ -32,6 +34,8 @@ pub mod audio_processing;
#[cfg(feature = "jarvis_app")]
pub mod ipc;
+pub mod voices;
+
// shared statics
// pub static APP_DIR: Lazy = Lazy::new(|| std::env::current_dir().unwrap());
pub static APP_DIR: Lazy = Lazy::new(|| {
@@ -40,7 +44,7 @@ pub static APP_DIR: Lazy = Lazy::new(|| {
.and_then(|p| p.parent().map(|p| p.to_path_buf()))
.unwrap_or_else(|| std::env::current_dir().unwrap())
});
-pub static SOUND_DIR: Lazy = Lazy::new(|| APP_DIR.clone().join("resources/sound"));
+pub static SOUND_DIR: Lazy = Lazy::new(|| APP_DIR.clone().join(config::SOUND_PATH));
pub static APP_DIRS: OnceCell = OnceCell::new();
pub static APP_CONFIG_DIR: OnceCell = OnceCell::new();
pub static APP_LOG_DIR: OnceCell = OnceCell::new();
diff --git a/crates/jarvis-core/src/time.rs b/crates/jarvis-core/src/time.rs
new file mode 100644
index 0000000..c392a21
--- /dev/null
+++ b/crates/jarvis-core/src/time.rs
@@ -0,0 +1,2 @@
+pub mod structs;
+pub use structs::*;
\ No newline at end of file
diff --git a/crates/jarvis-core/src/time/structs.rs b/crates/jarvis-core/src/time/structs.rs
new file mode 100644
index 0000000..bfe481d
--- /dev/null
+++ b/crates/jarvis-core/src/time/structs.rs
@@ -0,0 +1,21 @@
+use chrono::Timelike;
+
+#[derive(Debug, Clone, Copy)]
+pub enum TimeOfDay {
+ Morning, // 5:00 - 11:59
+ Day, // 12:00 - 16:59
+ Evening, // 17:00 - 21:59
+ Night, // 22:00 - 4:59
+}
+
+impl TimeOfDay {
+ pub fn now() -> Self {
+ let hour = chrono::Local::now().hour();
+ match hour {
+ 5..=11 => TimeOfDay::Morning,
+ 12..=16 => TimeOfDay::Day,
+ 17..=21 => TimeOfDay::Evening,
+ _ => TimeOfDay::Night,
+ }
+ }
+}
\ No newline at end of file
diff --git a/crates/jarvis-core/src/voices.rs b/crates/jarvis-core/src/voices.rs
new file mode 100644
index 0000000..3fe8e85
--- /dev/null
+++ b/crates/jarvis-core/src/voices.rs
@@ -0,0 +1,234 @@
+use std::fs;
+use std::path::{Path, PathBuf};
+use rand::prelude::*;
+use once_cell::sync::OnceCell;
+use parking_lot::RwLock;
+// use chrono::Timelike;
+
+use crate::{DB, SOUND_DIR, audio, config, time};
+
+pub mod structs;
+
+static VOICES: OnceCell> = OnceCell::new();
+static CURRENT_VOICE_ID: OnceCell> = OnceCell::new();
+
+pub fn init(default_voice: &str) -> Result<(), String> {
+ CURRENT_VOICE_ID.get_or_init(|| RwLock::new(default_voice.to_string()));
+
+ let voices = scan_voices()?;
+
+ if voices.is_empty() {
+ return Err("No voices found".into());
+ }
+
+ info!("Loaded {} voice(s): {:?}",
+ voices.len(),
+ voices.iter().map(|v| &v.voice.id).collect::>()
+ );
+
+ VOICES.set(voices).map_err(|_| "Voices already initialized")?;
+
+ Ok(())
+}
+
+pub fn scan_voices() -> Result, String> {
+ let voices_dir = SOUND_DIR.join(&config::VOICES_PATH);
+
+ if !voices_dir.exists() {
+ return Err(format!("Voices directory not found: {:?}", voices_dir));
+ }
+
+ let mut voices = Vec::new();
+
+ let entries = fs::read_dir(&voices_dir)
+ .map_err(|e| format!("Failed to read voices directory: {}", e))?;
+
+ for entry in entries.flatten() {
+ let voice_path = entry.path();
+ if !voice_path.is_dir() {
+ continue;
+ }
+
+ let toml_path = voice_path.join("voice.toml");
+ if !toml_path.exists() {
+ warn!("Voice folder {:?} missing voice.toml, skipping", voice_path);
+ continue;
+ }
+
+ match load_voice_config(&toml_path, &voice_path) {
+ Ok(config) => voices.push(config),
+ Err(e) => warn!("Failed to load voice {:?}: {}", voice_path, e),
+ }
+ }
+
+ Ok(voices)
+}
+
+fn load_voice_config(toml_path: &Path, voice_path: &Path) -> Result {
+ let content = fs::read_to_string(toml_path)
+ .map_err(|e| format!("Failed to read voice.toml: {}", e))?;
+
+ let mut config: structs::VoiceConfig = toml::from_str(&content)
+ .map_err(|e| format!("Failed to parse voice.toml: {}", e))?;
+
+ config.path = voice_path.to_path_buf();
+
+ Ok(config)
+}
+
+
+
+pub fn list_voices() -> Vec {
+ VOICES.get().cloned().unwrap_or_default()
+}
+
+pub fn get_voice(voice_id: &str) -> Option {
+ VOICES.get()?.iter().find(|v| v.voice.id == voice_id).cloned()
+}
+
+pub fn get_current_voice() -> Option {
+ let current_id = CURRENT_VOICE_ID.get()?.read().clone();
+ get_voice(¤t_id)
+}
+
+pub fn set_current_voice(voice_id: &str) {
+ if let Some(lock) = CURRENT_VOICE_ID.get() {
+ *lock.write() = voice_id.to_string();
+ }
+}
+
+fn get_current_language() -> String {
+ DB.get()
+ .map(|db| db.read().language.clone())
+ .unwrap_or_else(|| "ru".to_string())
+}
+
+
+
+fn find_sound_file(voice_path: &Path, lang: &str, sound_name: &str) -> Option {
+ let extensions = ["mp3", "wav", "ogg"];
+ let lang_path = voice_path.join(lang);
+
+ // try language subfolder first
+ for ext in &extensions {
+ let file_path = lang_path.join(format!("{}.{}", sound_name, ext));
+ if file_path.exists() {
+ return Some(file_path);
+ }
+ }
+
+ // fallback to root voice folder
+ for ext in &extensions {
+ let file_path = voice_path.join(format!("{}.{}", sound_name, ext));
+ if file_path.exists() {
+ return Some(file_path);
+ }
+ }
+
+ None
+}
+
+fn play_random_from(sounds: &[String]) {
+ if sounds.is_empty() {
+ return;
+ }
+
+ let voice = match get_current_voice() {
+ Some(v) => v,
+ None => {
+ warn!("No current voice set");
+ return;
+ }
+ };
+
+ let lang = get_current_language();
+ let sound_name = sounds.choose(&mut rand::thread_rng()).unwrap();
+
+ match find_sound_file(&voice.path, &lang, sound_name) {
+ Some(path) => {
+ debug!("Playing: {:?}", path);
+ audio::play_sound(&path);
+ }
+ None => {
+ warn!("Sound not found: {} (lang: {}, voice: {})", sound_name, lang, voice.voice.id);
+ }
+ }
+}
+
+pub fn play(reaction: structs::Reaction) {
+ let voice = match get_current_voice() {
+ Some(v) => v,
+ None => {
+ warn!("No current voice set");
+ return;
+ }
+ };
+
+ let sounds = match reaction {
+ structs::Reaction::Greet => {
+ // try time specific first
+ let time_specific = match time::TimeOfDay::now() {
+ time::TimeOfDay::Morning => &voice.reactions.greet_morning,
+ time::TimeOfDay::Day => &voice.reactions.greet_day,
+ time::TimeOfDay::Evening => &voice.reactions.greet_evening,
+ time::TimeOfDay::Night => &voice.reactions.greet_night,
+ };
+
+ if time_specific.is_empty() {
+ // fallback to simple run voice (not time specific)
+ &voice.reactions.greet
+ } else {
+ time_specific
+ }
+ }
+ structs::Reaction::Reply => &voice.reactions.reply,
+ structs::Reaction::Ok => &voice.reactions.ok,
+ structs::Reaction::NotFound => &voice.reactions.not_found,
+ structs::Reaction::Thanks => &voice.reactions.thanks,
+ structs::Reaction::Error => &voice.reactions.error,
+ structs::Reaction::Goodbye => &voice.reactions.goodbye,
+ };
+
+ play_random_from(sounds);
+}
+
+// Play a preview sound for a specific voice
+pub fn play_preview(voice_id: &str) {
+ let voice = match get_voice(voice_id) {
+ Some(v) => v,
+ None => {
+ warn!("Voice not found for preview: {}", voice_id);
+ return;
+ }
+ };
+
+ let lang = get_current_language();
+
+ // pick from reply or ok sounds for preview
+ let sounds: Vec<&String> = voice.reactions.reply.iter()
+ .chain(voice.reactions.ok.iter())
+ .chain(voice.reactions.greet.iter())
+ .collect();
+
+ if sounds.is_empty() {
+ warn!("No preview sounds for voice: {}", voice_id);
+ return;
+ }
+
+ let sound_name = sounds.choose(&mut rand::thread_rng()).unwrap();
+
+ if let Some(path) = find_sound_file(&voice.path, &lang, sound_name) {
+ debug!("Playing preview: {:?}", path);
+ audio::play_sound(&path);
+ }
+}
+
+
+// shortcuts
+pub fn play_greet() { play(structs::Reaction::Greet); } // app startup
+pub fn play_reply() { play(structs::Reaction::Reply); } // wake word detected
+pub fn play_ok() { play(structs::Reaction::Ok); } // command executed
+pub fn play_not_found() { play(structs::Reaction::NotFound); }
+pub fn play_thanks() { play(structs::Reaction::Thanks); }
+pub fn play_error() { play(structs::Reaction::Error); }
+pub fn play_goodbye() { play(structs::Reaction::Goodbye); }
\ No newline at end of file
diff --git a/crates/jarvis-core/src/voices/structs.rs b/crates/jarvis-core/src/voices/structs.rs
new file mode 100644
index 0000000..022d3d1
--- /dev/null
+++ b/crates/jarvis-core/src/voices/structs.rs
@@ -0,0 +1,70 @@
+use std::path::PathBuf;
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct VoiceConfig {
+ #[serde(skip)]
+ pub path: PathBuf,
+ pub voice: VoiceMeta,
+ pub reactions: VoiceReactions,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct VoiceMeta {
+ pub id: String,
+ pub name: String,
+ #[serde(default)]
+ pub author: String,
+ pub languages: Vec,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, Default)]
+pub struct VoiceReactions {
+ // app startup (time-based or generic)
+ #[serde(default)]
+ pub greet: Vec,
+
+ #[serde(default)]
+ pub greet_morning: Vec,
+ #[serde(default)]
+ pub greet_day: Vec,
+ #[serde(default)]
+ pub greet_evening: Vec,
+ #[serde(default)]
+ pub greet_night: Vec,
+
+ // wake word detected
+ #[serde(default)]
+ pub reply: Vec,
+
+ // command executed
+ #[serde(default)]
+ pub ok: Vec,
+
+ // command not found
+ #[serde(default)]
+ pub not_found: Vec,
+
+ // thank you
+ #[serde(default)]
+ pub thanks: Vec,
+
+ // error
+ #[serde(default)]
+ pub error: Vec,
+
+ // shutdown
+ #[serde(default)]
+ pub goodbye: Vec,
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum Reaction {
+ Greet, // app startup
+ Reply, // wake word detected
+ Ok, // command executed
+ NotFound,
+ Thanks,
+ Error,
+ Goodbye,
+}
diff --git a/crates/jarvis-gui/capabilities/default.json b/crates/jarvis-gui/capabilities/default.json
index 9205ee1..1cbe2ee 100644
--- a/crates/jarvis-gui/capabilities/default.json
+++ b/crates/jarvis-gui/capabilities/default.json
@@ -15,6 +15,12 @@
"allow": [
"$RESOURCE/**"
]
- }
+ },
+ "core:window:allow-show",
+ "core:window:allow-hide",
+ "core:window:allow-set-focus",
+ "core:window:allow-unminimize",
+ "core:window:allow-minimize",
+ "core:window:allow-close"
]
}
diff --git a/crates/jarvis-gui/gen/schemas/capabilities.json b/crates/jarvis-gui/gen/schemas/capabilities.json
index fe71707..79f69cd 100644
--- a/crates/jarvis-gui/gen/schemas/capabilities.json
+++ b/crates/jarvis-gui/gen/schemas/capabilities.json
@@ -1 +1 @@
-{"default":{"identifier":"default","description":"Default capabilities for Jarvis","local":true,"windows":["main"],"permissions":["core:default","shell:allow-open","dialog:allow-message","fs:default","fs:allow-read","fs:allow-write",{"identifier":"fs:scope","allow":["$RESOURCE/**"]}]}}
\ No newline at end of file
+{"default":{"identifier":"default","description":"Default capabilities for Jarvis","local":true,"windows":["main"],"permissions":["core:default","shell:allow-open","dialog:allow-message","fs:default","fs:allow-read","fs:allow-write",{"identifier":"fs:scope","allow":["$RESOURCE/**"]},"core:window:allow-show","core:window:allow-hide","core:window:allow-set-focus","core:window:allow-unminimize","core:window:allow-minimize","core:window:allow-close"]}}
\ No newline at end of file
diff --git a/crates/jarvis-gui/src/main.rs b/crates/jarvis-gui/src/main.rs
index f6655c2..0d16504 100644
--- a/crates/jarvis-gui/src/main.rs
+++ b/crates/jarvis-gui/src/main.rs
@@ -1,7 +1,7 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
-use jarvis_core::{config, db, i18n, APP_CONFIG_DIR, APP_LOG_DIR, DB};
+use jarvis_core::{config, db, i18n, voices, APP_CONFIG_DIR, APP_LOG_DIR, DB};
use parking_lot::RwLock;
use std::sync::Arc;
@@ -30,6 +30,16 @@ fn main() {
// init i18n
i18n::init(&settings.language);
+ // init voices
+ if let Err(e) = voices::init(&settings.voice) {
+ eprintln!("Failed to init voices: {}", e);
+ }
+
+ // init audio backend
+ if let Err(e) = jarvis_core::audio::init() {
+ eprintln!("Failed to init audio: {:?}", e);
+ }
+
// set db
DB.set(Arc::new(RwLock::new(settings)))
.expect("DB already initialized");
@@ -55,6 +65,8 @@ fn main() {
tauri_commands::get_author_name,
tauri_commands::get_repository_link,
tauri_commands::get_tg_official_link,
+ tauri_commands::get_boosty_link,
+ tauri_commands::get_patreon_link,
tauri_commands::get_feedback_link,
// fs
@@ -79,6 +91,15 @@ fn main() {
tauri_commands::get_current_language,
tauri_commands::set_language,
tauri_commands::get_supported_languages,
+
+ // commands
+ tauri_commands::get_commands_count,
+ tauri_commands::get_commands_list,
+
+ // voices
+ tauri_commands::list_voices,
+ tauri_commands::get_voice,
+ tauri_commands::preview_voice,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
diff --git a/crates/jarvis-gui/src/tauri_commands.rs b/crates/jarvis-gui/src/tauri_commands.rs
index dbc1faa..c6e757e 100644
--- a/crates/jarvis-gui/src/tauri_commands.rs
+++ b/crates/jarvis-gui/src/tauri_commands.rs
@@ -29,4 +29,12 @@ pub use stt::*;
// import i18n commands
mod i18n;
-pub use i18n::*;
\ No newline at end of file
+pub use i18n::*;
+
+// import commands commands xD
+mod commands;
+pub use commands::*;
+
+// import voices commands
+mod voices;
+pub use voices::*;
\ No newline at end of file
diff --git a/crates/jarvis-gui/src/tauri_commands/commands.rs b/crates/jarvis-gui/src/tauri_commands/commands.rs
new file mode 100644
index 0000000..f2bfb9b
--- /dev/null
+++ b/crates/jarvis-gui/src/tauri_commands/commands.rs
@@ -0,0 +1,22 @@
+use jarvis_core::commands::{self, JCommand, JCommandsList};
+use once_cell::sync::Lazy;
+
+static COMMANDS: Lazy> = Lazy::new(|| {
+ commands::parse_commands().unwrap_or_default()
+});
+
+#[tauri::command]
+pub fn get_commands_count() -> usize {
+ COMMANDS
+ .iter()
+ .map(|list| list.commands.len())
+ .sum()
+}
+
+#[tauri::command]
+pub fn get_commands_list() -> Vec {
+ COMMANDS
+ .iter()
+ .flat_map(|list| list.commands.clone())
+ .collect()
+}
\ No newline at end of file
diff --git a/crates/jarvis-gui/src/tauri_commands/voices.rs b/crates/jarvis-gui/src/tauri_commands/voices.rs
new file mode 100644
index 0000000..81beb66
--- /dev/null
+++ b/crates/jarvis-gui/src/tauri_commands/voices.rs
@@ -0,0 +1,16 @@
+use jarvis_core::voices::{self, structs::VoiceConfig};
+
+#[tauri::command]
+pub fn list_voices() -> Vec {
+ voices::list_voices()
+}
+
+#[tauri::command]
+pub fn get_voice(voice_id: String) -> Option {
+ voices::get_voice(&voice_id)
+}
+
+#[tauri::command]
+pub fn preview_voice(voice_id: String) {
+ voices::play_preview(&voice_id);
+}
\ No newline at end of file
diff --git a/crates/jarvis-gui/tauri.conf.json b/crates/jarvis-gui/tauri.conf.json
index db12b8b..d962ab3 100644
--- a/crates/jarvis-gui/tauri.conf.json
+++ b/crates/jarvis-gui/tauri.conf.json
@@ -7,7 +7,7 @@
"beforeDevCommand": "npm run dev",
"beforeBuildCommand": "npm run build",
"devUrl": "http://localhost:1420",
- "frontendDist": "../../frontend/dist"
+ "frontendDist": "../../frontend/dist/client"
},
"app": {
"withGlobalTauri": false,
@@ -35,11 +35,11 @@
],
"targets": "all",
"resources": {
- "../../resources/commands": "commands",
- "../../resources/sound": "sound",
- "../../resources/rustpotter": "rustpotter",
- "../../resources/vosk": "vosk",
- "../../resources/keywords": "keywords"
+ "../../resources/commands": "resources/commands",
+ "../../resources/sound": "resources/sound",
+ "../../resources/rustpotter": "resources/rustpotter",
+ "../../resources/vosk": "resources/vosk",
+ "../../resources/keywords": "resources/keywords"
}
}
}
diff --git a/frontend/_src/App.svelte b/frontend/_src/App.svelte
deleted file mode 100644
index 04206ea..0000000
--- a/frontend/_src/App.svelte
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/frontend/_src/Events.svelte b/frontend/_src/Events.svelte
deleted file mode 100644
index 1159d97..0000000
--- a/frontend/_src/Events.svelte
+++ /dev/null
@@ -1,44 +0,0 @@
-
\ No newline at end of file
diff --git a/frontend/_src/components/Footer.svelte b/frontend/_src/components/Footer.svelte
deleted file mode 100644
index d199f4c..0000000
--- a/frontend/_src/components/Footer.svelte
+++ /dev/null
@@ -1,73 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/frontend/_src/components/Header.svelte b/frontend/_src/components/Header.svelte
deleted file mode 100644
index 2028185..0000000
--- a/frontend/_src/components/Header.svelte
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
\ No newline at end of file
diff --git a/frontend/_src/components/Nav.svelte b/frontend/_src/components/Nav.svelte
deleted file mode 100644
index c8f8ccb..0000000
--- a/frontend/_src/components/Nav.svelte
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
\ No newline at end of file
diff --git a/frontend/_src/components/elements/ArcReactor.svelte b/frontend/_src/components/elements/ArcReactor.svelte
deleted file mode 100644
index adf9ee5..0000000
--- a/frontend/_src/components/elements/ArcReactor.svelte
+++ /dev/null
@@ -1,647 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/frontend/_src/components/elements/HDivider.svelte b/frontend/_src/components/elements/HDivider.svelte
deleted file mode 100644
index 5c8cea9..0000000
--- a/frontend/_src/components/elements/HDivider.svelte
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/frontend/_src/components/elements/SearchBar.svelte b/frontend/_src/components/elements/SearchBar.svelte
deleted file mode 100644
index 3e48b10..0000000
--- a/frontend/_src/components/elements/SearchBar.svelte
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/frontend/_src/components/elements/Stats.svelte b/frontend/_src/components/elements/Stats.svelte
deleted file mode 100644
index fe98d49..0000000
--- a/frontend/_src/components/elements/Stats.svelte
+++ /dev/null
@@ -1,377 +0,0 @@
-
-
-
-
-
-
- Микрофон
- {microphone_label}
-
-
-
-
-
- Нейросети
- {nn_details["ww_engine"]} + {nn_details["stt_engine"]}
-
-
-
-
-
- Ресурсы
- RAM {resources_ram_usage}mb
-
-
-
-
-
diff --git a/frontend/_src/css/main.scss b/frontend/_src/css/main.scss
deleted file mode 100644
index 2cc520d..0000000
--- a/frontend/_src/css/main.scss
+++ /dev/null
@@ -1,308 +0,0 @@
-$prim-font: "Roboto", sans-serif;
-$sec-font: "Roboto Condensed", sans-serif;
-
-html, body {
- overflow-x: hidden;
-}
-
-::-webkit-scrollbar {
- width: 15px;
-}
-
-::-webkit-scrollbar-track {
- -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
-}
-
-::-webkit-scrollbar-thumb {
- background: -webkit-gradient(linear,left top,left bottom,from(#999),to(#27292F));
- background: linear-gradient(to bottom,#999,#27292F);
-}
-
-::selection {
- background: #FF8901!important; /* WebKit/Blink Browsers */
- color: white!important;
- text-shadow: 1px 1px 1px rgba(0, 0, 0, .5)!important;
-}
-::-moz-selection {
- background: #FF8901!important; /* WebKit/Blink Browsers */
- color: white!important;
- text-shadow: 1px 1px 1px rgba(0, 0, 0, .5)!important;
-}
-
-/*
- * HEADER
- */
-
-#header {
- height: 80px;
- background-color: #0c1013;
- box-shadow: 0 0 19px 2px rgba(0, 0, 0, 0.91);
- position: relative;
- z-index: 100;
- text-align: justify;
-
- display: flex;
- justify-content: space-between;
-
- .logo {
- margin-top: 12px;
-
- & > a > img {
- width: 57px;
- display: inline-block;
- transition: .5s opacity ease-in;
-
- &:hover {
- opacity: .8;
- }
- }
-
- & > div {
- display: inline-block;
- margin-left: 13px;
- margin-top: 8px;
- vertical-align: top;
- }
-
- h1 {
- color: #ffffff;
- font-family: $sec-font;
- font-size: 20px;
- font-weight: bold;
- text-transform: uppercase;
- letter-spacing: 0.1px;
- margin: 0;
-
- & > a {
- color: white;
- text-shadow: 1px 1px 1px rgba(0, 0, 0, .5);
- text-decoration: none;
-
- &:hover {
- color: #8AC832;
- }
- }
- }
-
- h2 {
- color: #888;
- font-family: $sec-font;
- font-size: 14px;
- font-weight: bold;
- letter-spacing: 0.1px;
- margin-top: 2px;
- letter-spacing: -0.01px;
- }
- }
-
- .top-menu {
- vertical-align: top;
- margin-top: 18px;
- margin-left: 50px;
-
- & > ul {
- margin: 0;
- padding: 0;
- list-style: none;
-
- & > li {
- display: inline-block;
- position: relative;
-
- &:not(:first-child) {
- margin-left: 20px;
- }
-
- & > a {
- display: inline-block;
- text-decoration: none;
- color: #c6c6c6;
- font-family: $sec-font;
- font-size: 16px;
- text-transform: uppercase;
- font-weight: bold;
- transition: .2s color;
- padding: 10px 0;
-
- & > svg {
- height: 20px;
- width: 20px;
- vertical-align: middle;
- margin-bottom: 4px;
- margin-right: 3px;
- margin-left: 3px;
- }
-
- &:hover, &.active {
- color: #8AC832;
-
- &:after {
- -webkit-transform: scaleX(1);
- -ms-transform: scaleX(1);
- transform: scaleX(1);
- -webkit-transform-origin: left;
- -ms-transform-origin: left;
- transform-origin: left;
- }
- }
-
- &:after {
- content: "";
- display: block;
- height: 3px;
- border-radius: 10px;
- -webkit-transform: scaleX(0);
- -ms-transform: scaleX(0);
- transform: scaleX(0);
- transition: .4s -webkit-transform;
- transition: .4s transform;
- -webkit-transform-origin: 100% 0;
- -ms-transform-origin: 100% 0;
- transform-origin: 100% 0;
- background: #8AC832;
- opacity: 0.90;
- position: absolute;
- bottom: 5px;
- left: 0;
- width: 100%;
- z-index: 333;
- }
-
- }
- }
- }
- }
-}
-
-.search {
- display: block;
- margin: 20px 0;
- text-align: center;
-
- & > form {
- position: relative;
-
- & > input {
- width: 380px;
- height: 38px;
- box-shadow: inset 0 0 5px 1px rgba(0, 0, 0, 0.6);
- border: 1px solid rgba(6, 6, 6, 0.99);
- background-color: #0f1012;
- outline: none;
-
- color: #D1D1D1;
- font-family: $sec-font;
- font-size: 17px;
- font-weight: 600;
- line-height: 70.58px;
- padding-left: 20px;
- padding-right: 45px;
- padding-right: 45px;
-
- &::placeholder {
- color: #676767;
- font-weight: 400;
- }
-
- &:focus + button + small {
- opacity: .3;
- }
- }
-
- & > button {
- position: absolute;
- padding: 0;
- margin: 0;
- background: none;
- border: none;
- right: 35px;
- top: 4px;
- width: 30px;
- height: 30px;
- opacity: 1;
- transition: .3s opacity;
-
- &::before {
- content: "";
- position: absolute;
- top: 0;
- left: 0;
- width: 110%;
- height: 110%;
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAbCAYAAABvCO8sAAAC4ElEQVRIiaXWX4hXRRTA8c/etkwy+gNBEVRCmxFl1ouZGlEWFUQZPfVQRLAjKvSylEFBf16EiChJOxhLQY8RtA+lRZtpRv8ohUJQNMiHooKyojZNtoeZy95+/fZ3r+uBy5xz5pzznYGZc2dodHRUB1lVviUYwXkYwk84iK8wGRHb2goNtQDXYxRXdVkV9mFrRDx/osCl2IxrG75PsAv75Z1NyzsdwQosb8R+jXURsbML8AG82rDH8TI+77eyiAAppSVYg9SYXhsRW5rxVU/+Qw3Yd7ip+PrCesB7ImKNvNMDxb05pbR+NuBSvFL0L7EYH7SB+oA/Lrm7i2tTSmlVL3AYbxf9eyzDkROFNaBTWCmfYJhIKc1rAh/DuUW/HUfnCmtAp3FrMefjmRp4Kh4tE+PYO6DO6fKp7Ao9hJeKOZZSWlBhNc4ozidbaqyUr8W4/x+42aSuOYT7KtxZHF/gcEvyvDI+KF/yFW20iPgZk8W8o8KVxZjsn/IfOdbQL8OulNLTHfJ2lHFRhYuKcbB/bKs8kVLanVK6ZkBMfS8vqLCgGL/NEQjX48MB8/UVm1/hz2KceRLAfbh3wHxde2pYbmFnY+EcYVsiYm1LzKVl/KHCN8W4sUPxUxr6j7i7AwxuKOP+ykxLW4bzWxKnyziBRXirjZRSOstMx9lW4Q38XRyPt+R/hOtwF35tgzVqDhX99QpTeK441pWVzya/49OOICmlCzFWzE0RcaRuT0/hj6Jv71qwg7xbxuNKv66BR820uIuxE6edDCmltB1XFPOeiPiL/B+sZQcexgtyk96L+3X42/eAFuM1+YUHGyJiop4f7ol/Ef/Iv5TL8VlZQMiXexBoRH7hjTXcj0TEs8242V5tNxdo8wC9J5/Sfq+25bitEfut/Gp7p7dw7w5reV/e4Yay6oW4pXyD5DC2YmNEHOsXMBuwlo3lWy3v+mpcgnPku/WL3Br3yA+uNyPi+KCC/wJQGMGINsMjCwAAAABJRU5ErkJggg==");
- background-repeat: no-repeat;
- background-size: 75%;
- background-position: center center;
- transition: all .3s;
- }
-
- &:hover {
- &::before {
- cursor: pointer;
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAbCAYAAABvCO8sAAABN2lDQ1BBZG9iZSBSR0IgKDE5OTgpAAAokZWPv0rDUBSHvxtFxaFWCOLgcCdRUGzVwYxJW4ogWKtDkq1JQ5ViEm6uf/oQjm4dXNx9AidHwUHxCXwDxamDQ4QMBYvf9J3fORzOAaNi152GUYbzWKt205Gu58vZF2aYAoBOmKV2q3UAECdxxBjf7wiA10277jTG+38yH6ZKAyNguxtlIYgK0L/SqQYxBMygn2oQD4CpTto1EE9AqZf7G1AKcv8ASsr1fBBfgNlzPR+MOcAMcl8BTB1da4Bakg7UWe9Uy6plWdLuJkEkjweZjs4zuR+HiUoT1dFRF8jvA2AxH2w3HblWtay99X/+PRHX82Vun0cIQCw9F1lBeKEuf1UYO5PrYsdwGQ7vYXpUZLs3cLcBC7dFtlqF8hY8Dn8AwMZP/fNTP8gAAAAJcEhZcwAACxMAAAsTAQCanBgAAAXRaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzE0MiA3OS4xNjA5MjQsIDIwMTcvMDcvMTMtMDE6MDY6MzkgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE4IChXaW5kb3dzKSIgeG1wOkNyZWF0ZURhdGU9IjIwMjMtMDQtMjNUMDQ6MzE6NTgrMDU6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDIzLTA0LTIzVDA0OjM0OjI3KzA1OjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIzLTA0LTIzVDA0OjM0OjI3KzA1OjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowZDA5NTdiMi0zYmM3LTcxNDItODcyNS01ODA3MjA2NTFlYTIiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDoxM2UwZWYxNi03OGM0LTE2NGMtODc1Mi0xYjY5OTQ1OTczMGMiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo5YjNkZTI4Yy1iOTBmLTNjNDUtYjAwNS1kNTExOTE3ZDhkNzIiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjliM2RlMjhjLWI5MGYtM2M0NS1iMDA1LWQ1MTE5MTdkOGQ3MiIgc3RFdnQ6d2hlbj0iMjAyMy0wNC0yM1QwNDozMTo1OCswNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTggKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDowZDA5NTdiMi0zYmM3LTcxNDItODcyNS01ODA3MjA2NTFlYTIiIHN0RXZ0OndoZW49IjIwMjMtMDQtMjNUMDQ6MzQ6MjcrMDU6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE4IChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4Wh528AAAC7UlEQVRIiaXWX4gXVRTA8c9v2jLJ6A8ERVAJbUakTr2YqRFl0QRRRk89FBGYqNDLUgY51PQiRERJWhhLQY8RtA+OFm2mGf2RGqEQDAvyoaigrKhNk+3h3mGnX7/9zbgeGO45555zvvfCvWdub/POpTrI6vilGMVF6OEnHMEXmCyyaldboV4LcCPWYnGXVeEQdhRZ9fypApdhG65v+D7GPhwWdjYt7HQUK7GiEfslNhRZtbcL8EG81rDH8TI+G7SyIqtAXqYp1uGRxvT6Iqu2N+OTvvyHG7DvcEv0DYT1gasiq9YJO/06urflZbpxNuAyvBr1z7EE77eBBoA/irn7o2trXqar+4Ej2Bn177Ecx04V1oBOYZVwgmEiL9N5TeATuDDqGY7PFdaATuP2aM7HMzXwTDweJ8ZxcEids4VT2RX6DV6K5lhepgsSrME50flUS41VwrUY9/8DN5vUNXu4P8Fd0XEAR1uS58XxIeGSr2yjFVn1MyajeWeCa6MxOTjlP3KioV+FfXmZFh3y9sRxUYLLonFkcGyrbM7LdH9eptcNianv5SUJFkTjtzkC4UZ8MGS+vmLzE/wZjXNPA3gI9w2Zr2tPjQgt7HwsnCNse5FV61tirozjDwm+isbNHYqf0dB/xD0dYHBTHA8nZlraclzckjgdxwkswtttpLxMzzPTcXYleBN/R8eTLfkf4gbcjV/bYI2avai/kWAKz0XHhrjy2eR3fNIRJC/TSzEWza1FVh2r29PT+CPqu7sW7CDvxPGk2K9r4HEzLe5y7MVZp0PKy3Q3ronmvUVW/UX4D9ayB4/iBaFJH8QDOvzt+0BL8LrwwoNNRVZN1PMjffEv4h/hl3I1Po0LeEW43MNAo8ILb6zhfqzIqmebcbO92m6N0OYBelc4pYNebStwRyP2W+HVVvYX7t9hLe8JO9wUV70Qt8VvmBzFDmwpsurEoIDZgLVsid8aYddLcQUuEO7WL0JrrIQH11tFVp0cVvBfVZDA+HDoxOQAAAAASUVORK5CYII=");
- }
- }
- }
-
- & > small {
- position: absolute;
- font-family: $sec-font;
- font-size: 14px;
- font-weight: bold;
- line-height: 1.7em;
- top: 8px;
- right: 75px;
- background-color: #D1D1D1;
- color: #080C0F;
- padding: 0 7px;
- padding-top: 2px;
- border-radius: 4px;
- opacity: 0;
- transition: opacity .3s;
- cursor: default;
- }
- }
-
- &.active {
- small {
- opacity: .3;
- }
- }
-}
-
-@media (max-width: 1364px) {
- #content>.inner>section.materials>header>h1 {
- font-size: 26px;
- }
-
- #content>.inner>section.materials>article>.details>h1 {
- font-size: 25px;
- }
-
- #content>.inner>section.materials>article>.details>blockquote {
- font-size: 17px;
- line-height: 24px;
- -webkit-line-clamp: 4;
- }
-
- .btn {
- font-size: 16px!important;
- }
-
- #paginator>header>h1 {
- font-size: 30px!important;
- }
-
- #paginator>.paginator-wrapper>.paginator-box {
- transform: scale(1.5);
- margin-top: 15px;
- }
-
- #paginator>.paginator-wrapper>small:first-child {
- display: none;
- }
-
- #paginator>.paginator-wrapper>small:last-child {
- display: none;
- }
-}
\ No newline at end of file
diff --git a/frontend/_src/css/styles.scss b/frontend/_src/css/styles.scss
deleted file mode 100644
index 1cbb6ab..0000000
--- a/frontend/_src/css/styles.scss
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- SOME DEFAULT CSS.
-*/
-html, body {
- margin: 0;
- padding: 0;
- min-height: 100vh;
- min-width: 100vw;
-
- background-color: #4C5062;
- color: white;
-
-
- &.assist-page {
- background: rgb(24,123,123);
- background: -moz-radial-gradient(circle, rgba(24,123,123,0.4906337535014006) 0%, rgba(13,15,19,1) 64%);
- background: -webkit-radial-gradient(circle, rgba(24,123,123,0.4906337535014006) 0%, rgba(13,15,19,1) 64%);
- background: radial-gradient(circle, rgba(24,123,123,0.4906337535014006) 0%, rgba(13,15,19,1) 64%);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#187b7b",endColorstr="#0d0f13",GradientType=1);
-
- &.assist-active {
- background: rgb(24,81,123);
- background: -moz-radial-gradient(circle, rgba(24,81,123,0.6418942577030813) 0%, rgba(13,15,19,1) 64%);
- background: -webkit-radial-gradient(circle, rgba(24,81,123,0.6418942577030813) 0%, rgba(13,15,19,1) 64%);
- background: radial-gradient(circle, rgba(24,81,123,0.6418942577030813) 0%, rgba(13,15,19,1) 64%);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#18517b",endColorstr="#0d0f13",GradientType=1);
- }
-
- }
-}
-
-:root {
- font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
- font-size: 16px;
- line-height: 24px;
- font-weight: 400;
-
- color: #0f0f0f;
- background-color: #f6f6f6;
-
- font-synthesis: none;
- text-rendering: optimizeLegibility;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- -webkit-text-size-adjust: 100%;
-}
-
-
-a {
- color: #2A9CD0;
- text-decoration: none;
- font-weight: bold;
-
- &:hover {
- color: #1dabed;
- text-decoration: underline;
- }
-}
-
-/*
- OVERRIDES.
-*/
-#wrapper {
- padding: 0;
- margin: 0;
-}
-
-#header, main {
- padding: 0 30px;
-}
-
-select, input {
- color: #ccc!important;
-}
-
-.form {
- label {
- font-weight: bold!important;
- color: #8AC832!important;
- font-size: 15px!important;
- border-bottom: 1px dotted gray;
- margin-bottom: 10px!important;
-
- & + div {
- line-height: 1.2em;
- }
- }
-
- .svelteui-Tab-label {
- font-size: 18px!important;
- }
-}
\ No newline at end of file
diff --git a/frontend/_src/functions.ts b/frontend/_src/functions.ts
deleted file mode 100644
index 10a7e00..0000000
--- a/frontend/_src/functions.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-import { invoke } from "@tauri-apps/api/core"
-import { is_listening, isListening } from "@/stores"
-import { clearInterval, clearTimeout, setInterval, setTimeout } from 'worker-timers';
-
-// setInterval(() => {
-// (async () => {
-// is_listening.set(await invoke("is_listening"));
-// })().catch(err => {
-// console.error(err);
-// });
-// }, 1000);
-
-export function startListening() {
- (async () => {
- invoke('start_listening')
- .then((message) => {
- is_listening.set(true);
- })
- .catch((error) => {
- is_listening.set(false);
- console.error(error);
- // alert("Ошибка: " + error);
- })
- })().catch(err => {
- console.error(err);
- });
-}
-
-export function stopListening(callback: () => void) {
- (async () => {
- invoke('stop_listening')
- .then((message) => {
- is_listening.set(false);
- if(callback) {
- callback();
- }
- })
- .catch((error) => {
- console.error(error);
- })
- })().catch(err => {
- console.error(err);
- });
-}
-
-export function capitalizeFirstLetter(string: string) {
- return string.charAt(0).toUpperCase() + string.slice(1);
-}
-
-export function showInExplorer(path: any) {
- (async () => {
- invoke('show_in_folder', {path: path})
- .then((message) => {})
- .catch((error) => {
- console.error(error);
- // alert("Ошибка: " + error);
- })
- })().catch(err => {
- console.error(err);
- });
-}
\ No newline at end of file
diff --git a/frontend/_src/main.ts b/frontend/_src/main.ts
deleted file mode 100644
index 3fbc72c..0000000
--- a/frontend/_src/main.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-// Klondike project old CSS file
-import "./css/main.scss";
-
-// App current CSS file
-import "./css/styles.scss";
-
-// deploy app
-import App from "./App.svelte";
-const app = new App({
- target: document.getElementById("app")!,
-});
-
-export default app;
\ No newline at end of file
diff --git a/frontend/_src/routes/_module.svelte b/frontend/_src/routes/_module.svelte
deleted file mode 100644
index f307626..0000000
--- a/frontend/_src/routes/_module.svelte
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/frontend/_src/routes/commands/index.svelte b/frontend/_src/routes/commands/index.svelte
deleted file mode 100644
index d724d7b..0000000
--- a/frontend/_src/routes/commands/index.svelte
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
- Тут будет список команд + полноценный редактор команд.
- Следите за обновлениями в нашем телеграм канале!
-
-
-
-

-
-
-
-
\ No newline at end of file
diff --git a/frontend/_src/routes/index.svelte b/frontend/_src/routes/index.svelte
deleted file mode 100644
index ef8e1a6..0000000
--- a/frontend/_src/routes/index.svelte
+++ /dev/null
@@ -1,71 +0,0 @@
-
-
-
-{#if !is_listening__val}
-
-В данный момент ассистент не прослушивает команды.
-Пожалуйста, перейдите в настройки и введите ключ Picovoice.
-
-
-{:else}
-
-
-{/if}
-
-
-
-
-
-
\ No newline at end of file
diff --git a/frontend/_src/routes/settings/index.svelte b/frontend/_src/routes/settings/index.svelte
deleted file mode 100644
index 4e14b70..0000000
--- a/frontend/_src/routes/settings/index.svelte
+++ /dev/null
@@ -1,188 +0,0 @@
-
-
-
-
-
- Часть функций может работать некорректно.
- Сообщайте обо всех найденных багах в наш телеграм бот.
-
-
-
-
-
-
-{#if settings_saved }
-
-
-{/if}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {#if selected_wake_word_engine == "picovoice"}
-
-
-
-
- Мы ждем официального патча от разработчиков.
-
-
-
-
- Введите сюда свой ключ Picovoice.
- Он выдается бесплатно при регистрации в Picovoice Console.
-
-
-
-
-
- {/if}
-
-
-
-
-
- В данный момент ChatGPT не поддерживается. Он будет добавлен в ближайших обновлениях.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/frontend/_src/stores.ts b/frontend/_src/stores.ts
deleted file mode 100644
index dcdd2fa..0000000
--- a/frontend/_src/stores.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { invoke } from "@tauri-apps/api/core"
-import { writable } from 'svelte/store'
-
-// listen state
-export const is_listening = writable(true);
-let is_listening__val: boolean;
-is_listening.subscribe(value => {
- is_listening__val = value;
-});
-export function isListening() {return is_listening__val}
-
-// assistant voice
-export const assistant_voice = writable("");
-
-(async () => {
- assistant_voice.set(await invoke("db_read", {key: "assistant_voice"}));
-})().catch(err => {
- console.error(err);
-});
-
-// etc
-export let tg_official_link = "";
-export let feedback_link = "";
-export let github_repository_link = "";
-export let log_file_path = "";
-
-(async () => {
- tg_official_link = await invoke("get_tg_official_link")
- feedback_link = await invoke("get_feedback_link")
- github_repository_link = await invoke("get_repository_link")
- log_file_path = await invoke("get_log_file_path")
-})().catch(err => {
- console.error(err);
-});
\ No newline at end of file
diff --git a/frontend/_src/vite-env.d.ts b/frontend/_src/vite-env.d.ts
deleted file mode 100644
index 4078e74..0000000
--- a/frontend/_src/vite-env.d.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-///
-///
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index b2e1d75..91d158c 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -11,7 +11,7 @@
"@svelteuidev/composables": "^0.15.7",
"@svelteuidev/core": "^0.15.7",
"@svelteuidev/motion": "^0.15.7",
- "@tauri-apps/api": "^2",
+ "@tauri-apps/api": "^2.9.1",
"@tauri-apps/plugin-dialog": "^2",
"@tauri-apps/plugin-fs": "^2",
"@tauri-apps/plugin-shell": "^2",
@@ -819,9 +819,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.54.0.tgz",
- "integrity": "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz",
+ "integrity": "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==",
"cpu": [
"arm"
],
@@ -833,9 +833,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.54.0.tgz",
- "integrity": "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz",
+ "integrity": "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==",
"cpu": [
"arm64"
],
@@ -847,9 +847,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.54.0.tgz",
- "integrity": "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz",
+ "integrity": "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==",
"cpu": [
"arm64"
],
@@ -861,9 +861,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.54.0.tgz",
- "integrity": "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz",
+ "integrity": "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==",
"cpu": [
"x64"
],
@@ -875,9 +875,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.54.0.tgz",
- "integrity": "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz",
+ "integrity": "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==",
"cpu": [
"arm64"
],
@@ -889,9 +889,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.54.0.tgz",
- "integrity": "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz",
+ "integrity": "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==",
"cpu": [
"x64"
],
@@ -903,9 +903,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.54.0.tgz",
- "integrity": "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz",
+ "integrity": "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==",
"cpu": [
"arm"
],
@@ -917,9 +917,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.54.0.tgz",
- "integrity": "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz",
+ "integrity": "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==",
"cpu": [
"arm"
],
@@ -931,9 +931,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.54.0.tgz",
- "integrity": "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz",
+ "integrity": "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==",
"cpu": [
"arm64"
],
@@ -945,9 +945,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.54.0.tgz",
- "integrity": "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz",
+ "integrity": "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==",
"cpu": [
"arm64"
],
@@ -959,9 +959,23 @@
]
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.54.0.tgz",
- "integrity": "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz",
+ "integrity": "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz",
+ "integrity": "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==",
"cpu": [
"loong64"
],
@@ -973,9 +987,23 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.54.0.tgz",
- "integrity": "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz",
+ "integrity": "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz",
+ "integrity": "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==",
"cpu": [
"ppc64"
],
@@ -987,9 +1015,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.54.0.tgz",
- "integrity": "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz",
+ "integrity": "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==",
"cpu": [
"riscv64"
],
@@ -1001,9 +1029,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.54.0.tgz",
- "integrity": "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz",
+ "integrity": "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==",
"cpu": [
"riscv64"
],
@@ -1015,9 +1043,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.54.0.tgz",
- "integrity": "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz",
+ "integrity": "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==",
"cpu": [
"s390x"
],
@@ -1029,9 +1057,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.54.0.tgz",
- "integrity": "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz",
+ "integrity": "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==",
"cpu": [
"x64"
],
@@ -1043,9 +1071,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.54.0.tgz",
- "integrity": "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz",
+ "integrity": "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==",
"cpu": [
"x64"
],
@@ -1056,10 +1084,24 @@
"linux"
]
},
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz",
+ "integrity": "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
"node_modules/@rollup/rollup-openharmony-arm64": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.54.0.tgz",
- "integrity": "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz",
+ "integrity": "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==",
"cpu": [
"arm64"
],
@@ -1071,9 +1113,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.54.0.tgz",
- "integrity": "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz",
+ "integrity": "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==",
"cpu": [
"arm64"
],
@@ -1085,9 +1127,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.54.0.tgz",
- "integrity": "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz",
+ "integrity": "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==",
"cpu": [
"ia32"
],
@@ -1099,9 +1141,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.54.0.tgz",
- "integrity": "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz",
+ "integrity": "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==",
"cpu": [
"x64"
],
@@ -1113,9 +1155,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.54.0.tgz",
- "integrity": "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz",
+ "integrity": "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==",
"cpu": [
"x64"
],
@@ -2784,9 +2826,9 @@
}
},
"node_modules/rollup": {
- "version": "4.54.0",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz",
- "integrity": "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==",
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz",
+ "integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2800,28 +2842,31 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.54.0",
- "@rollup/rollup-android-arm64": "4.54.0",
- "@rollup/rollup-darwin-arm64": "4.54.0",
- "@rollup/rollup-darwin-x64": "4.54.0",
- "@rollup/rollup-freebsd-arm64": "4.54.0",
- "@rollup/rollup-freebsd-x64": "4.54.0",
- "@rollup/rollup-linux-arm-gnueabihf": "4.54.0",
- "@rollup/rollup-linux-arm-musleabihf": "4.54.0",
- "@rollup/rollup-linux-arm64-gnu": "4.54.0",
- "@rollup/rollup-linux-arm64-musl": "4.54.0",
- "@rollup/rollup-linux-loong64-gnu": "4.54.0",
- "@rollup/rollup-linux-ppc64-gnu": "4.54.0",
- "@rollup/rollup-linux-riscv64-gnu": "4.54.0",
- "@rollup/rollup-linux-riscv64-musl": "4.54.0",
- "@rollup/rollup-linux-s390x-gnu": "4.54.0",
- "@rollup/rollup-linux-x64-gnu": "4.54.0",
- "@rollup/rollup-linux-x64-musl": "4.54.0",
- "@rollup/rollup-openharmony-arm64": "4.54.0",
- "@rollup/rollup-win32-arm64-msvc": "4.54.0",
- "@rollup/rollup-win32-ia32-msvc": "4.54.0",
- "@rollup/rollup-win32-x64-gnu": "4.54.0",
- "@rollup/rollup-win32-x64-msvc": "4.54.0",
+ "@rollup/rollup-android-arm-eabi": "4.55.1",
+ "@rollup/rollup-android-arm64": "4.55.1",
+ "@rollup/rollup-darwin-arm64": "4.55.1",
+ "@rollup/rollup-darwin-x64": "4.55.1",
+ "@rollup/rollup-freebsd-arm64": "4.55.1",
+ "@rollup/rollup-freebsd-x64": "4.55.1",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.55.1",
+ "@rollup/rollup-linux-arm-musleabihf": "4.55.1",
+ "@rollup/rollup-linux-arm64-gnu": "4.55.1",
+ "@rollup/rollup-linux-arm64-musl": "4.55.1",
+ "@rollup/rollup-linux-loong64-gnu": "4.55.1",
+ "@rollup/rollup-linux-loong64-musl": "4.55.1",
+ "@rollup/rollup-linux-ppc64-gnu": "4.55.1",
+ "@rollup/rollup-linux-ppc64-musl": "4.55.1",
+ "@rollup/rollup-linux-riscv64-gnu": "4.55.1",
+ "@rollup/rollup-linux-riscv64-musl": "4.55.1",
+ "@rollup/rollup-linux-s390x-gnu": "4.55.1",
+ "@rollup/rollup-linux-x64-gnu": "4.55.1",
+ "@rollup/rollup-linux-x64-musl": "4.55.1",
+ "@rollup/rollup-openbsd-x64": "4.55.1",
+ "@rollup/rollup-openharmony-arm64": "4.55.1",
+ "@rollup/rollup-win32-arm64-msvc": "4.55.1",
+ "@rollup/rollup-win32-ia32-msvc": "4.55.1",
+ "@rollup/rollup-win32-x64-gnu": "4.55.1",
+ "@rollup/rollup-win32-x64-msvc": "4.55.1",
"fsevents": "~2.3.2"
}
},
@@ -2859,9 +2904,9 @@
}
},
"node_modules/sass": {
- "version": "1.97.1",
- "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.1.tgz",
- "integrity": "sha512-uf6HoO8fy6ClsrShvMgaKUn14f2EHQLQRtpsZZLeU/Mv0Q1K5P0+x2uvH6Cub39TVVbWNSrraUhDAoFph6vh0A==",
+ "version": "1.97.2",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.2.tgz",
+ "integrity": "sha512-y5LWb0IlbO4e97Zr7c3mlpabcbBtS+ieiZ9iwDooShpFKWXf62zz5pEPdwrLYm+Bxn1fnbwFGzHuCLSA9tBmrw==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -3151,9 +3196,9 @@
}
},
"node_modules/undici": {
- "version": "7.16.0",
- "resolved": "https://registry.npmjs.org/undici/-/undici-7.16.0.tgz",
- "integrity": "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==",
+ "version": "7.18.2",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.18.2.tgz",
+ "integrity": "sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==",
"dev": true,
"license": "MIT",
"engines": {
diff --git a/frontend/package.json b/frontend/package.json
index 3d7bb48..80643ae 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -14,7 +14,7 @@
"@svelteuidev/composables": "^0.15.7",
"@svelteuidev/core": "^0.15.7",
"@svelteuidev/motion": "^0.15.7",
- "@tauri-apps/api": "^2",
+ "@tauri-apps/api": "^2.9.1",
"@tauri-apps/plugin-dialog": "^2",
"@tauri-apps/plugin-fs": "^2",
"@tauri-apps/plugin-shell": "^2",
diff --git a/frontend/src/components/Footer.svelte b/frontend/src/components/Footer.svelte
index 7de6982..2241d54 100644
--- a/frontend/src/components/Footer.svelte
+++ b/frontend/src/components/Footer.svelte
@@ -46,13 +46,13 @@
{#if $currentLanguage === "ru"}
- {t('footer-support')}
+ {t('footer-support')}
Boosty
.
{/if}
{#if $currentLanguage === "ua" || $currentLanguage === "en"}
- {t('footer-support')}
+ {t('footer-support')}
Patreon
.
diff --git a/frontend/src/components/elements/ArcReactor.svelte b/frontend/src/components/elements/ArcReactor.svelte
index 68963c4..00e5afa 100644
--- a/frontend/src/components/elements/ArcReactor.svelte
+++ b/frontend/src/components/elements/ArcReactor.svelte
@@ -1,20 +1,12 @@
diff --git a/frontend/src/components/elements/SearchBar.svelte b/frontend/src/components/elements/SearchBar.svelte
index 0b57c03..b7a13c0 100644
--- a/frontend/src/components/elements/SearchBar.svelte
+++ b/frontend/src/components/elements/SearchBar.svelte
@@ -1,22 +1,85 @@
-
+
+
\ No newline at end of file
diff --git a/frontend/src/components/elements/Stats.svelte b/frontend/src/components/elements/Stats.svelte
index d461dc1..1d795ec 100644
--- a/frontend/src/components/elements/Stats.svelte
+++ b/frontend/src/components/elements/Stats.svelte
@@ -12,10 +12,10 @@
$: t = (key: string) => translate($translations, key)
- let microphoneName = "Загрузка..."
+ let microphoneName = ""
let wakeWordEngine = "Rustpotter"
let sttEngine = "Vosk"
- let vadInfo = "Snip + ChatGPT"
+ let vadInfo = ""
onMount(async () => {
microphoneName = t('stats-loading')
@@ -68,7 +68,7 @@
{t('stats-resources')}
- {#if jarvisRamUsage }RAM {$jarvisRamUsage}mb{:else}...{/if}
+ {#if $jarvisRamUsage }RAM {$jarvisRamUsage}mb{:else}...{/if}
diff --git a/frontend/src/lib/ipc.ts b/frontend/src/lib/ipc.ts
index 6ece44e..e4e1beb 100644
--- a/frontend/src/lib/ipc.ts
+++ b/frontend/src/lib/ipc.ts
@@ -1,4 +1,6 @@
import { writable, get } from "svelte/store"
+import { invoke } from "@tauri-apps/api/core"
+import { getCurrentWindow } from "@tauri-apps/api/window"
// ### IPC STORES ###
@@ -30,57 +32,32 @@ export function disableIpc() {
disconnectIpc()
}
-export function connectIpc() {
- if (!enabled) {
- console.log("IPC: Not enabled, skipping connection")
- return
+export function connectIpc(port: number = 9712) {
+ if (ws?.readyState === WebSocket.OPEN) return
+
+ ws = new WebSocket(`ws://127.0.0.1:${port}`)
+
+ ws.onopen = () => {
+ ipcConnected.set(true)
+ jarvisState.set("idle")
+ console.log("[IPC] connected")
}
- if (ws?.readyState === WebSocket.OPEN || ws?.readyState === WebSocket.CONNECTING) {
- return
+ ws.onclose = () => {
+ ipcConnected.set(false)
+ console.log("[IPC] disconnected")
}
- manualDisconnect = false
+ ws.onerror = (err) => {
+ console.error("[IPC] error:", err)
+ }
- console.log("IPC: Connecting to", IPC_URL)
-
- try {
- ws = new WebSocket(IPC_URL)
-
- ws.onopen = () => {
- console.log("IPC: Connected")
- ipcConnected.set(true)
- jarvisState.set("idle")
- sendAction("ping")
- }
-
- ws.onclose = (event) => {
- console.log("IPC: Disconnected", event.code)
- ipcConnected.set(false)
- jarvisState.set("disconnected")
- ws = null
-
- if (!manualDisconnect && enabled) {
- scheduleReconnect()
- }
- }
-
- ws.onerror = () => {
- // error is handled in onclose, just suppress console spam
- }
-
- ws.onmessage = (event) => {
- try {
- const data = JSON.parse(event.data)
- handleEvent(data)
- } catch (e) {
- console.error("IPC: Failed to parse message:", event.data, e)
- }
- }
- } catch (e) {
- // suppress errors when server isn't running
- if (enabled) {
- scheduleReconnect()
+ ws.onmessage = (event) => {
+ try {
+ const msg = JSON.parse(event.data)
+ handleEvent(msg)
+ } catch (e) {
+ console.error("[IPC] failed to parse message:", e)
}
}
}
@@ -151,6 +128,11 @@ function handleEvent(data: any) {
case "pong":
// connection verified
break
+
+ case "reveal_window":
+ // bring window to foreground
+ revealWindow()
+ break
}
}
@@ -171,4 +153,35 @@ export function stopJarvisApp() {
export function reloadCommands() {
return sendAction("reload_commands")
-}
\ No newline at end of file
+}
+
+export function sendIpcMessage(message: object): Promise {
+ return new Promise((resolve, reject) => {
+ if (!ws || ws.readyState !== WebSocket.OPEN) {
+ reject(new Error("IPC not connected"))
+ return
+ }
+
+ try {
+ ws.send(JSON.stringify(message))
+ resolve()
+ } catch (err) {
+ reject(err)
+ }
+ })
+}
+
+export function sendTextCommand(text: string): boolean {
+ return sendAction("text_command", { text })
+}
+
+async function revealWindow() {
+ try {
+ const window = getCurrentWindow()
+ await window.show()
+ await window.unminimize()
+ await window.setFocus()
+ } catch (e) {
+ console.error("[IPC] Failed to reveal window:", e)
+ }
+}
diff --git a/frontend/src/routes/index.svelte b/frontend/src/routes/index.svelte
index 1f1d474..56f85aa 100644
--- a/frontend/src/routes/index.svelte
+++ b/frontend/src/routes/index.svelte
@@ -21,13 +21,17 @@
let processRunning = false
let launching = false
+ let wasRunning = false // track previous state
isJarvisRunning.subscribe((value) => {
processRunning = value
if (value) {
enableIpc()
- } else {
+ wasRunning = true
+ } else if (wasRunning) {
+ // only disable if it was running before
disableIpc()
+ wasRunning = false
}
})
diff --git a/frontend/src/routes/settings/index.svelte b/frontend/src/routes/settings/index.svelte
index 79d9583..91ebc77 100644
--- a/frontend/src/routes/settings/index.svelte
+++ b/frontend/src/routes/settings/index.svelte
@@ -35,6 +35,30 @@
$: t = (key: string) => translate($translations, key)
+ interface VoiceMeta {
+ id: string
+ name: string
+ author: string
+ languages: string[]
+ }
+
+ interface VoiceConfig {
+ voice: VoiceMeta
+ }
+
+ let availableVoices: VoiceMeta[] = []
+
+ async function selectVoice(voiceId: string) {
+ voiceVal = voiceId
+
+ // play preview sound
+ try {
+ await invoke("preview_voice", { voiceId })
+ } catch (err) {
+ console.error("Failed to preview voice:", err)
+ }
+ }
+
// ### STATE
interface MicrophoneOption {
label: string
@@ -113,11 +137,20 @@
// ### INIT
onMount(async () => {
+ // load voices
+ try {
+ const voices = await invoke("list_voices")
+ availableVoices = voices.map(v => v.voice)
+ } catch (err) {
+ console.error("Failed to load voices:", err)
+ availableVoices = []
+ }
+
try {
// load microphones
const mics = await invoke("pv_get_audio_devices")
availableMicrophones = [
- { label: "По умолчанию (Система)", value: "-1" }, // system default
+ { label: t('settings-mic-default'), value: "-1" }, // system default
...mics.map((name, idx) => ({
label: name,
value: String(idx)
@@ -200,18 +233,42 @@
-
+
+
+
{t('settings-voice-desc')}
+
+
+ {#each availableVoices as voice}
+
+ {/each}
+
+ {#if availableVoices.length === 0}
+
{t('settings-no-voices')}
+ {/if}
+
+
@@ -390,3 +447,112 @@
+
+
\ No newline at end of file
diff --git a/frontend/src/stores.ts b/frontend/src/stores.ts
index 746d9b9..51603ed 100644
--- a/frontend/src/stores.ts
+++ b/frontend/src/stores.ts
@@ -13,6 +13,8 @@ export {
disableIpc,
disconnectIpc,
sendAction,
+ sendIpcMessage,
+ sendTextCommand,
stopJarvisApp,
reloadCommands
} from "./lib/ipc"
diff --git a/frontend/vite.config.ts.timestamp-1767798766136-6d509bc794b39.mjs b/frontend/vite.config.ts.timestamp-1767798766136-6d509bc794b39.mjs
new file mode 100644
index 0000000..cbc86eb
--- /dev/null
+++ b/frontend/vite.config.ts.timestamp-1767798766136-6d509bc794b39.mjs
@@ -0,0 +1,40 @@
+// vite.config.ts
+import { defineConfig } from "file:///D:/Rust/jarvis-app/frontend/node_modules/vite/dist/node/index.js";
+import { svelte } from "file:///D:/Rust/jarvis-app/frontend/node_modules/@sveltejs/vite-plugin-svelte/src/index.js";
+import sveltePreprocess from "file:///D:/Rust/jarvis-app/frontend/node_modules/svelte-preprocess/dist/index.js";
+import tsconfigPaths from "file:///D:/Rust/jarvis-app/frontend/node_modules/vite-tsconfig-paths/dist/index.mjs";
+import routify from "file:///D:/Rust/jarvis-app/frontend/node_modules/@roxi/routify/lib/extra/vite-plugin/vite-plugin.js";
+var vite_config_default = defineConfig({
+ plugins: [
+ svelte({
+ preprocess: [
+ sveltePreprocess({
+ typescript: true
+ })
+ ],
+ onwarn: (warning, handler) => {
+ const { code, frame } = warning;
+ if (code === "css-unused-selector")
+ return;
+ handler(warning);
+ }
+ }),
+ routify(),
+ tsconfigPaths()
+ ],
+ clearScreen: false,
+ server: {
+ port: 1420,
+ strictPort: true
+ },
+ envPrefix: ["VITE_", "TAURI_"],
+ build: {
+ target: process.env.TAURI_PLATFORM == "windows" ? "chrome105" : "safari13",
+ minify: !process.env.TAURI_DEBUG ? "esbuild" : false,
+ sourcemap: !!process.env.TAURI_DEBUG
+ }
+});
+export {
+ vite_config_default as default
+};
+//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJEOlxcXFxSdXN0XFxcXGphcnZpcy1hcHBcXFxcZnJvbnRlbmRcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIkQ6XFxcXFJ1c3RcXFxcamFydmlzLWFwcFxcXFxmcm9udGVuZFxcXFx2aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vRDovUnVzdC9qYXJ2aXMtYXBwL2Zyb250ZW5kL3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSBcInZpdGVcIjtcclxuaW1wb3J0IHsgc3ZlbHRlIH0gZnJvbSBcIkBzdmVsdGVqcy92aXRlLXBsdWdpbi1zdmVsdGVcIjtcclxuaW1wb3J0IHN2ZWx0ZVByZXByb2Nlc3MgZnJvbSBcInN2ZWx0ZS1wcmVwcm9jZXNzXCI7XHJcbmltcG9ydCB0c2NvbmZpZ1BhdGhzIGZyb20gJ3ZpdGUtdHNjb25maWctcGF0aHMnXHJcbmltcG9ydCByb3V0aWZ5IGZyb20gJ0Byb3hpL3JvdXRpZnkvdml0ZS1wbHVnaW4nXHJcblxyXG5leHBvcnQgZGVmYXVsdCBkZWZpbmVDb25maWcoe1xyXG4gIHBsdWdpbnM6IFtcclxuICAgIHN2ZWx0ZSh7XHJcbiAgICAgIHByZXByb2Nlc3M6IFtcclxuICAgICAgICBzdmVsdGVQcmVwcm9jZXNzKHtcclxuICAgICAgICAgIHR5cGVzY3JpcHQ6IHRydWUsXHJcbiAgICAgICAgfSksXHJcbiAgICAgIF0sXHJcbiAgICAgIG9ud2FybjogKHdhcm5pbmcsIGhhbmRsZXIpID0+IHtcclxuICAgICAgICBjb25zdCB7IGNvZGUsIGZyYW1lIH0gPSB3YXJuaW5nO1xyXG4gICAgICAgIGlmIChjb2RlID09PSBcImNzcy11bnVzZWQtc2VsZWN0b3JcIilcclxuICAgICAgICAgICAgcmV0dXJuO1xyXG5cclxuICAgICAgICBoYW5kbGVyKHdhcm5pbmcpO1xyXG4gICAgICB9LFxyXG4gICAgfSksXHJcbiAgICByb3V0aWZ5KCksXHJcbiAgICB0c2NvbmZpZ1BhdGhzKClcclxuICBdLFxyXG5cclxuICBjbGVhclNjcmVlbjogZmFsc2UsXHJcbiAgc2VydmVyOiB7XHJcbiAgICBwb3J0OiAxNDIwLFxyXG4gICAgc3RyaWN0UG9ydDogdHJ1ZSxcclxuICB9LFxyXG4gIGVudlByZWZpeDogW1wiVklURV9cIiwgXCJUQVVSSV9cIl0sXHJcbiAgYnVpbGQ6IHtcclxuICAgIHRhcmdldDogcHJvY2Vzcy5lbnYuVEFVUklfUExBVEZPUk0gPT0gXCJ3aW5kb3dzXCIgPyBcImNocm9tZTEwNVwiIDogXCJzYWZhcmkxM1wiLFxyXG4gICAgbWluaWZ5OiAhcHJvY2Vzcy5lbnYuVEFVUklfREVCVUcgPyBcImVzYnVpbGRcIiA6IGZhbHNlLFxyXG4gICAgc291cmNlbWFwOiAhIXByb2Nlc3MuZW52LlRBVVJJX0RFQlVHLFxyXG4gIH0sXHJcbn0pOyJdLAogICJtYXBwaW5ncyI6ICI7QUFBMlEsU0FBUyxvQkFBb0I7QUFDeFMsU0FBUyxjQUFjO0FBQ3ZCLE9BQU8sc0JBQXNCO0FBQzdCLE9BQU8sbUJBQW1CO0FBQzFCLE9BQU8sYUFBYTtBQUVwQixJQUFPLHNCQUFRLGFBQWE7QUFBQSxFQUMxQixTQUFTO0FBQUEsSUFDUCxPQUFPO0FBQUEsTUFDTCxZQUFZO0FBQUEsUUFDVixpQkFBaUI7QUFBQSxVQUNmLFlBQVk7QUFBQSxRQUNkLENBQUM7QUFBQSxNQUNIO0FBQUEsTUFDQSxRQUFRLENBQUMsU0FBUyxZQUFZO0FBQzVCLGNBQU0sRUFBRSxNQUFNLE1BQU0sSUFBSTtBQUN4QixZQUFJLFNBQVM7QUFDVDtBQUVKLGdCQUFRLE9BQU87QUFBQSxNQUNqQjtBQUFBLElBQ0YsQ0FBQztBQUFBLElBQ0QsUUFBUTtBQUFBLElBQ1IsY0FBYztBQUFBLEVBQ2hCO0FBQUEsRUFFQSxhQUFhO0FBQUEsRUFDYixRQUFRO0FBQUEsSUFDTixNQUFNO0FBQUEsSUFDTixZQUFZO0FBQUEsRUFDZDtBQUFBLEVBQ0EsV0FBVyxDQUFDLFNBQVMsUUFBUTtBQUFBLEVBQzdCLE9BQU87QUFBQSxJQUNMLFFBQVEsUUFBUSxJQUFJLGtCQUFrQixZQUFZLGNBQWM7QUFBQSxJQUNoRSxRQUFRLENBQUMsUUFBUSxJQUFJLGNBQWMsWUFBWTtBQUFBLElBQy9DLFdBQVcsQ0FBQyxDQUFDLFFBQVEsSUFBSTtBQUFBLEVBQzNCO0FBQ0YsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K
diff --git a/resources/sound/jarvis-howdy/game_mode.wav b/resources/sound/voices/jarvis-howdy/ru/game_mode.wav
similarity index 100%
rename from resources/sound/jarvis-howdy/game_mode.wav
rename to resources/sound/voices/jarvis-howdy/ru/game_mode.wav
diff --git a/resources/sound/jarvis-howdy/joke1.wav b/resources/sound/voices/jarvis-howdy/ru/joke1.wav
similarity index 100%
rename from resources/sound/jarvis-howdy/joke1.wav
rename to resources/sound/voices/jarvis-howdy/ru/joke1.wav
diff --git a/resources/sound/jarvis-howdy/joke2.wav b/resources/sound/voices/jarvis-howdy/ru/joke2.wav
similarity index 100%
rename from resources/sound/jarvis-howdy/joke2.wav
rename to resources/sound/voices/jarvis-howdy/ru/joke2.wav
diff --git a/resources/sound/jarvis-howdy/joke3.wav b/resources/sound/voices/jarvis-howdy/ru/joke3.wav
similarity index 100%
rename from resources/sound/jarvis-howdy/joke3.wav
rename to resources/sound/voices/jarvis-howdy/ru/joke3.wav
diff --git a/resources/sound/jarvis-howdy/joke4.wav b/resources/sound/voices/jarvis-howdy/ru/joke4.wav
similarity index 100%
rename from resources/sound/jarvis-howdy/joke4.wav
rename to resources/sound/voices/jarvis-howdy/ru/joke4.wav
diff --git a/resources/sound/jarvis-howdy/joke5.wav b/resources/sound/voices/jarvis-howdy/ru/joke5.wav
similarity index 100%
rename from resources/sound/jarvis-howdy/joke5.wav
rename to resources/sound/voices/jarvis-howdy/ru/joke5.wav
diff --git a/resources/sound/jarvis-howdy/not_found.wav b/resources/sound/voices/jarvis-howdy/ru/not_found.wav
similarity index 100%
rename from resources/sound/jarvis-howdy/not_found.wav
rename to resources/sound/voices/jarvis-howdy/ru/not_found.wav
diff --git a/resources/sound/jarvis-howdy/ok1.wav b/resources/sound/voices/jarvis-howdy/ru/ok1.wav
similarity index 100%
rename from resources/sound/jarvis-howdy/ok1.wav
rename to resources/sound/voices/jarvis-howdy/ru/ok1.wav
diff --git a/resources/sound/jarvis-howdy/ok2.wav b/resources/sound/voices/jarvis-howdy/ru/ok2.wav
similarity index 100%
rename from resources/sound/jarvis-howdy/ok2.wav
rename to resources/sound/voices/jarvis-howdy/ru/ok2.wav
diff --git a/resources/sound/jarvis-howdy/ok3.wav b/resources/sound/voices/jarvis-howdy/ru/ok3.wav
similarity index 100%
rename from resources/sound/jarvis-howdy/ok3.wav
rename to resources/sound/voices/jarvis-howdy/ru/ok3.wav
diff --git a/resources/sound/jarvis-howdy/ok4.wav b/resources/sound/voices/jarvis-howdy/ru/ok4.wav
similarity index 100%
rename from resources/sound/jarvis-howdy/ok4.wav
rename to resources/sound/voices/jarvis-howdy/ru/ok4.wav
diff --git a/resources/sound/jarvis-howdy/ready.wav b/resources/sound/voices/jarvis-howdy/ru/ready.wav
similarity index 100%
rename from resources/sound/jarvis-howdy/ready.wav
rename to resources/sound/voices/jarvis-howdy/ru/ready.wav
diff --git a/resources/sound/jarvis-howdy/greet1.wav b/resources/sound/voices/jarvis-howdy/ru/reply1.wav
similarity index 100%
rename from resources/sound/jarvis-howdy/greet1.wav
rename to resources/sound/voices/jarvis-howdy/ru/reply1.wav
diff --git a/resources/sound/jarvis-howdy/greet2.wav b/resources/sound/voices/jarvis-howdy/ru/reply2.wav
similarity index 100%
rename from resources/sound/jarvis-howdy/greet2.wav
rename to resources/sound/voices/jarvis-howdy/ru/reply2.wav
diff --git a/resources/sound/jarvis-howdy/greet3.wav b/resources/sound/voices/jarvis-howdy/ru/reply3.wav
similarity index 100%
rename from resources/sound/jarvis-howdy/greet3.wav
rename to resources/sound/voices/jarvis-howdy/ru/reply3.wav
diff --git a/resources/sound/jarvis-howdy/run.wav b/resources/sound/voices/jarvis-howdy/ru/run.wav
similarity index 100%
rename from resources/sound/jarvis-howdy/run.wav
rename to resources/sound/voices/jarvis-howdy/ru/run.wav
diff --git a/resources/sound/jarvis-howdy/stupid.wav b/resources/sound/voices/jarvis-howdy/ru/stupid.wav
similarity index 100%
rename from resources/sound/jarvis-howdy/stupid.wav
rename to resources/sound/voices/jarvis-howdy/ru/stupid.wav
diff --git a/resources/sound/jarvis-howdy/thanks.wav b/resources/sound/voices/jarvis-howdy/ru/thanks.wav
similarity index 100%
rename from resources/sound/jarvis-howdy/thanks.wav
rename to resources/sound/voices/jarvis-howdy/ru/thanks.wav
diff --git a/resources/sound/voices/jarvis-howdy/voice.toml b/resources/sound/voices/jarvis-howdy/voice.toml
new file mode 100644
index 0000000..ba51ed5
--- /dev/null
+++ b/resources/sound/voices/jarvis-howdy/voice.toml
@@ -0,0 +1,27 @@
+[voice]
+id = "jarvis-howdy"
+name = "Jarvis Howdy"
+author = "Abraham (Priler)"
+languages = ["ru"]
+
+[reactions]
+# app startup
+greet = ["run", "ready"]
+
+# wake word detected
+reply = ["reply1", "reply2", "reply3"]
+
+# command executed
+ok = ["ok1", "ok2", "ok3", "ok4"]
+
+# command not found
+not_found = ["not_found"]
+
+# thanks
+thanks = ["thanks"]
+
+# error
+error = []
+
+# goodbye
+goodbye = ["off"]
\ No newline at end of file
diff --git a/resources/sound/jarvis-og/game_mode.wav b/resources/sound/voices/jarvis-og/ru/game_mode.wav
similarity index 100%
rename from resources/sound/jarvis-og/game_mode.wav
rename to resources/sound/voices/jarvis-og/ru/game_mode.wav
diff --git a/resources/sound/jarvis-og/not_found.wav b/resources/sound/voices/jarvis-og/ru/not_found.wav
similarity index 100%
rename from resources/sound/jarvis-og/not_found.wav
rename to resources/sound/voices/jarvis-og/ru/not_found.wav
diff --git a/resources/sound/jarvis-og/off.wav b/resources/sound/voices/jarvis-og/ru/off.wav
similarity index 100%
rename from resources/sound/jarvis-og/off.wav
rename to resources/sound/voices/jarvis-og/ru/off.wav
diff --git a/resources/sound/jarvis-og/ok1.wav b/resources/sound/voices/jarvis-og/ru/ok1.wav
similarity index 100%
rename from resources/sound/jarvis-og/ok1.wav
rename to resources/sound/voices/jarvis-og/ru/ok1.wav
diff --git a/resources/sound/jarvis-og/ok2.wav b/resources/sound/voices/jarvis-og/ru/ok2.wav
similarity index 100%
rename from resources/sound/jarvis-og/ok2.wav
rename to resources/sound/voices/jarvis-og/ru/ok2.wav
diff --git a/resources/sound/jarvis-og/ok3.wav b/resources/sound/voices/jarvis-og/ru/ok3.wav
similarity index 100%
rename from resources/sound/jarvis-og/ok3.wav
rename to resources/sound/voices/jarvis-og/ru/ok3.wav
diff --git a/resources/sound/jarvis-og/ok4.wav b/resources/sound/voices/jarvis-og/ru/ok4.wav
similarity index 100%
rename from resources/sound/jarvis-og/ok4.wav
rename to resources/sound/voices/jarvis-og/ru/ok4.wav
diff --git a/resources/sound/jarvis-og/greet1.wav b/resources/sound/voices/jarvis-og/ru/reply1.wav
similarity index 100%
rename from resources/sound/jarvis-og/greet1.wav
rename to resources/sound/voices/jarvis-og/ru/reply1.wav
diff --git a/resources/sound/jarvis-og/greet2.wav b/resources/sound/voices/jarvis-og/ru/reply2.wav
similarity index 100%
rename from resources/sound/jarvis-og/greet2.wav
rename to resources/sound/voices/jarvis-og/ru/reply2.wav
diff --git a/resources/sound/jarvis-og/greet3.wav b/resources/sound/voices/jarvis-og/ru/reply3.wav
similarity index 100%
rename from resources/sound/jarvis-og/greet3.wav
rename to resources/sound/voices/jarvis-og/ru/reply3.wav
diff --git a/resources/sound/jarvis-og/run.wav b/resources/sound/voices/jarvis-og/ru/run.wav
similarity index 100%
rename from resources/sound/jarvis-og/run.wav
rename to resources/sound/voices/jarvis-og/ru/run.wav
diff --git a/resources/sound/jarvis-og/stupid.wav b/resources/sound/voices/jarvis-og/ru/stupid.wav
similarity index 100%
rename from resources/sound/jarvis-og/stupid.wav
rename to resources/sound/voices/jarvis-og/ru/stupid.wav
diff --git a/resources/sound/jarvis-og/thanks.wav b/resources/sound/voices/jarvis-og/ru/thanks.wav
similarity index 100%
rename from resources/sound/jarvis-og/thanks.wav
rename to resources/sound/voices/jarvis-og/ru/thanks.wav
diff --git a/resources/sound/voices/jarvis-og/voice.toml b/resources/sound/voices/jarvis-og/voice.toml
new file mode 100644
index 0000000..1059f2e
--- /dev/null
+++ b/resources/sound/voices/jarvis-og/voice.toml
@@ -0,0 +1,27 @@
+[voice]
+id = "jarvis-og"
+name = "Jarvis OG"
+author = "Abraham (Priler)"
+languages = ["ru"]
+
+[reactions]
+# app startup
+greet = ["run"]
+
+# wake word detected
+reply = ["reply1", "reply2", "reply3"]
+
+# command executed
+ok = ["ok1", "ok2", "ok3", "ok4"]
+
+# command not found
+not_found = ["not_found"]
+
+# thanks
+thanks = ["thanks"]
+
+# error
+error = []
+
+# goodbye
+goodbye = ["off"]
\ No newline at end of file
diff --git a/resources/sound/jarvis-remaster/greet1.mp3 b/resources/sound/voices/jarvis-remaster/ru/greet1.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/greet1.mp3
rename to resources/sound/voices/jarvis-remaster/ru/greet1.mp3
diff --git a/resources/sound/jarvis-remaster/greet_day.mp3 b/resources/sound/voices/jarvis-remaster/ru/greet_day.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/greet_day.mp3
rename to resources/sound/voices/jarvis-remaster/ru/greet_day.mp3
diff --git a/resources/sound/jarvis-remaster/greet_evening.mp3 b/resources/sound/voices/jarvis-remaster/ru/greet_evening.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/greet_evening.mp3
rename to resources/sound/voices/jarvis-remaster/ru/greet_evening.mp3
diff --git a/resources/sound/jarvis-remaster/greet_morning.mp3 b/resources/sound/voices/jarvis-remaster/ru/greet_morning.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/greet_morning.mp3
rename to resources/sound/voices/jarvis-remaster/ru/greet_morning.mp3
diff --git a/resources/sound/jarvis-remaster/greet_night.mp3 b/resources/sound/voices/jarvis-remaster/ru/greet_night.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/greet_night.mp3
rename to resources/sound/voices/jarvis-remaster/ru/greet_night.mp3
diff --git a/resources/sound/jarvis-remaster/joke1.mp3 b/resources/sound/voices/jarvis-remaster/ru/joke1.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/joke1.mp3
rename to resources/sound/voices/jarvis-remaster/ru/joke1.mp3
diff --git a/resources/sound/jarvis-remaster/joke2.mp3 b/resources/sound/voices/jarvis-remaster/ru/joke2.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/joke2.mp3
rename to resources/sound/voices/jarvis-remaster/ru/joke2.mp3
diff --git a/resources/sound/jarvis-remaster/joke3.mp3 b/resources/sound/voices/jarvis-remaster/ru/joke3.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/joke3.mp3
rename to resources/sound/voices/jarvis-remaster/ru/joke3.mp3
diff --git a/resources/sound/jarvis-remaster/joke4.mp3 b/resources/sound/voices/jarvis-remaster/ru/joke4.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/joke4.mp3
rename to resources/sound/voices/jarvis-remaster/ru/joke4.mp3
diff --git a/resources/sound/jarvis-remaster/joke5.mp3 b/resources/sound/voices/jarvis-remaster/ru/joke5.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/joke5.mp3
rename to resources/sound/voices/jarvis-remaster/ru/joke5.mp3
diff --git a/resources/sound/jarvis-remaster/joke6.mp3 b/resources/sound/voices/jarvis-remaster/ru/joke6.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/joke6.mp3
rename to resources/sound/voices/jarvis-remaster/ru/joke6.mp3
diff --git a/resources/sound/jarvis-remaster/joke7.mp3 b/resources/sound/voices/jarvis-remaster/ru/joke7.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/joke7.mp3
rename to resources/sound/voices/jarvis-remaster/ru/joke7.mp3
diff --git a/resources/sound/jarvis-remaster/joke8.mp3 b/resources/sound/voices/jarvis-remaster/ru/joke8.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/joke8.mp3
rename to resources/sound/voices/jarvis-remaster/ru/joke8.mp3
diff --git a/resources/sound/jarvis-remaster/ok1.mp3 b/resources/sound/voices/jarvis-remaster/ru/ok1.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/ok1.mp3
rename to resources/sound/voices/jarvis-remaster/ru/ok1.mp3
diff --git a/resources/sound/jarvis-remaster/ok2.mp3 b/resources/sound/voices/jarvis-remaster/ru/ok2.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/ok2.mp3
rename to resources/sound/voices/jarvis-remaster/ru/ok2.mp3
diff --git a/resources/sound/jarvis-remaster/ok3.mp3 b/resources/sound/voices/jarvis-remaster/ru/ok3.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/ok3.mp3
rename to resources/sound/voices/jarvis-remaster/ru/ok3.mp3
diff --git a/resources/sound/jarvis-remaster/ok4.mp3 b/resources/sound/voices/jarvis-remaster/ru/ok4.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/ok4.mp3
rename to resources/sound/voices/jarvis-remaster/ru/ok4.mp3
diff --git a/resources/sound/jarvis-remaster/reply1.mp3 b/resources/sound/voices/jarvis-remaster/ru/reply1.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/reply1.mp3
rename to resources/sound/voices/jarvis-remaster/ru/reply1.mp3
diff --git a/resources/sound/jarvis-remaster/reply2.mp3 b/resources/sound/voices/jarvis-remaster/ru/reply2.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/reply2.mp3
rename to resources/sound/voices/jarvis-remaster/ru/reply2.mp3
diff --git a/resources/sound/jarvis-remaster/reply3.mp3 b/resources/sound/voices/jarvis-remaster/ru/reply3.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/reply3.mp3
rename to resources/sound/voices/jarvis-remaster/ru/reply3.mp3
diff --git a/resources/sound/jarvis-remaster/reply4.mp3 b/resources/sound/voices/jarvis-remaster/ru/reply4.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/reply4.mp3
rename to resources/sound/voices/jarvis-remaster/ru/reply4.mp3
diff --git a/resources/sound/jarvis-remaster/reply5.mp3 b/resources/sound/voices/jarvis-remaster/ru/reply5.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/reply5.mp3
rename to resources/sound/voices/jarvis-remaster/ru/reply5.mp3
diff --git a/resources/sound/jarvis-remaster/reply6.mp3 b/resources/sound/voices/jarvis-remaster/ru/reply6.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/reply6.mp3
rename to resources/sound/voices/jarvis-remaster/ru/reply6.mp3
diff --git a/resources/sound/jarvis-remaster/stupid.mp3 b/resources/sound/voices/jarvis-remaster/ru/stupid.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/stupid.mp3
rename to resources/sound/voices/jarvis-remaster/ru/stupid.mp3
diff --git a/resources/sound/jarvis-remaster/thanks.mp3 b/resources/sound/voices/jarvis-remaster/ru/thanks.mp3
similarity index 100%
rename from resources/sound/jarvis-remaster/thanks.mp3
rename to resources/sound/voices/jarvis-remaster/ru/thanks.mp3
diff --git a/resources/sound/voices/jarvis-remaster/voice.toml b/resources/sound/voices/jarvis-remaster/voice.toml
new file mode 100644
index 0000000..a31892e
--- /dev/null
+++ b/resources/sound/voices/jarvis-remaster/voice.toml
@@ -0,0 +1,31 @@
+[voice]
+id = "jarvis-remaster"
+name = "Jarvis New"
+author = "Abraham (Priler)"
+languages = ["ru", "en"]
+
+[reactions]
+# app startup
+greet = ["greet1"]
+greet_morning = ["greet_morning"]
+greet_day = ["greet_day"]
+greet_evening = ["greet_evening"]
+greet_night = ["greet_night"]
+
+# wake word detected
+reply = ["reply1", "reply2", "reply3", "reply4", "reply5", "reply6"]
+
+# command executed
+ok = ["ok1", "ok2", "ok3", "ok4"]
+
+# command not found
+not_found = []
+
+# thanks
+thanks = ["thanks"]
+
+# error
+error = []
+
+# goodbye
+goodbye = []
\ No newline at end of file