Architecture — close the trigger-strip duplication:
lua/api/text.rs (new):
+ jarvis.text.strip_trigger(phrase, triggers) — case-insensitive
"longest trigger wins" stripping, returns trimmed remainder. The
11 packs that previously inlined a `for _, t in ipairs(triggers) do
string.find ... s == 1 then sub` loop can drop to one call.
Demonstrated in spelling/, window_switch/, summarize/.
+ jarvis.text.contains_any(phrase, needles) — boolean check for any
of N substrings, case-insensitive. Useful for keyword routing.
5 new portable Lua packs (sandbox=full). 36 → 41 total. cargo test
commands::tests still 3/3.
brightness/ — brightness_up / down / max / min. WMI WmiMonitorBrightness
(read CurrentBrightness) + WmiMonitorBrightnessMethods.WmiSetBrightness
(write). ±20% step for up/down; max=100, min=10. Desktop monitors that
don'\''t expose the WMI namespace get a friendly "не поддерживается"
toast instead of a silent failure.
window_switch/ — switch_to_window. Strip trigger ("переключись на" /
"switch to" / etc.), apply the same alias map as process_kill
(хром→chrome, телега→telegram, ...), then PowerShell finds processes
by name OR window-title substring sorted by StartTime desc and uses
(New-Object -ComObject WScript.Shell).AppActivate($pid) on the top
match. Speaks the window title that got focus.
spelling/ — spell_out. Strips "произнеси по буквам" / "spell out" /
etc., iterates `word:gmatch(".")`, joins letters with ". " so SAPI
gives each one a natural micro-pause. Uses jarvis.text.strip_trigger
+ jarvis.speak — 24 lines total.
network/ — open_wifi_settings (ms-settings:network-wifi) +
open_bluetooth_settings (ms-settings:bluetooth) + my_ip. my_ip pulls
the first non-loopback, non-APIPA IPv4 with Get-NetIPAddress and
fetches WAN IP from api.ipify.org (5s timeout); speaks "Локальный X.
Внешний Y".
summarize/ — summarize_selection. Synthesises Ctrl+C through user32!
keybd_event (so the focused window copies its selection), waits
220 ms for the clipboard to populate, reads via
jarvis.system.clipboard.get(), bails if < 20 chars, truncates at
8000, sends to Groq @ temp 0.3 with a "3-5 sentences, keep names"
prompt, drops the summary back into the clipboard and speaks it.
Lets you select ANY text in ANY app (browser, PDF, IDE) and say
"суммируй" to get a spoken TL;DR.
The new packs explicitly use the new jarvis.text + jarvis.llm +
jarvis.speak API surface — no inline PS-SAPI boilerplate, no inline
Groq plumbing. Code is now intent-first.
51 lines
2.2 KiB
Lua
51 lines
2.2 KiB
Lua
local lang = jarvis.context.language
|
||
local triggers = {
|
||
"переключись на", "переключи на", "активируй окно", "покажи окно", "перейди в",
|
||
"switch to", "activate window", "focus window",
|
||
"перемкни на", "активуй вікно",
|
||
}
|
||
local query = jarvis.text.strip_trigger(jarvis.context.phrase or "", triggers):lower()
|
||
if query == "" then
|
||
jarvis.system.notify("Switch", lang == "ru" and "Какое окно?" or "Which window?")
|
||
jarvis.audio.play_error()
|
||
return { chain = false }
|
||
end
|
||
|
||
local aliases = {
|
||
["хром"] = "chrome", ["хрома"] = "chrome",
|
||
["едж"] = "msedge", ["эдж"] = "msedge",
|
||
["файрфокс"] = "firefox", ["фаерфокс"] = "firefox",
|
||
["вскод"] = "code", ["vs code"] = "code",
|
||
["дискорд"] = "discord",
|
||
["телега"] = "telegram", ["телеграм"] = "telegram",
|
||
["стим"] = "steam",
|
||
["обс"] = "obs64",
|
||
["проводник"] = "explorer",
|
||
["блокнот"] = "notepad",
|
||
["терминал"] = "windowsterminal",
|
||
["спотифай"] = "spotify",
|
||
["яндекс"] = "browser",
|
||
["браузер"] = "browser",
|
||
}
|
||
local target = aliases[query] or query
|
||
|
||
local ps = string.format([[
|
||
$procs = Get-Process | Where-Object { $_.MainWindowTitle -and ($_.ProcessName -like '*%s*' -or $_.MainWindowTitle -like '*%s*') } | Sort-Object -Property StartTime -Descending
|
||
if ($procs.Count -eq 0) { Write-Output 'NOT_FOUND'; exit 0 }
|
||
$p = $procs[0]
|
||
$wsh = New-Object -ComObject WScript.Shell
|
||
$null = $wsh.AppActivate($p.Id)
|
||
Write-Output $p.MainWindowTitle
|
||
]], target:gsub("'", ""), target:gsub("'", ""))
|
||
|
||
local res = jarvis.system.exec(string.format('powershell -NoProfile -Command "%s"', ps:gsub('"', '\\"')))
|
||
local out = (res.stdout or ""):gsub("[\r\n]+$", ""):gsub("^%s+", "")
|
||
|
||
if out == "NOT_FOUND" or out == "" then
|
||
jarvis.system.notify("Switch", (lang == "ru" and "Не нашёл: " or "Not found: ") .. query)
|
||
jarvis.audio.play_not_found()
|
||
else
|
||
jarvis.system.notify(lang == "ru" and "Переключаюсь" or "Switching", out:sub(1, 80))
|
||
jarvis.audio.play_ok()
|
||
end
|
||
return { chain = false }
|