arch + features: jarvis.text API + brightness/window_switch/spelling/network/summarize (41 packs)
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.
This commit is contained in:
parent
a16d2401e7
commit
a7c002c9d4
14 changed files with 432 additions and 1 deletions
|
|
@ -7,3 +7,4 @@ pub mod state;
|
||||||
pub mod system;
|
pub mod system;
|
||||||
pub mod tts;
|
pub mod tts;
|
||||||
pub mod llm;
|
pub mod llm;
|
||||||
|
pub mod text;
|
||||||
65
crates/jarvis-core/src/lua/api/text.rs
Normal file
65
crates/jarvis-core/src/lua/api/text.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
use mlua::{Lua, Table, Value};
|
||||||
|
|
||||||
|
// Tiny text helpers that every trigger-driven pack reinvents. Centralising
|
||||||
|
// here removes ~10 lines of identical boilerplate per pack.
|
||||||
|
pub fn register(lua: &Lua, jarvis: &Table) -> mlua::Result<()> {
|
||||||
|
let text = lua.create_table()?;
|
||||||
|
|
||||||
|
// jarvis.text.strip_trigger(phrase, triggers) -> remainder
|
||||||
|
//
|
||||||
|
// Lowercases `phrase`, finds the FIRST trigger from `triggers` that
|
||||||
|
// matches at the start (case-insensitive substring), and returns
|
||||||
|
// everything after it trimmed. If nothing matches, returns the original
|
||||||
|
// phrase trimmed. Empty input returns "".
|
||||||
|
//
|
||||||
|
// Triggers should be ordered longest-first so "напомни мне через" wins
|
||||||
|
// over "напомни" — the function does NOT re-sort for the caller.
|
||||||
|
let strip_fn = lua.create_function(|lua, (phrase, triggers): (String, Table)| {
|
||||||
|
let lowered = phrase.to_lowercase();
|
||||||
|
let mut chosen_end: Option<usize> = None;
|
||||||
|
|
||||||
|
for v in triggers.sequence_values::<Value>() {
|
||||||
|
let t = match v {
|
||||||
|
Ok(Value::String(s)) => s.to_str()?.to_lowercase(),
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
if t.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if lowered.starts_with(&t) {
|
||||||
|
let after = lowered[t.len()..].chars().next();
|
||||||
|
if after.map_or(true, |c| !c.is_alphanumeric()) {
|
||||||
|
chosen_end = Some(t.len());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let rest = match chosen_end {
|
||||||
|
Some(end) => &phrase[end..],
|
||||||
|
None => phrase.as_str(),
|
||||||
|
};
|
||||||
|
Ok(lua.create_string(rest.trim())?)
|
||||||
|
})?;
|
||||||
|
text.set("strip_trigger", strip_fn)?;
|
||||||
|
|
||||||
|
// jarvis.text.contains_any(phrase, needles) -> boolean
|
||||||
|
// case-insensitive "does any needle appear anywhere in the phrase".
|
||||||
|
let contains_fn = lua.create_function(|_, (phrase, needles): (String, Table)| {
|
||||||
|
let lowered = phrase.to_lowercase();
|
||||||
|
for v in needles.sequence_values::<Value>() {
|
||||||
|
let n = match v {
|
||||||
|
Ok(Value::String(s)) => s.to_str()?.to_lowercase(),
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
if !n.is_empty() && lowered.contains(&n) {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(false)
|
||||||
|
})?;
|
||||||
|
text.set("contains_any", contains_fn)?;
|
||||||
|
|
||||||
|
jarvis.set("text", text)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
@ -77,6 +77,7 @@ impl LuaEngine {
|
||||||
api::audio::register(&self.lua, &jarvis)?;
|
api::audio::register(&self.lua, &jarvis)?;
|
||||||
api::context::register(&self.lua, &jarvis, context)?;
|
api::context::register(&self.lua, &jarvis, context)?;
|
||||||
api::tts::register(&self.lua, &jarvis)?;
|
api::tts::register(&self.lua, &jarvis)?;
|
||||||
|
api::text::register(&self.lua, &jarvis)?;
|
||||||
|
|
||||||
// sandbox-controlled APIs
|
// sandbox-controlled APIs
|
||||||
if self.sandbox.allows_http() {
|
if self.sandbox.allows_http() {
|
||||||
|
|
|
||||||
50
resources/commands/brightness/command.toml
Normal file
50
resources/commands/brightness/command.toml
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
[[commands]]
|
||||||
|
id = "brightness_up"
|
||||||
|
type = "lua"
|
||||||
|
script = "set.lua"
|
||||||
|
sandbox = "full"
|
||||||
|
timeout = 5000
|
||||||
|
|
||||||
|
[commands.phrases]
|
||||||
|
ru = ["ярче", "сделай ярче", "увеличь яркость", "поярче"]
|
||||||
|
en = ["brighter", "increase brightness"]
|
||||||
|
ua = ["яскравіше", "збільш яскравість"]
|
||||||
|
|
||||||
|
|
||||||
|
[[commands]]
|
||||||
|
id = "brightness_down"
|
||||||
|
type = "lua"
|
||||||
|
script = "set.lua"
|
||||||
|
sandbox = "full"
|
||||||
|
timeout = 5000
|
||||||
|
|
||||||
|
[commands.phrases]
|
||||||
|
ru = ["темнее", "сделай темнее", "уменьши яркость", "потемнее"]
|
||||||
|
en = ["darker", "decrease brightness"]
|
||||||
|
ua = ["темніше", "зменш яскравість"]
|
||||||
|
|
||||||
|
|
||||||
|
[[commands]]
|
||||||
|
id = "brightness_max"
|
||||||
|
type = "lua"
|
||||||
|
script = "set.lua"
|
||||||
|
sandbox = "full"
|
||||||
|
timeout = 5000
|
||||||
|
|
||||||
|
[commands.phrases]
|
||||||
|
ru = ["максимальная яркость", "яркость на максимум"]
|
||||||
|
en = ["max brightness", "brightness max"]
|
||||||
|
ua = ["максимальна яскравість"]
|
||||||
|
|
||||||
|
|
||||||
|
[[commands]]
|
||||||
|
id = "brightness_min"
|
||||||
|
type = "lua"
|
||||||
|
script = "set.lua"
|
||||||
|
sandbox = "full"
|
||||||
|
timeout = 5000
|
||||||
|
|
||||||
|
[commands.phrases]
|
||||||
|
ru = ["минимальная яркость", "яркость на минимум"]
|
||||||
|
en = ["min brightness", "brightness min"]
|
||||||
|
ua = ["мінімальна яскравість"]
|
||||||
44
resources/commands/brightness/set.lua
Normal file
44
resources/commands/brightness/set.lua
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
local lang = jarvis.context.language
|
||||||
|
local cmd_id = jarvis.context.command_id
|
||||||
|
|
||||||
|
local ps = [[
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
try {
|
||||||
|
$cur = (Get-WmiObject -Namespace root\WMI -Class WmiMonitorBrightness -ErrorAction Stop).CurrentBrightness
|
||||||
|
} catch {
|
||||||
|
Write-Output 'no_wmi'
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
$target = switch ('__OP__') {
|
||||||
|
'up' { [Math]::Min(100, $cur + 20) }
|
||||||
|
'down' { [Math]::Max(0, $cur - 20) }
|
||||||
|
'max' { 100 }
|
||||||
|
'min' { 10 }
|
||||||
|
default { $cur }
|
||||||
|
}
|
||||||
|
$m = Get-WmiObject -Namespace root\WMI -Class WmiMonitorBrightnessMethods
|
||||||
|
$m.WmiSetBrightness(1, $target) | Out-Null
|
||||||
|
Write-Output $target
|
||||||
|
]]
|
||||||
|
|
||||||
|
local op = ({
|
||||||
|
brightness_up = "up", brightness_down = "down",
|
||||||
|
brightness_max = "max", brightness_min = "min",
|
||||||
|
})[cmd_id] or "up"
|
||||||
|
|
||||||
|
local script = ps:gsub("__OP__", op)
|
||||||
|
local cmd = string.format('powershell -NoProfile -ExecutionPolicy Bypass -Command "%s"', script:gsub('"', '\\"'))
|
||||||
|
local res = jarvis.system.exec(cmd)
|
||||||
|
local out = (res.stdout or ""):gsub("[\r\n]+$", ""):gsub("^%s+", "")
|
||||||
|
|
||||||
|
if out == "no_wmi" or not res.success then
|
||||||
|
jarvis.system.notify("Brightness",
|
||||||
|
lang == "ru" and "Не поддерживается на этом дисплее"
|
||||||
|
or "Not supported on this display")
|
||||||
|
jarvis.audio.play_error()
|
||||||
|
return { chain = false }
|
||||||
|
end
|
||||||
|
|
||||||
|
jarvis.system.notify(lang == "ru" and "Яркость" or "Brightness", out .. "%")
|
||||||
|
jarvis.audio.play_ok()
|
||||||
|
return { chain = false }
|
||||||
37
resources/commands/network/command.toml
Normal file
37
resources/commands/network/command.toml
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
[[commands]]
|
||||||
|
id = "open_wifi_settings"
|
||||||
|
type = "lua"
|
||||||
|
script = "open.lua"
|
||||||
|
sandbox = "full"
|
||||||
|
timeout = 3000
|
||||||
|
|
||||||
|
[commands.phrases]
|
||||||
|
ru = ["настройки wifi", "настройки вайфай", "wifi настройки", "сеть"]
|
||||||
|
en = ["wifi settings", "open wifi"]
|
||||||
|
ua = ["налаштування wi-fi"]
|
||||||
|
|
||||||
|
|
||||||
|
[[commands]]
|
||||||
|
id = "open_bluetooth_settings"
|
||||||
|
type = "lua"
|
||||||
|
script = "open.lua"
|
||||||
|
sandbox = "full"
|
||||||
|
timeout = 3000
|
||||||
|
|
||||||
|
[commands.phrases]
|
||||||
|
ru = ["настройки блютуз", "блютуз", "включи блютуз", "выключи блютуз"]
|
||||||
|
en = ["bluetooth settings", "open bluetooth", "toggle bluetooth"]
|
||||||
|
ua = ["налаштування bluetooth"]
|
||||||
|
|
||||||
|
|
||||||
|
[[commands]]
|
||||||
|
id = "my_ip"
|
||||||
|
type = "lua"
|
||||||
|
script = "ip.lua"
|
||||||
|
sandbox = "full"
|
||||||
|
timeout = 5000
|
||||||
|
|
||||||
|
[commands.phrases]
|
||||||
|
ru = ["мой ай пи", "мой ip", "какой у меня айпи", "айпишник"]
|
||||||
|
en = ["my ip", "what is my ip"]
|
||||||
|
ua = ["мій ip"]
|
||||||
24
resources/commands/network/ip.lua
Normal file
24
resources/commands/network/ip.lua
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
local lang = jarvis.context.language
|
||||||
|
|
||||||
|
local ps = [[
|
||||||
|
$lan = (Get-NetIPAddress -AddressFamily IPv4 -ErrorAction SilentlyContinue |
|
||||||
|
Where-Object { $_.PrefixOrigin -eq 'Dhcp' -or $_.PrefixOrigin -eq 'Manual' } |
|
||||||
|
Where-Object { $_.IPAddress -notmatch '^127\.' -and $_.IPAddress -notmatch '^169\.254\.' } |
|
||||||
|
Select-Object -First 1).IPAddress
|
||||||
|
if (-not $lan) { $lan = '(none)' }
|
||||||
|
$wan = try { (Invoke-RestMethod -Uri 'https://api.ipify.org?format=json' -TimeoutSec 5).ip } catch { 'unavailable' }
|
||||||
|
Write-Output ('LAN ' + $lan + ' WAN ' + $wan)
|
||||||
|
]]
|
||||||
|
local res = jarvis.system.exec(string.format('powershell -NoProfile -Command "%s"', ps:gsub('"', '\\"')))
|
||||||
|
local out = (res.stdout or ""):gsub("[\r\n]+$", "")
|
||||||
|
|
||||||
|
if out == "" then
|
||||||
|
jarvis.audio.play_error()
|
||||||
|
return { chain = false }
|
||||||
|
end
|
||||||
|
|
||||||
|
jarvis.system.notify("IP", out)
|
||||||
|
local say = out:gsub("LAN", lang == "ru" and "Локальный" or "LAN"):gsub("WAN", lang == "ru" and ". Внешний" or ". WAN")
|
||||||
|
jarvis.speak(say, { lang = lang })
|
||||||
|
jarvis.audio.play_ok()
|
||||||
|
return { chain = false }
|
||||||
16
resources/commands/network/open.lua
Normal file
16
resources/commands/network/open.lua
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
local cmd_id = jarvis.context.command_id
|
||||||
|
local lang = jarvis.context.language
|
||||||
|
|
||||||
|
local uris = {
|
||||||
|
open_wifi_settings = "ms-settings:network-wifi",
|
||||||
|
open_bluetooth_settings = "ms-settings:bluetooth",
|
||||||
|
}
|
||||||
|
|
||||||
|
local uri = uris[cmd_id] or "ms-settings:network"
|
||||||
|
jarvis.system.open(uri)
|
||||||
|
jarvis.system.notify(
|
||||||
|
cmd_id == "open_wifi_settings" and "Wi-Fi" or "Bluetooth",
|
||||||
|
lang == "ru" and "Открываю настройки" or "Opening settings"
|
||||||
|
)
|
||||||
|
jarvis.audio.play_ok()
|
||||||
|
return { chain = false }
|
||||||
21
resources/commands/spelling/command.toml
Normal file
21
resources/commands/spelling/command.toml
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
[[commands]]
|
||||||
|
id = "spell_out"
|
||||||
|
type = "lua"
|
||||||
|
script = "spell.lua"
|
||||||
|
sandbox = "full"
|
||||||
|
timeout = 15000
|
||||||
|
|
||||||
|
[commands.phrases]
|
||||||
|
ru = [
|
||||||
|
"произнеси по буквам",
|
||||||
|
"продиктуй по буквам",
|
||||||
|
"произнеси буквы",
|
||||||
|
"по буквам",
|
||||||
|
]
|
||||||
|
en = [
|
||||||
|
"spell out",
|
||||||
|
"spell it",
|
||||||
|
]
|
||||||
|
ua = [
|
||||||
|
"вимов по буквах",
|
||||||
|
]
|
||||||
29
resources/commands/spelling/spell.lua
Normal file
29
resources/commands/spelling/spell.lua
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
local lang = jarvis.context.language
|
||||||
|
local triggers = {
|
||||||
|
"продиктуй по буквам", "произнеси по буквам", "произнеси буквы", "по буквам",
|
||||||
|
"spell out", "spell it",
|
||||||
|
"вимов по буквах",
|
||||||
|
}
|
||||||
|
local word = jarvis.text.strip_trigger(jarvis.context.phrase or "", triggers)
|
||||||
|
if word == "" then
|
||||||
|
jarvis.system.notify("Spelling", lang == "ru" and "Что произнести?" or "What to spell?")
|
||||||
|
jarvis.audio.play_error()
|
||||||
|
return { chain = false }
|
||||||
|
end
|
||||||
|
|
||||||
|
local letters = {}
|
||||||
|
for ch in word:gmatch(".") do
|
||||||
|
if ch ~= " " then table.insert(letters, ch) end
|
||||||
|
end
|
||||||
|
if #letters == 0 then
|
||||||
|
jarvis.audio.play_error()
|
||||||
|
return { chain = false }
|
||||||
|
end
|
||||||
|
|
||||||
|
local spoken = table.concat(letters, ". ") .. "."
|
||||||
|
|
||||||
|
jarvis.system.notify(lang == "ru" and "По буквам" or "Spelling",
|
||||||
|
word .. "\n" .. spoken)
|
||||||
|
jarvis.speak(spoken, { lang = lang })
|
||||||
|
jarvis.audio.play_ok()
|
||||||
|
return { chain = false }
|
||||||
25
resources/commands/summarize/command.toml
Normal file
25
resources/commands/summarize/command.toml
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
[[commands]]
|
||||||
|
id = "summarize_selection"
|
||||||
|
type = "lua"
|
||||||
|
script = "summarize.lua"
|
||||||
|
sandbox = "full"
|
||||||
|
timeout = 25000
|
||||||
|
|
||||||
|
[commands.phrases]
|
||||||
|
ru = [
|
||||||
|
"суммируй",
|
||||||
|
"перескажи выделенное",
|
||||||
|
"кратко перескажи",
|
||||||
|
"о чём это",
|
||||||
|
"что выделил",
|
||||||
|
"перескажи это",
|
||||||
|
]
|
||||||
|
en = [
|
||||||
|
"summarise this",
|
||||||
|
"summarize selection",
|
||||||
|
"tldr",
|
||||||
|
]
|
||||||
|
ua = [
|
||||||
|
"перекажи це",
|
||||||
|
"стисло перекажи",
|
||||||
|
]
|
||||||
43
resources/commands/summarize/summarize.lua
Normal file
43
resources/commands/summarize/summarize.lua
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
local lang = jarvis.context.language
|
||||||
|
|
||||||
|
local copy_ps = [[Add-Type -Name K -Namespace W -MemberDefinition '[DllImport("user32.dll")] public static extern void keybd_event(byte vk, byte sc, uint flags, System.UIntPtr extra);'; [W.K]::keybd_event(0x11,0,0,[UIntPtr]::Zero); [W.K]::keybd_event(0x43,0,0,[UIntPtr]::Zero); Start-Sleep -Milliseconds 40; [W.K]::keybd_event(0x43,0,2,[UIntPtr]::Zero); [W.K]::keybd_event(0x11,0,2,[UIntPtr]::Zero); Start-Sleep -Milliseconds 220]]
|
||||||
|
jarvis.system.exec(string.format('powershell -NoProfile -Command "%s"', copy_ps:gsub('"', '\\"')))
|
||||||
|
|
||||||
|
local raw = jarvis.system.clipboard.get() or ""
|
||||||
|
local text = raw:gsub("^%s+", ""):gsub("%s+$", "")
|
||||||
|
|
||||||
|
if #text < 20 then
|
||||||
|
jarvis.system.notify("Summarize",
|
||||||
|
lang == "ru" and "Сначала выдели текст и повтори" or "Select text first, then retry")
|
||||||
|
jarvis.audio.play_not_found()
|
||||||
|
return { chain = false }
|
||||||
|
end
|
||||||
|
|
||||||
|
if #text > 8000 then
|
||||||
|
text = text:sub(1, 8000) .. " ..."
|
||||||
|
end
|
||||||
|
|
||||||
|
local sys = lang == "ru"
|
||||||
|
and "Перескажи следующий текст одним абзацем (3-5 предложений). Сохрани ключевые факты и имена, без 'вот суммарно' и других вступлений."
|
||||||
|
or "Summarise the following text in 3-5 sentences. Keep key facts and names, no preamble."
|
||||||
|
|
||||||
|
local reply = jarvis.llm(
|
||||||
|
{ { role = "system", content = sys },
|
||||||
|
{ role = "user", content = text } },
|
||||||
|
{ max_tokens = 400, temperature = 0.3 }
|
||||||
|
)
|
||||||
|
|
||||||
|
if not reply or reply == "" then
|
||||||
|
jarvis.system.notify("Summarize", lang == "ru" and "LLM не ответила" or "LLM failed")
|
||||||
|
jarvis.audio.play_error()
|
||||||
|
return { chain = false }
|
||||||
|
end
|
||||||
|
|
||||||
|
jarvis.system.clipboard.set(reply)
|
||||||
|
jarvis.system.notify(
|
||||||
|
lang == "ru" and "Кратко" or "TL;DR",
|
||||||
|
reply:sub(1, 280)
|
||||||
|
)
|
||||||
|
jarvis.speak(reply, { lang = lang })
|
||||||
|
jarvis.audio.play_ok()
|
||||||
|
return { chain = false }
|
||||||
24
resources/commands/window_switch/command.toml
Normal file
24
resources/commands/window_switch/command.toml
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[[commands]]
|
||||||
|
id = "switch_to_window"
|
||||||
|
type = "lua"
|
||||||
|
script = "switch.lua"
|
||||||
|
sandbox = "full"
|
||||||
|
timeout = 5000
|
||||||
|
|
||||||
|
[commands.phrases]
|
||||||
|
ru = [
|
||||||
|
"переключись на",
|
||||||
|
"переключи на",
|
||||||
|
"активируй окно",
|
||||||
|
"покажи окно",
|
||||||
|
"перейди в",
|
||||||
|
]
|
||||||
|
en = [
|
||||||
|
"switch to",
|
||||||
|
"activate window",
|
||||||
|
"focus window",
|
||||||
|
]
|
||||||
|
ua = [
|
||||||
|
"перемкни на",
|
||||||
|
"активуй вікно",
|
||||||
|
]
|
||||||
51
resources/commands/window_switch/switch.lua
Normal file
51
resources/commands/window_switch/switch.lua
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
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 }
|
||||||
Loading…
Add table
Add a link
Reference in a new issue