mirror of
https://github.com/Priler/jarvis.git
synced 2026-05-26 07:08:11 +00:00
Tray options implementation + Voice system rewrite + build fixes
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -3123,6 +3123,7 @@ dependencies = [
|
||||
name = "jarvis-core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fluent",
|
||||
"fluent-bundle",
|
||||
"futures-util",
|
||||
|
||||
@@ -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"
|
||||
unic-langid = "0.9"
|
||||
chrono = "0.4"
|
||||
@@ -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<String>) -> Result<(), ()> {
|
||||
// start the loop
|
||||
main_loop()
|
||||
main_loop(text_cmd_rx)
|
||||
}
|
||||
|
||||
fn main_loop() -> Result<(), ()> {
|
||||
fn main_loop(text_cmd_rx: Receiver<String>) -> 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<i16> = 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);
|
||||
}
|
||||
|
||||
@@ -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::<String>();
|
||||
|
||||
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();
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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 }
|
||||
|
||||
@@ -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<PathBuf> {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Vec<JCommandsList>, String> {
|
||||
// collect commands
|
||||
let mut commands: Vec<JCommandsList> = 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<bool, String> {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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 = Голоса не найдены
|
||||
@@ -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 = Голоси не знайдено
|
||||
@@ -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};
|
||||
pub use server::{init, send, set_action_handler, start_server, has_clients, IPC_ADDR, IPC_PORT};
|
||||
@@ -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 },
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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<PathBuf> = Lazy::new(|| std::env::current_dir().unwrap());
|
||||
pub static APP_DIR: Lazy<PathBuf> = Lazy::new(|| {
|
||||
@@ -40,7 +44,7 @@ pub static APP_DIR: Lazy<PathBuf> = 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<PathBuf> = Lazy::new(|| APP_DIR.clone().join("resources/sound"));
|
||||
pub static SOUND_DIR: Lazy<PathBuf> = Lazy::new(|| APP_DIR.clone().join(config::SOUND_PATH));
|
||||
pub static APP_DIRS: OnceCell<AppDirs> = OnceCell::new();
|
||||
pub static APP_CONFIG_DIR: OnceCell<PathBuf> = OnceCell::new();
|
||||
pub static APP_LOG_DIR: OnceCell<PathBuf> = OnceCell::new();
|
||||
|
||||
2
crates/jarvis-core/src/time.rs
Normal file
2
crates/jarvis-core/src/time.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod structs;
|
||||
pub use structs::*;
|
||||
21
crates/jarvis-core/src/time/structs.rs
Normal file
21
crates/jarvis-core/src/time/structs.rs
Normal file
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
234
crates/jarvis-core/src/voices.rs
Normal file
234
crates/jarvis-core/src/voices.rs
Normal file
@@ -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<Vec<structs::VoiceConfig>> = OnceCell::new();
|
||||
static CURRENT_VOICE_ID: OnceCell<RwLock<String>> = 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::<Vec<_>>()
|
||||
);
|
||||
|
||||
VOICES.set(voices).map_err(|_| "Voices already initialized")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn scan_voices() -> Result<Vec<structs::VoiceConfig>, 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<structs::VoiceConfig, String> {
|
||||
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<structs::VoiceConfig> {
|
||||
VOICES.get().cloned().unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn get_voice(voice_id: &str) -> Option<structs::VoiceConfig> {
|
||||
VOICES.get()?.iter().find(|v| v.voice.id == voice_id).cloned()
|
||||
}
|
||||
|
||||
pub fn get_current_voice() -> Option<structs::VoiceConfig> {
|
||||
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<PathBuf> {
|
||||
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); }
|
||||
70
crates/jarvis-core/src/voices/structs.rs
Normal file
70
crates/jarvis-core/src/voices/structs.rs
Normal file
@@ -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<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct VoiceReactions {
|
||||
// app startup (time-based or generic)
|
||||
#[serde(default)]
|
||||
pub greet: Vec<String>,
|
||||
|
||||
#[serde(default)]
|
||||
pub greet_morning: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub greet_day: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub greet_evening: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub greet_night: Vec<String>,
|
||||
|
||||
// wake word detected
|
||||
#[serde(default)]
|
||||
pub reply: Vec<String>,
|
||||
|
||||
// command executed
|
||||
#[serde(default)]
|
||||
pub ok: Vec<String>,
|
||||
|
||||
// command not found
|
||||
#[serde(default)]
|
||||
pub not_found: Vec<String>,
|
||||
|
||||
// thank you
|
||||
#[serde(default)]
|
||||
pub thanks: Vec<String>,
|
||||
|
||||
// error
|
||||
#[serde(default)]
|
||||
pub error: Vec<String>,
|
||||
|
||||
// shutdown
|
||||
#[serde(default)]
|
||||
pub goodbye: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Reaction {
|
||||
Greet, // app startup
|
||||
Reply, // wake word detected
|
||||
Ok, // command executed
|
||||
NotFound,
|
||||
Thanks,
|
||||
Error,
|
||||
Goodbye,
|
||||
}
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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/**"]}]}}
|
||||
{"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"]}}
|
||||
@@ -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");
|
||||
|
||||
@@ -29,4 +29,12 @@ pub use stt::*;
|
||||
|
||||
// import i18n commands
|
||||
mod i18n;
|
||||
pub use i18n::*;
|
||||
pub use i18n::*;
|
||||
|
||||
// import commands commands xD
|
||||
mod commands;
|
||||
pub use commands::*;
|
||||
|
||||
// import voices commands
|
||||
mod voices;
|
||||
pub use voices::*;
|
||||
22
crates/jarvis-gui/src/tauri_commands/commands.rs
Normal file
22
crates/jarvis-gui/src/tauri_commands/commands.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use jarvis_core::commands::{self, JCommand, JCommandsList};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
static COMMANDS: Lazy<Vec<JCommandsList>> = 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<JCommand> {
|
||||
COMMANDS
|
||||
.iter()
|
||||
.flat_map(|list| list.commands.clone())
|
||||
.collect()
|
||||
}
|
||||
16
crates/jarvis-gui/src/tauri_commands/voices.rs
Normal file
16
crates/jarvis-gui/src/tauri_commands/voices.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use jarvis_core::voices::{self, structs::VoiceConfig};
|
||||
|
||||
#[tauri::command]
|
||||
pub fn list_voices() -> Vec<VoiceConfig> {
|
||||
voices::list_voices()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_voice(voice_id: String) -> Option<VoiceConfig> {
|
||||
voices::get_voice(&voice_id)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn preview_voice(voice_id: String) {
|
||||
voices::play_preview(&voice_id);
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
<!-- src/App.svelte -->
|
||||
<script>
|
||||
import { Router } from "@roxi/routify";
|
||||
import routes from "../.routify/routes.default.js";
|
||||
|
||||
import { SvelteUIProvider } from '@svelteuidev/core';
|
||||
|
||||
import Events from "./Events.svelte";
|
||||
|
||||
/** START LISTENING **/
|
||||
// import { startListening } from "./functions";
|
||||
// startListening();
|
||||
</script>
|
||||
|
||||
<SvelteUIProvider themeObserver='dark' withNormalizeCSS withGlobalStyles>
|
||||
<Router {routes} />
|
||||
</SvelteUIProvider>
|
||||
|
||||
<Events />
|
||||
@@ -1,44 +0,0 @@
|
||||
<script>
|
||||
import { onMount, onDestroy } from 'svelte'
|
||||
import { emit, listen } from '@tauri-apps/api/event'
|
||||
|
||||
import { resolveResource } from '@tauri-apps/api/path'
|
||||
|
||||
import {Howl, Howler} from 'howler';
|
||||
|
||||
let assistant_voice_val = "jarvis-og";
|
||||
import { assistant_voice } from "@/stores"
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
assistant_voice.subscribe(value => {
|
||||
assistant_voice_val = value;
|
||||
});
|
||||
|
||||
onMount(async () => {
|
||||
await listen('audio-play', async (event) => {
|
||||
// event.event is the event name (useful if you want to use a single callback fn for multiple event types)
|
||||
// event.payload is the payload object
|
||||
// let path = await resolveResource('sound/' + (assistant_voice_val == "" ? "jarvis-remake":assistant_voice_val) + '/' + event.payload['data'] + '.wav');
|
||||
// console.log(path);
|
||||
// let sound = new Howl({
|
||||
// src: [path],
|
||||
// html5: true
|
||||
// });
|
||||
|
||||
// sound.play();
|
||||
|
||||
let filename = 'sound/' + (assistant_voice_val == "" ? "jarvis-remake":assistant_voice_val) + '/' + event.payload['data'] + '.wav';
|
||||
await invoke("play_sound", {
|
||||
filename: filename,
|
||||
sleep: true
|
||||
});
|
||||
});
|
||||
|
||||
await listen('assistant-greet', (event) => {
|
||||
document.getElementById("arc-reactor")?.classList.add("active");
|
||||
});
|
||||
|
||||
await listen('assistant-waiting', (event) => {
|
||||
document.getElementById("arc-reactor")?.classList.remove("active");
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -1,73 +0,0 @@
|
||||
<script>
|
||||
import { invoke } from "@tauri-apps/api/core"
|
||||
|
||||
import { tg_official_link, github_repository_link } from "@/stores";
|
||||
|
||||
let current_year = new Date().getFullYear();
|
||||
let author_name = "";
|
||||
|
||||
(async () => {
|
||||
author_name = await invoke("get_author_name")
|
||||
|
||||
})().catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
</script>
|
||||
|
||||
<footer id="footer">
|
||||
<p>© {current_year}. Автор проекта: {author_name}</p>
|
||||
<p style="margin-top: 5px;margin-bottom: 15px;">
|
||||
<a href="{tg_official_link}" target="_blank" class="special-link"><img src="/media/icons/howdy-logo.png" alt="" width="20px"> Наш телеграм</a> канал.
|
||||
|
||||
<a href="{github_repository_link}" target="_blank"><img src="/media/icons/github-logo.png" alt="" width="18px"> Github репозиторий</a> проекта.</p>
|
||||
</footer>
|
||||
|
||||
<style lang="scss">
|
||||
#footer {
|
||||
text-align: center;
|
||||
color: #565759;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
line-height: 1.7em;
|
||||
margin-top: 15px;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #185876;
|
||||
text-decoration: none;
|
||||
transition: opacity .5s;
|
||||
|
||||
img {
|
||||
opacity: .5;
|
||||
transition: opacity .5s;
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #2A9CD0;
|
||||
|
||||
img {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.special-link {
|
||||
color: #941d92;
|
||||
display: inline-block;
|
||||
|
||||
&:hover {
|
||||
color: #FF07FC;
|
||||
|
||||
background: url(/media/images/bg/bg24.gif);
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,28 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { invoke } from "@tauri-apps/api/core"
|
||||
import { Dashboard, Gear } from 'radix-icons-svelte'
|
||||
import {isActive} from '@roxi/routify'
|
||||
|
||||
let app_version = "";
|
||||
|
||||
(async () => {
|
||||
app_version = await invoke("get_app_version")
|
||||
})().catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
</script>
|
||||
<header id="header">
|
||||
<div class="logo">
|
||||
<a href="/" title="Проект канала Хауди Хо!"><img src="/media/header-logo.png" alt=""></a>
|
||||
<div>
|
||||
<h1><a href="/">JARVIS</a></h1>
|
||||
<h2>v{app_version} <small style="color: #8AC832;opacity: .9;font-size: 13px;">BETA</small></h2>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="top-menu">
|
||||
<ul>
|
||||
<li><a href="/commands" class:active={$isActive('/commands')}><Dashboard /> Команды</a></li>
|
||||
<li><a href="/settings" class:active={$isActive('/settings')}><Gear /> Настройки</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
<nav>
|
||||
<a href="/index">Main Page</a>
|
||||
<a href="/settings">Настройки</a>
|
||||
</nav>
|
||||
@@ -1,647 +0,0 @@
|
||||
<!-- Based on: https://github.com/rembertdesigns/Iron-Man-Arc-Reactor-Pure-CSS and https://codepen.io/FlyingEmu/pen/DZNqEj -->
|
||||
<div id="arc-reactor" class="reactor-container">
|
||||
<div class="reactor-container-inner circle abs-center">
|
||||
<ul class="marks"><li></li><li></li><li></li><li></li><li></li><li></li>
|
||||
<li></li><li></li><li></li><li></li><li></li><li></li>
|
||||
<li></li><li></li><li></li><li></li><li></li><li></li>
|
||||
<li></li><li></li><li></li><li></li><li></li><li></li>
|
||||
<li></li><li></li><li></li><li></li><li></li><li></li>
|
||||
<li></li><li></li><li></li><li></li><li></li><li></li>
|
||||
<li></li><li></li><li></li><li></li><li></li><li></li>
|
||||
<li></li><li></li><li></li><li></li><li></li><li></li>
|
||||
<li></li><li></li><li></li><li></li><li></li><li></li>
|
||||
<li></li><li></li><li></li><li></li><li></li><li></li></ul>
|
||||
<div class="e7">
|
||||
<div class="semi_arc_3 e5_1">
|
||||
<div class="semi_arc_3 e5_2">
|
||||
<div class="semi_arc_3 e5_3">
|
||||
<div class="semi_arc_3 e5_4" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tunnel circle abs-center" />
|
||||
<div class="core-wrapper circle abs-center" />
|
||||
<div class="core-outer circle abs-center" />
|
||||
<div class="core-inner circle abs-center" />
|
||||
<div class="coil-container">
|
||||
<div class="coil coil-1" />
|
||||
<div class="coil coil-2" />
|
||||
<div class="coil coil-3" />
|
||||
<div class="coil coil-4" />
|
||||
<div class="coil coil-5" />
|
||||
<div class="coil coil-6" />
|
||||
<div class="coil coil-7" />
|
||||
<div class="coil coil-8" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss" global>
|
||||
$arc_radius: 130px;
|
||||
$size3: 6px;
|
||||
$cshadow: rgba(2, 254, 255, 0.8);
|
||||
$marks_color_1: rgba(2, 254, 255, 1);
|
||||
$marks_color_2: rgba(2, 254, 255, 0.3);
|
||||
$colour1: rgba(2, 255, 255, 0.15);
|
||||
$colour3: rgba(2, 255, 255, 0.3);
|
||||
$cshadow: rgba(2, 254, 255, 0.8);
|
||||
|
||||
.reactor-container {
|
||||
width: 300px;
|
||||
height: 320px;
|
||||
margin: auto;
|
||||
// border: 1px dashed #888;
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
transition: scale 1s ease, opacity .5s ease;
|
||||
scale: 0.9;
|
||||
opacity: .9;
|
||||
// background-color: #384c50;
|
||||
// border: 1px solid #121414;
|
||||
// box-shadow: 0px 0px 32px 8px #121414, 0px 0px 4px 1px #121414 inset;
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
.reactor-container-inner {
|
||||
height: 238px;
|
||||
width: 238px;
|
||||
background-color: #161a1b;
|
||||
box-shadow: 0px 0px 50px 15px $colour3, inset 0px 0px 50px 15px $colour3;
|
||||
// box-shadow: 0px 0px 4px 1px #52fefe;
|
||||
}
|
||||
.circle {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.abs-center {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
margin: auto;
|
||||
}
|
||||
.core-inner {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
border: 5px solid #1b4e5f;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0px 0px 7px 5px #52fefe, 0px 0px 10px 10px #52fefe inset;
|
||||
}
|
||||
.core-outer {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border: 1px solid #52fefe;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0px 0px 2px 1px #52fefe, 0px 0px 10px 5px #52fefe inset;
|
||||
}
|
||||
.core-wrapper {
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
background-color: #073c4b;
|
||||
box-shadow: 0px 0px 5px 4px #52fefe, 0px 0px 6px 2px #52fefe inset;
|
||||
}
|
||||
.tunnel {
|
||||
width: 220px;
|
||||
height: 220px;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0px 0px 5px 1px #52fefe, 0px 0px 5px 4px #52fefe inset;
|
||||
}
|
||||
.coil-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
animation: 10s infinite linear reactor-anim;
|
||||
transition: animation 1s;
|
||||
}
|
||||
.coil {
|
||||
position: absolute;
|
||||
width: 30px;
|
||||
height: 20px;
|
||||
top: calc(50% - 110px);
|
||||
left: calc(50% - 15px);
|
||||
transform-origin: 15px 110px;
|
||||
background-color: #073c4b;
|
||||
box-shadow: 0px 0px 5px #52fefe inset;
|
||||
}
|
||||
.coil-1 {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
.coil-2 {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.coil-3 {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
.coil-4 {
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
.coil-5 {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
.coil-6 {
|
||||
transform: rotate(225deg);
|
||||
}
|
||||
.coil-7 {
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
.coil-8 {
|
||||
transform: rotate(315deg);
|
||||
}
|
||||
@keyframes reactor-anim {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin border-radius($pixel...) {
|
||||
border-radius: $pixel;
|
||||
}
|
||||
|
||||
.e7 {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 160%;
|
||||
height: 160%;
|
||||
left: -32.5%;
|
||||
top: -32.5%;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
border: $size3 solid transparent;
|
||||
background: transparent;
|
||||
@include border-radius(50%);
|
||||
transform: rotateZ(0deg);
|
||||
transition: box-shadow 3s ease;
|
||||
text-align: center;
|
||||
opacity: .3;
|
||||
}
|
||||
|
||||
.semi_arc {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border: 6px solid #02feff;
|
||||
background: rgba(2, 254, 255, 0.2);
|
||||
-moz-border-radius: 50%;
|
||||
-webkit-border-radius: 50%;
|
||||
border-radius: 50%;
|
||||
transform: rotateZ(0deg);
|
||||
transition: box-shadow 3s ease;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.semi_arc:hover {
|
||||
box-shadow: 0px 0px 30px $cshadow;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.semi_arc_2 {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 94%;
|
||||
height: 94%;
|
||||
left: 3%;
|
||||
top: 3%;
|
||||
border: 5px solid #02feff;
|
||||
-moz-border-radius: 50%;
|
||||
-webkit-border-radius: 50%;
|
||||
border-radius: 50%;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
animation: rotate 4s linear infinite;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.semi_arc_2:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 94%;
|
||||
height: 94%;
|
||||
left: 3%;
|
||||
top: 3%;
|
||||
border: 4px solid #02feff;
|
||||
-moz-border-radius: 50%;
|
||||
-webkit-border-radius: 50%;
|
||||
border-radius: 50%;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
animation: rotate_anti 2s linear infinite;
|
||||
}
|
||||
|
||||
.semi_arc_3 {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 94%;
|
||||
height: 94%;
|
||||
left: 3%;
|
||||
top: 3%;
|
||||
border: 5px solid #02feff;
|
||||
-moz-border-radius: 50%;
|
||||
-webkit-border-radius: 50%;
|
||||
border-radius: 50%;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
animation: rotate 4s linear infinite;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.e1:after {
|
||||
border-color: rgba(2, 255, 255, 0.6);
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
}
|
||||
|
||||
.e2:after {
|
||||
border-color: rgba(2, 255, 255, 0.6);
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
border-bottom: 5px solid transparent;
|
||||
}
|
||||
|
||||
.e3 {
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
animation: rotate 5s linear infinite;
|
||||
}
|
||||
|
||||
.e3:after {
|
||||
border-color: rgba(2, 255, 255, 0.6);
|
||||
border-top: 5px solid transparent;
|
||||
border-bottom: 5px solid transparent;
|
||||
}
|
||||
|
||||
.e4 {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
.e4_1 {
|
||||
border-color: rgba(2, 255, 255, 0.3);
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
}
|
||||
|
||||
.e4_1:after {
|
||||
border-color: rgba(2, 255, 255, 0.6);
|
||||
border-top: 4px solid transparent;
|
||||
border-bottom: 4px solid transparent;
|
||||
}
|
||||
|
||||
.e5 {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.e5_1 {
|
||||
color: rgba(2, 255, 255, 0.15);
|
||||
border: 2px solid;
|
||||
border-left: 2px solid transparent;
|
||||
animation: rotate 5s linear infinite;
|
||||
}
|
||||
|
||||
.e5_2 {
|
||||
color: rgba(2, 255, 255, 0.7);
|
||||
border: 4px solid;
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
animation: rotate_anti 4s linear infinite;
|
||||
}
|
||||
|
||||
.e5_3 {
|
||||
color: rgba(2, 255, 255, 0.5);
|
||||
border: 2px solid;
|
||||
border-left: 2px solid transparent;
|
||||
border-right: 2px solid transparent;
|
||||
animation: rotate 3s linear infinite;
|
||||
}
|
||||
|
||||
.e5_4 {
|
||||
color: rgba(2, 255, 255, 0.15);
|
||||
border: 4px solid;
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-bottom: 4px solid transparent;
|
||||
animation: rotate_anti 2s linear infinite;
|
||||
}
|
||||
|
||||
.e6 {
|
||||
border-color: transparent;
|
||||
background: rgba(255, 255, 255, 0);
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
@keyframes rotate_anti {
|
||||
0% {
|
||||
transform: rotateZ(360deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotateZ(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
0% {
|
||||
transform: rotateZ(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotateZ(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.marks {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
|
||||
li {
|
||||
display: block;
|
||||
width: 3px;
|
||||
height: 11px;
|
||||
background: $cshadow;
|
||||
position: absolute;
|
||||
margin-left: 117.5px;
|
||||
margin-top: 113.5px;
|
||||
animation: colour_ease2 3s infinite ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes colour_ease2 {
|
||||
0% {
|
||||
background: $marks_color_1;
|
||||
}
|
||||
50% {
|
||||
background: $marks_color_2;
|
||||
}
|
||||
100% {
|
||||
background: $marks_color_1;
|
||||
}
|
||||
}
|
||||
|
||||
.marks li:first-child {
|
||||
transform: rotate(6deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(2) {
|
||||
transform: rotate(12deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(3) {
|
||||
transform: rotate(18deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(4) {
|
||||
transform: rotate(24deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(5) {
|
||||
transform: rotate(30deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(6) {
|
||||
transform: rotate(36deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(7) {
|
||||
transform: rotate(42deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(8) {
|
||||
transform: rotate(48deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(9) {
|
||||
transform: rotate(54deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(10) {
|
||||
transform: rotate(60deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(11) {
|
||||
transform: rotate(66deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(12) {
|
||||
transform: rotate(72deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(13) {
|
||||
transform: rotate(78deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(14) {
|
||||
transform: rotate(84deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(15) {
|
||||
transform: rotate(90deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(16) {
|
||||
transform: rotate(96deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(17) {
|
||||
transform: rotate(102deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(18) {
|
||||
transform: rotate(108deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(19) {
|
||||
transform: rotate(114deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(20) {
|
||||
transform: rotate(120deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(21) {
|
||||
transform: rotate(126deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(22) {
|
||||
transform: rotate(132deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(23) {
|
||||
transform: rotate(138deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(24) {
|
||||
transform: rotate(144deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(25) {
|
||||
transform: rotate(150deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(26) {
|
||||
transform: rotate(156deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(27) {
|
||||
transform: rotate(162deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(28) {
|
||||
transform: rotate(168deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(29) {
|
||||
transform: rotate(174deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(30) {
|
||||
transform: rotate(180deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(31) {
|
||||
transform: rotate(186deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(32) {
|
||||
transform: rotate(192deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(33) {
|
||||
transform: rotate(198deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(34) {
|
||||
transform: rotate(204deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(35) {
|
||||
transform: rotate(210deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(36) {
|
||||
transform: rotate(216deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(37) {
|
||||
transform: rotate(222deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(38) {
|
||||
transform: rotate(228deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(39) {
|
||||
transform: rotate(234deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(40) {
|
||||
transform: rotate(240deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(41) {
|
||||
transform: rotate(246deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(42) {
|
||||
transform: rotate(252deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(43) {
|
||||
transform: rotate(258deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(44) {
|
||||
transform: rotate(264deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(45) {
|
||||
transform: rotate(270deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(46) {
|
||||
transform: rotate(276deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(47) {
|
||||
transform: rotate(282deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(48) {
|
||||
transform: rotate(288deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(49) {
|
||||
transform: rotate(294deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(50) {
|
||||
transform: rotate(300deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(51) {
|
||||
transform: rotate(306deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(52) {
|
||||
transform: rotate(312deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(53) {
|
||||
transform: rotate(318deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(54) {
|
||||
transform: rotate(324deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(55) {
|
||||
transform: rotate(330deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(56) {
|
||||
transform: rotate(336deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(57) {
|
||||
transform: rotate(342deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(58) {
|
||||
transform: rotate(348deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(59) {
|
||||
transform: rotate(354deg) translateY($arc_radius);
|
||||
}
|
||||
.marks li:nth-child(60) {
|
||||
transform: rotate(360deg) translateY($arc_radius);
|
||||
}
|
||||
|
||||
/*
|
||||
Some overrides.
|
||||
*/
|
||||
.reactor-container.active {
|
||||
$arc_radius: 130px;
|
||||
$size3: 6px;
|
||||
$cshadow: rgba(2, 254, 255, 0.8);
|
||||
$marks_color_1: rgba(2, 254, 255, 1);
|
||||
$marks_color_2: rgba(2, 254, 255, 0.3);
|
||||
$colour1: rgba(2, 255, 255, 0.15);
|
||||
$colour3: rgba(2, 255, 255, 0.3);
|
||||
$cshadow: rgba(2, 254, 255, 0.8);
|
||||
|
||||
scale: 1.1;
|
||||
opacity: 1;
|
||||
.coil-container {
|
||||
animation: 3s infinite linear reactor-anim;
|
||||
}
|
||||
|
||||
.reactor-container-inner {
|
||||
box-shadow: 0px 0px 50px 15px $colour3, inset 0px 0px 50px 15px $colour3;
|
||||
}
|
||||
|
||||
.core-inner {
|
||||
border: 5px solid #1b4e5f;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0px 0px 7px 5px #52fefe, 0px 0px 10px 10px #52fefe inset;
|
||||
}
|
||||
|
||||
.core-outer {
|
||||
border: 1px solid #52fefe;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0px 0px 2px 1px #52fefe, 0px 0px 10px 5px #52fefe inset;
|
||||
}
|
||||
.core-wrapper {
|
||||
background-color: #073c4b;
|
||||
box-shadow: 0px 0px 5px 4px #52fefe, 0px 0px 6px 2px #52fefe inset;
|
||||
}
|
||||
.tunnel {
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0px 0px 5px 1px #52fefe, 0px 0px 5px 4px #52fefe inset;
|
||||
}
|
||||
.coil {
|
||||
background-color: #073c4b;
|
||||
box-shadow: 0px 0px 5px #52fefe inset;
|
||||
}
|
||||
|
||||
.semi_arc {
|
||||
border: 6px solid #02feff;
|
||||
background: rgba(2, 254, 255, 0.2);
|
||||
}
|
||||
|
||||
|
||||
.e5_1 {
|
||||
animation: rotate 3s linear infinite;
|
||||
}
|
||||
|
||||
.e5_2 {
|
||||
animation: rotate_anti 2s linear infinite;
|
||||
}
|
||||
|
||||
.e5_3 {
|
||||
animation: rotate 2s linear infinite;
|
||||
}
|
||||
|
||||
.e5_4 {
|
||||
animation: rotate_anti 2s linear infinite;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,18 +0,0 @@
|
||||
<script>
|
||||
export let no_margin = false;
|
||||
</script>
|
||||
|
||||
<div class="h-divider" class:no-margin="{no_margin}"></div>
|
||||
|
||||
<style lang="scss">
|
||||
.h-divider {
|
||||
margin: 20px 0;
|
||||
height: 40px;
|
||||
background-image: url(media/images/decor.png);
|
||||
background-position: center;
|
||||
|
||||
&.no-margin {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,11 +0,0 @@
|
||||
<script>
|
||||
let search_q = "";
|
||||
</script>
|
||||
|
||||
<div id="search-form" class="search" class:active={search_q != ""}>
|
||||
<form action="#" method="GET">
|
||||
<input bind:value={search_q} type="text" name="q" placeholder="Введите команду или скажите «Джарвис» ..." autocomplete="off" minlength="3" maxlength="30">
|
||||
<button type="submit"></button>
|
||||
<small>Enter</small>
|
||||
</form>
|
||||
</div>
|
||||
@@ -1,377 +0,0 @@
|
||||
<script>
|
||||
// IMPORTS
|
||||
import { invoke } from "@tauri-apps/api/core"
|
||||
import { onMount } from 'svelte'
|
||||
import { capitalizeFirstLetter } from "@/functions";
|
||||
|
||||
// VARIABLES
|
||||
let selected_microphone = 0;
|
||||
let microphone_label = "";
|
||||
|
||||
let nn_details = {
|
||||
"ww_engine": "",
|
||||
"stt_engine": "Vosk"
|
||||
}
|
||||
|
||||
// let resources_cpu_temp = 0;
|
||||
// let resources_cpu_usage = 0;
|
||||
let resources_ram_usage = "-";
|
||||
|
||||
// CODE
|
||||
setInterval(() => {
|
||||
(async () => {
|
||||
resources_ram_usage = Number(await invoke("get_current_ram_usage")).toFixed(2);
|
||||
// resources_cpu_temp = await invoke("get_cpu_temp");
|
||||
// resources_cpu_usage = +Number(await invoke("get_cpu_usage")).toFixed(2);
|
||||
})().catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
onMount(async () => {
|
||||
(async () => {
|
||||
selected_microphone = +Number(await invoke("db_read", {key: "selected_microphone"}));
|
||||
microphone_label = await invoke("pv_get_audio_device_name", {idx: selected_microphone});
|
||||
|
||||
nn_details["ww_engine"] = capitalizeFirstLetter(await invoke("db_read", {key: "selected_wake_word_engine"}));
|
||||
|
||||
// resources_cpu_temp = await invoke("get_cpu_temp");
|
||||
// resources_cpu_usage = +Number(await invoke("get_cpu_usage")).toFixed(2);
|
||||
})().catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="statistics">
|
||||
<div class="online">
|
||||
<div class="pulse"><div class="wave"></div></div>
|
||||
<div class="info">
|
||||
<span class="num">Микрофон</span>
|
||||
<small title="{microphone_label}">{microphone_label}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="files">
|
||||
<div class="pulse"><div class="wave"></div></div>
|
||||
<div class="info">
|
||||
<span class="num">Нейросети</span>
|
||||
<small>{nn_details["ww_engine"]} + {nn_details["stt_engine"]}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="downloads hint--bottom" aria-label="Общее количество скачиваний по всему проекту">
|
||||
<div class="pulse"><div class="wave"></div></div>
|
||||
<div class="info">
|
||||
<span class="num">Ресурсы</span>
|
||||
<small><!-- CPU {resources_cpu_usage}%<br /> -->RAM {resources_ram_usage}mb</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.statistics {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
padding: 0 10px;
|
||||
height: 100px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
& > div {
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
.info {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
& > .online {
|
||||
position: relative;
|
||||
width: 40%;
|
||||
|
||||
$base-color: rgba(0, 191, 8, 1);
|
||||
$mid-color: rgba(0, 191, 8, 0.4);
|
||||
$end-color: rgba(0, 191, 8, 0);
|
||||
|
||||
& > .pulse::before {
|
||||
background-color: rgba(0, 191, 8, 1);
|
||||
}
|
||||
& > .pulse::after {
|
||||
background-color: rgba(0, 191, 8, 1);
|
||||
animation: online-cdot linear 3s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
& > .pulse .wave {
|
||||
background-color: rgba(0, 191, 8, 0.4);
|
||||
animation: online-radarWave cubic-bezier(0, 0.54, 0.53, 1) 3s 0s;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
& > .pulse .wave::after {
|
||||
background-color: rgba(0, 191, 8, 0.4);
|
||||
animation: online-radarWave cubic-bezier(0, 0.54, 0.53, 1) 3s
|
||||
0.1s;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
& > .info {
|
||||
position: absolute;
|
||||
top: 26px;
|
||||
left: 26px;
|
||||
|
||||
& > span.num {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #00bf08;
|
||||
}
|
||||
& > small {
|
||||
display: block;
|
||||
color: #535a60;
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
top: 0;
|
||||
width: 130px;
|
||||
max-height: 40px;
|
||||
overflow: hidden;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes online-cdot {
|
||||
0% {
|
||||
opacity: 0.3;
|
||||
background: $base-color;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
background: $end-color;
|
||||
}
|
||||
}
|
||||
@keyframes online-radarWave {
|
||||
0% {
|
||||
opacity: 0.1;
|
||||
transform: scale(0);
|
||||
}
|
||||
5% {
|
||||
background: $mid-color;
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1.2);
|
||||
background: $end-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > .files {
|
||||
position: relative;
|
||||
width: 35%;
|
||||
|
||||
$base-color: rgba(255, 129, 48, 1);
|
||||
$mid-color: rgba(255, 129, 48, 0.4);
|
||||
$end-color: rgba(255, 129, 48, 0);
|
||||
|
||||
& > .pulse::before {
|
||||
background-color: $base-color;
|
||||
}
|
||||
& > .pulse::after {
|
||||
background-color: $base-color;
|
||||
animation: files-cdot linear 5s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
& > .pulse .wave {
|
||||
background-color: $mid-color;
|
||||
animation: files-radarWave cubic-bezier(0, 0.54, 0.53, 1) 5s 0s;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
& > .pulse .wave::after {
|
||||
background-color: $mid-color;
|
||||
animation: files-radarWave cubic-bezier(0, 0.54, 0.53, 1) 5s
|
||||
0.1s;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
& > .info {
|
||||
position: absolute;
|
||||
top: 26px;
|
||||
left: 26px;
|
||||
|
||||
& > span.num {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #ff8130;
|
||||
}
|
||||
& > small {
|
||||
display: block;
|
||||
color: #535a60;
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes files-cdot {
|
||||
0% {
|
||||
opacity: 0.3;
|
||||
background: $base-color;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
background: $end-color;
|
||||
}
|
||||
}
|
||||
@keyframes files-radarWave {
|
||||
0% {
|
||||
opacity: 0.1;
|
||||
transform: scale(0);
|
||||
}
|
||||
5% {
|
||||
background: $mid-color;
|
||||
transform: scale(0.2);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.8);
|
||||
background: $end-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > .downloads {
|
||||
position: relative;
|
||||
|
||||
$base-color: rgba(11,66,166, 1);
|
||||
$mid-color: rgba(32, 150, 243, 0.4);
|
||||
$end-color: rgba(32, 150, 243, 0);
|
||||
|
||||
& > .pulse::before {
|
||||
background: rgba(32, 150, 243, 1);
|
||||
}
|
||||
& > .pulse::after {
|
||||
background: rgba(32, 150, 243, 1);
|
||||
animation: downloads-cdot linear 7s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
& > .pulse .wave {
|
||||
background-color: $mid-color;
|
||||
animation: downloads-radarWave cubic-bezier(0, 0.54, 0.53, 1) 7s
|
||||
0s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
& > .pulse .wave::after {
|
||||
background-color: $mid-color;
|
||||
animation: downloads-radarWave cubic-bezier(0, 0.54, 0.53, 1) 7s
|
||||
0.1s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
& > .info {
|
||||
position: absolute;
|
||||
top: 26px;
|
||||
left: 26px;
|
||||
|
||||
& > span.num {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #1b78a6;
|
||||
}
|
||||
|
||||
& > small {
|
||||
display: block;
|
||||
color: #535a60;
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes downloads-cdot {
|
||||
0% {
|
||||
opacity: 0.3;
|
||||
background: $base-color;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
background: $end-color;
|
||||
}
|
||||
}
|
||||
@keyframes downloads-radarWave {
|
||||
0% {
|
||||
opacity: 0.1;
|
||||
transform: scale(0);
|
||||
}
|
||||
5% {
|
||||
background: $mid-color;
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.7);
|
||||
background: $end-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pulse {
|
||||
position: relative;
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
margin: 0;
|
||||
left: -43px;
|
||||
top: 0px;
|
||||
z-index: 5;
|
||||
}
|
||||
.pulse::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
border-radius: 50%;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
opacity: .5;
|
||||
}
|
||||
.pulse::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.pulse .wave {
|
||||
position: absolute;
|
||||
left: 7%;
|
||||
top: 7%;
|
||||
width: 86%;
|
||||
height: 86%;
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
}
|
||||
.pulse .wave::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 7%;
|
||||
top: 7%;
|
||||
width: 86%;
|
||||
height: 86%;
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
@@ -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;
|
||||
@@ -1,12 +0,0 @@
|
||||
<!-- _layout.svelte -->
|
||||
<script>
|
||||
import { Container } from '@svelteuidev/core'
|
||||
import Header from '@/components/Header.svelte'
|
||||
</script>
|
||||
|
||||
<Container fluid id="wrapper">
|
||||
<Header />
|
||||
<main>
|
||||
<slot></slot>
|
||||
</main>
|
||||
</Container>
|
||||
@@ -1,23 +0,0 @@
|
||||
<script lang="ts">
|
||||
import HDivider from "@/components/elements/HDivider.svelte"
|
||||
import Footer from "@/components/Footer.svelte"
|
||||
|
||||
import { Notification, Space } from '@svelteuidev/core';
|
||||
import { InfoCircled } from 'radix-icons-svelte';
|
||||
|
||||
import { tg_official_link } from "@/stores";
|
||||
</script>
|
||||
|
||||
<Space h="xl" />
|
||||
|
||||
<Notification title='[404] Этот раздел еще находится в разработке!' icon={InfoCircled} color='blue' withCloseButton={false}>
|
||||
Тут будет список команд + полноценный редактор команд.<br>
|
||||
Следите за обновлениями в <a href="{tg_official_link}" target="_blank">нашем телеграм канале</a>!
|
||||
</Notification>
|
||||
|
||||
<div style="text-align: center;margin-top: 25px;">
|
||||
<img src="/media/images/tenor.gif" alt="bruh" width="320px">
|
||||
</div>
|
||||
|
||||
<HDivider />
|
||||
<Footer />
|
||||
@@ -1,71 +0,0 @@
|
||||
<script lang="ts">
|
||||
import SearchBar from "@/components/elements/SearchBar.svelte"
|
||||
import ArcReactor from "@/components/elements/ArcReactor.svelte"
|
||||
import HDivider from "@/components/elements/HDivider.svelte"
|
||||
import Stats from "@/components/elements/Stats.svelte"
|
||||
import Footer from "@/components/Footer.svelte"
|
||||
|
||||
import { Notification, Space } from '@svelteuidev/core'
|
||||
import { InfoCircled } from 'radix-icons-svelte'
|
||||
import { onMount, onDestroy } from 'svelte'
|
||||
|
||||
onMount(async () => {
|
||||
document.body.classList.add('assist-page');
|
||||
});
|
||||
|
||||
onDestroy(async () => {
|
||||
document.body.classList.remove('assist-page');
|
||||
});
|
||||
|
||||
import { is_listening } from "@/stores"
|
||||
let is_listening__val: boolean;
|
||||
is_listening.subscribe(value => {
|
||||
is_listening__val = value;
|
||||
});
|
||||
</script>
|
||||
|
||||
<HDivider />
|
||||
{#if !is_listening__val}
|
||||
<Notification title='Внимание!' icon={InfoCircled} color='cyan' withCloseButton={false}>
|
||||
В данный момент ассистент не прослушивает команды.<br />
|
||||
Пожалуйста, <a href="/settings">перейдите в настройки</a> и введите ключ Picovoice.
|
||||
</Notification>
|
||||
<!-- <SearchBar /> -->
|
||||
{:else}
|
||||
<!-- <SearchBar /> -->
|
||||
<ArcReactor />
|
||||
{/if}
|
||||
<HDivider no_margin />
|
||||
<Stats />
|
||||
<Footer />
|
||||
|
||||
|
||||
<!--
|
||||
<Title order={1}>This is h1 title</Title>
|
||||
<Title order={1} variant='gradient' gradient={{from: 'blue', to: 'red', deg: 45}}>This is h1 title with a twist</Title>
|
||||
|
||||
<Menu>
|
||||
<Button slot="control" variant="gradient" gradient={{ from: 'blue', to: 'teal', deg: 50 }} radius="md" size="md">Toggle Menu</Button>
|
||||
<Menu.Label>Application</Menu.Label>
|
||||
<Menu.Item icon={Gear}>Settings</Menu.Item>
|
||||
<Menu.Item icon={ChatBubble}>Messages</Menu.Item>
|
||||
<Menu.Item icon={Camera}>Gallery</Menu.Item>
|
||||
<Menu.Item icon={MagnifyingGlass}>
|
||||
<svelte:fragment slot='rightSection'>
|
||||
<Text size="xs" color="dimmed">⌘K</Text>
|
||||
</svelte:fragment>
|
||||
Search
|
||||
</Menu.Item>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Menu.Label>Danger zone</Menu.Label>
|
||||
<Menu.Item icon={Width}>Transfer my data</Menu.Item>
|
||||
<Menu.Item color="red" icon={Trash}>Delete my account</Menu.Item>
|
||||
</Menu>
|
||||
|
||||
<Checkbox bind:checked={checked} label="I agree to sell my privacy" />
|
||||
{checked}
|
||||
{#if checked}
|
||||
YEP!
|
||||
{/if} -->
|
||||
@@ -1,188 +0,0 @@
|
||||
<script lang="ts">
|
||||
// IMPORTS
|
||||
import { invoke } from "@tauri-apps/api/core"
|
||||
import { goto } from '@roxi/routify'
|
||||
import { onMount } from 'svelte'
|
||||
import { startListening, stopListening, showInExplorer } from "@/functions";
|
||||
// import { setTimeout } from 'worker-timers';
|
||||
|
||||
import { feedback_link, log_file_path } from "@/stores";
|
||||
|
||||
// COMPONENTS & STUFF
|
||||
import HDivider from "@/components/elements/HDivider.svelte"
|
||||
import Footer from "@/components/Footer.svelte"
|
||||
|
||||
import { Notification, Button, Text, Tabs, Space, Alert, Input, InputWrapper, NativeSelect } from '@svelteuidev/core';
|
||||
import { Check, Mix, Cube, Code, Gear, QuestionMarkCircled, CrossCircled } from 'radix-icons-svelte';
|
||||
|
||||
// VARIABLES
|
||||
|
||||
let available_microphones: { label: string; value: number }[] = [];
|
||||
let settings_saved = false;
|
||||
let save_button_disabled = false;
|
||||
|
||||
let assistant_voice_val = ""; // shared
|
||||
let selected_microphone = "";
|
||||
|
||||
let selected_wake_word_engine = "";
|
||||
let api_key__picovoice = "";
|
||||
let api_key__openai = "";
|
||||
|
||||
// SHARED VALUES
|
||||
import { assistant_voice } from "@/stores"
|
||||
assistant_voice.subscribe(value => {
|
||||
assistant_voice_val = value;
|
||||
});
|
||||
|
||||
// FUNCTIONS
|
||||
async function save_settings() {
|
||||
save_button_disabled = true; // disable save button for a while
|
||||
settings_saved = false; // hide alert
|
||||
|
||||
await invoke("db_write", {key: "assistant_voice", val: assistant_voice_val});
|
||||
await invoke("db_write", {key: "selected_microphone", val: selected_microphone});
|
||||
|
||||
await invoke("db_write", {key: "selected_wake_word_engine", val: selected_wake_word_engine});
|
||||
await invoke("db_write", {key: "api_key__picovoice", val: api_key__picovoice});
|
||||
await invoke("db_write", {key: "api_key__openai", val: api_key__openai});
|
||||
|
||||
// update shared
|
||||
assistant_voice.set(assistant_voice_val);
|
||||
|
||||
settings_saved = true; // show alert
|
||||
setTimeout(() => {
|
||||
settings_saved = false; // hide alert again after N seconds
|
||||
}, 5000);
|
||||
|
||||
setTimeout(() => {
|
||||
save_button_disabled = false; // enable save button again
|
||||
}, 1000);
|
||||
|
||||
// restart listening everytime new settings is saved
|
||||
stopListening(() => {
|
||||
startListening();
|
||||
});
|
||||
}
|
||||
|
||||
// CODE
|
||||
onMount(async () => {
|
||||
// preload some vars
|
||||
let _available_microphones: Array<Number> = await invoke("pv_get_audio_devices");
|
||||
Object.entries(_available_microphones).forEach(entry => {
|
||||
const [k, v] = entry;
|
||||
|
||||
available_microphones.push({
|
||||
label: String(v),
|
||||
value: Number(k)
|
||||
});
|
||||
});
|
||||
|
||||
available_microphones = available_microphones; // update component options
|
||||
|
||||
// load values from db
|
||||
// assistant_voice.set(await invoke("db_read", {key: "assistant_voice"}));
|
||||
selected_microphone = await invoke("db_read", {key: "selected_microphone"});
|
||||
|
||||
selected_wake_word_engine = await invoke("db_read", {key: "selected_wake_word_engine"});
|
||||
api_key__picovoice = await invoke("db_read", {key: "api_key__picovoice"});
|
||||
api_key__openai = await invoke("db_read", {key: "api_key__openai"});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<Space h="xl" />
|
||||
|
||||
<Notification title='БЕТА версия!' icon={QuestionMarkCircled} color='blue' withCloseButton={false}>
|
||||
Часть функций может работать некорректно.<br />
|
||||
Сообщайте обо всех найденных багах в <a href="{feedback_link}" target="_blank">наш телеграм бот</a>.
|
||||
<Space h="sm" />
|
||||
<Button color="gray" radius="md" size="xs" uppercase on:click={() => {showInExplorer(log_file_path)}}>Открыть папку с логами</Button>
|
||||
</Notification>
|
||||
|
||||
<Space h="xl" />
|
||||
|
||||
{#if settings_saved }
|
||||
<Notification title='Настройки сохранены!' icon={Check} color='teal' on:close="{() => {settings_saved = false}}"></Notification>
|
||||
<Space h="xl" />
|
||||
{/if}
|
||||
|
||||
<Tabs class="form" color='#8AC832' position="left">
|
||||
<Tabs.Tab label='Общее' icon={Gear}>
|
||||
<Space h="sm" />
|
||||
|
||||
<NativeSelect data={[
|
||||
{ label: 'Jarvis ремейк (от Хауди)', value: 'jarvis-remake' },
|
||||
{ label: 'Jarvis OG (из фильмов)', value: 'jarvis-og' }
|
||||
]}
|
||||
label="Голос ассистента"
|
||||
description="Не все команды работают со всеми звуковыми пакетами."
|
||||
variant="filled"
|
||||
bind:value={assistant_voice_val}
|
||||
/>
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab label='Устройства' icon={Mix}>
|
||||
<Space h="sm" />
|
||||
|
||||
<NativeSelect data={available_microphones}
|
||||
label="Выберите микрофон"
|
||||
description="Его будет слушать ассистент."
|
||||
variant="filled"
|
||||
bind:value={selected_microphone}
|
||||
/>
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab label='Нейросети' icon={Cube}>
|
||||
<Space h="sm" />
|
||||
|
||||
<NativeSelect data={[
|
||||
{ label: 'Rustpotter', value: 'Rustpotter' },
|
||||
{ label: 'Vosk (медленный)', value: 'Vosk' },
|
||||
{ label: 'Picovoice Porcupine (требует API ключ)', value: 'Picovoice' }
|
||||
]}
|
||||
label="Распознавание активационной фразы (Wake Word)"
|
||||
description="Выберите, какая нейросеть будет отвечать за распознавание активационной фразы."
|
||||
variant="filled"
|
||||
bind:value={selected_wake_word_engine}
|
||||
/>
|
||||
|
||||
{#if selected_wake_word_engine == "picovoice"}
|
||||
<Space h="sm" />
|
||||
<Alert title="Внимание!" color="#868E96" variant="outline">
|
||||
|
||||
<Notification title='Эта нейросеть работает не у всех!' icon={CrossCircled} color='orange' withCloseButton={false}>
|
||||
Мы ждем официального патча от разработчиков.
|
||||
</Notification>
|
||||
<Space h="sm" />
|
||||
|
||||
<Text size='sm' color="gray">
|
||||
Введите сюда свой ключ Picovoice.<br />
|
||||
Он выдается бесплатно при регистрации в <a href='https://console.picovoice.ai/' target="_blank">Picovoice Console</a>.<br>
|
||||
</Text>
|
||||
<Space h="sm" />
|
||||
<Input icon={Code} placeholder='Ключ Picovoice' variant='filled' autocomplete="off" bind:value={api_key__picovoice}/>
|
||||
|
||||
</Alert>
|
||||
{/if}
|
||||
|
||||
<Space h="xl" />
|
||||
|
||||
<InputWrapper label="Ключ OpenAI">
|
||||
<!-- <Text size='sm' color="gray">Введите сюда свой ключ OpenAI, он требуется для работы ChatGPT.<br />Получить его можно <a href="https://chat.openai.com/auth/login" target="_blank">на официальном сайте OpenAI</a>.</Text> -->
|
||||
<Text size='sm' color="gray">В данный момент ChatGPT <u>не поддерживается</u>. Он будет добавлен в ближайших обновлениях.</Text>
|
||||
<Space h="sm" />
|
||||
<Input icon={Code} placeholder='Ключ OpenAI' variant='filled' autocomplete="off" bind:value={api_key__openai} disabled/>
|
||||
</InputWrapper>
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
|
||||
<Space h="xl" />
|
||||
|
||||
<Button color="lime" radius="md" size="sm" uppercase ripple fullSize on:click={save_settings} disabled={save_button_disabled}>
|
||||
Сохранить
|
||||
</Button>
|
||||
<Space h="sm" />
|
||||
<Button color="gray" radius="md" size="sm" uppercase fullSize on:click={() => {$goto('/')}}>
|
||||
Назад
|
||||
</Button>
|
||||
|
||||
<HDivider />
|
||||
<Footer />
|
||||
@@ -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);
|
||||
});
|
||||
2
frontend/_src/vite-env.d.ts
vendored
2
frontend/_src/vite-env.d.ts
vendored
@@ -1,2 +0,0 @@
|
||||
/// <reference types="svelte" />
|
||||
/// <reference types="vite/client" />
|
||||
241
frontend/package-lock.json
generated
241
frontend/package-lock.json
generated
@@ -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": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -46,13 +46,13 @@
|
||||
</p>
|
||||
<p class="links last">
|
||||
{#if $currentLanguage === "ru"}
|
||||
{t('footer-support')} <a href={tgLink} target="_blank" class="telegram-link">
|
||||
{t('footer-support')} <a href={boostyLink} target="_blank" class="telegram-link">
|
||||
<img src="/media/icons/boosty.webp" alt="Boosty" width="18px" />
|
||||
<span>Boosty</span>
|
||||
</a>.
|
||||
{/if}
|
||||
{#if $currentLanguage === "ua" || $currentLanguage === "en"}
|
||||
{t('footer-support')} <a href={tgLink} target="_blank" class="telegram-link">
|
||||
{t('footer-support')} <a href={patreonLink} target="_blank" class="telegram-link">
|
||||
<img src="/media/icons/patreon.png" alt="Patreon" width="18px" />
|
||||
<span>Patreon</span>
|
||||
</a>.
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { jarvisState } from "@/stores"
|
||||
|
||||
$: stateClass = getStateClass($jarvisState)
|
||||
|
||||
function getStateClass(state: string): string {
|
||||
switch (state) {
|
||||
case "listening":
|
||||
case "processing":
|
||||
return "active"
|
||||
case "idle":
|
||||
return "idle"
|
||||
case "disconnected":
|
||||
default:
|
||||
return "disconnected"
|
||||
}
|
||||
}
|
||||
$: stateClass = {
|
||||
'disconnected': 'disconnected',
|
||||
'idle': 'idle',
|
||||
'listening': 'active',
|
||||
'processing': 'active'
|
||||
}[$jarvisState] || 'disconnected'
|
||||
</script>
|
||||
|
||||
<div id="arc-reactor" class="reactor-container {stateClass} arc-white">
|
||||
|
||||
@@ -1,22 +1,85 @@
|
||||
<script lang="ts">
|
||||
import { translations, translate } from "@/stores"
|
||||
import { translations, translate, isJarvisRunning, ipcConnected, sendTextCommand } from "@/stores"
|
||||
|
||||
$: t = (key: string) => translate($translations, key)
|
||||
|
||||
let searchQuery = ""
|
||||
let isProcessing = false
|
||||
let statusMessage = ""
|
||||
|
||||
async function handleSubmit(e: Event) {
|
||||
e.preventDefault()
|
||||
|
||||
const command = searchQuery.trim()
|
||||
if (!command || isProcessing) return
|
||||
|
||||
if (!$isJarvisRunning || !$ipcConnected) {
|
||||
statusMessage = t('search-error-not-running')
|
||||
setTimeout(() => statusMessage = "", 3000)
|
||||
return
|
||||
}
|
||||
|
||||
isProcessing = true
|
||||
statusMessage = ""
|
||||
|
||||
try {
|
||||
await sendTextCommand(command)
|
||||
searchQuery = ""
|
||||
} catch (err) {
|
||||
console.error("Failed to send command:", err)
|
||||
statusMessage = t('search-error-failed')
|
||||
setTimeout(() => statusMessage = "", 3000)
|
||||
} finally {
|
||||
isProcessing = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleKeydown(e: KeyboardEvent) {
|
||||
if (e.key === "Escape") {
|
||||
searchQuery = ""
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="search-form" class="search" class:active={searchQuery !== ""}>
|
||||
<form action="#" method="GET">
|
||||
<div id="search-form" class="search" class:active={searchQuery !== ""} class:processing={isProcessing}>
|
||||
<form on:submit={handleSubmit}>
|
||||
<input
|
||||
bind:value={searchQuery}
|
||||
on:keydown={handleKeydown}
|
||||
type="text"
|
||||
name="q"
|
||||
placeholder={t('search-placeholder')}
|
||||
autocomplete="off"
|
||||
minlength="3"
|
||||
maxlength="30"
|
||||
minlength="1"
|
||||
maxlength="200"
|
||||
disabled={isProcessing}
|
||||
/>
|
||||
<small>Enter</small>
|
||||
<small>{isProcessing ? '...' : 'Enter'}</small>
|
||||
</form>
|
||||
</div>
|
||||
{#if statusMessage}
|
||||
<div class="search-status">{statusMessage}</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.search.processing input {
|
||||
opacity: 0.6;
|
||||
cursor: wait;
|
||||
}
|
||||
|
||||
.search-status {
|
||||
position: absolute;
|
||||
bottom: -24px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 0.75rem;
|
||||
color: rgba(82, 254, 254, 0.8);
|
||||
white-space: nowrap;
|
||||
animation: fadeIn 0.2s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateX(-50%) translateY(-5px); }
|
||||
to { opacity: 1; transform: translateX(-50%) translateY(0); }
|
||||
}
|
||||
</style>
|
||||
@@ -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 @@
|
||||
<span class="stat-dot" class:active={$ipcConnected} style="--color: #3b82f6;"></span>
|
||||
<div class="stat-content">
|
||||
<span class="stat-label">{t('stats-resources')}</span>
|
||||
<span class="stat-value">{#if jarvisRamUsage }RAM {$jarvisRamUsage}mb{:else}...{/if}</span>
|
||||
<span class="stat-value">{#if $jarvisRamUsage }RAM {$jarvisRamUsage}mb{:else}...{/if}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
export function sendIpcMessage(message: object): Promise<void> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -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<VoiceConfig[]>("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<string[]>("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 @@
|
||||
<Tabs class="form" color="#8AC832" position="left">
|
||||
<Tabs.Tab label={t('settings-general')} icon={Gear}>
|
||||
<Space h="sm" />
|
||||
<NativeSelect
|
||||
data={[
|
||||
{ label: "Jarvis New (ремастер)", value: "jarvis-remaster" },
|
||||
{ label: "Рик из «Рик и Морти»", value: "rick-morty" },
|
||||
{ label: "Jarvis (от Хауди)", value: "jarvis-howdy" },
|
||||
{ label: "Jarvis OG (из фильмов)", value: "jarvis-og" }
|
||||
]}
|
||||
label={t('settings-voice')}
|
||||
description={t('settings-voice-desc')}
|
||||
variant="filled"
|
||||
bind:value={voiceVal}
|
||||
/>
|
||||
<div class="voice-select">
|
||||
<label>{t('settings-voice')}</label>
|
||||
<p class="description">{t('settings-voice-desc')}</p>
|
||||
|
||||
<div class="voice-options">
|
||||
{#each availableVoices as voice}
|
||||
<button
|
||||
type="button"
|
||||
class="voice-option"
|
||||
class:selected={voiceVal === voice.id}
|
||||
on:click={() => selectVoice(voice.id)}
|
||||
>
|
||||
<div class="voice-info">
|
||||
<span class="voice-name">{voice.name}</span>
|
||||
{#if voice.author}
|
||||
<span class="voice-author">by {voice.author}</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="voice-languages">
|
||||
{#each voice.languages as lang}
|
||||
<img
|
||||
src="/media/flags/{lang.toUpperCase()}.png"
|
||||
alt={lang}
|
||||
width="20"
|
||||
title={lang}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
|
||||
{#if availableVoices.length === 0}
|
||||
<p class="no-voices">{t('settings-no-voices')}</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.Tab>
|
||||
|
||||
<Tabs.Tab label={t('settings-devices')} icon={Mix}>
|
||||
@@ -390,3 +447,112 @@
|
||||
|
||||
<HDivider />
|
||||
<Footer />
|
||||
|
||||
<style lang="scss">
|
||||
.voice-select {
|
||||
margin-bottom: 1rem;
|
||||
|
||||
label {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
color: #fff;
|
||||
display: block;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 0.75rem;
|
||||
color: rgba(255,255,255,0.5);
|
||||
margin: 0 0 0.75rem;
|
||||
white-space: pre-line;
|
||||
}
|
||||
}
|
||||
|
||||
$voice-item-height: 70px;
|
||||
$voice-item-gap: 0.5rem;
|
||||
$voice-max-visible: 3;
|
||||
|
||||
.voice-options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $voice-item-gap;
|
||||
max-height: $voice-item-height * $voice-max-visible;
|
||||
overflow-y: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 3px;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.voice-option {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.75rem 1rem;
|
||||
background: rgba(30, 40, 45, 0.8);
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
|
||||
&:hover {
|
||||
background: rgba(40, 55, 60, 0.9);
|
||||
border-color: rgba(255,255,255,0.2);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background: rgba(82, 254, 254, 0.1);
|
||||
border-color: rgba(82, 254, 254, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
.voice-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.15rem;
|
||||
}
|
||||
|
||||
.voice-name {
|
||||
font-size: 0.85rem;
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.voice-author {
|
||||
font-size: 0.7rem;
|
||||
color: rgba(255,255,255,0.4);
|
||||
}
|
||||
|
||||
.voice-languages {
|
||||
display: flex;
|
||||
gap: 0.35rem;
|
||||
|
||||
img {
|
||||
opacity: 0.8;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.no-voices {
|
||||
font-size: 0.8rem;
|
||||
color: rgba(255,255,255,0.4);
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
||||
@@ -13,6 +13,8 @@ export {
|
||||
disableIpc,
|
||||
disconnectIpc,
|
||||
sendAction,
|
||||
sendIpcMessage,
|
||||
sendTextCommand,
|
||||
stopJarvisApp,
|
||||
reloadCommands
|
||||
} from "./lib/ipc"
|
||||
|
||||
@@ -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
|
||||
27
resources/sound/voices/jarvis-howdy/voice.toml
Normal file
27
resources/sound/voices/jarvis-howdy/voice.toml
Normal file
@@ -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"]
|
||||
27
resources/sound/voices/jarvis-og/voice.toml
Normal file
27
resources/sound/voices/jarvis-og/voice.toml
Normal file
@@ -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"]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user