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).
Before: clicking "Запустить" in jarvis-gui spawned jarvis-app, which
silently exited with code 1 (or in release builds, looked like the
console window flashed and closed). The pv_recorder library returns
INVALID_ARGUMENT from pv_recorder_init when Windows Core Audio reports
zero capture endpoints (some other app holding the mic exclusively,
or all input devices disabled in mmsys.cpl). User saw no actionable
feedback.
Now: on recorder::init failure jarvis-app calls notify_mic_problem()
which (Windows-only):
- Fires a long-duration Windows toast titled "J.A.R.V.I.S.: микрофон
не найден" with a hint pointing at mmsys.cpl / Recording.
- Spawns "start ms-settings:sound" so the Sound settings page opens
automatically — user can re-enable the mic in two clicks.
Then the original app::close(1, ...) path runs to keep the same exit
behaviour the GUI's get_jarvis_app_stats poller expects.
Cargo.toml: jarvis-app now pulls winrt-notification (already in
workspace.dependencies via jarvis-core) for the toast.
Also incidentally fixed: the release-build C0000139 (entrypoint not
found) loader crash that was showing up before this change. It went
away after the workspace dep was added and the release relink ran.
Most likely the previous release exe had a stale import table from an
earlier partial rebuild; the clean relink resolves it.
Non-Windows builds get a no-op eprintln so the binary still compiles
for Linux/macOS.
new module crates/jarvis-app/src/llm_fallback.rs holds an optional
LlmClient + ConversationHistory built once at startup. if GROQ_TOKEN is
unset the module logs a warning and stays disabled — voice commands keep
working as before.
both the wake-word voice path (recognize_command in app.rs) and the
gui-side text command path (process_text_command) now check whether the
recognized phrase starts with one of the configured trigger phrases
(ru: 'скажи' / 'ответь' / 'произнеси'). when it does, the remainder of
the phrase is sent to Groq and the reply is published as a new
IpcEvent::LlmReply { text } so the gui can speak it.
on api error the trailing user turn is popped from history, the russian
fallback line is sent over ipc and a 'error' voice cue plays. the loop
itself never panics.