- decisions/2026-05-05-mac-dictation-groq-hammerspoon.md: полный план, грабли с раскладкой, fallback на whisper-cpp, восстановление на новом Mac - notes/ru-geoblocked-services.md: реестр CDN с RU-блоком (cdn.spokenly, dl.wisprflow и пр.) + принципы обхода - snippets/mac-dictation/: рабочая версия скриптов и init.lua Триггер — одиночный Fn, Groq cloud first → tiny local fallback, вставка через hs.eventtap.event keycode 9 (минует ru-keymap warnings). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
53 lines
2.0 KiB
Lua
53 lines
2.0 KiB
Lua
-- Groq Dictate: trigger по одиночному нажатию Fn (Globe)
|
||
-- Fn нельзя биндить через hs.hotkey, поэтому слушаем flagsChanged
|
||
|
||
local SCRIPT = os.getenv("HOME") .. "/bin/groq-dictate.sh"
|
||
|
||
local function runScript()
|
||
hs.task.new("/bin/bash", function(exitCode, stdOut, stdErr)
|
||
local f = io.open("/tmp/groq-dictate.last", "r")
|
||
if f then
|
||
local text = f:read("*a")
|
||
f:close()
|
||
os.remove("/tmp/groq-dictate.last")
|
||
if text and #text > 0 then
|
||
hs.pasteboard.setContents(text)
|
||
-- keycode 9 = физическая V, в обход keymap lookup чтобы не спамить warnings на ru-раскладке
|
||
hs.eventtap.event.newKeyEvent({"cmd"}, 9, true):post()
|
||
hs.eventtap.event.newKeyEvent({"cmd"}, 9, false):post()
|
||
end
|
||
end
|
||
end, {SCRIPT}):start()
|
||
end
|
||
|
||
-- Отслеживаем Fn: keycode 63 — это physical Fn/Globe
|
||
-- Триггер по press (а не по release), чтобы было снапи
|
||
local fnPressed = false
|
||
local fnTime = 0
|
||
|
||
fnTap = hs.eventtap.new({hs.eventtap.event.types.flagsChanged}, function(event)
|
||
local kc = event:getKeyCode()
|
||
if kc ~= 63 then return false end -- 63 = Fn/Globe
|
||
|
||
local flags = event:getFlags()
|
||
local now = hs.timer.absoluteTime() / 1e6 -- ms
|
||
|
||
if flags.fn and not fnPressed then
|
||
-- Fn pressed
|
||
fnPressed = true
|
||
fnTime = now
|
||
elseif (not flags.fn) and fnPressed then
|
||
-- Fn released
|
||
fnPressed = false
|
||
local elapsed = now - fnTime
|
||
-- Только короткое нажатие (<400ms) триггерит — long-press для других целей
|
||
if elapsed < 400 then
|
||
runScript()
|
||
end
|
||
end
|
||
return false -- не блокировать event для других слушателей
|
||
end)
|
||
fnTap:start()
|
||
|
||
hs.alert.show("Groq Dictate: Fn (Globe) ready")
|