feat: GUI /memory page + disk/date_math/sleep_timer packs (10 new commands)
GUI: Memory management completes the management trio (/macros + /scheduler + /memory).
Tauri commands (crates/jarvis-gui/src/tauri_commands/memory.rs)
- memory_list → Vec<MemoryFact{key, value, created_at, last_used_at, use_count}>
- memory_remember(k, v) → persist new fact (or overwrite)
- memory_forget(key) → delete by exact key
- memory_search(q, limit) → substring search
GUI /memory page (frontend/src/routes/memory/index.svelte)
- Top: add-fact form (key + value inputs + "+Добавить" button)
- Substring filter input for the list
- Per-card: key + use_count badge + value (highlighted box) + timestamps +
"Забыть" button with confirm() guard
- Auto-sorted by recency (last_used_at desc)
- Empty state shows voice-hint: 'скажи Jarvis-у "запомни что я люблю чай улун"'
Header (frontend/src/components/Header.svelte)
- New "Память" button between /scheduler and /settings.
- i18n: header-memory in ru/en/ua FTL files.
New voice packs
resources/commands/disk/ (2 cmds)
- disk.free "сколько свободно на диске" / "сколько места на диске C"
PowerShell Get-PSDrive → speaks "Свободно X ГБ из Y, это N%"
- disk.list "какие у меня диски" → "C 120 ГБ, D 300 ГБ, E 50 ГБ"
resources/commands/date_math/ (2 cmds)
- date.days_until "сколько дней до нового года" / "сколько до 8 марта" /
"сколько до 15 марта" — recognises Russian months,
holidays (Новый год, Рождество, 8 марта, 9 мая).
Auto-rolls to next year if target already passed.
Russian-grammar pluralisation (день/дня/дней).
- date.day_of_week "какой сегодня день недели" — Zeller's congruence,
maps to ru day name.
resources/commands/sleep_timer/ (3 cmds)
- sleep_timer.pause_in "выключи музыку через 30 минут"
→ scheduler one-shot lua action that fires media
VK_MEDIA_PLAY_PAUSE via the existing media_keys helper.
(Auto-generates _fire_pause.lua wrapper if missing.)
- sleep_timer.shutdown_in "выключи компьютер через 1 час"
→ shutdown.exe /s /t <secs>. Caps at 24 hours.
Speaks "Скажите 'отмени таймер' чтобы передумать."
- sleep_timer.cancel "отмени таймер выключения" / "не выключай компьютер"
→ shutdown /a + scheduler.remove("sleep_timer_pause"),
idempotent.
Pack count: 64 → 67. Tests: 112/112 pass.
Build: cargo build --release -p jarvis-gui green.
This commit is contained in:
parent
84d3b57ddc
commit
d3180b7d78
18 changed files with 717 additions and 4 deletions
9
resources/commands/sleep_timer/cancel.lua
Normal file
9
resources/commands/sleep_timer/cancel.lua
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
-- Cancel both shutdown and pause timers (best-effort, ok if one was inactive).
|
||||
local removed_pause = jarvis.scheduler.remove("sleep_timer_pause")
|
||||
local sd_res = jarvis.system.exec("shutdown.exe /a")
|
||||
local sd_ok = sd_res and sd_res.success
|
||||
|
||||
if removed_pause or sd_ok then
|
||||
return jarvis.cmd.ok("Таймер отменён.")
|
||||
end
|
||||
return jarvis.cmd.not_found("Активных таймеров нет.")
|
||||
56
resources/commands/sleep_timer/command.toml
Normal file
56
resources/commands/sleep_timer/command.toml
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# Sleep timer — pause media + optional shutdown after N minutes.
|
||||
|
||||
[[commands]]
|
||||
id = "sleep_timer.pause_in"
|
||||
type = "lua"
|
||||
script = "pause_in.lua"
|
||||
sandbox = "standard"
|
||||
timeout = 5000
|
||||
|
||||
[commands.phrases]
|
||||
ru = [
|
||||
"выключи музыку через",
|
||||
"приостанови музыку через",
|
||||
"пауза через",
|
||||
"пауза музыки через",
|
||||
]
|
||||
en = ["pause music in", "pause in"]
|
||||
ua = ["пауза через"]
|
||||
|
||||
|
||||
[[commands]]
|
||||
id = "sleep_timer.shutdown_in"
|
||||
type = "lua"
|
||||
script = "shutdown_in.lua"
|
||||
sandbox = "full"
|
||||
timeout = 5000
|
||||
|
||||
[commands.phrases]
|
||||
ru = [
|
||||
"выключи компьютер через",
|
||||
"выключи пк через",
|
||||
"выключи комп через",
|
||||
"таймер выключения",
|
||||
"ложусь спать через",
|
||||
]
|
||||
en = ["shutdown in", "sleep timer"]
|
||||
ua = ["вимкни комп'ютер через"]
|
||||
|
||||
|
||||
[[commands]]
|
||||
id = "sleep_timer.cancel"
|
||||
type = "lua"
|
||||
script = "cancel.lua"
|
||||
sandbox = "standard"
|
||||
timeout = 3000
|
||||
|
||||
[commands.phrases]
|
||||
ru = [
|
||||
"отмени таймер выключения",
|
||||
"отмени таймер сна",
|
||||
"отмени паузу",
|
||||
"не выключай компьютер",
|
||||
"не выключай пк",
|
||||
]
|
||||
en = ["cancel sleep timer", "cancel shutdown"]
|
||||
ua = ["скасуй таймер вимкнення"]
|
||||
58
resources/commands/sleep_timer/pause_in.lua
Normal file
58
resources/commands/sleep_timer/pause_in.lua
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
-- "Выключи музыку через 30 минут" → scheduler one-shot фраза-команда "пауза"
|
||||
local phrase = (jarvis.context.phrase or ""):lower()
|
||||
local n_str, unit = phrase:match("(%d+)%s+(%S+)")
|
||||
if not n_str then
|
||||
return jarvis.cmd.error("Не понял через сколько.")
|
||||
end
|
||||
local n = tonumber(n_str)
|
||||
local secs
|
||||
if unit:match("^минут") then secs = "in " .. n .. " minutes"
|
||||
elseif unit:match("^час") then secs = "in " .. n .. " hours"
|
||||
elseif unit:match("^секунд") then secs = "in " .. n .. " seconds"
|
||||
else
|
||||
return jarvis.cmd.error("Не понял единицу.")
|
||||
end
|
||||
|
||||
-- Build path to media_keys/_media.ps1 (lives next door)
|
||||
local cmd_path = jarvis.context.command_path
|
||||
local helper = cmd_path:gsub("sleep_timer$", "media_keys") .. "\\_media.ps1"
|
||||
helper = helper:gsub("/", "\\")
|
||||
|
||||
-- VK_MEDIA_PLAY_PAUSE = 0xB3 = 179
|
||||
local lua_path = cmd_path:gsub("\\", "/") .. "/_pause_lua.lua"
|
||||
-- Simpler: just speak "пауза" and let agent router handle it? No — agent
|
||||
-- router only fires for unmatched commands. Better: directly invoke media key
|
||||
-- via a one-shot Lua script.
|
||||
|
||||
local script_path = cmd_path .. "\\_fire_pause.lua"
|
||||
script_path = script_path:gsub("/", "\\")
|
||||
|
||||
-- Write the helper script once
|
||||
local exists = jarvis.fs.is_file(script_path)
|
||||
if not exists then
|
||||
jarvis.fs.write(script_path, string.format([[
|
||||
local helper = "%s"
|
||||
jarvis.system.exec(string.format(
|
||||
'powershell -NoProfile -ExecutionPolicy Bypass -File "%%s" 179',
|
||||
helper
|
||||
))
|
||||
return { chain = false }
|
||||
]], helper:gsub("\\", "\\\\")))
|
||||
end
|
||||
|
||||
local ok, err = pcall(function()
|
||||
jarvis.scheduler.remove("sleep_timer_pause")
|
||||
jarvis.scheduler.add({
|
||||
id = "sleep_timer_pause",
|
||||
name = "Sleep timer: pause music",
|
||||
schedule = secs,
|
||||
action = { type = "lua", script_path = script_path },
|
||||
})
|
||||
end)
|
||||
|
||||
if not ok then
|
||||
jarvis.log("warn", "sleep_timer.pause_in: " .. tostring(err))
|
||||
return jarvis.cmd.error("Не получилось.")
|
||||
end
|
||||
|
||||
return jarvis.cmd.ok("Поставлю на паузу через " .. n_str .. " " .. unit .. ".")
|
||||
46
resources/commands/sleep_timer/shutdown_in.lua
Normal file
46
resources/commands/sleep_timer/shutdown_in.lua
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
-- "Выключи компьютер через 30 минут" → shutdown.exe /s /t <seconds>
|
||||
-- Использует штатный shutdown с обратным таймером.
|
||||
local phrase = (jarvis.context.phrase or ""):lower()
|
||||
local n_str, unit = phrase:match("(%d+)%s+(%S+)")
|
||||
if not n_str then
|
||||
return jarvis.cmd.error("Не понял через сколько.")
|
||||
end
|
||||
local n = tonumber(n_str)
|
||||
local secs
|
||||
if unit:match("^минут") then secs = n * 60
|
||||
elseif unit:match("^час") then secs = n * 3600
|
||||
elseif unit:match("^секунд") then secs = n
|
||||
else
|
||||
return jarvis.cmd.error("Не понял единицу. Минуты или часы.")
|
||||
end
|
||||
|
||||
if secs > 86400 then
|
||||
return jarvis.cmd.error("Слишком большое значение — максимум 24 часа.")
|
||||
end
|
||||
|
||||
local cmd = string.format(
|
||||
'shutdown.exe /s /t %d /c "Sleep timer fired"',
|
||||
secs
|
||||
)
|
||||
local res = jarvis.system.exec(cmd)
|
||||
if not res.success then
|
||||
return jarvis.cmd.error("Не получилось поставить таймер.")
|
||||
end
|
||||
|
||||
-- Pluralisation
|
||||
local label
|
||||
if unit:match("^минут") then
|
||||
local last = n % 10
|
||||
if last == 1 and n % 100 ~= 11 then label = n .. " минуту"
|
||||
elseif last >= 2 and last <= 4 and (n % 100 < 10 or n % 100 >= 20) then label = n .. " минуты"
|
||||
else label = n .. " минут" end
|
||||
elseif unit:match("^час") then
|
||||
local last = n % 10
|
||||
if last == 1 and n % 100 ~= 11 then label = n .. " час"
|
||||
elseif last >= 2 and last <= 4 and (n % 100 < 10 or n % 100 >= 20) then label = n .. " часа"
|
||||
else label = n .. " часов" end
|
||||
else
|
||||
label = n .. " секунд"
|
||||
end
|
||||
|
||||
return jarvis.cmd.ok("Выключу компьютер через " .. label .. ". Скажите 'отмени таймер выключения' чтобы передумать.")
|
||||
Loading…
Add table
Add a link
Reference in a new issue