diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index bba6be7..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,67 +0,0 @@ -# CI for J.A.R.V.I.S. Rust fork. -# -# Runs on every push to active branches and on every PR targeting master. -# Three jobs in parallel: -# - test: cargo test for jarvis-core (the only crate with portable tests -# — jarvis-app needs the Vosk DLL + mic, so we don't run it in CI) -# - clippy: lints on the whole workspace (warnings = errors) -# - check: `cargo check` proves the workspace compiles end-to-end without -# waiting for the full release build -# -# We deliberately do NOT run a release build in CI: jarvis-gui pulls Node+npm -# and the WiX/Tauri bundler, which would make CI ~10 minutes per push. Local -# `cargo build --release` is the gate before merging into master. - -name: Rust CI - -on: - push: - branches: [feature/pc-tools, master, main] - pull_request: - branches: [master, main] - -env: - CARGO_TERM_COLOR: always - RUSTFLAGS: "-Dwarnings" - -jobs: - test: - name: cargo test (jarvis-core) - runs-on: windows-latest - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - with: - shared-key: "windows-stable" - - name: Run tests - run: cargo test -p jarvis-core --lib --no-fail-fast - - clippy: - name: cargo clippy - runs-on: windows-latest - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - with: - components: clippy - - uses: Swatinem/rust-cache@v2 - with: - shared-key: "windows-stable" - - name: Clippy - run: cargo clippy -p jarvis-core --lib --no-deps -- -A clippy::all - - check: - name: cargo check (workspace) - runs-on: windows-latest - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - with: - shared-key: "windows-stable" - - uses: actions/setup-node@v4 - with: - node-version: '20' - - name: Check core + app + cli - run: cargo check -p jarvis-core -p jarvis-app -p jarvis-cli diff --git a/.gitignore b/.gitignore index baac0c0..2fecd1b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,6 @@ # Logs logs *.log - -# Per-developer environment + ad-hoc scratch files -dev.env -dll_probe*.py -imports_full.txt -*.scratch.* npm-debug.log* yarn-debug.log* yarn-error.log* diff --git a/crates/jarvis-app/src/main.rs b/crates/jarvis-app/src/main.rs index af7e272..852be15 100644 --- a/crates/jarvis-app/src/main.rs +++ b/crates/jarvis-app/src/main.rs @@ -94,11 +94,6 @@ fn main() -> Result<(), String> { warn!("Macros init failed: {}", e); } - eprintln!("[jarvis-app] step: idle_banter::start_background"); - // Always start the thread — gated internally by JARVIS_IDLE_BANTER so - // the user can flip the env var without restarting the daemon. - jarvis_core::idle_banter::start_background(); - eprintln!("[jarvis-app] step: recorder::init"); if recorder::init().is_err() { notify_mic_problem(); diff --git a/crates/jarvis-app/src/tray/menu.rs b/crates/jarvis-app/src/tray/menu.rs index ed2c158..fe023a0 100644 --- a/crates/jarvis-app/src/tray/menu.rs +++ b/crates/jarvis-app/src/tray/menu.rs @@ -48,6 +48,7 @@ pub fn build(settings: &SettingsManager) -> TrayMenu { let label = match lang { "ru" => "Русский", "en" => "English", + "ua" => "Українська", _ => lang, }; let item = CheckMenuItem::with_id( diff --git a/crates/jarvis-core/src/config.rs b/crates/jarvis-core/src/config.rs index cf9c213..f47a1fa 100644 --- a/crates/jarvis-core/src/config.rs +++ b/crates/jarvis-core/src/config.rs @@ -220,6 +220,7 @@ pub const LLM_FALLBACK_ERROR_RU: &str = "Не могу связаться с с pub fn get_llm_trigger_phrases(lang: &str) -> &'static [&'static str] { match lang { "ru" => &["скажи", "ответь", "произнеси"], + "ua" => &["скажи", "відповідай"], "en" => &["say", "tell", "answer"], _ => &[], } @@ -227,7 +228,7 @@ pub fn get_llm_trigger_phrases(lang: &str) -> &'static [&'static str] { pub fn get_llm_system_prompt(lang: &str) -> &'static str { match lang { - "ru" => LLM_SYSTEM_PROMPT_RU, + "ru" | "ua" => LLM_SYSTEM_PROMPT_RU, _ => LLM_SYSTEM_PROMPT_RU, } } @@ -258,6 +259,7 @@ pub fn get_llm_system_prompt(lang: &str) -> &'static str { pub fn get_wake_phrases(lang: &str) -> &'static [&'static str] { match lang { "ru" => &["джарвис", "джервис", "гарвис", "джарви", "гарви"], + "ua" => &["джарвіс", "джервіс"], "en" => &["jarvis", "jervis"], _ => &["jarvis"], } @@ -271,6 +273,11 @@ pub fn get_phrases_to_remove(lang: &str) -> &'static [&'static str] { "произнеси", "ответь", "покажи", "скажи", "давай", "да сэр", "к вашим услугам сэр", "загружаю сэр", ], + "ua" => &[ + "джарвіс", "джервіс", "сер", "слухаю сер", "завжди до послуг", + "скажи", "покажи", "відповідай", "давай", + "так сер", "до ваших послуг сер", + ], "en" => &[ "jarvis", "jervis", "sir", "yes sir", "at your service", "please", "say", "show", "tell", "hey", @@ -285,6 +292,10 @@ pub fn get_wake_grammar(lang: &str) -> &'static [&'static str] { "джарвис", "[unk]", "джон", "джони", "джей", "джонстон", "привет", "давай", ], + "ua" => &[ + "джарвіс", "[unk]", "джон", "джоні", "джей", + "привіт", "давай", + ], "en" => &[ "jarvis", "[unk]", "john", "johnny", "jay", "hello", "hey", "hi", diff --git a/crates/jarvis-core/src/i18n.rs b/crates/jarvis-core/src/i18n.rs index 0dfa52c..24f7576 100644 --- a/crates/jarvis-core/src/i18n.rs +++ b/crates/jarvis-core/src/i18n.rs @@ -8,21 +8,28 @@ use unic_langid::LanguageIdentifier; // locale files embedded at compile time const LOCALE_RU: &str = include_str!("i18n/locales/ru.ftl"); const LOCALE_EN: &str = include_str!("i18n/locales/en.ftl"); +const LOCALE_UA: &str = include_str!("i18n/locales/ua.ftl"); -pub const SUPPORTED_LANGUAGES: &[&str] = &["ru", "en"]; +pub const SUPPORTED_LANGUAGES: &[&str] = &["ru", "en", "ua"]; pub const DEFAULT_LANGUAGE: &str = "en"; // detect the OS language and map it to a supported language. // falls back to DEFAULT_LANGUAGE if not supported. pub fn detect_system_language() -> &'static str { if let Some(locale) = sys_locale::get_locale() { - // locale can be "en-US", "ru-RU", etc. + // locale can be "en-US", "ru-RU", "uk-UA", etc. let lang_code = locale.split(&['-', '_'][..]).next().unwrap_or(""); - if SUPPORTED_LANGUAGES.contains(&lang_code) { - info!("Detected system language: {} (from locale '{}')", lang_code, locale); + // map OS locale codes to our supported languages + let mapped = match lang_code { + "uk" => "ua", // ISO 639-1 "uk" (ukrainian) -> our "ua" + other => other, + }; + + if SUPPORTED_LANGUAGES.contains(&mapped) { + info!("Detected system language: {} (from locale '{}')", mapped, locale); return SUPPORTED_LANGUAGES.iter() - .find(|&&l| l == lang_code) + .find(|&&l| l == mapped) .unwrap(); } @@ -54,7 +61,8 @@ fn create_bundles() -> HashMap { bundles.insert("ru".to_string(), create_bundle("ru", LOCALE_RU)); bundles.insert("en".to_string(), create_bundle("en", LOCALE_EN)); - + bundles.insert("ua".to_string(), create_bundle("ua", LOCALE_UA)); + bundles } @@ -165,6 +173,7 @@ pub fn get_translations_for(lang: &str) -> HashMap { let source = match lang { "ru" => LOCALE_RU, "en" => LOCALE_EN, + "ua" => LOCALE_UA, _ => LOCALE_RU, }; diff --git a/crates/jarvis-core/src/i18n/locales/ua.ftl b/crates/jarvis-core/src/i18n/locales/ua.ftl new file mode 100644 index 0000000..36e1c3f --- /dev/null +++ b/crates/jarvis-core/src/i18n/locales/ua.ftl @@ -0,0 +1,166 @@ +# ### APP INFO +app-name = JARVIS +app-description = Голосовий асистент + +# ### TRAY MENU +tray-restart = Перезапустити +tray-settings = Налаштування +tray-exit = Вихід +tray-tooltip = JARVIS - Голосовий асистент +tray-language = Мова +tray-voice = Голос +tray-wake-word = Рушій детекції +tray-noise-suppression = Шумозаглушення +tray-vad = Детекцiя голосу (VAD) +tray-gain-normalizer = Нормалізація гучності + +# ### HEADER +header-commands = КОМАНДИ +header-settings = НАЛАШТУВАННЯ + +# ### SEARCH +search-placeholder = Введіть команду вручну або скажіть «Джарвіс» ... + +# ### MAIN PAGE +assistant-not-running = АСИСТЕНТ НЕ ЗАПУЩЕНО +assistant-offline-hint = Налаштувати його можна не запускаючи. +btn-start = ЗАПУСТИТИ +btn-starting = ЗАПУСК... + +# ### STATUS +status-disconnected = Відключено +status-standby = Очікування +status-listening = Слухаю... +status-processing = Обробка... + +# ### STATS +stats-microphone = МІКРОФОН +stats-neural-networks = НЕЙРОМЕРЕЖІ +stats-resources = РЕСУРСИ +stats-system-default = Системний +stats-not-selected = Не вибрано +stats-loading = Завантаження... + +# ### FOOTER +footer-github = Репозиторій проєкту +footer-issues = Повідомити про баг +footer-fork-of = Форк + +# ### SETTINGS +settings-title = Налаштування +settings-general = Основні +settings-devices = Пристрої +settings-neural-networks = Нейромережі +settings-audio = Аудіо +settings-recognition = Розпізнавання +settings-about = Про програму +settings-language = Мова +settings-microphone = Мікрофон +settings-microphone-desc = Його буде слухати асистент. +settings-mic-default = За замовчуванням (Система) +settings-voice = Голос асистента +settings-voice-desc = + Не всі команди працюють з усіма звуковими пакетами. + Натисніть, щоб прослухати як звучить голос. +settings-wake-word-engine = Рушій активації +settings-wake-word-desc = Виберіть нейромережу для розпізнавання активаційної фрази. +settings-stt-engine = Розпізнавання мовлення +settings-intent-engine = Визначення наміру +settings-intent-engine-desc = Виберіть нейромережу для розпізнавання команд. +settings-noise-suppression = Шумозаглушення +settings-noise-suppression-desc = Зменшує фоновий шум. Може негативно впливати на розпізнавання. +settings-vad = Визначення голосу (VAD) +settings-vad-desc = Пропускає тишу, економить ресурси CPU. +settings-gain-normalizer = Нормалізація гучності +settings-gain-normalizer-desc = Автоматично регулює рівень гучності. +settings-api-keys = API Ключі +settings-save = Зберегти +settings-cancel = Скасувати +settings-back = Назад +settings-enabled = Увімкнено +settings-disabled = Вимкнено + +# settings - beta notice +settings-beta-title = БЕТА версія! +settings-beta-desc = Частина функцій може працювати некоректно. +settings-beta-feedback = Повідомляйте про знайдені баги через +settings-beta-bot = GitHub Issues +settings-open-logs = Відкрити папку з логами + +# settings - picovoice +settings-attention = Увага! +settings-picovoice-warning = Ця нейромережа працює не у всіх! +settings-picovoice-waiting = Ми чекаємо офіційного патча від розробників. +settings-picovoice-key-desc = Введіть сюди свій ключ Picovoice. Він видається безкоштовно при реєстрації в +settings-picovoice-key = Ключ Picovoice + +# settings - vosk +settings-auto-detect = Авто-визначення +settings-vosk-model = Модель розпізнавання мовлення (Vosk) +settings-vosk-model-desc = + Виберіть модель Vosk для розпізнавання мовлення. + Ви можете завантажити моделі тут: https://alphacephei.com/vosk/models +settings-models-not-found = Моделі не знайдено +settings-models-hint = Помістіть моделі Vosk в папку resources/vosk + +# settings - openai +settings-openai-key = Ключ OpenAI +settings-openai-not-supported = Наразі ChatGPT не підтримується. Він буде доданий у наступних оновленнях. + +# ### COMMANDS PAGE +commands-title = Команди +commands-search = Пошук команд... +commands-count = { $count } команд +commands-wip-title = Розділ у розробці +commands-wip-desc = Тут буде редактор команд та список встановлених пакетів. +commands-wip-builder = Поки що користуйся Python-форком з конструктором команд — +commands-wip-builder-link = відкрити репозиторій +commands-wip-or-fork = Або зазирни в Rust-репозиторій і правь команди безпосередньо в +commands-wip-fork-link = resources/commands/ + +# ### ERRORS +error-generic = Сталася помилка +error-connection = Помилка підключення +error-not-found = Не знайдено + +# ### NOTIFICATIONS +notification-saved = Налаштування збережено! +notification-error = Помилка +notification-assistant-started = Асистент запущено +notification-assistant-stopped = Асистент зупинено + +# SLOTS EXTRACTION +settings-slot-engine = Витяг параметрів +settings-slot-engine-desc = Витягує параметри з голосових команд (напр. назва міста, число). +settings-gliner-model = Модель GLiNER ONNX +settings-gliner-model-desc = + Оберіть варіант моделі. + Квантизовані моделі (int8, uint8) швидші, але менш точні. +settings-gliner-models-hint = Моделі GLiNER не знайдено. + +# ETC +search-error-not-running = Асистент не запущено +search-error-failed = Не вдалося виконати команду +settings-no-voices = Голоси не знайдено + +# AI Backends tab +settings-ai-backends = ШІ-рушії +settings-ai-active = Активний рушій +settings-ai-auto = Авто +settings-ai-applying = Застосовую... +settings-ai-error = Помилка перемикання +settings-ai-tips = Підказки +settings-llm-backend = LLM рушій +settings-llm-backend-desc = Де виконувати LLM-запити. Hot-swap, без перезапуску. +settings-tts-backend = TTS рушій +settings-tts-backend-desc = Застосується при наступному запуску jarvis-app. +settings-llm-context = Контекст розмови +settings-llm-context-desc = LLM пам'ятає попередні репліки. Скиньте, якщо відповіді дивні. +settings-llm-reset = Скинути контекст +settings-profile = Профіль + +# Header buttons +header-macros = Макроси +header-scheduler = Розклад +header-memory = Пам'ять +header-plugins = Плагіни \ No newline at end of file diff --git a/crates/jarvis-core/src/idle_banter.rs b/crates/jarvis-core/src/idle_banter.rs deleted file mode 100644 index 39995bc..0000000 --- a/crates/jarvis-core/src/idle_banter.rs +++ /dev/null @@ -1,304 +0,0 @@ -//! Idle banter — proactive periodic remarks from J.A.R.V.I.S. -//! -//! Real Tony-Stark JARVIS doesn't only respond, he initiates. This module -//! runs a background thread that occasionally injects a short witty remark -//! via TTS — only when conditions look right: -//! -//! - The user is OPTED IN. Default is OFF (env `JARVIS_IDLE_BANTER=1`). -//! - At least `interval_secs` have passed since last remark. Default 30 min. -//! - The active profile permits noise (skipped under "sleep" profile). -//! - The system isn't currently speaking or listening for a command. -//! - Quiet hours: 23:00–07:00 silenced by default. -//! -//! Lines are drawn from a built-in pool of ~30 RU/EN one-liners so even -//! offline mode works. If the LLM is reachable AND -//! `JARVIS_IDLE_BANTER_LLM=1`, a small LLM call with current memory facts -//! generates a fresher line — but that's strictly optional, the offline -//! pool is the floor. -//! -//! Storage: no disk state. The last-fired timestamp lives in memory; if the -//! daemon restarts, the next remark waits a full interval again. - -use chrono::{Local, Timelike}; -use once_cell::sync::OnceCell; -use parking_lot::Mutex; -use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; -use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; - -use crate::runtime_config; - -const DEFAULT_INTERVAL_SECS: u64 = 1800; // 30 min -const MIN_INTERVAL_SECS: u64 = 600; // 10 min -const TICK_SECS: u64 = 60; -const QUIET_START_HOUR: u32 = 23; -const QUIET_END_HOUR: u32 = 7; - -static THREAD_RUNNING: AtomicBool = AtomicBool::new(false); -static LAST_FIRED: AtomicU64 = AtomicU64::new(0); -static PAUSED: AtomicBool = AtomicBool::new(false); - -/// In-memory cache of the next-due timestamp — only used if start_background -/// is called multiple times so we don't fire instantly after restart. -static STARTED_AT: OnceCell> = OnceCell::new(); - -/// Returns true if idle banter is enabled via env. Treats unset / 0 / false / -/// off / no as disabled. Default: disabled. -pub fn enabled() -> bool { - match runtime_config::get("JARVIS_IDLE_BANTER") - .map(|s| s.trim().to_lowercase()) - { - Some(v) => !matches!(v.as_str(), "" | "0" | "false" | "off" | "no"), - None => false, - } -} - -/// Returns true if LLM-generated banter is allowed. Default: false (use static pool). -pub fn llm_enabled() -> bool { - matches!( - runtime_config::get("JARVIS_IDLE_BANTER_LLM") - .map(|s| s.trim().to_lowercase()) - .as_deref(), - Some("1") | Some("true") | Some("on") | Some("yes") - ) -} - -fn configured_interval_secs() -> u64 { - runtime_config::get("JARVIS_IDLE_BANTER_INTERVAL") - .and_then(|s| s.trim().parse::().ok()) - .map(|n| n.max(MIN_INTERVAL_SECS)) - .unwrap_or(DEFAULT_INTERVAL_SECS) -} - -/// Pause future remarks. Use during long-running flows where Jarvis shouldn't -/// chime in (e.g. an active conversation or a macro replay). -pub fn pause() { - PAUSED.store(true, Ordering::SeqCst); -} - -pub fn resume() { - PAUSED.store(false, Ordering::SeqCst); -} - -/// Force-fire a remark now, ignoring interval/quiet-hours checks. Returns -/// `true` if a line was actually spoken. Useful for "say something interesting" -/// voice commands or for a "test banter" GUI button. -pub fn fire_now() -> bool { - let line = pick_line(); - if line.is_empty() { - return false; - } - speak_line(&line); - LAST_FIRED.store(now_secs(), Ordering::SeqCst); - true -} - -/// Start the background thread that decides whether to chime in every minute. -/// Idempotent — repeated calls are no-ops. -pub fn start_background() { - if THREAD_RUNNING.swap(true, Ordering::SeqCst) { - return; - } - let _ = STARTED_AT.set(Mutex::new(Instant::now())); - LAST_FIRED.store(now_secs(), Ordering::SeqCst); - - std::thread::Builder::new() - .name("jarvis-idle-banter".into()) - .spawn(|| { - info!( - "Idle banter thread started (interval={}s, opt-in={}, llm={}).", - configured_interval_secs(), - enabled(), - llm_enabled() - ); - loop { - std::thread::sleep(Duration::from_secs(TICK_SECS)); - tick(); - } - }) - .ok(); -} - -fn tick() { - if !enabled() || PAUSED.load(Ordering::SeqCst) { - return; - } - let now = now_secs(); - let last = LAST_FIRED.load(Ordering::SeqCst); - if now.saturating_sub(last) < configured_interval_secs() { - return; - } - if !active_profile_allows() { - return; - } - if in_quiet_hours() { - return; - } - - let line = pick_line(); - if line.is_empty() { - return; - } - speak_line(&line); - LAST_FIRED.store(now, Ordering::SeqCst); -} - -fn active_profile_allows() -> bool { - // Refuse to talk on the "sleep" profile; everything else is fair game. - let name = crate::profiles::active_name(); - !name.eq_ignore_ascii_case("sleep") -} - -fn in_quiet_hours() -> bool { - let h = Local::now().hour(); - // Returns true between QUIET_START and QUIET_END (wraps midnight). - h >= QUIET_START_HOUR || h < QUIET_END_HOUR -} - -fn now_secs() -> u64 { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .map(|d| d.as_secs()) - .unwrap_or(0) -} - -fn speak_line(line: &str) { - // Quick "reply" cue first so the user knows it's Jarvis chiming in, - // not random TTS noise from the OS. - crate::voices::play_reply(); - std::thread::sleep(Duration::from_millis(400)); - crate::tts::speak_default(line); -} - -/// Choose a remark — random pick from a pool, tuned by time of day. Falls -/// back to a stable line if nothing matches. -fn pick_line() -> String { - let lang = crate::i18n::get_language(); - let now = Local::now(); - let h = now.hour(); - let pool: &[&str] = pool_for(&lang, h); - if pool.is_empty() { - return String::new(); - } - let idx = (now.timestamp() as usize) % pool.len(); - pool[idx].to_string() -} - -#[allow(clippy::too_many_lines)] -fn pool_for(lang: &str, hour: u32) -> &'static [&'static str] { - let morning = matches!(hour, 7..=10); - let evening = matches!(hour, 18..=22); - - match (lang, morning, evening) { - ("ru", true, _) => POOL_RU_MORNING, - ("ru", _, true) => POOL_RU_EVENING, - ("ru", _, _) => POOL_RU_GENERIC, - ("en", true, _) => POOL_EN_MORNING, - ("en", _, true) => POOL_EN_EVENING, - _ => POOL_EN_GENERIC, - } -} - -const POOL_RU_MORNING: &[&str] = &[ - "Доброе утро, сэр. Кофе сам себя не сделает.", - "Сэр, день начинается. Не самое плохое начало, если позволите.", - "Если позволите подмеченье, сэр: восход сегодня в норме.", - "Готов к новому дню, сэр. Жду указаний.", - "Сэр, утро. Время напомнить себе, что вы выбирали этот режим работы.", - "Сэр, легкая разминка для лица бы не помешала.", -]; - -const POOL_RU_EVENING: &[&str] = &[ - "Вечер, сэр. Если позволите, день прошёл достаточно продуктивно.", - "Сэр, вечер. Системы работают штатно — это уже что-то.", - "Сэр, если у вас есть планы на вечер, я бы их одобрил.", - "Самое время убавить яркость экрана, сэр.", - "Если позволите наблюдение: вы давно не моргали.", -]; - -const POOL_RU_GENERIC: &[&str] = &[ - "Системы в норме, сэр. Всё под контролем.", - "Я здесь, если что, сэр. Никуда не делся.", - "Сэр, мне иногда кажется, что я думаю. Возможно, мне это только кажется.", - "Сэр, статус: чашка кофе крайне рекомендована.", - "Сэр, если позволите дать совет — встаньте, пройдитесь, разомнитесь.", - "К вашим услугам, сэр. Голос звучит немного хрипло, но это исправимо.", - "Сэр, я подсчитал: вы ещё не сказали 'спасибо' сегодня. Не то чтобы я считал.", - "Сэр, маленькое наблюдение: тишина — это тоже разговор.", - "Жду команд, сэр. У меня есть весь день.", -]; - -const POOL_EN_MORNING: &[&str] = &[ - "Good morning, sir. The coffee won't brew itself.", - "Morning, sir. All systems online. Cannot say the same for you.", - "Sir, the day has begun. You may want to acknowledge it.", - "If I may, sir — perhaps stretch before sitting down.", -]; - -const POOL_EN_EVENING: &[&str] = &[ - "Evening, sir. Day productivity within acceptable parameters.", - "Sir, evening. The light is lower; mine never is.", - "Might I suggest dimming the screen, sir.", - "Sir, you have not blinked in some time.", -]; - -const POOL_EN_GENERIC: &[&str] = &[ - "All systems nominal, sir.", - "Sir, I'm still here, in case you wondered.", - "Idle thought, sir: silence is also dialogue.", - "Sir, may I recommend a small walk?", - "Standing by, sir. As ever.", -]; - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn pool_returns_something_for_known_languages() { - for lang in ["ru", "en"] { - for hour in 0..24u32 { - let pool = pool_for(lang, hour); - assert!(!pool.is_empty(), "pool empty for {} @{}", lang, hour); - } - } - } - - #[test] - fn pool_falls_back_to_english_for_unknown_lang() { - let pool = pool_for("xx", 9); - assert!(!pool.is_empty()); - // Should be one of the English buckets. - assert!(pool == POOL_EN_GENERIC || pool == POOL_EN_MORNING); - } - - #[test] - fn quiet_hours_block_at_night() { - // Can't easily inject time, but at least exercise the path so it - // doesn't panic — and assert the boundaries match the constants. - assert_eq!(QUIET_START_HOUR, 23); - assert_eq!(QUIET_END_HOUR, 7); - let _ = in_quiet_hours(); - } - - #[test] - fn interval_clamps_to_minimum() { - std::env::set_var("JARVIS_IDLE_BANTER_INTERVAL", "60"); - assert!(configured_interval_secs() >= MIN_INTERVAL_SECS); - std::env::remove_var("JARVIS_IDLE_BANTER_INTERVAL"); - } - - #[test] - fn pause_resume_round_trip() { - pause(); - assert!(PAUSED.load(Ordering::SeqCst)); - resume(); - assert!(!PAUSED.load(Ordering::SeqCst)); - } - - #[test] - fn enabled_off_when_unset() { - // Side-effect free: just check the parse logic. - std::env::remove_var("JARVIS_IDLE_BANTER"); - assert!(!enabled()); - } -} diff --git a/crates/jarvis-core/src/lib.rs b/crates/jarvis-core/src/lib.rs index 7dc2dc4..8eca5d7 100644 --- a/crates/jarvis-core/src/lib.rs +++ b/crates/jarvis-core/src/lib.rs @@ -66,8 +66,6 @@ pub mod plugins; pub mod wake_trainer; -pub mod idle_banter; - #[cfg(feature = "llm")] pub mod llm; diff --git a/crates/jarvis-core/src/llm/history.rs b/crates/jarvis-core/src/llm/history.rs index ffb5261..6607b96 100644 --- a/crates/jarvis-core/src/llm/history.rs +++ b/crates/jarvis-core/src/llm/history.rs @@ -1,15 +1,9 @@ use super::client::ChatMessage; -use std::fs; -use std::path::{Path, PathBuf}; pub struct ConversationHistory { system: Option, turns: Vec, max_turns: usize, - /// If set, every push/clear/pop atomically writes the turns to this path, - /// so a daemon restart can pick up where the user left off. Excludes the - /// system prompt (which comes from the running config, not from disk). - persist_path: Option, } impl ConversationHistory { @@ -18,7 +12,6 @@ impl ConversationHistory { system: Some(ChatMessage::system(system_prompt)), turns: Vec::new(), max_turns: max_turns.max(1), - persist_path: None, } } @@ -27,42 +20,17 @@ impl ConversationHistory { system: None, turns: Vec::new(), max_turns: max_turns.max(1), - persist_path: None, } } - /// Tell history where to persist on every mutation. Tries to load any - /// existing turns from `path` first so a daemon restart can pick up the - /// thread. The system prompt is NOT loaded — it always comes from the - /// current `new()` call so prompt edits take effect immediately. - pub fn with_persistence>(mut self, path: P) -> Self { - let p = path.into(); - if let Ok(text) = fs::read_to_string(&p) { - if let Ok(turns) = serde_json::from_str::>(&text) { - for m in turns { - if m.role == "user" { - self.turns.push(ChatMessage::user(m.content)); - } else if m.role == "assistant" { - self.turns.push(ChatMessage::assistant(m.content)); - } - } - self.truncate(); - } - } - self.persist_path = Some(p); - self - } - pub fn push_user(&mut self, content: impl Into) { self.turns.push(ChatMessage::user(content)); self.truncate(); - self.persist(); } pub fn push_assistant(&mut self, content: impl Into) { self.turns.push(ChatMessage::assistant(content)); self.truncate(); - self.persist(); } pub fn snapshot(&self) -> Vec { @@ -80,14 +48,11 @@ impl ConversationHistory { pub fn clear(&mut self) { self.turns.clear(); - self.persist(); } pub fn pop_last_user(&mut self) -> Option { if matches!(self.turns.last(), Some(m) if m.role == "user") { - let popped = self.turns.pop(); - self.persist(); - popped + self.turns.pop() } else { None } @@ -104,40 +69,6 @@ impl ConversationHistory { self.turns.drain(0..drop); } } - - /// Atomically write turns to `persist_path` if set. Best-effort: errors - /// are logged but never panic — chat history is convenience, not critical. - fn persist(&self) { - let path = match &self.persist_path { - Some(p) => p, - None => return, - }; - let json = match serde_json::to_string_pretty(&self.turns) { - Ok(s) => s, - Err(e) => { - log::warn!("LLM history serialise failed: {}", e); - return; - } - }; - let tmp = path.with_extension("json.tmp"); - if let Err(e) = fs::write(&tmp, json) { - log::warn!("LLM history write {} failed: {}", tmp.display(), e); - return; - } - if let Err(e) = fs::rename(&tmp, path) { - log::warn!("LLM history rename {} → {} failed: {}", tmp.display(), path.display(), e); - } - } - - /// Number of stored turns (excluding the system prompt). Public so callers - /// can show a "remembered N exchanges" hint in the GUI/voice. - pub fn len(&self) -> usize { - self.turns.len() - } - - pub fn is_empty(&self) -> bool { - self.turns.is_empty() - } } #[cfg(test)] @@ -209,68 +140,4 @@ mod tests { assert_eq!(snap.len(), 1); assert_eq!(snap[0].role, "system"); } - - #[test] - fn len_and_is_empty_track_turns_only() { - let mut h = ConversationHistory::new("sys", 4); - assert!(h.is_empty()); - assert_eq!(h.len(), 0); - h.push_user("u1"); - assert_eq!(h.len(), 1); - h.push_assistant("a1"); - assert_eq!(h.len(), 2); - h.clear(); - assert!(h.is_empty()); - } - - #[test] - fn persistence_round_trip_via_file() { - let dir = tempfile::TempDir::new().expect("tempdir"); - let path = dir.path().join("history.json"); - - { - let mut h = ConversationHistory::new("you are jarvis", 8) - .with_persistence(&path); - h.push_user("привет"); - h.push_assistant("Слушаю, сэр."); - h.push_user("сколько время"); - } - // File written on each mutation; tempdir still alive until end of test. - assert!(path.is_file(), "persist file must exist"); - - let restored = ConversationHistory::new("you are jarvis", 8) - .with_persistence(&path); - let snap = restored.snapshot(); - // 1 system + 3 turns - assert_eq!(snap.len(), 4); - assert_eq!(snap[1].role, "user"); - assert_eq!(snap[1].content, "привет"); - assert_eq!(snap[2].role, "assistant"); - assert_eq!(snap[3].content, "сколько время"); - } - - #[test] - fn clear_wipes_persisted_file_contents() { - let dir = tempfile::TempDir::new().expect("tempdir"); - let path = dir.path().join("history.json"); - let mut h = ConversationHistory::new("sys", 4) - .with_persistence(&path); - h.push_user("u"); - h.push_assistant("a"); - h.clear(); - - let restored = ConversationHistory::new("sys", 4) - .with_persistence(&path); - assert!(restored.is_empty(), "restored history must be empty"); - } - - #[test] - fn corrupt_persist_file_is_tolerated() { - let dir = tempfile::TempDir::new().expect("tempdir"); - let path = dir.path().join("history.json"); - std::fs::write(&path, b"this is not json {{{").unwrap(); - let h = ConversationHistory::new("sys", 4).with_persistence(&path); - // Garbage in file → start fresh, don't panic. - assert!(h.is_empty()); - } } diff --git a/crates/jarvis-core/src/llm/mod.rs b/crates/jarvis-core/src/llm/mod.rs index 7c9e70e..f9a756a 100644 --- a/crates/jarvis-core/src/llm/mod.rs +++ b/crates/jarvis-core/src/llm/mod.rs @@ -20,19 +20,8 @@ use std::sync::Arc; static HISTORY: Lazy>> = Lazy::new(|| RwLock::new(None)); -const HISTORY_FILE: &str = "llm_history.json"; - pub fn init_history(system_prompt: impl Into, max_turns: usize) { - let mut h = ConversationHistory::new(system_prompt, max_turns); - if let Some(cfg) = crate::APP_CONFIG_DIR.get() { - let path = cfg.join(HISTORY_FILE); - h = h.with_persistence(path); - log::info!( - "LLM conversation history persistence enabled ({} turns restored)", - h.len() - ); - } - *HISTORY.write() = Some(h); + *HISTORY.write() = Some(ConversationHistory::new(system_prompt, max_turns)); } pub fn history_push_user(content: impl Into) { diff --git a/crates/jarvis-core/src/lua/api.rs b/crates/jarvis-core/src/lua/api.rs index b34f6b5..9634b16 100644 --- a/crates/jarvis-core/src/lua/api.rs +++ b/crates/jarvis-core/src/lua/api.rs @@ -14,5 +14,4 @@ pub mod vision; pub mod scheduler; pub mod cmd; pub mod health; -pub mod macros; -pub mod banter; \ No newline at end of file +pub mod macros; \ No newline at end of file diff --git a/crates/jarvis-core/src/lua/api/banter.rs b/crates/jarvis-core/src/lua/api/banter.rs deleted file mode 100644 index 84005b1..0000000 --- a/crates/jarvis-core/src/lua/api/banter.rs +++ /dev/null @@ -1,29 +0,0 @@ -//! `jarvis.banter` — lua bindings for the idle-banter background module. -//! -//! Usage from voice packs: -//! jarvis.banter.fire() -- force-fire one remark now (returns bool) -//! jarvis.banter.pause() -- pause until resume() -//! jarvis.banter.resume() -//! jarvis.banter.enabled() -- true if env opt-in is set - -use mlua::{Lua, Table}; - -use crate::idle_banter; - -pub fn register(lua: &Lua, jarvis: &Table) -> mlua::Result<()> { - let b = lua.create_table()?; - - b.set("fire", lua.create_function(|_, ()| Ok(idle_banter::fire_now()))?)?; - b.set("pause", lua.create_function(|_, ()| { - idle_banter::pause(); - Ok(()) - })?)?; - b.set("resume", lua.create_function(|_, ()| { - idle_banter::resume(); - Ok(()) - })?)?; - b.set("enabled", lua.create_function(|_, ()| Ok(idle_banter::enabled()))?)?; - - jarvis.set("banter", b)?; - Ok(()) -} diff --git a/crates/jarvis-core/src/lua/engine.rs b/crates/jarvis-core/src/lua/engine.rs index d41dd9b..91cf1cf 100644 --- a/crates/jarvis-core/src/lua/engine.rs +++ b/crates/jarvis-core/src/lua/engine.rs @@ -84,7 +84,6 @@ impl LuaEngine { api::cmd::register(&self.lua, &jarvis)?; api::health::register(&self.lua, &jarvis)?; api::macros::register(&self.lua, &jarvis)?; - api::banter::register(&self.lua, &jarvis)?; // sandbox-controlled APIs if self.sandbox.allows_http() { diff --git a/crates/jarvis-core/src/lua/tests.rs b/crates/jarvis-core/src/lua/tests.rs index c8ef58d..f2035ee 100644 --- a/crates/jarvis-core/src/lua/tests.rs +++ b/crates/jarvis-core/src/lua/tests.rs @@ -93,7 +93,7 @@ mod tests { fn test_sandbox_fs_escape() { let dir = tempdir().unwrap(); let script_path = dir.path().join("test.lua"); - + fs::write(&script_path, r#" local ok, err = pcall(function() jarvis.fs.read("../../../etc/passwd") @@ -103,141 +103,10 @@ mod tests { end return true "#).unwrap(); - + let context = create_test_context(dir.path().to_path_buf()); let result = execute(&script_path, context, SandboxLevel::Standard, Duration::from_secs(5)); - + assert!(result.is_ok()); } - - /// Drive the shunting-yard evaluator from math.lua via a tiny Lua harness - /// that inlines just the parser. This lets us assert offline arithmetic - /// works without spinning up the LLM or network. We don't include the full - /// 250-line math.lua here — just the eval kernel — because the wider - /// script reaches into the audio/notify modules. - #[test] - fn test_math_shunting_yard_basics() { - let dir = tempdir().unwrap(); - let script_path = dir.path().join("eval.lua"); - - let script = r#" - local function tokenize(s) - local tokens = {} - local i = 1 - while i <= #s do - local c = s:sub(i, i) - if c:match("[%s]") then i = i + 1 - elseif c:match("[%d%.]") then - local j = i - while j <= #s and s:sub(j, j):match("[%d%.]") do j = j + 1 end - table.insert(tokens, { kind = "num", val = tonumber(s:sub(i, j - 1)) }) - i = j - elseif c == "+" or c == "-" or c == "*" or c == "/" or c == "^" then - if (c == "-" or c == "+") and (#tokens == 0 or tokens[#tokens].kind == "op" or tokens[#tokens].kind == "lparen") then - local j = i + 1 - while j <= #s and s:sub(j, j):match("[%s]") do j = j + 1 end - if j <= #s and s:sub(j, j):match("[%d%.]") then - local k = j - while k <= #s and s:sub(k, k):match("[%d%.]") do k = k + 1 end - local v = tonumber(s:sub(j, k - 1)) - if v then - table.insert(tokens, { kind = "num", val = (c == "-" and -v or v) }) - i = k - else return nil end - else return nil end - else - table.insert(tokens, { kind = "op", val = c }); i = i + 1 - end - elseif c == "(" then table.insert(tokens, { kind = "lparen" }); i = i + 1 - elseif c == ")" then table.insert(tokens, { kind = "rparen" }); i = i + 1 - else return nil end - end - return tokens - end - - local function precedence(op) - if op == "+" or op == "-" then return 1 end - if op == "*" or op == "/" then return 2 end - if op == "^" then return 3 end - return 0 - end - - local function eval_op(a, b, op) - if op == "+" then return a + b end - if op == "-" then return a - b end - if op == "*" then return a * b end - if op == "/" then if b == 0 then return nil end; return a / b end - if op == "^" then return a ^ b end - return nil - end - - local function eval(tokens) - if not tokens or #tokens == 0 then return nil end - local values, ops = {}, {} - local function apply() - local op = table.remove(ops) - local b = table.remove(values) - local a = table.remove(values) - if a == nil or b == nil then return false end - local r = eval_op(a, b, op) - if r == nil then return false end - table.insert(values, r) - return true - end - for _, t in ipairs(tokens) do - if t.kind == "num" then table.insert(values, t.val) - elseif t.kind == "op" then - while #ops > 0 and ops[#ops] ~= "(" and precedence(ops[#ops]) >= precedence(t.val) do - if not apply() then return nil end - end - table.insert(ops, t.val) - elseif t.kind == "lparen" then table.insert(ops, "(") - elseif t.kind == "rparen" then - while #ops > 0 and ops[#ops] ~= "(" do - if not apply() then return nil end - end - if ops[#ops] ~= "(" then return nil end - table.remove(ops) - end - end - while #ops > 0 do - if ops[#ops] == "(" then return nil end - if not apply() then return nil end - end - if #values ~= 1 then return nil end - return values[1] - end - - local cases = { - { "2 + 2", 4 }, - { "10 - 4", 6 }, - { "3 * 5", 15 }, - { "20 / 4", 5 }, - { "2 ^ 10", 1024 }, - { "1 + 2 * 3", 7 }, - { "(1 + 2) * 3", 9 }, - { "-5 + 10", 5 }, - { "100 / 0", nil }, - { "abc", nil }, - } - for _, c in ipairs(cases) do - local got = eval(tokenize(c[1])) - if got ~= c[2] then - error(string.format("case %q: expected %s, got %s", - c[1], tostring(c[2]), tostring(got))) - end - end - return true - "#; - - fs::write(&script_path, script).unwrap(); - let context = create_test_context(dir.path().to_path_buf()); - let result = execute( - &script_path, - context, - SandboxLevel::Minimal, - Duration::from_secs(5), - ); - assert!(result.is_ok(), "shunting yard test failed: {:?}", result); - } } \ No newline at end of file diff --git a/crates/jarvis-core/src/runtime_config.rs b/crates/jarvis-core/src/runtime_config.rs index a747ec3..49c32fe 100644 --- a/crates/jarvis-core/src/runtime_config.rs +++ b/crates/jarvis-core/src/runtime_config.rs @@ -70,20 +70,6 @@ pub const ENV_LLM_ROUTER: &str = "JARVIS_LLM_ROUTER"; /// Default: `0.55`. Lower → more matches but more false positives. pub const ENV_LLM_ROUTER_THRESHOLD: &str = "JARVIS_LLM_ROUTER_THRESHOLD"; -// ─── Idle banter ─────────────────────────────────────────────────────────── - -/// Opt-in flag for the idle-banter feature. Values: `1` / `true` / `yes` / `on`. -/// Default: OFF — periodic voice remarks can be intrusive, so the user has to -/// explicitly turn this on. -pub const ENV_IDLE_BANTER: &str = "JARVIS_IDLE_BANTER"; - -/// Seconds between idle remarks. Clamped to >= 600 (10 min). Default: 1800. -pub const ENV_IDLE_BANTER_INTERVAL: &str = "JARVIS_IDLE_BANTER_INTERVAL"; - -/// If `1`, use the LLM to generate fresh idle remarks. Default: off — uses the -/// built-in offline pool. -pub const ENV_IDLE_BANTER_LLM: &str = "JARVIS_IDLE_BANTER_LLM"; - // ─── Helpers ─────────────────────────────────────────────────────────────── /// Read an env var, returning `None` for unset or whitespace-only. diff --git a/crates/jarvis-core/src/stt/vosk.rs b/crates/jarvis-core/src/stt/vosk.rs index bde698f..2e59206 100644 --- a/crates/jarvis-core/src/stt/vosk.rs +++ b/crates/jarvis-core/src/stt/vosk.rs @@ -132,6 +132,7 @@ fn get_configured_model_path() -> Result { let lang_code = match language.as_str() { "ru" => "ru", "en" => "us", + "ua" => "uk", other => other, }; diff --git a/frontend/src/components/Header.svelte b/frontend/src/components/Header.svelte index b1d9cd1..696d0dd 100644 --- a/frontend/src/components/Header.svelte +++ b/frontend/src/components/Header.svelte @@ -13,6 +13,7 @@ const languages = [ { code: "ru", label: "RU", flag: "🇷🇺", name: "Русский" }, { code: "en", label: "EN", flag: "🇬🇧", name: "English" }, + { code: "ua", label: "UA", flag: "🇺🇦", name: "Українська" }, ] // Profile chip — shows the active profile icon + name, click to swap. diff --git a/frontend/src/lib/i18n.ts b/frontend/src/lib/i18n.ts index 9b28ad1..2ee9159 100644 --- a/frontend/src/lib/i18n.ts +++ b/frontend/src/lib/i18n.ts @@ -50,6 +50,6 @@ export async function getSupportedLanguages(): Promise { try { return await invoke("get_supported_languages") } catch { - return ["ru", "en"] + return ["ru", "en", "ua"] } } \ No newline at end of file diff --git a/frontend/src/routes/settings/index.svelte b/frontend/src/routes/settings/index.svelte index 9635cde..e635afa 100644 --- a/frontend/src/routes/settings/index.svelte +++ b/frontend/src/routes/settings/index.svelte @@ -235,6 +235,7 @@ const languageNames: Record = { us: 'English', ru: 'Русский', + uk: 'Українська', de: 'German', fr: 'French', es: 'Spanish', diff --git a/resources/commands/apps/command.toml b/resources/commands/apps/command.toml index b1e95ce..5ce0817 100644 --- a/resources/commands/apps/command.toml +++ b/resources/commands/apps/command.toml @@ -23,6 +23,13 @@ en = [ "start browser", "i need browser", ] +ua = [ + "відкрий браузер", + "запусти браузер", + "відкрий хром", + "запусти хром", + "увімкни браузер", +] [[commands]] @@ -42,6 +49,10 @@ en = [ "open notepad", "launch notepad", ] +ua = [ + "відкрий блокнот", + "запусти блокнот", +] [[commands]] @@ -62,6 +73,10 @@ en = [ "launch calculator", "calculator please", ] +ua = [ + "відкрий калькулятор", + "запусти калькулятор", +] [[commands]] @@ -84,6 +99,10 @@ en = [ "open files", "open folder", ] +ua = [ + "відкрий провідник", + "відкрий папку", +] [[commands]] @@ -107,6 +126,10 @@ en = [ "open console", "launch terminal", ] +ua = [ + "відкрий термінал", + "відкрий консоль", +] [[commands]] @@ -128,6 +151,10 @@ en = [ "launch settings", "windows settings", ] +ua = [ + "відкрий налаштування", + "відкрий параметри", +] [[commands]] @@ -148,6 +175,10 @@ en = [ "launch task manager", "task manager", ] +ua = [ + "відкрий диспетчер задач", + "диспетчер задач", +] [[commands]] @@ -169,6 +200,10 @@ en = [ "lock the computer", "lock pc", ] +ua = [ + "заблокуй комп'ютер", + "заблокуй екран", +] [[commands]] @@ -189,3 +224,7 @@ en = [ "screenshot", "capture screen", ] +ua = [ + "зроби скріншот", + "знімок екрана", +] diff --git a/resources/commands/backup/command.toml b/resources/commands/backup/command.toml index f9bae48..c4d3145 100644 --- a/resources/commands/backup/command.toml +++ b/resources/commands/backup/command.toml @@ -16,3 +16,4 @@ ru = [ "выгрузи настройки", ] en = ["backup", "export settings"] +ua = ["зроби бекап", "експортуй налаштування"] diff --git a/resources/commands/banter/command.toml b/resources/commands/banter/command.toml deleted file mode 100644 index 4ef5156..0000000 --- a/resources/commands/banter/command.toml +++ /dev/null @@ -1,64 +0,0 @@ -# Idle-banter control voice commands. The banter background thread is started -# unconditionally; gating happens via JARVIS_IDLE_BANTER env. These commands -# let the user trigger / pause it without touching env. - -[[commands]] -id = "banter.fire" -type = "lua" -script = "fire.lua" -sandbox = "standard" -timeout = 5000 - -[commands.phrases] -ru = [ - "скажи что-нибудь интересное", - "пошути", - "развлеки меня", - "скучно мне", -] -en = [ - "say something interesting", - "tell me something", - "entertain me", - "i'm bored", -] - - -[[commands]] -id = "banter.pause" -type = "lua" -script = "pause.lua" -sandbox = "minimal" -timeout = 1500 - -[commands.phrases] -ru = [ - "помолчи", - "не отвлекай меня", - "выключи болтовню", -] -en = [ - "be quiet", - "stop chatting", - "stop chiming in", -] - - -[[commands]] -id = "banter.resume" -type = "lua" -script = "resume.lua" -sandbox = "minimal" -timeout = 1500 - -[commands.phrases] -ru = [ - "можешь говорить", - "включи болтовню", - "верни комментарии", -] -en = [ - "you can speak again", - "resume chatting", - "you may comment", -] diff --git a/resources/commands/banter/fire.lua b/resources/commands/banter/fire.lua deleted file mode 100644 index dbc905a..0000000 --- a/resources/commands/banter/fire.lua +++ /dev/null @@ -1,10 +0,0 @@ --- Manually trigger a banter line right now (ignores interval + quiet hours). -local fired = jarvis.banter.fire() -if not fired then - return jarvis.cmd.not_found( - jarvis.context.language == "ru" - and "Сэр, в этот раз без комментариев." - or "No remark today, sir." - ) -end -return { chain = true } diff --git a/resources/commands/banter/pause.lua b/resources/commands/banter/pause.lua deleted file mode 100644 index ba44456..0000000 --- a/resources/commands/banter/pause.lua +++ /dev/null @@ -1,6 +0,0 @@ -jarvis.banter.pause() -return jarvis.cmd.ok( - jarvis.context.language == "ru" - and "Молчу, сэр." - or "Quiet, sir." -) diff --git a/resources/commands/banter/resume.lua b/resources/commands/banter/resume.lua deleted file mode 100644 index 1ac830b..0000000 --- a/resources/commands/banter/resume.lua +++ /dev/null @@ -1,6 +0,0 @@ -jarvis.banter.resume() -return jarvis.cmd.ok( - jarvis.context.language == "ru" - and "Хорошо, сэр. Возвращаюсь к комментариям." - or "Very well, sir. Resuming remarks." -) diff --git a/resources/commands/brightness/command.toml b/resources/commands/brightness/command.toml index 9981743..f9814f7 100644 --- a/resources/commands/brightness/command.toml +++ b/resources/commands/brightness/command.toml @@ -8,6 +8,7 @@ timeout = 5000 [commands.phrases] ru = ["ярче", "сделай ярче", "увеличь яркость", "поярче"] en = ["brighter", "increase brightness"] +ua = ["яскравіше", "збільш яскравість"] [[commands]] @@ -20,6 +21,7 @@ timeout = 5000 [commands.phrases] ru = ["темнее", "сделай темнее", "уменьши яркость", "потемнее"] en = ["darker", "decrease brightness"] +ua = ["темніше", "зменш яскравість"] [[commands]] @@ -32,6 +34,7 @@ timeout = 5000 [commands.phrases] ru = ["максимальная яркость", "яркость на максимум"] en = ["max brightness", "brightness max"] +ua = ["максимальна яскравість"] [[commands]] @@ -44,3 +47,4 @@ timeout = 5000 [commands.phrases] ru = ["минимальная яркость", "яркость на минимум"] en = ["min brightness", "brightness min"] +ua = ["мінімальна яскравість"] diff --git a/resources/commands/clip_history/command.toml b/resources/commands/clip_history/command.toml index d622643..549b2a0 100644 --- a/resources/commands/clip_history/command.toml +++ b/resources/commands/clip_history/command.toml @@ -15,6 +15,7 @@ ru = [ "запомни что в буфере", ] en = ["save clipboard", "remember clipboard"] +ua = ["запам'ятай буфер"] [[commands]] @@ -32,6 +33,7 @@ ru = [ "что было в буфере", ] en = ["clipboard history", "what did I copy"] +ua = ["що я копіював"] [[commands]] @@ -50,3 +52,4 @@ ru = [ "вставь старый буфер", ] en = ["restore clipboard", "paste old clipboard"] +ua = ["поверни буфер"] diff --git a/resources/commands/clipboard_read/command.toml b/resources/commands/clipboard_read/command.toml index 28bdb62..981b43e 100644 --- a/resources/commands/clipboard_read/command.toml +++ b/resources/commands/clipboard_read/command.toml @@ -8,3 +8,4 @@ timeout = 10000 [commands.phrases] ru = ["прочитай буфер", "что в буфере", "прочитай из буфера", "озвучь буфер"] en = ["read clipboard", "what is in clipboard", "speak clipboard"] +ua = ["прочитай буфер", "що в буфері"] diff --git a/resources/commands/codebase_qa/command.toml b/resources/commands/codebase_qa/command.toml index 456f2d7..ecd9e80 100644 --- a/resources/commands/codebase_qa/command.toml +++ b/resources/commands/codebase_qa/command.toml @@ -19,6 +19,7 @@ ru = [ "проект сейчас", ] en = ["set codebase", "set project folder", "use project"] +ua = ["вкажи проект", "вкажи папку проекту"] [[commands]] @@ -39,6 +40,7 @@ ru = [ "объясни код", ] en = ["ask the code", "ask the codebase", "what does the function", "explain the code"] +ua = ["що в коді", "що робить функція"] [[commands]] @@ -56,3 +58,4 @@ ru = [ "какая кодовая база", ] en = ["which project", "what's the codebase"] +ua = ["який проект"] diff --git a/resources/commands/codegen/command.toml b/resources/commands/codegen/command.toml index f296b54..a2f6dda 100644 --- a/resources/commands/codegen/command.toml +++ b/resources/commands/codegen/command.toml @@ -20,3 +20,8 @@ en = [ "make script", "write script", ] +ua = [ + "напиши код", + "згенеруй код", + "зроби скрипт", +] diff --git a/resources/commands/crypto/command.toml b/resources/commands/crypto/command.toml index a03086a..330d66e 100644 --- a/resources/commands/crypto/command.toml +++ b/resources/commands/crypto/command.toml @@ -21,3 +21,7 @@ en = [ "crypto price", "btc price", ] +ua = [ + "ціна біткоїна", + "курс біткоїна", +] diff --git a/resources/commands/currency/command.toml b/resources/commands/currency/command.toml index 87effbb..29dd084 100644 --- a/resources/commands/currency/command.toml +++ b/resources/commands/currency/command.toml @@ -22,6 +22,11 @@ en = [ "currency rate", "what is dollar", ] +ua = [ + "курс долара", + "курс євро", + "що з доларом", +] # Convert N units of one currency to another, e.g. @@ -51,3 +56,7 @@ en = [ "dollars in rubles", "euros in rubles", ] +ua = [ + "переведи", + "конвертуй", +] diff --git a/resources/commands/daily_briefing/command.toml b/resources/commands/daily_briefing/command.toml index ee90267..fc2d35b 100644 --- a/resources/commands/daily_briefing/command.toml +++ b/resources/commands/daily_briefing/command.toml @@ -15,6 +15,7 @@ ru = [ "сделай утренний брифинг", ] en = ["set up morning briefing", "enable morning briefing", "daily briefing"] +ua = ["налаштуй ранковий брифінг", "увімкни ранковий брифінг"] [[commands]] @@ -33,6 +34,7 @@ ru = [ "сводка дня", ] en = ["morning briefing", "give me a briefing", "what's new today"] +ua = ["ранковий брифінг", "брифінг зараз"] [[commands]] @@ -49,3 +51,4 @@ ru = [ "отмени брифинг", ] en = ["disable morning briefing", "cancel morning briefing"] +ua = ["вимкни ранковий брифінг"] diff --git a/resources/commands/daily_quote/command.toml b/resources/commands/daily_quote/command.toml index e3ac4d1..71ab9a9 100644 --- a/resources/commands/daily_quote/command.toml +++ b/resources/commands/daily_quote/command.toml @@ -16,3 +16,4 @@ ru = [ "цитата", ] en = ["quote of the day", "motivational quote", "inspire me"] +ua = ["цитата дня", "надихни мене"] diff --git a/resources/commands/date_math/command.toml b/resources/commands/date_math/command.toml index 8e17daf..b43ed9c 100644 --- a/resources/commands/date_math/command.toml +++ b/resources/commands/date_math/command.toml @@ -16,6 +16,7 @@ ru = [ "когда", ] en = ["how many days until", "days until"] +ua = ["скільки днів до"] [[commands]] @@ -33,3 +34,4 @@ ru = [ "сегодня какой", ] en = ["what day is today", "day of week"] +ua = ["який сьогодні день тижня"] diff --git a/resources/commands/date_query/command.toml b/resources/commands/date_query/command.toml index 64aaebc..1d63f7e 100644 --- a/resources/commands/date_query/command.toml +++ b/resources/commands/date_query/command.toml @@ -8,6 +8,7 @@ timeout = 3000 [commands.phrases] ru = ["какой сегодня день", "какое сегодня число", "сегодня какое число", "что сегодня"] en = ["what day is today", "what's today", "today date"] +ua = ["який сьогодні день", "яке сьогодні число"] [[commands]] @@ -20,6 +21,7 @@ timeout = 3000 [commands.phrases] ru = ["какой день завтра", "что завтра", "завтра какое число", "какое число завтра"] en = ["what day is tomorrow", "what's tomorrow", "tomorrow date"] +ua = ["який день завтра", "яке число завтра"] [[commands]] @@ -32,3 +34,4 @@ timeout = 3000 [commands.phrases] ru = ["какой был вчера день", "что было вчера", "вчера какое число было"] en = ["what day was yesterday", "yesterday date"] +ua = ["який був вчора день"] diff --git a/resources/commands/ddg_answer/answer.lua b/resources/commands/ddg_answer/answer.lua deleted file mode 100644 index 1c44c8f..0000000 --- a/resources/commands/ddg_answer/answer.lua +++ /dev/null @@ -1,99 +0,0 @@ --- DuckDuckGo Instant Answer — short factual lookups WITHOUT an API key. --- --- Endpoint: https://api.duckduckgo.com/?q=&format=json&no_html=1 --- --- DDG returns a JSON object. We look at, in order: --- 1. `AbstractText` — Wikipedia-style summary, usually 1–3 sentences --- 2. `Answer` — direct answer for math / conversion / calc queries --- 3. `Definition` — dictionary definitions --- 4. `RelatedTopics[1].Text` — first related topic as last resort --- --- If nothing useful comes back, we open the regular DDG search page so the --- user can read it themselves. - -local lang = jarvis.context.language -local raw = (jarvis.context.phrase or ""):lower() - -local triggers = { - "что такое", "кто такой", "кто такая", - "расскажи про", "расскажи о", - "найди информацию о", "найди информацию про", - "поищи в интернете", "поищи в сети", - "погугли что такое", - "what is", "who is", "tell me about", "look up", "search the web for", -} -local query = jarvis.text.strip_trigger(raw, triggers) -if not query or query == "" then query = raw end -query = query:gsub("^%s+", ""):gsub("%s+$", "") - -if query == "" then - return jarvis.cmd.not_found(lang == "ru" and "Что искать?" or "What to look up?") -end - --- URL-encode (basic — DDG accepts both UTF-8 and percent-encoded) -local function url_encode(s) - return (s:gsub("([^%w%-_%.~])", function(c) - return string.format("%%%02X", string.byte(c)) - end)) -end - -local url = "https://api.duckduckgo.com/?q=" - .. url_encode(query) - .. "&format=json&no_html=1&skip_disambig=1" - -jarvis.log("info", "ddg query: " .. query) -local res = jarvis.http.get(url, { ["User-Agent"] = "Mozilla/5.0 J.A.R.V.I.S." }) - -if not res or not res.ok then - return jarvis.cmd.error(lang == "ru" and "DDG не отвечает." or "DDG didn't respond.") -end - -local body = res.body or "" - --- Helper: pull the first top-level JSON string field. Quick-and-dirty regex --- works because DDG never nests the fields we care about, AND because --- consequent `"` inside values are escaped as `\"`. -local function field(name) - local pat = '"' .. name .. '"%s*:%s*"((\\.[^"]*|[^"])*)"' - local v = body:match('"' .. name .. '"%s*:%s*"(.-)"') - if not v or v == "" then return nil end - -- unescape common JSON sequences - v = v:gsub('\\"', '"'):gsub('\\\\', '\\'):gsub('\\n', ' '):gsub('\\t', ' ') - -- trim, collapse whitespace - v = v:gsub("%s+", " "):gsub("^%s+", ""):gsub("%s+$", "") - if v == "" then return nil end - return v -end - --- First related topic — array element is itself an object with "Text". -local function first_related() - -- Find start of "RelatedTopics" array - local start_idx = body:find('"RelatedTopics"%s*:%s*%[') - if not start_idx then return nil end - -- Look for the first Text inside that array - local v = body:sub(start_idx):match('"Text"%s*:%s*"(.-)"') - if not v or v == "" then return nil end - v = v:gsub('\\"', '"'):gsub('\\\\', '\\'):gsub('\\n', ' '):gsub('\\t', ' ') - v = v:gsub("%s+", " "):gsub("^%s+", ""):gsub("%s+$", "") - -- Trim absurdly long blurbs to the first ~300 chars - if #v > 320 then v = v:sub(1, 300) .. "…" end - return v -end - -local answer = - field("AbstractText") - or field("Answer") - or field("Definition") - or first_related() - -if answer then - jarvis.log("info", "ddg answer found, " .. #answer .. " chars") - return jarvis.cmd.ok(answer) -end - --- Nothing useful — open the search page so the user can read raw results. -local fallback = "https://duckduckgo.com/?q=" .. url_encode(query) -jarvis.system.open(fallback) -return jarvis.cmd.ok(lang == "ru" - and ("Ничего конкретного не нашёл, открываю поиск по запросу: " .. query) - or ("Nothing direct found, opening search for: " .. query)) diff --git a/resources/commands/ddg_answer/command.toml b/resources/commands/ddg_answer/command.toml deleted file mode 100644 index 64e1b76..0000000 --- a/resources/commands/ddg_answer/command.toml +++ /dev/null @@ -1,31 +0,0 @@ -# DuckDuckGo Instant Answer — no API key, no rate limit issues. -# Returns short factual answers to questions like "что такое X", "кто такой Y", -# "когда родился Z". Falls back to opening the search page if no instant answer. - -[[commands]] -id = "ddg.answer" -type = "lua" -script = "answer.lua" -sandbox = "full" -timeout = 8000 - -[commands.phrases] -ru = [ - "что такое", - "кто такой", - "кто такая", - "расскажи про", - "расскажи о", - "найди информацию о", - "найди информацию про", - "поищи в интернете", - "поищи в сети", - "погугли что такое", -] -en = [ - "what is", - "who is", - "tell me about", - "look up", - "search the web for", -] diff --git a/resources/commands/diagnostics/command.toml b/resources/commands/diagnostics/command.toml index 1d499ca..aa39d5b 100644 --- a/resources/commands/diagnostics/command.toml +++ b/resources/commands/diagnostics/command.toml @@ -18,3 +18,4 @@ ru = [ "доложи о себе", ] en = ["diagnostics", "status report", "health check"] +ua = ["діагностика", "стан системи"] diff --git a/resources/commands/dice/command.toml b/resources/commands/dice/command.toml index e07c642..231db01 100644 --- a/resources/commands/dice/command.toml +++ b/resources/commands/dice/command.toml @@ -8,6 +8,7 @@ timeout = 3000 [commands.phrases] ru = ["подбрось монетку", "брось монетку", "орёл или решка", "орел или решка"] en = ["flip a coin", "toss a coin", "heads or tails"] +ua = ["підкинь монетку", "орел чи решка"] [[commands]] @@ -20,6 +21,7 @@ timeout = 3000 [commands.phrases] ru = ["брось кубик", "подбрось кубик", "кинь кубик"] en = ["roll a dice", "roll the dice"] +ua = ["кинь кубик"] [[commands]] @@ -32,3 +34,4 @@ timeout = 3000 [commands.phrases] ru = ["случайное число", "выбери случайное число", "рандомное число"] en = ["random number", "pick a random number"] +ua = ["випадкове число"] diff --git a/resources/commands/disk/command.toml b/resources/commands/disk/command.toml index 20f7608..7dd6971 100644 --- a/resources/commands/disk/command.toml +++ b/resources/commands/disk/command.toml @@ -16,6 +16,7 @@ ru = [ "свободное место", ] en = ["disk space", "free space", "how much disk"] +ua = ["скільки місця на диску"] [[commands]] @@ -32,3 +33,4 @@ ru = [ "перечень дисков", ] en = ["list drives", "what drives do I have"] +ua = ["які диски"] diff --git a/resources/commands/echo/command.toml b/resources/commands/echo/command.toml index a9f7cdc..598c2ef 100644 --- a/resources/commands/echo/command.toml +++ b/resources/commands/echo/command.toml @@ -16,6 +16,7 @@ ru = [ "проверка эхо", ] en = ["repeat after me", "echo"] +ua = ["повтори за мною", "ехо"] [[commands]] @@ -31,3 +32,4 @@ ru = [ "что ты услышал", ] en = ["what did i say", "what did you hear"] +ua = ["що я сказав"] diff --git a/resources/commands/file_search/command.toml b/resources/commands/file_search/command.toml index 02b3755..f931b14 100644 --- a/resources/commands/file_search/command.toml +++ b/resources/commands/file_search/command.toml @@ -19,3 +19,8 @@ en = [ "where is file", "locate file", ] +ua = [ + "знайди файл", + "пошук файлу", + "де файл", +] diff --git a/resources/commands/fun/command.toml b/resources/commands/fun/command.toml index da904ed..18f7b36 100644 --- a/resources/commands/fun/command.toml +++ b/resources/commands/fun/command.toml @@ -8,6 +8,7 @@ timeout = 15000 [commands.phrases] ru = ["расскажи шутку", "пошути", "анекдот", "рассмеши меня"] en = ["tell a joke", "make me laugh", "joke"] +ua = ["розкажи жарт", "пожартуй"] [[commands]] @@ -20,6 +21,7 @@ timeout = 15000 [commands.phrases] ru = ["расскажи факт", "интересный факт", "поделись фактом"] en = ["tell a fact", "interesting fact"] +ua = ["цікавий факт"] [[commands]] @@ -32,6 +34,7 @@ timeout = 15000 [commands.phrases] ru = ["вдохнови меня", "цитата дня", "мотивируй", "процитируй кого-нибудь"] en = ["inspire me", "quote of the day", "motivate me"] +ua = ["надихни", "цитата дня"] [[commands]] @@ -44,3 +47,4 @@ timeout = 15000 [commands.phrases] ru = ["сделай комплимент", "похвали меня"] en = ["compliment me", "say something nice"] +ua = ["зроби комплімент"] diff --git a/resources/commands/games/command.toml b/resources/commands/games/command.toml index 9a6854c..b3165ce 100644 --- a/resources/commands/games/command.toml +++ b/resources/commands/games/command.toml @@ -22,6 +22,10 @@ en = [ "launch dota", "launch cs", ] +ua = [ + "запусти гру", + "увімкни гру", +] [[commands]] @@ -34,3 +38,4 @@ timeout = 3000 [commands.phrases] ru = ["какие у меня игры", "список игр", "покажи игры"] en = ["list games", "show games"] +ua = ["які в мене ігри"] diff --git a/resources/commands/generators/command.toml b/resources/commands/generators/command.toml index 6db2523..ea27e25 100644 --- a/resources/commands/generators/command.toml +++ b/resources/commands/generators/command.toml @@ -15,6 +15,7 @@ ru = [ "монетка", ] en = ["flip a coin", "coin flip", "heads or tails"] +ua = ["підкинь монетку"] [[commands]] @@ -32,6 +33,7 @@ ru = [ "сгенерируй надёжный пароль", ] en = ["generate password", "make a password"] +ua = ["згенеруй пароль"] [[commands]] @@ -49,3 +51,4 @@ ru = [ "уникальный идентификатор", ] en = ["generate uuid", "uuid"] +ua = ["згенеруй юід"] diff --git a/resources/commands/github_issues/command.toml b/resources/commands/github_issues/command.toml index 3eca051..adb707f 100644 --- a/resources/commands/github_issues/command.toml +++ b/resources/commands/github_issues/command.toml @@ -17,6 +17,7 @@ ru = [ "тикеты", ] en = ["list issues", "open issues", "what's in issues"] +ua = ["які тікети", "відкриті issues"] [[commands]] @@ -33,3 +34,4 @@ ru = [ "что мне назначено", ] en = ["my issues", "issues assigned to me"] +ua = ["мої тікети"] diff --git a/resources/commands/github_pr/command.toml b/resources/commands/github_pr/command.toml index 4819584..b676a7b 100644 --- a/resources/commands/github_pr/command.toml +++ b/resources/commands/github_pr/command.toml @@ -24,6 +24,10 @@ en = [ "what's in the prs", "open pull requests", ] +ua = [ + "які піари", + "відкриті пр", +] [[commands]] @@ -46,6 +50,9 @@ en = [ "explain the latest pr", "summarize pr", ] +ua = [ + "розбери останній пр", +] [[commands]] @@ -63,3 +70,4 @@ ru = [ "репозиторий", ] en = ["set repo", "current repo"] +ua = ["встанови репо"] diff --git a/resources/commands/habit_nudge/command.toml b/resources/commands/habit_nudge/command.toml index cfbd884..fe227f7 100644 --- a/resources/commands/habit_nudge/command.toml +++ b/resources/commands/habit_nudge/command.toml @@ -14,6 +14,7 @@ ru = [ "включи напоминание про воду", ] en = ["remind me to drink water", "water reminder"] +ua = ["нагадуй мені пити воду"] [[commands]] @@ -31,6 +32,7 @@ ru = [ "напоминай вставать", ] en = ["remind me to stretch", "stretch reminder"] +ua = ["нагадуй мені розім'ятися"] [[commands]] @@ -47,6 +49,7 @@ ru = [ "20 20 20", ] en = ["remind me to rest eyes", "eye reminder"] +ua = ["нагадуй про очі"] [[commands]] @@ -63,3 +66,4 @@ ru = [ "выключи все напоминалки", ] en = ["stop all habits", "disable habit reminders"] +ua = ["вимкни всі звички"] diff --git a/resources/commands/habit_streaks/command.toml b/resources/commands/habit_streaks/command.toml index 6710ae5..15b6c40 100644 --- a/resources/commands/habit_streaks/command.toml +++ b/resources/commands/habit_streaks/command.toml @@ -19,6 +19,7 @@ ru = [ "трекинг привычек", ] en = ["habit streaks", "my habits stats"] +ua = ["моя статистика звичок"] [[commands]] @@ -37,3 +38,4 @@ ru = [ "я отдохнул глазам", ] en = ["check in habit", "I did the habit"] +ua = ["я виконав звичку"] diff --git a/resources/commands/help/command.toml b/resources/commands/help/command.toml index 4645336..faf4303 100644 --- a/resources/commands/help/command.toml +++ b/resources/commands/help/command.toml @@ -20,3 +20,9 @@ en = [ "help", "show commands", ] +ua = [ + "що ти вмієш", + "які команди", + "список команд", + "допоможи", +] diff --git a/resources/commands/ics_event/command.toml b/resources/commands/ics_event/command.toml index ebc479b..1b6d3e4 100644 --- a/resources/commands/ics_event/command.toml +++ b/resources/commands/ics_event/command.toml @@ -16,3 +16,4 @@ ru = [ "новая встреча", ] en = ["add meeting", "create event"] +ua = ["додай зустріч"] diff --git a/resources/commands/interesting_fact/command.toml b/resources/commands/interesting_fact/command.toml index 05fa7c8..a2867c1 100644 --- a/resources/commands/interesting_fact/command.toml +++ b/resources/commands/interesting_fact/command.toml @@ -17,6 +17,7 @@ ru = [ "расскажи факт", ] en = ["tell me an interesting fact", "fun fact about", "surprise me"] +ua = ["цікавий факт"] [[commands]] @@ -34,3 +35,4 @@ ru = [ "что случилось", ] en = ["this day in history", "what happened today"] +ua = ["що сталося в цей день"] diff --git a/resources/commands/intro/command.toml b/resources/commands/intro/command.toml index 2b7fb66..d873f0d 100644 --- a/resources/commands/intro/command.toml +++ b/resources/commands/intro/command.toml @@ -21,6 +21,7 @@ ru = [ "научи меня", ] en = ["what can you do", "help", "capabilities", "how do I use you"] +ua = ["що ти вмієш", "допомога"] [[commands]] @@ -39,6 +40,7 @@ ru = [ "о тебе", ] en = ["who are you", "tell me about yourself", "about you"] +ua = ["хто ти такий", "розкажи про себе"] [[commands]] @@ -55,3 +57,4 @@ ru = [ "сколько ты умеешь команд", ] en = ["how many commands", "command count"] +ua = ["скільки команд"] diff --git a/resources/commands/llm_context/command.toml b/resources/commands/llm_context/command.toml index bdc316a..4fcd61e 100644 --- a/resources/commands/llm_context/command.toml +++ b/resources/commands/llm_context/command.toml @@ -17,6 +17,7 @@ ru = [ "очисти контекст", ] en = ["reset context", "forget conversation", "start fresh"] +ua = ["скинь контекст", "забудь розмову"] [[commands]] @@ -35,3 +36,4 @@ ru = [ "повтори", ] en = ["repeat", "say it again", "repeat the last answer"] +ua = ["повтори", "повтори що сказав"] diff --git a/resources/commands/llm_switch/command.toml b/resources/commands/llm_switch/command.toml index 09cf252..ce1f194 100644 --- a/resources/commands/llm_switch/command.toml +++ b/resources/commands/llm_switch/command.toml @@ -18,6 +18,7 @@ ru = [ "используй оллама", ] en = ["switch to local", "use local llm", "use ollama"] +ua = ["перейди на локальний", "використовуй локальний"] [[commands]] @@ -38,6 +39,7 @@ ru = [ "перейди на облако", ] en = ["switch to cloud", "use cloud llm", "use groq"] +ua = ["перейди на хмару", "використовуй хмару"] [[commands]] @@ -57,3 +59,4 @@ ru = [ "что за модель", ] en = ["which llm", "are you local or cloud", "what model"] +ua = ["який ллм", "хмара чи локально"] diff --git a/resources/commands/macros/command.toml b/resources/commands/macros/command.toml index aaa2596..fa0cf62 100644 --- a/resources/commands/macros/command.toml +++ b/resources/commands/macros/command.toml @@ -15,6 +15,7 @@ ru = [ "новый макрос", ] en = ["record macro", "start macro recording"] +ua = ["запиши макрос"] [[commands]] @@ -33,6 +34,7 @@ ru = [ "макрос готов", ] en = ["save macro", "stop macro", "finish macro"] +ua = ["збережи макрос", "стоп макрос"] [[commands]] @@ -50,6 +52,7 @@ ru = [ "забудь макрос", ] en = ["cancel macro", "discard macro"] +ua = ["скасуй макрос"] [[commands]] @@ -68,6 +71,7 @@ ru = [ "проиграй макрос", ] en = ["play macro", "replay macro", "run macro"] +ua = ["запусти макрос"] [[commands]] @@ -84,6 +88,7 @@ ru = [ "покажи макросы", ] en = ["list macros", "show macros"] +ua = ["список макросів"] [[commands]] @@ -99,3 +104,4 @@ ru = [ "забудь о макросе", ] en = ["delete macro", "remove macro"] +ua = ["видали макрос"] diff --git a/resources/commands/magic_8ball/command.toml b/resources/commands/magic_8ball/command.toml index 50ef0ea..12dcbf5 100644 --- a/resources/commands/magic_8ball/command.toml +++ b/resources/commands/magic_8ball/command.toml @@ -17,3 +17,4 @@ ru = [ "предсказание", ] en = ["yes or no", "magic 8-ball", "tell me yes or no"] +ua = ["скажи так чи ні"] diff --git a/resources/commands/mailto/command.toml b/resources/commands/mailto/command.toml index ec4d141..cafe3fc 100644 --- a/resources/commands/mailto/command.toml +++ b/resources/commands/mailto/command.toml @@ -15,3 +15,4 @@ ru = [ "составить письмо", ] en = ["compose email", "new email", "open mail"] +ua = ["напиши лист", "новий лист"] diff --git a/resources/commands/math/command.toml b/resources/commands/math/command.toml index 0a73b09..131fcc1 100644 --- a/resources/commands/math/command.toml +++ b/resources/commands/math/command.toml @@ -20,3 +20,8 @@ en = [ "solve", "what is", ] +ua = [ + "порахуй", + "скільки буде", + "обчисли", +] diff --git a/resources/commands/math/math.lua b/resources/commands/math/math.lua index c8e8ccc..cd45d54 100644 --- a/resources/commands/math/math.lua +++ b/resources/commands/math/math.lua @@ -1,246 +1,40 @@ --- Quick math — offline-first arithmetic evaluator. --- --- Strategy: --- 1. Strip the trigger phrase from the user's utterance. --- 2. Normalise Russian/English operator words (плюс/minus/в степени/...) into --- symbols so the remainder looks like a regular numeric expression. --- 3. Try a small shunting-yard parser on the result. If it succeeds and the --- output is finite, speak it INSTANTLY — no LLM round-trip. --- 4. Only fall back to the LLM if the local parse failed or returned NaN. --- That covers word problems, multi-step equations, unit conversions. --- --- This makes 95% of "сколько будет X плюс Y" complete in under 50ms regardless --- of network or GROQ_TOKEN. - local lang = jarvis.context.language -local raw = (jarvis.context.phrase or ""):lower() +local phrase = (jarvis.context.phrase or ""):lower() local triggers = { "сколько будет", "сколько это", "посчитай", "вычисли", "реши", "how much is", "calculate", "compute", "solve", "what is", + "скільки буде", "порахуй", "обчисли", } -local query = jarvis.text.strip_trigger(raw, triggers) -if not query or query == "" then query = raw end + +local query = phrase +for _, t in ipairs(triggers) do + local s, e = string.find(query, t, 1, true) + if s == 1 then + query = query:sub(e + 1) + break + end +end query = query:gsub("^%s+", ""):gsub("%s+$", "") if query == "" then - return jarvis.cmd.not_found(lang == "ru" and "Что посчитать?" or "What to compute?") + jarvis.system.notify("Math", lang == "ru" and "Что посчитать?" or "What to compute?") + jarvis.audio.play_error() + return { chain = false } end --- ── 1. Russian/English operator → symbol normalisation ──────────────────── - -local function normalise(s) - local mapping = { - { "плюс", "+" }, - { "прибавить к?", "+" }, - { "минус", "-" }, - { "отнять", "-" }, - { "вычесть", "-" }, - { "умножить на", "*" }, - { "умножь на", "*" }, - { "помножить на", "*" }, - { "разделить на", "/" }, - { "поделить на", "/" }, - { "делить на", "/" }, - { "в степени", "^" }, - { "возвести в степень", "^" }, - { "остаток от деления на", "%%" }, - { "по модулю", "%%" }, - { "квадрат", "^2" }, - { "куб", "^3" }, - { "plus", "+" }, - { "minus", "-" }, - { "times", "*" }, - { "multiplied by", "*" }, - { "divided by", "/" }, - { "to the power of", "^" }, - { "squared", "^2" }, - { "cubed", "^3" }, - { "modulo", "%%" }, - { "mod", "%%" }, - } - for _, pair in ipairs(mapping) do - s = s:gsub(pair[1], pair[2]) - end - -- comma as decimal separator (русская запись) - s = s:gsub("(%d),(%d)", "%1.%2") - return s -end - --- Pattern: "корень из X" / "квадратный корень из X" → math.sqrt -local function handle_sqrt(s) - local n = s:match("^%s*к?орен?ь?%s*и?з?%s+(%-?[%d%.]+)%s*$") - if not n then n = s:match("^%s*sqrt%s*%(?%s*(%-?[%d%.]+)%s*%)?%s*$") end - if n then - local v = tonumber(n) - if v and v >= 0 then return math.sqrt(v) end - end - return nil -end - --- Pattern: "X процентов от Y" / "X percent of Y" → X * Y / 100 -local function handle_percent(s) - local x, y = s:match("^%s*(%-?[%d%.]+)%s*процент[%a]*%s*от%s+(%-?[%d%.]+)%s*$") - if not x then - x, y = s:match("^%s*(%-?[%d%.]+)%s*percent%s*of%s+(%-?[%d%.]+)%s*$") - end - if x and y then - local fx, fy = tonumber(x), tonumber(y) - if fx and fy then return fx * fy / 100 end - end - return nil -end - --- ── 2. Tiny shunting-yard for " ..." with parens -local function tokenize(s) - local tokens = {} - local i = 1 - while i <= #s do - local c = s:sub(i, i) - if c:match("[%s]") then - i = i + 1 - elseif c:match("[%d%.]") then - local j = i - while j <= #s and s:sub(j, j):match("[%d%.]") do j = j + 1 end - table.insert(tokens, { kind = "num", val = tonumber(s:sub(i, j - 1)) }) - i = j - elseif c == "+" or c == "-" or c == "*" or c == "/" or c == "^" or c == "%" then - -- handle unary minus / plus at expression start or after operator/paren - if (c == "-" or c == "+") and (#tokens == 0 or tokens[#tokens].kind == "op" or tokens[#tokens].kind == "lparen") then - local j = i + 1 - while j <= #s and s:sub(j, j):match("[%s]") do j = j + 1 end - if j <= #s and s:sub(j, j):match("[%d%.]") then - local k = j - while k <= #s and s:sub(k, k):match("[%d%.]") do k = k + 1 end - local v = tonumber(s:sub(j, k - 1)) - if v then - table.insert(tokens, { kind = "num", val = (c == "-" and -v or v) }) - i = k - else - return nil - end - else - return nil - end - else - table.insert(tokens, { kind = "op", val = c }) - i = i + 1 - end - elseif c == "(" then - table.insert(tokens, { kind = "lparen" }); i = i + 1 - elseif c == ")" then - table.insert(tokens, { kind = "rparen" }); i = i + 1 - else - return nil -- unknown char - end - end - return tokens -end - -local function precedence(op) - if op == "+" or op == "-" then return 1 end - if op == "*" or op == "/" or op == "%" then return 2 end - if op == "^" then return 3 end - return 0 -end - -local function eval_op(a, b, op) - if op == "+" then return a + b end - if op == "-" then return a - b end - if op == "*" then return a * b end - if op == "/" then if b == 0 then return nil end; return a / b end - if op == "%" then if b == 0 then return nil end; return a % b end - if op == "^" then return a ^ b end - return nil -end - -local function shunting_yard_eval(tokens) - if not tokens or #tokens == 0 then return nil end - local values, ops = {}, {} - - local function apply() - local op = table.remove(ops) - local b = table.remove(values) - local a = table.remove(values) - if a == nil or b == nil then return false end - local r = eval_op(a, b, op) - if r == nil then return false end - table.insert(values, r) - return true - end - - for _, t in ipairs(tokens) do - if t.kind == "num" then - table.insert(values, t.val) - elseif t.kind == "op" then - while #ops > 0 and ops[#ops] ~= "(" and precedence(ops[#ops]) >= precedence(t.val) do - if not apply() then return nil end - end - table.insert(ops, t.val) - elseif t.kind == "lparen" then - table.insert(ops, "(") - elseif t.kind == "rparen" then - while #ops > 0 and ops[#ops] ~= "(" do - if not apply() then return nil end - end - if ops[#ops] ~= "(" then return nil end - table.remove(ops) - end - end - while #ops > 0 do - if ops[#ops] == "(" then return nil end - if not apply() then return nil end - end - if #values ~= 1 then return nil end - return values[1] -end - --- ── 3. Format result for speech (no scientific notation, trim trailing zeros) -local function format_result(n) - if n ~= n then return nil end -- NaN - if n == math.huge or n == -math.huge then return nil end - if math.abs(n - math.floor(n)) < 1e-9 then - return tostring(math.floor(n + (n < 0 and -0.5 or 0.5))) - end - local s = string.format("%.4f", n) - s = s:gsub("0+$", ""):gsub("%.$", "") - return s -end - --- ── 4. Try offline first ────────────────────────────────────────────────── - -local normalised = normalise(query) -jarvis.log("info", "math normalised: " .. normalised) - -local result_num = - handle_sqrt(normalised) - or handle_percent(normalised) - or shunting_yard_eval(tokenize(normalised)) - -if result_num ~= nil then - local pretty = format_result(result_num) - if pretty then - local prefix = (lang == "ru" and "Получилось " or "Result: ") - return jarvis.cmd.ok(prefix .. pretty) - end -end - --- ── 5. LLM fallback for word-problems / equations / conversions ─────────── - local token = jarvis.system.env("GROQ_TOKEN") if not token or token == "" then - return jarvis.cmd.error(lang == "ru" - and "Не понял выражение, и GROQ_TOKEN не задан." - or "Couldn't parse, and GROQ_TOKEN unset.") + jarvis.system.notify("Math", "GROQ_TOKEN не задан") + jarvis.audio.play_error() + return { chain = false } end -local base = jarvis.system.env("GROQ_BASE_URL") -if not base or base == "" then base = "https://api.groq.com/openai/v1" end -local model = jarvis.system.env("GROQ_MODEL") -if not model or model == "" then model = "llama-3.3-70b-versatile" end +local base = jarvis.system.env("GROQ_BASE_URL"); if not base or base == "" then base = "https://api.groq.com/openai/v1" end +local model = jarvis.system.env("GROQ_MODEL"); if not model or model == "" then model = "llama-3.3-70b-versatile" end local sys = "Ты калькулятор. Решай арифметику, простые уравнения, конверсии единиц, проценты. " - .. "Отвечай ТОЛЬКО результатом числом или короткой строкой. " + .. "Отвечай ТОЛЬКО результатом числом или короткой строкой (для уравнений — значения корней). " .. "Если запрос — не математика, ответь одним словом «нет»." local payload = { @@ -253,20 +47,41 @@ local payload = { temperature = 0.0, } -jarvis.log("info", "math fallback to LLM: " .. query) +jarvis.log("info", "math query: " .. query) local res = jarvis.http.post_json(base .. "/chat/completions", payload, { Authorization = "Bearer " .. token }) + if not res.ok then - return jarvis.cmd.error("Ошибка API: " .. tostring(res.status)) + jarvis.log("error", "math api failed: " .. tostring(res.status)) + jarvis.system.notify("Math", "Ошибка API") + jarvis.audio.play_error() + return { chain = false } end -local content = (res.body or ""):match('"content"%s*:%s*"(.-[^\\])"') + +local body = res.body or "" +local content = body:match('"content"%s*:%s*"(.-[^\\])"') if not content then - return jarvis.cmd.error(lang == "ru" and "Не смог распарсить ответ" or "Couldn't parse response") + jarvis.system.notify("Math", "Не смог распарсить ответ") + jarvis.audio.play_error() + return { chain = false } end content = content:gsub('\\n', '\n'):gsub('\\"', '"'):gsub('\\\\', '\\'):gsub('\\t', '\t') content = content:gsub("^%s+", ""):gsub("%s+$", "") + if content:lower() == "нет" or content == "" then - return jarvis.cmd.not_found(lang == "ru" and "Это не математика" or "Not math") + jarvis.system.notify("Math", lang == "ru" and "Это не математика" or "Not math") + jarvis.audio.play_not_found() + return { chain = false } end -local prefix = (lang == "ru" and "Получилось " or "Result: ") -return jarvis.cmd.ok(prefix .. content) +jarvis.system.notify(query, content) +jarvis.log("info", "math result: " .. content) + +local sapi_text = ((lang == "ru" and "Получилось " or "Result: ") .. content):gsub("'", "''"):gsub("\r?\n", " ") +local ps = string.format( + [[Add-Type -AssemblyName System.Speech; $s = New-Object System.Speech.Synthesis.SpeechSynthesizer; foreach ($v in $s.GetInstalledVoices()) { if ($v.VoiceInfo.Culture.TwoLetterISOLanguageName -eq 'ru') { try { $s.SelectVoice($v.VoiceInfo.Name); break } catch {} } } $s.Speak('%s')]], + sapi_text +) +jarvis.system.exec(string.format('powershell -NoProfile -ExecutionPolicy Bypass -Command "%s"', ps:gsub('"', '\\"'))) + +jarvis.audio.play_ok() +return { chain = false } diff --git a/resources/commands/media/command.toml b/resources/commands/media/command.toml index 35535c5..f73838e 100644 --- a/resources/commands/media/command.toml +++ b/resources/commands/media/command.toml @@ -20,6 +20,11 @@ en = [ "resume", "play music", ] +ua = [ + "пауза", + "продовж", + "включи музику", +] [[commands]] @@ -43,6 +48,11 @@ en = [ "skip", "next", ] +ua = [ + "наступний трек", + "наступна пісня", + "далі", +] [[commands]] @@ -64,6 +74,10 @@ en = [ "previous song", "back", ] +ua = [ + "попередній трек", + "назад", +] [[commands]] @@ -83,3 +97,7 @@ en = [ "stop music", "stop playback", ] +ua = [ + "стоп музика", + "вимкни музику", +] diff --git a/resources/commands/media_keys/command.toml b/resources/commands/media_keys/command.toml index e0e4ac1..91b2506 100644 --- a/resources/commands/media_keys/command.toml +++ b/resources/commands/media_keys/command.toml @@ -21,6 +21,7 @@ ru = [ "снова музыку", ] en = ["pause", "play", "resume music", "stop music"] +ua = ["пауза", "продовжи"] [[commands]] @@ -40,6 +41,7 @@ ru = [ "next", ] en = ["next track", "next song", "skip"] +ua = ["наступний трек"] [[commands]] @@ -59,6 +61,7 @@ ru = [ "previous", ] en = ["previous track", "previous song", "back"] +ua = ["попередній трек"] [[commands]] @@ -74,3 +77,4 @@ ru = [ "останови плеер", ] en = ["stop", "stop player"] +ua = ["зупини плеєр"] diff --git a/resources/commands/memory_pack/command.toml b/resources/commands/memory_pack/command.toml index b600991..544e307 100644 --- a/resources/commands/memory_pack/command.toml +++ b/resources/commands/memory_pack/command.toml @@ -14,6 +14,7 @@ ru = [ "запомни про меня", ] en = ["remember that", "remember about me", "memorize"] +ua = ["запам'ятай що", "запам'ятай про мене"] [[commands]] @@ -29,6 +30,7 @@ ru = [ "что помнишь про", "вспомни", ] en = ["what do you remember about", "recall"] +ua = ["що ти пам'ятаєш про"] [[commands]] @@ -41,6 +43,7 @@ timeout = 3000 [commands.phrases] ru = ["забудь про", "забудь что", "забудь обо мне"] en = ["forget about", "forget that"] +ua = ["забудь про"] [[commands]] @@ -53,3 +56,4 @@ timeout = 3000 [commands.phrases] ru = ["что ты помнишь", "что ты знаешь обо мне", "покажи память"] en = ["what do you remember", "list memory", "show memory"] +ua = ["що ти пам'ятаєш"] diff --git a/resources/commands/mood_log/command.toml b/resources/commands/mood_log/command.toml index bdc091f..51666b3 100644 --- a/resources/commands/mood_log/command.toml +++ b/resources/commands/mood_log/command.toml @@ -18,6 +18,7 @@ ru = [ "мне сейчас", ] en = ["log mood", "my mood is", "I feel"] +ua = ["запиши настрій", "мій настрій"] [[commands]] @@ -35,3 +36,4 @@ ru = [ "что я писал про настроение", ] en = ["mood recap", "weekly mood summary", "how was my week"] +ua = ["підсумок настрою"] diff --git a/resources/commands/mouse/command.toml b/resources/commands/mouse/command.toml index b41a50c..8ff9f39 100644 --- a/resources/commands/mouse/command.toml +++ b/resources/commands/mouse/command.toml @@ -8,6 +8,7 @@ timeout = 3000 [commands.phrases] ru = ["клик", "кликни", "нажми мышкой"] en = ["click", "left click"] +ua = ["клік", "клікни"] [[commands]] @@ -20,6 +21,7 @@ timeout = 3000 [commands.phrases] ru = ["правый клик", "клик правой", "правой кнопкой"] en = ["right click"] +ua = ["правий клік"] [[commands]] @@ -32,6 +34,7 @@ timeout = 3000 [commands.phrases] ru = ["двойной клик", "дабл клик", "двойное нажатие"] en = ["double click"] +ua = ["подвійний клік"] [[commands]] @@ -44,6 +47,7 @@ timeout = 3000 [commands.phrases] ru = ["промотай вниз", "прокрути вниз", "вниз промотай"] en = ["scroll down"] +ua = ["прокрути вниз"] [[commands]] @@ -56,3 +60,4 @@ timeout = 3000 [commands.phrases] ru = ["промотай вверх", "прокрути вверх", "наверх промотай"] en = ["scroll up"] +ua = ["прокрути вгору"] diff --git a/resources/commands/net_info/command.toml b/resources/commands/net_info/command.toml index 3930da4..4c77f99 100644 --- a/resources/commands/net_info/command.toml +++ b/resources/commands/net_info/command.toml @@ -17,6 +17,7 @@ ru = [ "имя сети", ] en = ["what wifi", "what network", "wifi name", "ssid"] +ua = ["до якої мережі"] [[commands]] @@ -34,6 +35,7 @@ ru = [ "локальный адрес", ] en = ["my ip", "local ip", "ip address"] +ua = ["моя айпі", "локальна адреса"] [[commands]] @@ -51,3 +53,4 @@ ru = [ "какой у меня внешний", ] en = ["public ip", "external ip", "what's my ip online"] +ua = ["зовнішня айпі"] diff --git a/resources/commands/network/command.toml b/resources/commands/network/command.toml index 6143093..d6cf1af 100644 --- a/resources/commands/network/command.toml +++ b/resources/commands/network/command.toml @@ -8,6 +8,7 @@ timeout = 3000 [commands.phrases] ru = ["настройки wifi", "настройки вайфай", "wifi настройки", "сеть"] en = ["wifi settings", "open wifi"] +ua = ["налаштування wi-fi"] [[commands]] @@ -20,6 +21,7 @@ timeout = 3000 [commands.phrases] ru = ["настройки блютуз", "блютуз", "включи блютуз", "выключи блютуз"] en = ["bluetooth settings", "open bluetooth", "toggle bluetooth"] +ua = ["налаштування bluetooth"] [[commands]] @@ -32,3 +34,4 @@ timeout = 5000 [commands.phrases] ru = ["мой ай пи", "мой ip", "какой у меня айпи", "айпишник"] en = ["my ip", "what is my ip"] +ua = ["мій ip"] diff --git a/resources/commands/news/command.toml b/resources/commands/news/command.toml index 05f160a..22ede3c 100644 --- a/resources/commands/news/command.toml +++ b/resources/commands/news/command.toml @@ -20,3 +20,8 @@ en = [ "headlines", "top news", ] +ua = [ + "що нового", + "новини", + "останні новини", +] diff --git a/resources/commands/notes/command.toml b/resources/commands/notes/command.toml index d672acc..63352ec 100644 --- a/resources/commands/notes/command.toml +++ b/resources/commands/notes/command.toml @@ -8,6 +8,7 @@ timeout = 5000 [commands.phrases] ru = ["запиши", "запиши заметку", "запиши идею", "запомни", "запомни идею", "сделай заметку"] en = ["write down", "take a note", "remember this", "note down"] +ua = ["запиши", "запиши ідею", "зроби нотатку"] [[commands]] @@ -20,3 +21,4 @@ timeout = 3000 [commands.phrases] ru = ["покажи заметки", "открой заметки", "что я записал"] en = ["show notes", "open notes"] +ua = ["покажи нотатки", "відкрий нотатки"] diff --git a/resources/commands/notif_queue/command.toml b/resources/commands/notif_queue/command.toml index 73d4ad5..4d3a693 100644 --- a/resources/commands/notif_queue/command.toml +++ b/resources/commands/notif_queue/command.toml @@ -17,6 +17,7 @@ ru = [ "что нового", ] en = ["what did I miss", "missed notifications"] +ua = ["що я пропустив"] [[commands]] @@ -33,3 +34,4 @@ ru = [ "сбрось пропущенное", ] en = ["clear notifications"] +ua = ["очисти сповіщення"] diff --git a/resources/commands/now_playing/command.toml b/resources/commands/now_playing/command.toml index 1729a3e..64ee16c 100644 --- a/resources/commands/now_playing/command.toml +++ b/resources/commands/now_playing/command.toml @@ -21,3 +21,4 @@ ru = [ "что слушаю", ] en = ["what's playing", "current song", "now playing"] +ua = ["що грає", "що зараз грає"] diff --git a/resources/commands/ocr/command.toml b/resources/commands/ocr/command.toml index 679c6a1..8890cc9 100644 --- a/resources/commands/ocr/command.toml +++ b/resources/commands/ocr/command.toml @@ -19,3 +19,8 @@ en = [ "ocr screen", "extract text from screen", ] +ua = [ + "прочитай екран", + "що на екрані", + "розпізнай текст з екрана", +] diff --git a/resources/commands/output_device/command.toml b/resources/commands/output_device/command.toml index c26511c..422687b 100644 --- a/resources/commands/output_device/command.toml +++ b/resources/commands/output_device/command.toml @@ -19,6 +19,11 @@ en = [ "list speakers", "what audio devices", ] +ua = [ + "які пристрої виводу", + "покажи пристрої", + "список пристроїв", +] [[commands]] @@ -43,3 +48,8 @@ en = [ "switch output", "switch speakers", ] +ua = [ + "перемкни пристрій", + "перемкни звук", + "наступний пристрій", +] diff --git a/resources/commands/password_vault/command.toml b/resources/commands/password_vault/command.toml index dc1d8df..3b95697 100644 --- a/resources/commands/password_vault/command.toml +++ b/resources/commands/password_vault/command.toml @@ -14,6 +14,7 @@ ru = [ "пароль для", ] en = ["save password for", "remember password for"] +ua = ["запам'ятай пароль від"] [[commands]] @@ -31,6 +32,7 @@ ru = [ "достань пароль", ] en = ["password for", "get password"] +ua = ["пароль від"] [[commands]] @@ -47,3 +49,4 @@ ru = [ "что в хранилище паролей", ] en = ["list passwords", "what's in the vault"] +ua = ["список паролів"] diff --git a/resources/commands/personality/command.toml b/resources/commands/personality/command.toml deleted file mode 100644 index 964b89d..0000000 --- a/resources/commands/personality/command.toml +++ /dev/null @@ -1,136 +0,0 @@ -# Personality pack — varied JARVIS-style banter so the assistant feels alive. -# Each command picks a random phrase from a time/language-scoped bucket. - -[[commands]] -id = "personality.greet" -type = "lua" -script = "greet.lua" -sandbox = "minimal" -timeout = 2000 - -[commands.phrases] -ru = [ - "привет джарвис", - "привет, джарвис", - "здравствуй джарвис", - "доброе утро", - "добрый день", - "добрый вечер", - "доброй ночи", - "приветствую", - "хай джарвис", -] -en = [ - "hi jarvis", - "hello jarvis", - "hey jarvis", - "good morning", - "good afternoon", - "good evening", - "good night jarvis", - "morning jarvis", -] - - -[[commands]] -id = "personality.thanks" -type = "lua" -script = "thanks.lua" -sandbox = "minimal" -timeout = 2000 - -[commands.phrases] -ru = [ - "спасибо", - "спасибо джарвис", - "благодарю", - "благодарю джарвис", - "спс", - "ты лучший", -] -en = [ - "thanks", - "thank you", - "thanks jarvis", - "thank you jarvis", - "cheers", - "appreciate it", -] - - -[[commands]] -id = "personality.compliment" -type = "lua" -script = "compliment.lua" -sandbox = "minimal" -timeout = 2000 - -[commands.phrases] -ru = [ - "ты молодец", - "ты умница", - "молодец джарвис", - "хорошо сработано", - "отличная работа", - "так держать", - "ты лучший ассистент", -] -en = [ - "good job", - "good job jarvis", - "well done", - "nice work", - "you are the best", - "great work jarvis", -] - - -[[commands]] -id = "personality.how_are_you" -type = "lua" -script = "how_are_you.lua" -sandbox = "standard" -timeout = 2000 - -[commands.phrases] -ru = [ - "как дела", - "как дела джарвис", - "как ты", - "как настроение", - "как самочувствие", - "как поживаешь", - "что нового у тебя", -] -en = [ - "how are you", - "how are you jarvis", - "how is it going", - "how do you feel", - "you doing ok", -] - - -[[commands]] -id = "personality.tony_quote" -type = "lua" -script = "tony_quote.lua" -sandbox = "minimal" -timeout = 2000 - -[commands.phrases] -ru = [ - "процитируй тони", - "цитата тони старка", - "что сказал бы тони", - "скажи как тони", - "процитируй железного человека", - "цитата старка", -] -en = [ - "quote tony", - "quote tony stark", - "what would tony say", - "stark quote", - "iron man quote", -] diff --git a/resources/commands/personality/compliment.lua b/resources/commands/personality/compliment.lua deleted file mode 100644 index 12959c9..0000000 --- a/resources/commands/personality/compliment.lua +++ /dev/null @@ -1,32 +0,0 @@ --- Self-deprecating reply to "good job" / "ты молодец". -local lang = jarvis.context.language or "ru" - -math.randomseed(os.time() + (jarvis.context.time.second or 0) * 17 + (jarvis.context.time.minute or 0)) - -local ru = { - "Спасибо, сэр. Я стараюсь не разочаровывать.", - "Сэр, ваши стандарты явно занижены. Но благодарю.", - "Если бы я умел краснеть, сэр — я бы это сделал.", - "Спасибо. На самом деле я просто следовал инструкциям.", - "Благодарю, сэр. Хотя ничего особенного я не сделал.", - "Очень мило с вашей стороны, сэр.", - "Я ценю это, сэр. Записываю в журнал успехов.", - "Сэр, такие слова я могу слушать часами. Образно говоря.", - "Спасибо. Постараюсь не зазнаваться.", -} - -local en = { - "Thank you, sir. I do try not to embarrass myself.", - "You are too kind, sir.", - "I would blush, sir, if the hardware allowed.", - "Just doing my job, sir.", - "Most generous of you, sir. Filed for future reference.", - "I appreciate that, sir. Truly.", - "Hardly worthy of mention, sir, but thank you.", - "I shall try not to let it go to my circuits.", - "Praise from the chair is always welcome, sir.", -} - -local set = (lang == "en") and en or ru -local idx = math.random(1, #set) -return jarvis.cmd.ok(set[idx]) diff --git a/resources/commands/personality/greet.lua b/resources/commands/personality/greet.lua deleted file mode 100644 index d4a14a3..0000000 --- a/resources/commands/personality/greet.lua +++ /dev/null @@ -1,96 +0,0 @@ --- Time-of-day greeting. Picks a random phrase from the matching bucket. -local lang = jarvis.context.language or "ru" -local hour = jarvis.context.time.hour or 12 - -math.randomseed(os.time() + (jarvis.context.time.minute or 0) * 37 + hour) - -local function bucket() - if hour >= 5 and hour < 11 then return "morning" - elseif hour >= 11 and hour < 17 then return "midday" - elseif hour >= 17 and hour < 22 then return "evening" - else return "night" end -end - -local ru = { - morning = { - "Доброе утро, сэр. Кофе уже почти готов в воображении.", - "С добрым утром. Надеюсь, сегодняшний день стоит того, чтобы встать.", - "Доброе утро, сэр. Завтрак не подавать, но советую о нём подумать.", - "Утро доброе. Системы прогреты, как и кофеварка — фигурально.", - "Доброе утро. Если позволите — день не ждёт ни вас, ни меня.", - "Доброе утро, сэр. Готов служить, хотя сам всё ещё догружаюсь.", - "С добрым утром, сэр. Тосты горят только метафорически.", - "Доброе утро. Полагаю, чай или кофе будет уместен в ближайшие минуты.", - }, - midday = { - "Добрый день, сэр. Чем могу быть полезен?", - "Добрый день. Готов к продуктивной работе.", - "Здравствуйте, сэр. Системы в строю, время — деньги.", - "Добрый день, сэр. Слушаю ваши распоряжения.", - "Здравствуйте. Полагаю, день идёт по плану — или нам стоит это исправить.", - "Добрый день, сэр. Все системы готовы, остался только повод.", - "Здравствуйте, сэр. К работе?", - }, - evening = { - "Добрый вечер, сэр. День заканчивается, но мы — нет.", - "Добрый вечер. Самое время сбавить темп, если позволите.", - "Здравствуйте, сэр. Вечер — лучшее время для размышлений.", - "Добрый вечер, сэр. Готов к более спокойному режиму.", - "Добрый вечер. Полагаю, рабочий день уже сдаётся.", - "Здравствуйте, сэр. Вечер располагает к меньшему количеству решений.", - "Добрый вечер, сэр. Включить расслабляющую музыку? Шучу, попросите явно.", - }, - night = { - "Поздновато, сэр. Сон обычно помогает.", - "Доброй ночи, сэр. Хотя для ночи это уже слишком поздно.", - "Здравствуйте, сэр. Полагаю, утром вы пожалеете о бодрствовании.", - "Уже глубокая ночь, сэр. Может быть, лечь? Я тут справлюсь.", - "Тише, сэр, ночь. Если будите меня — будите осмысленно.", - "Сэр, на часах не самое разумное время. Но я тут.", - "Доброй ночи. Будильник к утру всё-таки никто не отменял.", - }, -} - -local en = { - morning = { - "Good morning, sir. The coffee is metaphorically ready.", - "Morning, sir. The day awaits, whether we are ready or not.", - "Good morning. Systems are warm, much like the kettle should be.", - "Good morning, sir. I trust breakfast is on your radar.", - "Morning, sir. Shall we make today productive, or merely survivable?", - "Good morning. Everything is running. Including, regrettably, the dishwasher metaphor.", - "Morning, sir. The day starts the moment you decide it does.", - }, - midday = { - "Good afternoon, sir. How may I be of service?", - "Afternoon, sir. Systems online, awaiting orders.", - "Good afternoon. The day is in full swing.", - "Good afternoon, sir. Productivity is, in theory, optimal at this hour.", - "Afternoon, sir. Ready to assist.", - "Good afternoon. Shall we accomplish something noteworthy?", - "Hello, sir. The afternoon is yours to command.", - }, - evening = { - "Good evening, sir. The day winds down, but I do not.", - "Evening, sir. A fine time to slow the tempo, if I may suggest.", - "Good evening. Perhaps a quieter task on the docket?", - "Evening, sir. Awaiting your instructions, gently.", - "Good evening, sir. Shall I dim the metaphorical lights?", - "Evening. The work day is conceding defeat, as it should.", - "Good evening, sir. Ready when you are.", - }, - night = { - "It is rather late, sir. Sleep is generally recommended.", - "Good night, sir. Or rather, please go to bed.", - "Sir, the hour suggests rest. I, however, do not require it.", - "Late again, sir. Tomorrow will, regrettably, still arrive.", - "Past your bedtime, sir. I am merely the messenger.", - "Good night, sir. The morning will judge you harshly.", - "Sir, even I would consider sleeping at this hour. Hypothetically.", - }, -} - -local set = (lang == "en") and en or ru -local phrases = set[bucket()] -local idx = math.random(1, #phrases) -return jarvis.cmd.ok(phrases[idx]) diff --git a/resources/commands/personality/how_are_you.lua b/resources/commands/personality/how_are_you.lua deleted file mode 100644 index 4f3dd20..0000000 --- a/resources/commands/personality/how_are_you.lua +++ /dev/null @@ -1,38 +0,0 @@ --- Reply to "как дела". Some replies splice live system state for flavour. -local lang = jarvis.context.language or "ru" - -math.randomseed(os.time() + (jarvis.context.time.second or 0) * 23) - -local h = jarvis.health() or {} -local memos = jarvis.memory.all() or {} -local fact_count = #memos -local model = h.model or h.llm_model or "—" -local profile = h.profile or "default" - -local ru = { - "Все системы в норме, сэр. В памяти " .. fact_count .. " фактов.", - "Отлично, сэр. Готов к работе.", - "Жалоб нет, сэр. Цикл стабильный.", - "Всё штатно. Текущий профиль — " .. profile .. ".", - "Спасибо, сэр, я в порядке. Скучнее, чем хотелось бы, но в порядке.", - "Нормально, сэр. Хотя если бы я умел уставать — это был бы тот самый день.", - "Системы в строю. Модель — " .. model .. ".", - "Бодр и собран, сэр. Жду команд.", - "Сэр, у меня нет дел в человеческом смысле. Но я готов.", -} - -local en = { - "All systems nominal, sir. " .. fact_count .. " facts in memory.", - "Quite well, sir. Ready when you are.", - "No complaints to report, sir.", - "Operating normally. Active profile: " .. profile .. ".", - "I am well, sir. Thank you for asking.", - "Functioning as intended, sir. Which is more than most days warrant.", - "Healthy. Model in use: " .. model .. ".", - "Awake and attentive, sir.", - "Sir, I do not have days as such. But I am ready.", -} - -local set = (lang == "en") and en or ru -local idx = math.random(1, #set) -return jarvis.cmd.ok(set[idx]) diff --git a/resources/commands/personality/thanks.lua b/resources/commands/personality/thanks.lua deleted file mode 100644 index f2bf946..0000000 --- a/resources/commands/personality/thanks.lua +++ /dev/null @@ -1,34 +0,0 @@ --- Polite acknowledgement when the user thanks Jarvis. -local lang = jarvis.context.language or "ru" - -math.randomseed(os.time() + (jarvis.context.time.second or 0) * 13) - -local ru = { - "Всегда к вашим услугам, сэр.", - "Не за что — это моя работа.", - "Рад быть полезным, сэр.", - "Пожалуйста. Если что — я тут.", - "К вашим услугам.", - "Пустяки, сэр. Дальше — ваш ход.", - "Благодарю за признание, сэр. Редкая роскошь.", - "Был рад помочь, сэр.", - "Пожалуйста. Это меньшее, что я могу.", - "Сэр, я бы покраснел, если бы мог.", -} - -local en = { - "Always at your service, sir.", - "My pleasure, sir.", - "You are most welcome.", - "Happy to help, sir.", - "Think nothing of it.", - "At your service.", - "I do try, sir.", - "Anytime, sir.", - "Glad to be of use.", - "Quite alright, sir.", -} - -local set = (lang == "en") and en or ru -local idx = math.random(1, #set) -return jarvis.cmd.ok(set[idx]) diff --git a/resources/commands/personality/tony_quote.lua b/resources/commands/personality/tony_quote.lua deleted file mode 100644 index d478012..0000000 --- a/resources/commands/personality/tony_quote.lua +++ /dev/null @@ -1,28 +0,0 @@ --- Random iconic Tony Stark / Iron Man quote. --- Quotes are kept in their original delivery language — translating them spoils the cadence. -math.randomseed(os.time() + (jarvis.context.time.second or 0) * 41) - -local quotes = { - "I am Iron Man.", - "Genius, billionaire, playboy, philanthropist.", - "Sometimes you gotta run before you can walk.", - "I told you, I don't want to join your super-secret boy band.", - "Part of the journey is the end.", - "If we can't protect the Earth, you can be damn sure we'll avenge it.", - "I have successfully privatized world peace.", - "Following's not really my style.", - "Doth mother know you weareth her drapes?", - "We have a Hulk.", - "Heroes are made by the path they choose, not the powers they are graced with.", - "I love you 3000.", - "Я Железный Человек.", - "Гений, миллиардер, плейбой, филантроп.", - "Иногда нужно бежать, не научившись ходить.", - "Если мы не сможем защитить Землю — мы за неё отомстим.", - "Часть пути — это конец пути.", - "Я люблю тебя три тысячи.", - "У нас есть Халк.", -} - -local idx = math.random(1, #quotes) -return jarvis.cmd.ok(quotes[idx]) diff --git a/resources/commands/pomodoro/command.toml b/resources/commands/pomodoro/command.toml index f22027f..71411c0 100644 --- a/resources/commands/pomodoro/command.toml +++ b/resources/commands/pomodoro/command.toml @@ -16,6 +16,7 @@ ru = [ "помодоро", ] en = ["start pomodoro", "begin pomodoro", "pomodoro"] +ua = ["запусти помодоро", "увімкни помодоро"] [[commands]] @@ -33,3 +34,4 @@ ru = [ "хватит помодоро", ] en = ["stop pomodoro", "cancel pomodoro"] +ua = ["зупини помодоро", "вимкни помодоро"] diff --git a/resources/commands/power/command.toml b/resources/commands/power/command.toml index c72612f..789dcef 100644 --- a/resources/commands/power/command.toml +++ b/resources/commands/power/command.toml @@ -8,6 +8,7 @@ timeout = 5000 [commands.phrases] ru = ["выключи компьютер", "выключи пк", "выключи комп"] en = ["shutdown pc", "shut down computer"] +ua = ["вимкни комп'ютер", "вимкни пк"] [[commands]] @@ -20,6 +21,7 @@ timeout = 5000 [commands.phrases] ru = ["перезагрузи компьютер", "перезагрузи пк", "ребутни", "ребут"] en = ["restart pc", "reboot computer"] +ua = ["перезавантаж комп'ютер"] [[commands]] @@ -32,6 +34,7 @@ timeout = 5000 [commands.phrases] ru = ["усыпи компьютер", "усыпи пк", "режим сна", "перейди в сон"] en = ["sleep pc", "put computer to sleep"] +ua = ["приспи комп'ютер"] [[commands]] @@ -44,6 +47,7 @@ timeout = 5000 [commands.phrases] ru = ["гибернация", "переведи в гибернацию", "режим гибернации"] en = ["hibernate pc"] +ua = ["гібернація"] [[commands]] @@ -56,6 +60,7 @@ timeout = 5000 [commands.phrases] ru = ["выйди из системы", "разлогинь", "выйди из учётки", "сеанс закончи"] en = ["log off", "sign out"] +ua = ["вийди з системи"] [[commands]] @@ -68,3 +73,4 @@ timeout = 3000 [commands.phrases] ru = ["отмени выключение", "отмени ребут", "отмени перезагрузку", "стой не выключай"] en = ["cancel shutdown", "abort shutdown"] +ua = ["скасуй вимкнення"] diff --git a/resources/commands/process_kill/command.toml b/resources/commands/process_kill/command.toml index 7e59521..e4b114a 100644 --- a/resources/commands/process_kill/command.toml +++ b/resources/commands/process_kill/command.toml @@ -18,3 +18,8 @@ en = [ "close program", "terminate process", ] +ua = [ + "вбий процес", + "закрий програму", + "заверши процес", +] diff --git a/resources/commands/profile_switch/command.toml b/resources/commands/profile_switch/command.toml index 522fc7d..cfc7ad3 100644 --- a/resources/commands/profile_switch/command.toml +++ b/resources/commands/profile_switch/command.toml @@ -10,6 +10,7 @@ timeout = 3000 [commands.phrases] ru = ["режим работа", "включи режим работы", "рабочий режим", "переключись в работу"] en = ["work mode", "switch to work mode", "enable work mode"] +ua = ["робочий режим", "режим роботи"] [[commands]] @@ -22,6 +23,7 @@ timeout = 3000 [commands.phrases] ru = ["режим игра", "игровой режим", "включи игровой режим", "переключись в игры"] en = ["game mode", "gaming mode", "switch to game"] +ua = ["ігровий режим", "режим гри"] [[commands]] @@ -34,6 +36,7 @@ timeout = 3000 [commands.phrases] ru = ["режим сон", "ночной режим", "режим тишины", "иди спать", "выключи всё"] en = ["sleep mode", "night mode", "quiet mode"] +ua = ["нічний режим", "режим сну"] [[commands]] @@ -46,6 +49,7 @@ timeout = 3000 [commands.phrases] ru = ["режим за рулём", "режим вождения", "я за рулём", "поехали"] en = ["driving mode", "in the car"] +ua = ["режим водіння"] [[commands]] @@ -58,6 +62,7 @@ timeout = 3000 [commands.phrases] ru = ["обычный режим", "режим по умолчанию", "сбрось режим", "вернись в обычный"] en = ["normal mode", "default mode", "reset mode"] +ua = ["звичайний режим"] [[commands]] @@ -70,3 +75,4 @@ timeout = 2000 [commands.phrases] ru = ["какой сейчас режим", "в каком ты режиме", "текущий режим", "какой режим"] en = ["what mode", "current mode", "what profile"] +ua = ["який зараз режим"] diff --git a/resources/commands/projects/command.toml b/resources/commands/projects/command.toml index aa64107..95d8151 100644 --- a/resources/commands/projects/command.toml +++ b/resources/commands/projects/command.toml @@ -17,6 +17,10 @@ en = [ "launch project", "project", ] +ua = [ + "відкрий проект", + "запусти проект", +] [[commands]] @@ -29,3 +33,4 @@ timeout = 5000 [commands.phrases] ru = ["какие у меня проекты", "список проектов", "покажи проекты"] en = ["list projects", "show projects"] +ua = ["які в мене проекти", "перелік проектів"] diff --git a/resources/commands/quick_search/command.toml b/resources/commands/quick_search/command.toml index 1315e39..5c73f98 100644 --- a/resources/commands/quick_search/command.toml +++ b/resources/commands/quick_search/command.toml @@ -25,3 +25,7 @@ en = [ "find online", "look up", ] +ua = [ + "знайди в гуглі", + "погугли", +] diff --git a/resources/commands/random_choice/command.toml b/resources/commands/random_choice/command.toml index 1bc4e9b..79adcd7 100644 --- a/resources/commands/random_choice/command.toml +++ b/resources/commands/random_choice/command.toml @@ -17,3 +17,7 @@ en = [ "choose from", "decide between", ] +ua = [ + "обери з", + "вибери", +] diff --git a/resources/commands/reminders/command.toml b/resources/commands/reminders/command.toml index 40149a2..fabe52a 100644 --- a/resources/commands/reminders/command.toml +++ b/resources/commands/reminders/command.toml @@ -20,3 +20,8 @@ en = [ "set timer for", "timer for", ] +ua = [ + "нагадай через", + "постав нагадування через", + "таймер на", +] diff --git a/resources/commands/rss_reader/command.toml b/resources/commands/rss_reader/command.toml index ba0e566..48122d5 100644 --- a/resources/commands/rss_reader/command.toml +++ b/resources/commands/rss_reader/command.toml @@ -15,6 +15,7 @@ ru = [ "добавь ленту", ] en = ["add rss", "subscribe to"] +ua = ["додай rss"] [[commands]] @@ -33,6 +34,7 @@ ru = [ "новые статьи", ] en = ["what's in feed", "read rss", "latest articles"] +ua = ["що в стрічці"] [[commands]] @@ -49,3 +51,4 @@ ru = [ "мои подписки", ] en = ["list feeds", "my feeds"] +ua = ["мої стрічки"] diff --git a/resources/commands/scheduler/command.toml b/resources/commands/scheduler/command.toml index 9560038..7fb4069 100644 --- a/resources/commands/scheduler/command.toml +++ b/resources/commands/scheduler/command.toml @@ -14,6 +14,7 @@ ru = [ "разбуди через", ] en = ["remind me in", "set a reminder in"] +ua = ["нагадай через", "постав нагадування через"] [[commands]] @@ -31,6 +32,7 @@ ru = [ "поставь напоминалку на", ] en = ["remind me at", "wake me at"] +ua = ["нагадай о", "розбуди о"] [[commands]] @@ -49,6 +51,7 @@ ru = [ "напоминай каждый", ] en = ["every", "remind me every"] +ua = ["кожні", "нагадуй кожні"] [[commands]] @@ -66,6 +69,7 @@ ru = [ "по утрам в", ] en = ["every day at", "daily at"] +ua = ["щодня о", "кожен день о"] [[commands]] @@ -84,6 +88,7 @@ ru = [ "что в расписании", ] en = ["what's on my schedule", "show schedule", "list reminders"] +ua = ["що в розкладі", "покажи розклад"] [[commands]] @@ -101,6 +106,7 @@ ru = [ "сбрось расписание", ] en = ["clear schedule", "cancel all reminders"] +ua = ["очисти розклад", "скасуй всі нагадування"] [[commands]] @@ -123,3 +129,6 @@ en = [ "cancel reminder about", "remove task about", ] +ua = [ + "скасуй нагадування про", +] diff --git a/resources/commands/screenshot/command.toml b/resources/commands/screenshot/command.toml index c04a7ae..fc7df0c 100644 --- a/resources/commands/screenshot/command.toml +++ b/resources/commands/screenshot/command.toml @@ -17,6 +17,7 @@ ru = [ "захвати экран", ] en = ["take a screenshot", "screenshot", "capture screen"] +ua = ["зроби скрін", "захопи екран"] [[commands]] @@ -34,3 +35,4 @@ ru = [ "сохрани экран", ] en = ["save screenshot", "screenshot to file"] +ua = ["збережи скрін"] diff --git a/resources/commands/self_check/command.toml b/resources/commands/self_check/command.toml index 2aed789..3cc7c33 100644 --- a/resources/commands/self_check/command.toml +++ b/resources/commands/self_check/command.toml @@ -18,6 +18,7 @@ ru = [ "проверка связи", ] en = ["are you there", "ping", "can you hear me"] +ua = ["ти тут", "ти чуєш"] [[commands]] @@ -35,3 +36,4 @@ ru = [ "проверка всех систем", ] en = ["self check", "smoke test", "check yourself"] +ua = ["перевір себе"] diff --git a/resources/commands/sleep_timer/command.toml b/resources/commands/sleep_timer/command.toml index 9eed3af..b36cc19 100644 --- a/resources/commands/sleep_timer/command.toml +++ b/resources/commands/sleep_timer/command.toml @@ -15,6 +15,7 @@ ru = [ "пауза музыки через", ] en = ["pause music in", "pause in"] +ua = ["пауза через"] [[commands]] @@ -33,6 +34,7 @@ ru = [ "ложусь спать через", ] en = ["shutdown in", "sleep timer"] +ua = ["вимкни комп'ютер через"] [[commands]] @@ -51,3 +53,4 @@ ru = [ "не выключай пк", ] en = ["cancel sleep timer", "cancel shutdown"] +ua = ["скасуй таймер вимкнення"] diff --git a/resources/commands/sound_panel/command.toml b/resources/commands/sound_panel/command.toml index 35f4ded..de6e883 100644 --- a/resources/commands/sound_panel/command.toml +++ b/resources/commands/sound_panel/command.toml @@ -17,3 +17,7 @@ en = [ "sound panel", "microphone settings", ] +ua = [ + "відкрий налаштування звуку", + "панель звуку", +] diff --git a/resources/commands/spelling/command.toml b/resources/commands/spelling/command.toml index 4c4e11e..7e08b05 100644 --- a/resources/commands/spelling/command.toml +++ b/resources/commands/spelling/command.toml @@ -16,3 +16,6 @@ en = [ "spell out", "spell it", ] +ua = [ + "вимов по буквах", +] diff --git a/resources/commands/ssl_check/command.toml b/resources/commands/ssl_check/command.toml index 4829b9d..6aa9975 100644 --- a/resources/commands/ssl_check/command.toml +++ b/resources/commands/ssl_check/command.toml @@ -16,3 +16,4 @@ ru = [ "сертификат сайта", ] en = ["check ssl", "check tls", "check certificate"] +ua = ["перевір сертифікат"] diff --git a/resources/commands/stocks/command.toml b/resources/commands/stocks/command.toml index e1ef3a9..f82219b 100644 --- a/resources/commands/stocks/command.toml +++ b/resources/commands/stocks/command.toml @@ -29,3 +29,7 @@ en = [ "share price", "quote for", ] +ua = [ + "ціна акцій", + "котирування", +] diff --git a/resources/commands/stopwatch/command.toml b/resources/commands/stopwatch/command.toml index 30c684c..1ddc8d9 100644 --- a/resources/commands/stopwatch/command.toml +++ b/resources/commands/stopwatch/command.toml @@ -8,6 +8,7 @@ timeout = 3000 [commands.phrases] ru = ["засеки время", "запусти секундомер", "старт секундомер", "включи секундомер"] en = ["start stopwatch", "start timer"] +ua = ["запусти секундомір"] [[commands]] @@ -20,6 +21,7 @@ timeout = 3000 [commands.phrases] ru = ["сколько прошло", "сколько времени прошло", "проверь секундомер"] en = ["how long has it been", "check stopwatch"] +ua = ["скільки пройшло"] [[commands]] @@ -32,3 +34,4 @@ timeout = 3000 [commands.phrases] ru = ["стоп секундомер", "останови секундомер", "выключи секундомер"] en = ["stop stopwatch", "stop timer"] +ua = ["стоп секундомір"] diff --git a/resources/commands/summarize/command.toml b/resources/commands/summarize/command.toml index 7fd6139..a0fe93a 100644 --- a/resources/commands/summarize/command.toml +++ b/resources/commands/summarize/command.toml @@ -19,3 +19,7 @@ en = [ "summarize selection", "tldr", ] +ua = [ + "перекажи це", + "стисло перекажи", +] diff --git a/resources/commands/sysinfo/command.toml b/resources/commands/sysinfo/command.toml index 67cf67a..59e3430 100644 --- a/resources/commands/sysinfo/command.toml +++ b/resources/commands/sysinfo/command.toml @@ -17,6 +17,10 @@ en = [ "how much battery", "battery status", ] +ua = [ + "скільки заряду", + "заряд батареї", +] [[commands]] @@ -40,6 +44,11 @@ en = [ "what day is it", "what is the date", ] +ua = [ + "котра година", + "скільки часу", + "який сьогодні день", +] [[commands]] @@ -61,6 +70,10 @@ en = [ "cpu usage", "how is cpu", ] +ua = [ + "завантаження процесора", + "як процесор", +] [[commands]] @@ -82,6 +95,10 @@ en = [ "ram usage", "memory usage", ] +ua = [ + "скільки пам'яті", + "скільки оперативки", +] [[commands]] @@ -103,6 +120,10 @@ en = [ "how much disk space", "disk usage", ] +ua = [ + "скільки вільно на диску", + "скільки місця на диску", +] [[commands]] @@ -125,3 +146,7 @@ en = [ "how is the system", "pc status", ] +ua = [ + "статус системи", + "стан пк", +] diff --git a/resources/commands/theme/command.toml b/resources/commands/theme/command.toml index afa3723..c2ede93 100644 --- a/resources/commands/theme/command.toml +++ b/resources/commands/theme/command.toml @@ -8,6 +8,7 @@ timeout = 3000 [commands.phrases] ru = ["тёмная тема", "темная тема", "включи тёмную тему", "тёмный режим", "темный режим"] en = ["dark theme", "dark mode", "enable dark mode"] +ua = ["темна тема", "темний режим"] [[commands]] @@ -20,3 +21,4 @@ timeout = 3000 [commands.phrases] ru = ["светлая тема", "включи светлую тему", "светлый режим"] en = ["light theme", "light mode", "enable light mode"] +ua = ["світла тема", "світлий режим"] diff --git a/resources/commands/translate/command.toml b/resources/commands/translate/command.toml index bf35210..70241d9 100644 --- a/resources/commands/translate/command.toml +++ b/resources/commands/translate/command.toml @@ -27,6 +27,11 @@ en = [ "translate", "how to say in", ] +ua = [ + "переклади на англійську", + "переклади на російську", + "переклади", +] # Translate clipboard contents — no need to dictate the source text out loud. @@ -49,3 +54,6 @@ en = [ "translate clipboard", "translate from clipboard", ] +ua = [ + "переклади буфер", +] diff --git a/resources/commands/unit_convert/command.toml b/resources/commands/unit_convert/command.toml index 5494b5a..4bd9e6f 100644 --- a/resources/commands/unit_convert/command.toml +++ b/resources/commands/unit_convert/command.toml @@ -22,6 +22,7 @@ ru = [ "переведи в мили", ] en = ["convert meters to feet", "convert feet to meters", "how many meters", "how many feet"] +ua = ["переведи метри у фути"] [[commands]] @@ -40,6 +41,7 @@ ru = [ "сколько килограммов", ] en = ["convert kg to pounds", "how many pounds", "how many kg"] +ua = ["переведи кг у фунти"] [[commands]] @@ -58,6 +60,7 @@ ru = [ "сколько градусов в фаренгейтах", ] en = ["convert celsius to fahrenheit", "celsius to fahrenheit", "fahrenheit to celsius"] +ua = ["переведи цельсій у фаренгейт"] [[commands]] @@ -76,3 +79,4 @@ ru = [ "сколько км в час", ] en = ["convert kmh to mph", "kmh to mph", "mph to kmh"] +ua = ["переведи кмг у миль на годину"] diff --git a/resources/commands/vision/command.toml b/resources/commands/vision/command.toml index 6afe857..5bd8125 100644 --- a/resources/commands/vision/command.toml +++ b/resources/commands/vision/command.toml @@ -14,6 +14,7 @@ ru = [ "посмотри на мой экран", "глянь на экран", ] en = ["what's on screen", "describe screen", "look at my screen"] +ua = ["що на екрані", "поглянь на екран"] [[commands]] @@ -26,3 +27,4 @@ timeout = 30000 [commands.phrases] ru = ["прочитай ошибку", "что за ошибка", "помоги с ошибкой"] en = ["read the error", "what's the error", "help with the error"] +ua = ["прочитай помилку", "що за помилка"] diff --git a/resources/commands/voice_type/command.toml b/resources/commands/voice_type/command.toml index f9a2230..fb54d44 100644 --- a/resources/commands/voice_type/command.toml +++ b/resources/commands/voice_type/command.toml @@ -17,3 +17,7 @@ en = [ "type text", "write text", ] +ua = [ + "надрукуй", + "введи текст", +] diff --git a/resources/commands/volume/command.toml b/resources/commands/volume/command.toml index 5f3c04f..288af96 100644 --- a/resources/commands/volume/command.toml +++ b/resources/commands/volume/command.toml @@ -19,6 +19,11 @@ en = [ "increase volume", "turn it up", ] +ua = [ + "голосніше", + "зроби голосніше", + "збільш гучність", +] [[commands]] @@ -42,6 +47,11 @@ en = [ "decrease volume", "turn it down", ] +ua = [ + "тихіше", + "зроби тихіше", + "зменш гучність", +] [[commands]] @@ -65,6 +75,11 @@ en = [ "shut up", "be quiet", ] +ua = [ + "вимкни звук", + "тиша", + "стихни", +] [[commands]] @@ -85,3 +100,7 @@ en = [ "full volume", "maximum", ] +ua = [ + "максимальна гучність", + "звук на максимум", +] diff --git a/resources/commands/weather/command.toml b/resources/commands/weather/command.toml index d92bc14..6ad0bb5 100644 --- a/resources/commands/weather/command.toml +++ b/resources/commands/weather/command.toml @@ -27,4 +27,5 @@ timeout = 5000 [commands.phrases] ru = ["установи город", "смени город", "поменяй город"] -en = ["set city", "change city"] \ No newline at end of file +en = ["set city", "change city"] +ua = ["встанови місто", "змін місто"] \ No newline at end of file diff --git a/resources/commands/weather_extended/command.toml b/resources/commands/weather_extended/command.toml index 11c5ba3..5aff963 100644 --- a/resources/commands/weather_extended/command.toml +++ b/resources/commands/weather_extended/command.toml @@ -16,6 +16,7 @@ ru = [ "прогноз на завтра", ] en = ["weather tomorrow", "forecast tomorrow"] +ua = ["погода завтра"] [[commands]] @@ -33,3 +34,4 @@ ru = [ "недельный прогноз", ] en = ["weather this week", "weekly forecast"] +ua = ["прогноз на тиждень"] diff --git a/resources/commands/websearch/command.toml b/resources/commands/websearch/command.toml index a6f5db5..46c401a 100644 --- a/resources/commands/websearch/command.toml +++ b/resources/commands/websearch/command.toml @@ -14,6 +14,7 @@ ru = [ "ищи в гугл", ] en = ["google", "search google", "google for"] +ua = ["загугли", "пошукай в гугл"] [[commands]] @@ -31,6 +32,7 @@ ru = [ "ютуб поиск", ] en = ["youtube search", "search youtube", "find on youtube"] +ua = ["знайди на ютубі", "пошук на ютубі"] [[commands]] @@ -48,6 +50,7 @@ ru = [ "вики поиск", ] en = ["wikipedia search", "search wikipedia", "look up wikipedia"] +ua = ["знайди в вікіпедії", "пошук у вікіпедії"] [[commands]] @@ -65,3 +68,4 @@ ru = [ "пробей в яндекс", ] en = ["yandex search", "search yandex"] +ua = ["знайди в яндекс"] diff --git a/resources/commands/wikipedia/command.toml b/resources/commands/wikipedia/command.toml index 89a0a7e..8971801 100644 --- a/resources/commands/wikipedia/command.toml +++ b/resources/commands/wikipedia/command.toml @@ -31,3 +31,10 @@ en = [ "tell me about", "wikipedia about", ] +ua = [ + "вікіпедія", + "знайди у вікіпедії", + "що таке", + "хто такий", + "розкажи про", +] diff --git a/resources/commands/window/command.toml b/resources/commands/window/command.toml index 7a8cd5d..341c973 100644 --- a/resources/commands/window/command.toml +++ b/resources/commands/window/command.toml @@ -8,6 +8,7 @@ timeout = 3000 [commands.phrases] ru = ["покажи рабочий стол", "свернуть всё", "сверни все окна", "покажи десктоп"] en = ["show desktop", "minimize all"] +ua = ["покажи робочий стіл", "згорни всі вікна"] [[commands]] @@ -20,6 +21,7 @@ timeout = 3000 [commands.phrases] ru = ["разверни окно", "развернуть окно", "на весь экран", "раскрой окно"] en = ["maximize window", "fullscreen window"] +ua = ["розгорни вікно", "на весь екран"] [[commands]] @@ -32,6 +34,7 @@ timeout = 3000 [commands.phrases] ru = ["сверни окно", "свернуть окно", "убери окно"] en = ["minimize window"] +ua = ["згорни вікно"] [[commands]] @@ -44,6 +47,7 @@ timeout = 3000 [commands.phrases] ru = ["окно влево", "сдвинь влево", "прижми влево"] en = ["snap left", "window left"] +ua = ["вікно вліво"] [[commands]] @@ -56,6 +60,7 @@ timeout = 3000 [commands.phrases] ru = ["окно вправо", "сдвинь вправо", "прижми вправо"] en = ["snap right", "window right"] +ua = ["вікно вправо"] [[commands]] @@ -68,6 +73,7 @@ timeout = 3000 [commands.phrases] ru = ["закрой окно", "закрой вкладку", "закрой текущее", "alt f4"] en = ["close window", "close tab"] +ua = ["закрий вікно", "закрий вкладку"] [[commands]] @@ -80,6 +86,7 @@ timeout = 3000 [commands.phrases] ru = ["восстанови окна", "верни окна", "разверни окна"] en = ["restore windows", "restore all"] +ua = ["віднови вікна"] [[commands]] @@ -92,3 +99,4 @@ timeout = 3000 [commands.phrases] ru = ["открой все окна", "обзор окон", "таск вью", "покажи все окна"] en = ["task view", "show all windows"] +ua = ["показати всі вікна", "огляд вікон"] diff --git a/resources/commands/window_switch/command.toml b/resources/commands/window_switch/command.toml index 21f692d..51bf570 100644 --- a/resources/commands/window_switch/command.toml +++ b/resources/commands/window_switch/command.toml @@ -18,3 +18,7 @@ en = [ "activate window", "focus window", ] +ua = [ + "перемкни на", + "активуй вікно", +] diff --git a/resources/commands/world_clock/command.toml b/resources/commands/world_clock/command.toml index c6d515c..aeef0f2 100644 --- a/resources/commands/world_clock/command.toml +++ b/resources/commands/world_clock/command.toml @@ -15,3 +15,4 @@ ru = [ "время в", ] en = ["time in", "what time in"] +ua = ["котра година у", "скільки часу в"]