|
|
|
@@ -188,6 +188,122 @@ hs.keycodes.inputSourceChanged(function() -- 입력기 전환이 일어날 때
|
|
|
|
|
showInputSourceHud() -- 한글과 영문 전환 결과에 맞는 중앙 표시를 즉시 띄웁니다.
|
|
|
|
|
end) -- 입력기 변경 콜백 등록을 마칩니다.
|
|
|
|
|
|
|
|
|
|
-- `cmd`+`b`로 클립보드 문자열에서 하이픈을 제거한 뒤 연속 자동 입력하는 기능 설정을 시작합니다.
|
|
|
|
|
local clipboardTypeHotkeyModifiers = { "cmd" } -- 자동 입력 기능에 사용할 수정 키 조합을 정의합니다.
|
|
|
|
|
local clipboardTypeHotkeyKey = "b" -- 자동 입력 기능에 사용할 기본 키를 정의합니다.
|
|
|
|
|
local clipboardTypeInterval = 0.12 -- 각 문자 입력 사이에 둘 짧은 지연 시간을 초 단위로 정의합니다.
|
|
|
|
|
local clipboardTypingInProgress = false -- 자동 입력이 이미 진행 중인지 추적해 중복 실행을 막습니다.
|
|
|
|
|
local clipboardTypeHotkey = nil -- 충돌 검사 후 실제로 바인딩된 단축키 객체를 저장합니다.
|
|
|
|
|
|
|
|
|
|
-- 문자열의 각 문자를 순서대로 담은 큐 테이블을 만드는 함수를 정의합니다.
|
|
|
|
|
local function buildCharacterQueue(text) -- 하이픈 제거 후 남은 모든 문자를 순차 전송하기 위한 준비 함수입니다.
|
|
|
|
|
local queue = {} -- 순서대로 전송할 문자 목록을 빈 테이블로 초기화합니다.
|
|
|
|
|
for _, codepoint in utf8.codes(text) do -- UTF-8 문자열을 코드포인트 단위로 안전하게 순회합니다.
|
|
|
|
|
queue[#queue + 1] = utf8.char(codepoint) -- 읽어온 문자를 그대로 큐 뒤에 추가합니다.
|
|
|
|
|
end -- 문자열 전체를 큐로 변환하는 반복을 마칩니다.
|
|
|
|
|
return queue -- 완성된 문자 큐를 호출한 곳에 반환합니다.
|
|
|
|
|
end -- 문자 큐 생성 함수 정의를 마칩니다.
|
|
|
|
|
|
|
|
|
|
-- 현재 환경에서 자동 키 입력을 수행할 수 있는지 선검사하는 함수를 정의합니다.
|
|
|
|
|
local function ensureClipboardTypingPrerequisites() -- 권한과 보안 입력 상태를 미리 확인해 실패를 줄입니다.
|
|
|
|
|
if not hs.accessibilityState() then -- 손쉬운 사용 권한이 없으면 키 이벤트 전송이 불가능하므로 먼저 처리합니다.
|
|
|
|
|
hs.accessibilityState(true) -- macOS 권한 허용 안내를 띄워 사용자가 바로 승인할 수 있게 돕습니다.
|
|
|
|
|
hs.alert.show("Hammerspoon 손쉬운 사용 권한이 필요합니다") -- 권한 필요 안내를 화면에 표시합니다.
|
|
|
|
|
return false -- 아직 자동 입력을 진행할 수 없으므로 실패를 반환합니다.
|
|
|
|
|
end -- 손쉬운 사용 권한 검사 분기 종료입니다.
|
|
|
|
|
|
|
|
|
|
if hs.eventtap.isSecureInputEnabled() then -- 보안 입력이 활성화된 앱에서는 키 이벤트 주입이 막힐 수 있으므로 점검합니다.
|
|
|
|
|
hs.alert.show("보안 입력이 켜져 있어 자동 입력을 실행할 수 없습니다") -- 현재 상태를 사용자에게 즉시 알립니다.
|
|
|
|
|
return false -- 안전하지 않은 상태에서는 자동 입력을 중단합니다.
|
|
|
|
|
end -- 보안 입력 상태 검사 분기 종료입니다.
|
|
|
|
|
|
|
|
|
|
return true -- 필요한 선행 조건이 모두 충족됐음을 반환합니다.
|
|
|
|
|
end -- 자동 입력 선행 조건 검사 함수 정의를 마칩니다.
|
|
|
|
|
|
|
|
|
|
-- 한 글자를 현재 포커스된 앱으로 전송하는 함수를 정의합니다.
|
|
|
|
|
local function postClipboardCharacter(character) -- 남아 있는 문자를 현재 포커스된 앱에 한 글자씩 안전하게 전송합니다.
|
|
|
|
|
hs.eventtap.keyStrokes(character, 0) -- 현재 포커스된 앱에 해당 문자를 한 글자씩 전달합니다.
|
|
|
|
|
end -- 단일 문자 전송 함수 정의를 마칩니다.
|
|
|
|
|
|
|
|
|
|
-- 문자 큐를 타이머 기반으로 순차 입력하는 함수를 정의합니다.
|
|
|
|
|
local function typeClipboardQueue(queue, index) -- 긴 입력 중에도 설정 전체가 멈추지 않도록 비동기식으로 처리합니다.
|
|
|
|
|
if not clipboardTypingInProgress then -- 외부 오류나 상태 변경으로 이미 종료됐다면 즉시 빠져나옵니다.
|
|
|
|
|
return -- 종료된 작업을 더 진행하지 않도록 여기서 함수를 마칩니다.
|
|
|
|
|
end -- 실행 상태 검사 분기 종료입니다.
|
|
|
|
|
|
|
|
|
|
if index > #queue then -- 마지막 문자까지 모두 전송했다면 정상 완료 처리를 진행합니다.
|
|
|
|
|
clipboardTypingInProgress = false -- 다음 실행을 위해 진행 중 상태를 해제합니다.
|
|
|
|
|
return -- 더 이상 전송할 문자가 없으므로 함수를 종료합니다.
|
|
|
|
|
end -- 입력 완료 여부 검사 분기 종료입니다.
|
|
|
|
|
|
|
|
|
|
local ok, errorMessage = pcall(postClipboardCharacter, queue[index]) -- 현재 문자를 안전하게 전송하고 예외를 가로챕니다.
|
|
|
|
|
if not ok then -- 문자 전송 중 런타임 오류가 발생한 경우를 처리합니다.
|
|
|
|
|
clipboardTypingInProgress = false -- 오류 발생 시에도 다음 실행을 위해 상태를 정상적으로 복구합니다.
|
|
|
|
|
hs.alert.show("자동 입력 중 오류가 발생했습니다") -- 사용자에게 실패 사실을 화면으로 알려줍니다.
|
|
|
|
|
hs.printf("Clipboard typing failed: %s", tostring(errorMessage)) -- 디버깅을 위해 콘솔에 구체적인 오류를 기록합니다.
|
|
|
|
|
return -- 오류 이후 추가 입력이 이어지지 않도록 즉시 종료합니다.
|
|
|
|
|
end -- 문자 전송 예외 처리 분기 종료입니다.
|
|
|
|
|
|
|
|
|
|
hs.timer.doAfter(clipboardTypeInterval, function() -- 다음 문자 입력을 짧은 지연 뒤 예약해 앱이 입력을 안정적으로 받게 합니다.
|
|
|
|
|
typeClipboardQueue(queue, index + 1) -- 다음 위치의 문자를 계속 순차적으로 전송합니다.
|
|
|
|
|
end) -- 다음 문자 예약 콜백 정의를 마칩니다.
|
|
|
|
|
end -- 문자 큐 순차 입력 함수 정의를 마칩니다.
|
|
|
|
|
|
|
|
|
|
-- 클립보드 내용을 읽어 하이픈 제거 후 연속 자동 입력 작업을 시작하는 함수를 정의합니다.
|
|
|
|
|
local function typeClipboardAsContinuousSequence() -- 실제 단축키가 눌렸을 때 호출될 메인 작업 함수입니다.
|
|
|
|
|
if clipboardTypingInProgress then -- 이전 자동 입력이 아직 끝나지 않았다면 중복 실행을 막습니다.
|
|
|
|
|
hs.alert.show("자동 입력이 이미 진행 중입니다") -- 재진입이 거부된 이유를 사용자에게 알려줍니다.
|
|
|
|
|
return -- 중복 실행은 허용하지 않고 함수를 종료합니다.
|
|
|
|
|
end -- 재진입 방지 분기 종료입니다.
|
|
|
|
|
|
|
|
|
|
if not ensureClipboardTypingPrerequisites() then -- 권한이나 보안 입력 상태가 적절한지 먼저 확인합니다.
|
|
|
|
|
return -- 선행 조건이 맞지 않으면 작업을 시작하지 않습니다.
|
|
|
|
|
end -- 선행 조건 검사 분기 종료입니다.
|
|
|
|
|
|
|
|
|
|
local clipboardReadOk, clipboardText = pcall(hs.pasteboard.getContents) -- 클립보드 문자열 읽기를 예외 안전하게 시도합니다.
|
|
|
|
|
if not clipboardReadOk then -- 클립보드 접근 자체에서 오류가 발생한 경우를 처리합니다.
|
|
|
|
|
hs.alert.show("클립보드 내용을 읽는 중 오류가 발생했습니다") -- 읽기 실패를 사용자에게 알립니다.
|
|
|
|
|
hs.printf("Clipboard read failed: %s", tostring(clipboardText)) -- 콘솔에 구체적인 오류 정보를 남깁니다.
|
|
|
|
|
return -- 읽기에 실패했으므로 자동 입력을 시작하지 않습니다.
|
|
|
|
|
end -- 클립보드 읽기 예외 처리 분기 종료입니다.
|
|
|
|
|
|
|
|
|
|
if type(clipboardText) ~= "string" or clipboardText == "" then -- 빈 문자열이거나 문자열이 아닌 경우는 유효한 입력 원본이 아닙니다.
|
|
|
|
|
hs.alert.show("클립보드에 입력할 텍스트가 없습니다") -- 사용자가 먼저 텍스트를 복사하도록 안내합니다.
|
|
|
|
|
return -- 유효한 내용이 없으므로 여기서 종료합니다.
|
|
|
|
|
end -- 클립보드 내용 유효성 검사 분기 종료입니다.
|
|
|
|
|
|
|
|
|
|
local transformedText = clipboardText:gsub("%-", "") -- 자동 포커스 이동을 활용할 수 있도록 모든 하이픈을 제거합니다.
|
|
|
|
|
local queue = buildCharacterQueue(transformedText) -- 하이픈이 제거된 문자열을 문자 단위 큐로 만들어 순차 입력 준비를 합니다.
|
|
|
|
|
if #queue == 0 then -- 하이픈 제거 후 결과가 실질적으로 비어 있으면 입력할 것이 없으므로 막습니다.
|
|
|
|
|
hs.alert.show("하이픈 제거 후 입력할 문자가 없습니다") -- 의미 없는 실행이 되지 않도록 사용자에게 알려줍니다.
|
|
|
|
|
return -- 입력 큐가 비어 있으므로 종료합니다.
|
|
|
|
|
end -- 하이픈 제거 결과 유효성 검사 분기 종료입니다.
|
|
|
|
|
|
|
|
|
|
clipboardTypingInProgress = true -- 실제 입력을 시작하기 전에 진행 중 상태를 활성화합니다.
|
|
|
|
|
local typingStartOk, typingStartError = pcall(typeClipboardQueue, queue, 1) -- 순차 입력 시작 자체도 예외 안전하게 감쌉니다.
|
|
|
|
|
if not typingStartOk then -- 시작 단계에서 예상치 못한 오류가 발생한 경우를 처리합니다.
|
|
|
|
|
clipboardTypingInProgress = false -- 시작 실패 시 진행 상태를 즉시 되돌립니다.
|
|
|
|
|
hs.alert.show("자동 입력 시작에 실패했습니다") -- 사용자에게 시작 실패를 알립니다.
|
|
|
|
|
hs.printf("Clipboard typing start failed: %s", tostring(typingStartError)) -- 콘솔에 실패 원인을 기록합니다.
|
|
|
|
|
end -- 자동 입력 시작 예외 처리 분기 종료입니다.
|
|
|
|
|
end -- 클립보드 연속 자동 입력 메인 함수 정의를 마칩니다.
|
|
|
|
|
|
|
|
|
|
-- 시스템 레벨에 이미 예약된 단축키와 충돌하는지 검사합니다.
|
|
|
|
|
local clipboardTypeSystemConflict = hs.hotkey.systemAssigned(clipboardTypeHotkeyModifiers, clipboardTypeHotkeyKey) -- macOS가 점유한 조합이면 바인딩을 피하기 위해 조회합니다.
|
|
|
|
|
if clipboardTypeSystemConflict then -- 시스템이 이미 이 단축키를 사용 중인 경우를 처리합니다.
|
|
|
|
|
hs.alert.show("cmd-b 단축키가 시스템에 이미 할당되어 있어 등록하지 않았습니다") -- 충돌 사실을 화면에 알립니다.
|
|
|
|
|
hs.printf("Skipped binding cmd-b due to system conflict: %s", hs.inspect(clipboardTypeSystemConflict)) -- 콘솔에 충돌 세부 정보를 남깁니다.
|
|
|
|
|
else -- 시스템 충돌이 없을 때만 안전하게 단축키를 등록합니다.
|
|
|
|
|
clipboardTypeHotkey = hs.hotkey.bind(clipboardTypeHotkeyModifiers, clipboardTypeHotkeyKey, function() -- 전역 단축키를 눌렀을 때 메인 작업을 실행합니다.
|
|
|
|
|
local hotkeyOk, hotkeyError = pcall(typeClipboardAsContinuousSequence) -- 단축키 콜백 전체를 예외 안전하게 감싸 강제 종료를 막습니다.
|
|
|
|
|
if not hotkeyOk then -- 콜백 바깥쪽에서 예상치 못한 오류가 터진 경우를 처리합니다.
|
|
|
|
|
clipboardTypingInProgress = false -- 오류 뒤 다음 실행이 가능하도록 상태를 정리합니다.
|
|
|
|
|
hs.alert.show("cmd-b 자동 입력 실행에 실패했습니다") -- 사용자에게 실행 실패를 알려줍니다.
|
|
|
|
|
hs.printf("cmd-b clipboard hotkey failed: %s", tostring(hotkeyError)) -- 콘솔에 실제 오류 메시지를 기록합니다.
|
|
|
|
|
end -- 단축키 콜백 예외 처리 분기 종료입니다.
|
|
|
|
|
end) -- 전역 단축키 바인딩 정의를 마칩니다.
|
|
|
|
|
end -- 시스템 단축키 충돌 여부에 따른 등록 분기를 마칩니다.
|
|
|
|
|
|
|
|
|
|
overrideOtherMouseDown:start() -- 보조 버튼 눌림 감시를 시작합니다.
|
|
|
|
|
overrideOtherMouseUp:start() -- 보조 버튼 떼기 감시를 시작합니다.
|
|
|
|
|
dragOtherToScroll:start() -- 보조 버튼 드래그 스크롤 감시를 시작합니다.
|
|
|
|
|