feat: 5 new packs + dotenvy + regen icon.ico + shortcuts redo

Iron Man icon regenerated from resources/icons/icon.png at sizes
16/24/32/48/64/128/256 via Pillow (make_ico.py committed alongside);
icon.ico is now a clean 83 KB multi-res for sharp display at any
scale instead of whatever placeholder Tauri originally emitted.

dotenvy added to workspace deps. jarvis-app/main.rs and jarvis-gui/
main.rs both walk up from current_exe() looking for dev.env (up to
5 parent levels), so the Desktop shortcut can launch the exe
directly without a wrapper .bat to pre-set GROQ_TOKEN. Falls back
to dotenvy::dotenv() (cwd lookup) if nothing found near the binary.

5 new portable Lua command packs. Bumps total from 21 to 26.
cargo test -p jarvis-core --lib commands::tests still passes 3/3.

reminders/ — set_reminder. Parses "напомни через N (секунд|минут|
часов) <body>" or English equivalents; understands one Russian word
numerals (один, две, пять, десять, пятнадцать, ...) for the count
when speech recognition returns words instead of digits. Defaults to
5 minutes if number/unit missing. Spawns a detached PowerShell that
Start-Sleeps then fires: BurntToast if installed, System.Speech
SAPI (ru-RU voice preference), and a WScript.Shell.Popup as the
guaranteed-visible last resort.

date_query/ — today / tomorrow / yesterday. Single answer.lua keyed
by command_id, computes the offset via PowerShell Get-Date.AddDays()
in ru-RU culture so the weekday/month come out in Russian, then
speaks via SAPI.

voice_type/ — type_text. Strips the trigger, drops the remainder to
clipboard via jarvis.system.clipboard.set, then synthesises Ctrl+V
through user32!keybd_event so it lands in whatever window currently
has focus. Works in any text field (note app, browser, IDE,
Telegram, etc.).

dice/ — coin_flip / roll_dice / random_number. Single roll.lua,
seeds math.random with os.time + jitter, speaks the result. "1-6"
for dice, "1-100" for random.

stopwatch/ — start / check / stop. Uses jarvis.state.get/set
(persisted via jarvis-core's settings DB) to remember the start
timestamp, computes elapsed via jarvis.context.time.timestamp,
formats as "X сек" / "X мин Y сек" / "X ч Y мин Z сек".

All new packs follow the established patterns (sandbox=full, PS-via-
exec helper where needed, USERPROFILE/SystemDrive everywhere, no
hardcoded paths).
This commit is contained in:
Bossiara13 2026-05-15 12:28:44 +03:00
parent ae279aeef7
commit dcee98bddf
18 changed files with 516 additions and 1 deletions

View file

@ -11,6 +11,7 @@ jarvis-core = { path = "../jarvis-core", features = ["intent"] }
once_cell.workspace = true
log.workspace = true
simple-log = "2.4"
dotenvy.workspace = true
tray-icon = "0.21"
winit = "0.30"
image.workspace = true

View file

@ -28,6 +28,8 @@ mod tray;
static SHOULD_STOP: AtomicBool = AtomicBool::new(false);
fn main() -> Result<(), String> {
load_dotenv_near_exe();
eprintln!("[jarvis-app] step: init_dirs");
config::init_dirs()?;
@ -175,6 +177,27 @@ pub fn should_stop() -> bool {
SHOULD_STOP.load(Ordering::SeqCst)
}
// Look for dev.env next to the exe, then walk parents until we find one.
// Lets the user double-click jarvis-gui.exe / jarvis-app.exe without needing
// a wrapper .bat to pre-load GROQ_TOKEN and similar secrets.
fn load_dotenv_near_exe() {
if let Ok(exe) = std::env::current_exe() {
let mut dir = exe.parent().map(|p| p.to_path_buf());
for _ in 0..5 {
if let Some(d) = &dir {
let candidate = d.join("dev.env");
if candidate.is_file() {
let _ = dotenvy::from_path(&candidate);
eprintln!("[jarvis-app] loaded env: {}", candidate.display());
return;
}
dir = d.parent().map(|p| p.to_path_buf());
}
}
}
let _ = dotenvy::dotenv();
}
#[cfg(target_os = "windows")]
fn notify_mic_problem() {
use winrt_notification::{Toast, Duration as ToastDuration};