From e2e472a825a331be850ecc9dedb5265a5656fe25 Mon Sep 17 00:00:00 2001 From: Dmitry Surin Date: Fri, 21 Jun 2024 02:07:01 +0400 Subject: [PATCH] AHK. Added JSON support (config extracted). Chrome devtools. Some refactoring. --- win/ahk/JSON.ahk | 374 ++++++++++++++++++ win/ahk/actions.json | 243 ++++++++++++ win/ahk/alfred.ahk | 225 ++++++----- win/ahk/bedware.ahk | 72 +--- win/ahk/remap.ahk | 87 ++-- win/ahk/remap/chrome-devtools.ahk | 64 +++ win/ahk/user-functions.ahk | 30 +- win/ahk/utils.ahk | 11 + ...moving-around.ahk => vd-moving-around.ahk} | 0 9 files changed, 916 insertions(+), 190 deletions(-) create mode 100644 win/ahk/JSON.ahk create mode 100644 win/ahk/actions.json create mode 100644 win/ahk/remap/chrome-devtools.ahk rename win/ahk/{moving-around.ahk => vd-moving-around.ahk} (100%) diff --git a/win/ahk/JSON.ahk b/win/ahk/JSON.ahk new file mode 100644 index 0000000..975c528 --- /dev/null +++ b/win/ahk/JSON.ahk @@ -0,0 +1,374 @@ +/** + * Lib: JSON.ahk + * JSON lib for AutoHotkey. + * Version: + * v2.1.3 [updated 04/18/2016 (MM/DD/YYYY)] + * License: + * WTFPL [http://wtfpl.net/] + * Requirements: + * Latest version of AutoHotkey (v1.1+ or v2.0-a+) + * Installation: + * Use #Include JSON.ahk or copy into a function library folder and then + * use #Include + * Links: + * GitHub: - https://github.com/cocobelgica/AutoHotkey-JSON + * Forum Topic - http://goo.gl/r0zI8t + * Email: - cocobelgica gmail com + */ + + +/** + * Class: JSON + * The JSON object contains methods for parsing JSON and converting values + * to JSON. Callable - NO; Instantiable - YES; Subclassable - YES; + * Nestable(via #Include) - NO. + * Methods: + * Load() - see relevant documentation before method definition header + * Dump() - see relevant documentation before method definition header + */ +class JSON +{ + /** + * Method: Load + * Parses a JSON string into an AHK value + * Syntax: + * value := JSON.Load( text [, reviver ] ) + * Parameter(s): + * value [retval] - parsed value + * text [in, ByRef] - JSON formatted string + * reviver [in, opt] - function object, similar to JavaScript's + * JSON.parse() 'reviver' parameter + */ + class Load extends JSON.Functor + { + Call(self, ByRef text, reviver:="") + { + this.rev := IsObject(reviver) ? reviver : false + ; Object keys(and array indices) are temporarily stored in arrays so that + ; we can enumerate them in the order they appear in the document/text instead + ; of alphabetically. Skip if no reviver function is specified. + this.keys := this.rev ? {} : false + + static quot := Chr(34), bashq := "\" . quot + , json_value := quot . "{[01234567890-tfn" + , json_value_or_array_closing := quot . "{[]01234567890-tfn" + , object_key_or_object_closing := quot . "}" + + key := "" + is_key := false + root := {} + stack := [root] + next := json_value + pos := 0 + + while ((ch := SubStr(text, ++pos, 1)) != "") { + if InStr(" `t`r`n", ch) + continue + if !InStr(next, ch, 1) + this.ParseError(next, text, pos) + + holder := stack[1] + is_array := holder.IsArray + + if InStr(",:", ch) { + next := (is_key := !is_array && ch == ",") ? quot : json_value + + } else if InStr("}]", ch) { + ObjRemoveAt(stack, 1) + next := stack[1]==root ? "" : stack[1].IsArray ? ",]" : ",}" + + } else { + if InStr("{[", ch) { + ; Check if Array() is overridden and if its return value has + ; the 'IsArray' property. If so, Array() will be called normally, + ; otherwise, use a custom base object for arrays + static json_array := Func("Array").IsBuiltIn || ![].IsArray ? {IsArray: true} : 0 + + ; sacrifice readability for minor(actually negligible) performance gain + (ch == "{") + ? ( is_key := true + , value := {} + , next := object_key_or_object_closing ) + ; ch == "[" + : ( value := json_array ? new json_array : [] + , next := json_value_or_array_closing ) + + ObjInsertAt(stack, 1, value) + + if (this.keys) + this.keys[value] := [] + + } else { + if (ch == quot) { + i := pos + while (i := InStr(text, quot,, i+1)) { + value := StrReplace(SubStr(text, pos+1, i-pos-1), "\\", "\u005c") + + static tail := A_AhkVersion<"2" ? 0 : -1 + if (SubStr(value, tail) != "\") + break + } + + if (!i) + this.ParseError("'", text, pos) + + value := StrReplace(value, "\/", "/") + , value := StrReplace(value, bashq, quot) + , value := StrReplace(value, "\b", "`b") + , value := StrReplace(value, "\f", "`f") + , value := StrReplace(value, "\n", "`n") + , value := StrReplace(value, "\r", "`r") + , value := StrReplace(value, "\t", "`t") + + pos := i ; update pos + + i := 0 + while (i := InStr(value, "\",, i+1)) { + if !(SubStr(value, i+1, 1) == "u") + this.ParseError("\", text, pos - StrLen(SubStr(value, i+1))) + + uffff := Abs("0x" . SubStr(value, i+2, 4)) + if (A_IsUnicode || uffff < 0x100) + value := SubStr(value, 1, i-1) . Chr(uffff) . SubStr(value, i+6) + } + + if (is_key) { + key := value, next := ":" + continue + } + + } else { + value := SubStr(text, pos, i := RegExMatch(text, "[\]\},\s]|$",, pos)-pos) + + static number := "number", integer :="integer" + if value is %number% + { + if value is %integer% + value += 0 + } + else if (value == "true" || value == "false") + value := %value% + 0 + else if (value == "null") + value := "" + else + ; we can do more here to pinpoint the actual culprit + ; but that's just too much extra work. + this.ParseError(next, text, pos, i) + + pos += i-1 + } + + next := holder==root ? "" : is_array ? ",]" : ",}" + } ; If InStr("{[", ch) { ... } else + + is_array? key := ObjPush(holder, value) : holder[key] := value + + if (this.keys && this.keys.HasKey(holder)) + this.keys[holder].Push(key) + } + + } ; while ( ... ) + + return this.rev ? this.Walk(root, "") : root[""] + } + + ParseError(expect, ByRef text, pos, len:=1) + { + static quot := Chr(34), qurly := quot . "}" + + line := StrSplit(SubStr(text, 1, pos), "`n", "`r").Length() + col := pos - InStr(text, "`n",, -(StrLen(text)-pos+1)) + msg := Format("{1}`n`nLine:`t{2}`nCol:`t{3}`nChar:`t{4}" + , (expect == "") ? "Extra data" + : (expect == "'") ? "Unterminated string starting at" + : (expect == "\") ? "Invalid \escape" + : (expect == ":") ? "Expecting ':' delimiter" + : (expect == quot) ? "Expecting object key enclosed in double quotes" + : (expect == qurly) ? "Expecting object key enclosed in double quotes or object closing '}'" + : (expect == ",}") ? "Expecting ',' delimiter or object closing '}'" + : (expect == ",]") ? "Expecting ',' delimiter or array closing ']'" + : InStr(expect, "]") ? "Expecting JSON value or array closing ']'" + : "Expecting JSON value(string, number, true, false, null, object or array)" + , line, col, pos) + + static offset := A_AhkVersion<"2" ? -3 : -4 + throw Exception(msg, offset, SubStr(text, pos, len)) + } + + Walk(holder, key) + { + value := holder[key] + if IsObject(value) { + for i, k in this.keys[value] { + ; check if ObjHasKey(value, k) ?? + v := this.Walk(value, k) + if (v != JSON.Undefined) + value[k] := v + else + ObjDelete(value, k) + } + } + + return this.rev.Call(holder, key, value) + } + } + + /** + * Method: Dump + * Converts an AHK value into a JSON string + * Syntax: + * str := JSON.Dump( value [, replacer, space ] ) + * Parameter(s): + * str [retval] - JSON representation of an AHK value + * value [in] - any value(object, string, number) + * replacer [in, opt] - function object, similar to JavaScript's + * JSON.stringify() 'replacer' parameter + * space [in, opt] - similar to JavaScript's JSON.stringify() + * 'space' parameter + */ + class Dump extends JSON.Functor + { + Call(self, value, replacer:="", space:="") + { + this.rep := IsObject(replacer) ? replacer : "" + + this.gap := "" + if (space) { + static integer := "integer" + if space is %integer% + Loop, % ((n := Abs(space))>10 ? 10 : n) + this.gap .= " " + else + this.gap := SubStr(space, 1, 10) + + this.indent := "`n" + } + + return this.Str({"": value}, "") + } + + Str(holder, key) + { + value := holder[key] + + if (this.rep) + value := this.rep.Call(holder, key, ObjHasKey(holder, key) ? value : JSON.Undefined) + + if IsObject(value) { + ; Check object type, skip serialization for other object types such as + ; ComObject, Func, BoundFunc, FileObject, RegExMatchObject, Property, etc. + static type := A_AhkVersion<"2" ? "" : Func("Type") + if (type ? type.Call(value) == "Object" : ObjGetCapacity(value) != "") { + if (this.gap) { + stepback := this.indent + this.indent .= this.gap + } + + is_array := value.IsArray + ; Array() is not overridden, rollback to old method of + ; identifying array-like objects. Due to the use of a for-loop + ; sparse arrays such as '[1,,3]' are detected as objects({}). + if (!is_array) { + for i in value + is_array := i == A_Index + until !is_array + } + + str := "" + if (is_array) { + Loop, % value.Length() { + if (this.gap) + str .= this.indent + + v := this.Str(value, A_Index) + str .= (v != "") ? v . "," : "null," + } + } else { + colon := this.gap ? ": " : ":" + for k in value { + v := this.Str(value, k) + if (v != "") { + if (this.gap) + str .= this.indent + + str .= this.Quote(k) . colon . v . "," + } + } + } + + if (str != "") { + str := RTrim(str, ",") + if (this.gap) + str .= stepback + } + + if (this.gap) + this.indent := stepback + + return is_array ? "[" . str . "]" : "{" . str . "}" + } + + } else ; is_number ? value : "value" + return ObjGetCapacity([value], 1)=="" ? value : this.Quote(value) + } + + Quote(string) + { + static quot := Chr(34), bashq := "\" . quot + + if (string != "") { + string := StrReplace(string, "\", "\\") + ; , string := StrReplace(string, "/", "\/") ; optional in ECMAScript + , string := StrReplace(string, quot, bashq) + , string := StrReplace(string, "`b", "\b") + , string := StrReplace(string, "`f", "\f") + , string := StrReplace(string, "`n", "\n") + , string := StrReplace(string, "`r", "\r") + , string := StrReplace(string, "`t", "\t") + + static rx_escapable := A_AhkVersion<"2" ? "O)[^\x20-\x7e]" : "[^\x20-\x7e]" + while RegExMatch(string, rx_escapable, m) + string := StrReplace(string, m.Value, Format("\u{1:04x}", Ord(m.Value))) + } + + return quot . string . quot + } + } + + /** + * Property: Undefined + * Proxy for 'undefined' type + * Syntax: + * undefined := JSON.Undefined + * Remarks: + * For use with reviver and replacer functions since AutoHotkey does not + * have an 'undefined' type. Returning blank("") or 0 won't work since these + * can't be distnguished from actual JSON values. This leaves us with objects. + * Replacer() - the caller may return a non-serializable AHK objects such as + * ComObject, Func, BoundFunc, FileObject, RegExMatchObject, and Property to + * mimic the behavior of returning 'undefined' in JavaScript but for the sake + * of code readability and convenience, it's better to do 'return JSON.Undefined'. + * Internally, the property returns a ComObject with the variant type of VT_EMPTY. + */ + Undefined[] + { + get { + static empty := {}, vt_empty := ComObject(0, &empty, 1) + return vt_empty + } + } + + class Functor + { + __Call(method, ByRef arg, args*) + { + ; When casting to Call(), use a new instance of the "function object" + ; so as to avoid directly storing the properties(used across sub-methods) + ; into the "function object" itself. + if IsObject(method) + return (new this).Call(method, arg, args*) + else if (method == "") + return (new this).Call(arg, args*) + } + } +} \ No newline at end of file diff --git a/win/ahk/actions.json b/win/ahk/actions.json new file mode 100644 index 0000000..4f9888f --- /dev/null +++ b/win/ahk/actions.json @@ -0,0 +1,243 @@ +{ + "desktops": ["Browsing", "Dev", "Chats", "Office", "Creator", "Stream", "English", "Files", "Other"], + "apps": { + "adbg": { + "path": "G:\\My Drive\\Soft\\DebugView\\dbgview64.exe", + "selector": "ahk_exe dbgview64.exe", + "description": "Open debugger for AHK" + }, + "adoc": { + "path": "C:\\Program Files\\AutoHotkey\\AutoHotkey.chm", + "selector": "AutoHotkey Help", + "description": "Open documentation for AHK" + }, + "anki": { + "desktop": "Office", + "path": "C:\\Users\\dmitr\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Anki.lnk", + "selector": "ahk_exe anki.exe" + }, + "aspy": { + "path": "C:\\Program Files\\AutoHotkey\\WindowSpy.ahk", + "selector": "Window Spy" + }, + "book": { + "desktop": "Creator", + "path": "C:\\Program Files\\Calibre2\\calibre.exe", + "selector": "ahk_exe calibre.exe" + }, + "cmd": { + "desktop": "Files", + "path": "C:\\Program Files\\totalcmd\\TOTALCMD64.EXE", + "selector": "ahk_exe TOTALCMD64.EXE" + }, + "db": { + "desktop": "Dev", + "path": "C:\\Program Files\\JetBrains\\DataGrip 2019.1.4\\bin\\datagrip64.exe", + "selector": "ahk_exe datagrip64.exe" + }, + "dev": { + "desktop": "Browsing", + "selector": "^DevTools ahk_exe chrome.exe" + }, + "disc": { + "desktop": "Chats", + "path": "C:\\Users\\dmitr\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Discord Inc\\Discord.lnk", + "selector": "ahk_exe Discord.exe" + }, + "draw": { + "desktop": "Creator", + "path": "C:\\Users\\dmitr\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Excalidraw.lnk", + "selector": "Excalidraw ahk_exe msedge.exe" + }, + "duet": { + "path": "C:\\Program Files\\Kairos\\Duet Display\\duet.exe", + "selector": "ahk_exe duet.exe" + }, + "figma": { + "desktop": "Creator", + "path": "C:\\Users\\dmitr\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Figma.lnk", + "selector": "ahk_exe Figma.exe" + }, + "fire": { + "desktop": "Browsing", + "path": "C:\\Program Files\\Mozilla Firefox\\firefox.exe", + "selector": "ahk_exe firefox.exe" + }, + "ge": { + "postFunction": "georgian" + }, + "idea": { + "desktop": "Dev", + "path": "C:\\Users\\dmitr\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\JetBrains Toolbox\\IntelliJ IDEA Ultimate.lnk", + "selector": "ahk_exe idea64.exe" + }, + "jkit": { + "desktop": "Dev", + "path": "C:\\Program Files\\YourKit Java Profiler 2021.11-b227\\bin\\profiler.exe", + "selector": "YourKit" + }, + "jmc": { + "desktop": "Dev", + "path": "C:\\Users\\dmitr\\.jdks\\jmc-8.3.1_windows-x64\\JDK Mission Control\\jmc.exe", + "selector": "ahk_exe jmc.exe" + }, + "jvm": { + "desktop": "Dev", + "path": "C:\\Users\\dmitr\\.jdks\\visualvm_216\\bin\\visualvm-my-jdk.lnk", + "selector": "VisualVM" + }, + "keys": { + "path": "C:\\Users\\dmitr\\AppData\\Local\\carnac\\Carnac.exe", + "selector": "ahk_exe Carnac.exe" + }, + "lon": { + "path": "g:\\My Drive\\Soft\\LonelyScreen.exe", + "selector": "ahk_exe LonelyScreen.exe" + }, + "mail": { + "desktop": "Office", + "path": "C:\\Users\\dmitr\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Spark Desktop.lnk", + "selector": "ahk_exe Spark Desktop.exe" + }, + "music": { + "desktop": "Other", + "path": "C:\\Users\\dmitr\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\\u042f\u043d\u0434\u0435\u043a\u0441 \u041c\u0443\u0437\u044b\u043a\u0430.lnk", + "postFunction": "makeAnyWindowMaximized", + "selector": "ahk_exe \u042f\u043d\u0434\u0435\u043a\u0441 \u041c\u0443\u0437\u044b\u043a\u0430.exe" + }, + "note": { + "desktop": "Office", + "path": "C:\\Users\\dmitr\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Notion.lnk", + "postFunction": "makeAnyWindowMaximized", + "selector": "ahk_exe Notion.exe" + }, + "obs": { + "desktop": "Stream", + "path": "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\OBS Studio\\OBS Studio (64bit).lnk", + "selector": "ahk_exe obs64.exe" + }, + "paint": { + "path": "mspaint.exe", + "selector": "ahk_exe mspaint.exe" + }, + "pdf": { + "path": "C:\\Users\\dmitr\\AppData\\Local\\SumatraPDF\\SumatraPDF.exe", + "selector": "ahk_exe SumatraPDF.exe" + }, + "pip": { + "selector": "Picture in picture ahk_exe chrome.exe" + }, + "pod": { + "desktop": "Dev", + "path": "C:\\Users\\dmitr\\AppData\\Local\\Programs\\podman-desktop\\Podman Desktop.exe", + "selector": "ahk_exe Podman Desktop.exe" + }, + "post": { + "desktop": "Dev", + "path": "C:\\Users\\dmitr\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Postman\\Postman.lnk", + "selector": "ahk_exe Postman.exe" + }, + "quake": { + "path": "wt.exe -w _quake", + "selector": "ahk_exe WindowsTerminal.exe" + }, + "ru": { + "postFunction": "russian" + }, + "slack": { + "desktop": "Chats", + "path": "C:\\Users\\dmitr\\AppData\\Local\\slack\\slack.exe", + "selector": "ahk_exe slack.exe" + }, + "steam": { + "desktop": "Browsing", + "path": "C:\\Program Files (x86)\\Steam\\Steam.exe", + "selector": "ahk_exe steamwebhelper.exe" + }, + "task": { + "desktop": "Other", + "path": "Taskmgr.exe", + "postFunction": "makeAnyWindowMaximized", + "selector": "ahk_class TaskManagerWindow" + }, + "td": { + "desktop": "Dev", + "selector": "^Developer Tools ahk_exe Cypress.exe" + }, + "term": { + "desktop": "Dev", + "path": "\"C:\\Program Files\\Alacritty\\alacritty.exe\" --title windows", + "postFunction": "makeAnyWindowMaximized", + "selector": "windows ahk_exe alacritty.exe" + }, + "tg": { + "desktop": "Chats", + "path": "C:\\Users\\dmitr\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe", + "selector": "ahk_exe Telegram.exe" + }, + "tra": { + "desktop": "English", + "path": "C:\\Program Files (x86)\\ABBYY Lingvo x6\\Lingvo.exe" + }, + "trd": { + "desktop": "English", + "path": "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe --start-maximized --app=https://www.deepl.com/translator#ru/en/", + "selector": "DeepL Translate ahk_exe msedge.exe" + }, + "try": { + "desktop": "English", + "path": "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe --app=https://translate.yandex.com/en/", + "selector": "Yandex Translate.* ahk_exe msedge.exe" + }, + "tt": { + "desktop": "Dev", + "selector": "^(?!Developer Tools|Cypress) ahk_exe Cypress.exe" + }, + "turm": { + "desktop": "Dev", + "path": "\"C:\\Program Files\\Alacritty\\alacritty.exe\" --config-file C:\\Users\\dmitr\\.dotfiles\\all\\alacritty\\alacritty-work-profile.yml --title ubuntu --command wsl -d Ubuntu-22.04 --cd ~", + "postFunction": "makeAnyWindowMaximized", + "selector": "ubuntu ahk_exe alacritty.exe" + }, + "vedit": { + "desktop": "Creator", + "path": "C:\\Users\\dmitr\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\CapCut\\CapCut.lnk", + "selector": "ahk_exe CapCut.exe" + }, + "web": { + "desktop": "Browsing", + "path": "C:\\Users\\dmitr\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Chrome Apps\\Chrome.lnk", + "selector": "^(?!DevTools) ahk_exe chrome.exe" + }, + "work": { + "postFunction": "startWork" + }, + "\u043f\u0443": { + "postFunction": "georgian" + }, + "\u0443\u0442": { + "postFunction": "english" + }, + "\u10d4\u10dc": { + "postFunction": "english" + }, + "\u10e0\u10e3": { + "postFunction": "russian" + }, + "gpt": { + "postFunction": "open_gpt" + }, + "dot": { + "postFunction": "open_dot" + }, + "ahk": { + "postFunction": "open_ahk" + }, + "nvim": { + "postFunction": "open_nvim" + }, + "pwsh": { + "postFunction": "open_pwsh" + } + } +} \ No newline at end of file diff --git a/win/ahk/alfred.ahk b/win/ahk/alfred.ahk index 4e05f91..3b4078f 100644 --- a/win/ahk/alfred.ahk +++ b/win/ahk/alfred.ahk @@ -1,5 +1,5 @@ RunAlfred(apps) { - showAlfredAsync() + showAlfred() endKey := "LShift" timeout := "8" ; seconds @@ -7,102 +7,20 @@ RunAlfred(apps) { ; Wait for user input Input, userInput, T%timeout% L%length% C, {%endKey%}, % getShortuctsByComa(apps) - if (ErrorLevel = "Max") - { - showAlfredError("You entered '" userInput "', which is the maximum length '" length "' of the text.") - hideAlfred() - return - } - if (ErrorLevel = "Timeout") - { + if (ErrorLevel = "Max") { + showAlfredError("You entered '" userInput "' and have reached maximum length (" length ") of the text.") + } else if (ErrorLevel = "Timeout") { showAlfredError("You have reached the timeout of " timeout " seconds.") - hideAlfred() - return - } - if (ErrorLevel = "NewInput") { + } else if (ErrorLevel = "NewInput") { showAlfredError("New input begun.") - hideAlfred() - return - } - If InStr(ErrorLevel, "EndKey:") - { - ; showAlfredError("You have endkey '" endKey "' pressed.") - hideAlfred() + } else if InStr(ErrorLevel, "EndKey:") { KeyWait, %endKey% - return - } - executeInput(apps, userInput) - - hideAlfred() -} - -showAlfred() { - FormatTime, currentDateTime, %A_Now%, yyyy-MM-dd HH:mm:ss - color := "rgb(255, 222, 93)" - getHTMLForAlfred(color, currentDateTime) - - selector := "bedware.ahk" - if WinExist(selector) { - WinActivate - ; WinSet, AlwaysOnTop, On, %selector% - WinGet, activeHwnd, ID, %selector% - PinWindow(activeHwnd) - } - OutputDebug % "Alfred show" -} -showAlfredAsync() { - fu := Func("showAlfred") - SetTimer %fu%, -1 -} - -showAlfredRunning(text) { - color := "rgb(223, 255, 93)" - getHTMLForAlfred(color, text) - - selector := "bedware.ahk" - if WinExist(selector) { - WinActivate - ; WinSet, AlwaysOnTop, On, %selector% - WinGet, activeHwnd, ID, %selector% - PinWindow(activeHwnd) + hideAlfred() + } else { + executeInput(apps, userInput) + proceedAppHistory(userInput) } } -showAlfredError(errorText) { - color := "rgb(255, 0, 0)" - getHTMLForAlfred(color, errorText) - OutputDebug % "Alfred error show" - Sleep 4000 -} - -getHTMLForAlfred(bgColor, text) { - Gui, Destroy - Gui, -Caption -AlwaysOnTop +ToolWindow +DPIScale - Gui, Margin, 0, 0 - info := "bedware.software | " text - taskbarHeight := 48 - html = - ( - mshtml: -
%info%
- - ) - Gui, Add, ActiveX, w960 h%taskbarHeight%, %html% - Gui, Show, xCenter y1100 NoActivate - return html -} - -hideAlfred() { - Gui, Destroy - OutputDebug % "Alfred hide" -} getShortuctsByComa(apps) { ; e.g. "term,slack,tg" @@ -112,16 +30,6 @@ getShortuctsByComa(apps) { } return SubStr(shortcutsByComa, 1, -1) } -_getIfContains(arr, inp) { - result := false - for key, value in arr { - if (key == inp) { - result := value - break - } - } - return result -} executeInput(apps, userInput) { global desktops @@ -151,11 +59,120 @@ executeInput(apps, userInput) { } if (app.postFunction != "") { if (app.postFunctionParam != "") { - Func(app.postFunction).Call(app.postFunctionParam) + retval := Func(app.postFunction).Call(app.postFunctionParam) } else { - Func(app.postFunction).Call() + retval := Func(app.postFunction).Call() } } hideAlfred() } } + +; current +; prev | +; | | +altApp := ["", ""] +; 1 2 + +GoToAlternateApp(apps) { + global altApp + + ; Go to the previous app + executeInput(apps, altApp[1]) + + ; Ajust history + proceedAppHistory("justswap") +} + +proceedAppHistory(userInput) { + global altApp + if (userInput == "justswap") { ; Swap value between 2 cells of the array + tmp := altApp[1] + altApp[1] := altApp[2] + altApp[2] := tmp + } else if (altApp[2] != userInput) { ; Diffrent - we swap + altApp[1] := altApp[2] + altApp[2] := userInput + } else { + ; Same as current - do nothing + } + ; MsgBox % "Input:" . userInput . "Prev:" . altApp[1] . ". Curr:" . altApp[2] . "." +} + + +; Just visual stuff +; Using iexplorer pop-up (fast & ugly) +showAlfred() { + Menu, Tray, Icon, shell32.dll, 298 + ; FormatTime, currentDateTime, %A_Now%, yyyy-MM-dd HH:mm:ss + ; color := "rgb(255, 222, 93)" + ; getHTMLForAlfred(color, currentDateTime) + ; + ; selector := "bedware.ahk" + ; if WinExist(selector) { + ; WinActivate + ; ; WinSet, AlwaysOnTop, On, %selector% + ; WinGet, activeHwnd, ID, %selector% + ; PinWindow(activeHwnd) + ; } + ; OutputDebug % "Alfred show" +} +; showAlfredAsync() { +; fu := Func("showAlfred") +; SetTimer %fu%, -1 +; } + +showAlfredRunning(text) { + Menu, Tray, Icon, shell32.dll, 239 + ; color := "rgb(223, 255, 93)" + ; getHTMLForAlfred(color, text) + ; + ; selector := "bedware.ahk" + ; if WinExist(selector) { + ; WinActivate + ; ; WinSet, AlwaysOnTop, On, %selector% + ; WinGet, activeHwnd, ID, %selector% + ; PinWindow(activeHwnd) + ; } +} +showAlfredError(errorText) { + Menu, Tray, Icon, shell32.dll, 132 + MsgBox % errorText + Sleep 2000 + hideAlfred() + ; color := "rgb(255, 0, 0)" + ; getHTMLForAlfred(color, errorText) + ; OutputDebug % "Alfred error show" +} + +hideAlfred() { + IconByThemeAndDesktopNumber(GetCurrentDesktopNumber()) + ; Gui, Destroy + ; OutputDebug % "Alfred hide" +} + +getHTMLForAlfred(bgColor, text) { + Gui, Destroy + Gui, -Caption -AlwaysOnTop +ToolWindow +DPIScale + Gui, Margin, 0, 0 + info := "bedware.software | " text + taskbarHeight := 48 + html = + ( + mshtml: +
%info%
+ + ) + Gui, Add, ActiveX, w960 h%taskbarHeight%, %html% + Gui, Show, xCenter y1100 NoActivate + return html +} + diff --git a/win/ahk/bedware.ahk b/win/ahk/bedware.ahk index a69eeab..3ea0384 100644 --- a/win/ahk/bedware.ahk +++ b/win/ahk/bedware.ahk @@ -1,75 +1,29 @@ -#SingleInstance Force ; The script will Reload if launched while already running +; Directives +#SingleInstance Force ; The script will Reload if launched while already running #NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases -;#KeyHistory 0 ; Ensures user privacy when debugging is not needed #InstallKeybdHook #InstallMouseHook #UseHook #MaxHotkeysPerInterval 200 SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory SendMode Input ; Recommended for new scripts due to its superior speed and reliability -; SetCapslockState, AlwaysOff SetTitleMatchMode, RegEx ; Write selectors using regexp -; Constants -EnvGet, HOME, UserProfile -SplitPath, A_AhkPath,, AHK_FOLDER +; Imports +#Include %A_ScriptDir%/JSON.ahk -desktops := ["Browsing", "Dev", "Chats", "Office", "Creator", "Stream", "English", "Files", "Other"] -apps := {} - -apps["adbg"] := { selector: "ahk_exe dbgview64.exe", path: "G:\My Drive\Soft\DebugView\dbgview64.exe" } -apps["adoc"] := { selector: "AutoHotkey Help", path: AHK_FOLDER . "\AutoHotkey.chm" } -apps["anki"] := { desktop: "Office", selector: "ahk_exe anki.exe", path: "C:\Users\dmitr\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Anki.lnk" } -apps["aspy"] := { selector: "Window Spy", path: AHK_FOLDER . "\WindowSpy.ahk" } -apps["book"] := { desktop: "Office", selector: "ahk_exe calibre.exe", path: "C:\Program Files\Calibre2\calibre.exe" } -apps["cal"] := { desktop: "Office", selector: "DayCaptain ahk_exe msedge.exe", path: "C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe --start-maximized --app=https://daycaptain.com", postFunction: "makeAnyWindowMaximized" } -apps["cmd"] := { desktop: "Files", selector: "ahk_exe TOTALCMD64.EXE", path: "C:\Program Files\totalcmd\TOTALCMD64.EXE" } -apps["code"] := { desktop: "Dev", selector: "ubuntu ahk_exe alacritty.exe", path: """C:\Program Files\Alacritty\alacritty.exe"" --config-file " . HOME . "\.dotfiles\all\alacritty\alacritty-work-profile.yml" . " --title ubuntu --command wsl -d Ubuntu-22.04 --cd ~", postFunction: "makeAnyWindowFullscreen"} -apps["db"] := { desktop: "Dev", selector: "ahk_exe datagrip64.exe", path: "C:\Program Files\JetBrains\DataGrip 2019.1.4\bin\datagrip64.exe" } -apps["dev"] := { desktop: "Browsing", selector: "^DevTools ahk_exe chrome.exe" } -apps["disc"] := { desktop: "Chats", selector: "ahk_exe Discord.exe", path: "C:\Users\dmitr\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Discord Inc\Discord.lnk" } -apps["draw"] := { desktop: "Creator", selector: "Excalidraw ahk_exe msedge.exe", path: "C:\Users\dmitr\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Excalidraw.lnk" } -apps["vedit"] := { desktop: "Creator", selector: "ahk_exe CapCut.exe", path: "C:\Users\dmitr\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\CapCut\CapCut.lnk" } -apps["duet"] := { selector: "ahk_exe duet.exe", path: "C:\Program Files\Kairos\Duet Display\duet.exe" } -apps["figma"] := { desktop: "Creator", selector: "ahk_exe Figma.exe", path: "C:\Users\dmitr\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Figma.lnk" } -apps["fire"] := { desktop: "Browsing", selector: "ahk_exe firefox.exe", path: "C:\Program Files\Mozilla Firefox\firefox.exe" } -apps["idea"] := { desktop: "Dev", selector: "ahk_exe idea64.exe", path: "C:\Users\dmitr\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\JetBrains Toolbox\IntelliJ IDEA Ultimate.lnk" } -apps["jkit"] := { desktop: "Dev", selector: "YourKit", path: "C:\Program Files\YourKit Java Profiler 2021.11-b227\bin\profiler.exe" } -apps["jmc"] := { desktop: "Dev", selector: "ahk_exe jmc.exe", path: HOME . "\.jdks\jmc-8.3.1_windows-x64\JDK Mission Control\jmc.exe" } -apps["jvm"] := { desktop: "Dev", selector: "VisualVM", path: HOME . "\.jdks\visualvm_216\bin\visualvm-my-jdk.lnk" } -apps["keys"] := { selector: "ahk_exe Carnac.exe", path: "C:\Users\dmitr\AppData\Local\carnac\Carnac.exe" } -apps["lon"] := { selector: "ahk_exe LonelyScreen.exe", path: "g:\My Drive\Soft\LonelyScreen.exe" } -apps["mail"] := { desktop: "Office", selector: "ahk_exe Spark Desktop.exe", path: "C:\Users\dmitr\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Spark Desktop.lnk" } -apps["music"] := { desktop: "Other", selector: "ahk_exe Яндекс Музыка.exe", path: "C:\Users\dmitr\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Яндекс Музыка.lnk", postFunction: "makeAnyWindowMaximized" } -apps["note"] := { desktop: "Office", selector: "ahk_exe Notion.exe", path: "C:\Users\dmitr\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Notion.lnk", postFunction: "makeAnyWindowMaximized" } -apps["obs"] := { desktop: "Stream", selector: "ahk_exe obs64.exe", path: "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\OBS Studio\OBS Studio (64bit).lnk" } -apps["paint"] := { selector: "ahk_exe mspaint.exe", path: "mspaint.exe" } -apps["pdf"] := { desktop: "Files", selector: "ahk_exe SumatraPDF.exe", path: "C:\Users\dmitr\AppData\Local\SumatraPDF\SumatraPDF.exe" } -apps["pip"] := { selector: "Picture in picture ahk_exe chrome.exe" } -apps["pod"] := { desktop: "Dev", selector: "ahk_exe Podman Desktop.exe", path: "C:\Users\dmitr\AppData\Local\Programs\podman-desktop\Podman Desktop.exe" } -apps["post"] := { desktop: "Dev", selector: "ahk_exe Postman.exe", path: "C:\Users\dmitr\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Postman\Postman.lnk" } -apps["quake"] := { selector: "ahk_exe WindowsTerminal.exe", path: "wt.exe -w _quake" } -apps["slack"] := { desktop: "Chats", selector: "ahk_exe slack.exe", path: "C:\Users\dmitr\AppData\Local\slack\slack.exe" } -apps["steam"] := { desktop: "Browsing", selector: "ahk_exe steamwebhelper.exe", path: "C:\Program Files (x86)\Steam\Steam.exe" } -apps["task"] := { desktop: "Other", selector: "ahk_class TaskManagerWindow", path: "Taskmgr.exe", postFunction: "makeAnyWindowMaximized" } -apps["td"] := { desktop: "Dev", selector: "^Developer Tools ahk_exe Cypress.exe" } -apps["term"] := { desktop: "Dev", selector: "windows ahk_exe alacritty.exe", path: """C:\Program Files\Alacritty\alacritty.exe"" --title windows", postFunction: "makeAnyWindowFullscreen" } -apps["tg"] := { desktop: "Chats", selector: "ahk_exe Telegram.exe", path: "C:\Users\dmitr\AppData\Roaming\Telegram Desktop\Telegram.exe" } -apps["tra"] := { desktop: "English", path: "C:\Program Files (x86)\ABBYY Lingvo x6\Lingvo.exe" } -apps["trd"] := { desktop: "English", selector: "DeepL Translate ahk_exe msedge.exe", path: "C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe --start-maximized --app=https://www.deepl.com/translator#ru/en/" } -apps["try"] := { desktop: "English", selector: "Yandex Translate.* ahk_exe msedge.exe", path: "C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe --app=https://translate.yandex.com/en/" } -apps["tt"] := { desktop: "Dev", selector: "^(?!Developer Tools|Cypress) ahk_exe Cypress.exe" } -apps["web"] := { desktop: "Browsing", selector: "^(?!DevTools) ahk_exe chrome.exe", path: "C:\Users\dmitr\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Chrome Apps\Chrome.lnk" } - -; different -apps["work"] := { postFunction: "startWork" } +; Read config +FileRead, tmp, %A_ScriptDir%/actions.json +config := JSON.Load(tmp) +desktops := config["desktops"] +apps := config["apps"] ; Dependencies #Include %A_ScriptDir%/utils.ahk #Include %A_ScriptDir%/alfred.ahk #Include %A_ScriptDir%/chrome.ahk #Include %A_ScriptDir%/dependencies/virtual-desktop.ahk -#Include %A_ScriptDir%/moving-around.ahk +#Include %A_ScriptDir%/vd-moving-around.ahk #Include %A_ScriptDir%/user-functions.ahk Init(desktops) ; Must be run before hotkeys & hotstrings @@ -78,10 +32,12 @@ Init(desktops) ; Must be run before hotkeys & hotstrings #InputLevel 1 ; Key remaps #Include %A_ScriptDir%/remap.ahk - +#Include %A_ScriptDir%/remap/chrome-devtools.ahk ; Set InputLevel 0 to make hotstrings can be triggered by script events #InputLevel 0 + ; Hotstrings -#Hotstring ? ; Make it work inside a word +; #Hotstring ? ; Make it work inside a word #Hotstring EndChars -()[]{}`n `t #Include %A_ScriptDir%/hotstrings.ahk + diff --git a/win/ahk/remap.ahk b/win/ahk/remap.ahk index 6db8508..b892c82 100644 --- a/win/ahk/remap.ahk +++ b/win/ahk/remap.ahk @@ -79,8 +79,6 @@ return WinActivate Sleep 300 Send ^{F12} - Run, wt --window _quake new-tab --title [T]Thoughts -d "G:/My Drive/Notes/" -- nvim . - Sleep 20 Run, wt --window _quake new-tab --title [F].dotfiles -d "C:/Users/dmitr/.dotfiles/" -- nvim . Sleep 20 Run, wt --window _quake new-tab --title [A]AutoHotkey -d "C:/Users/dmitr/.dotfiles/win/ahk/" -- nvim . @@ -94,15 +92,6 @@ return } return -\ & g::Run, wt --window _quake focus-tab -t 0 -\ & t::Run, wt --window _quake focus-tab -t 1 -\ & f::Run, wt --window _quake focus-tab -t 2 -\ & a::Run, wt --window _quake focus-tab -t 3 -\ & v::Run, wt --window _quake focus-tab -t 4 -\ & c::Run, wt --window _quake focus-tab -t 5 -*\::\ - - #Enter:: Run wt WinWait, "Administrator: PowerShell ahk_exe WindowsTerminal.exe",, 3 @@ -124,6 +113,15 @@ raceMode := false return #if +; #if !raceMode && GetKeyState("RShift", "P") +; RShift up:: +; global apps +; if (A_PriorKey = "RShift") { +; GoToAlternateApp(apps) +; } +; return +; #if + *Tab:: if (A_PriorKey = "LAlt") { SendEvent {Blind}{Tab} @@ -183,17 +181,13 @@ return #if #if !raceMode && GetKeyState("Space", "P") - ; check out Get-WinUserLanguageList to find needed code - ; English - e Up::PostMessage, 0x0050, 0, 0x0000409,, A - ; Russian - r Up::PostMessage, 0x0050, 0, 0x0000419,, A - ; Georgian - g Up::PostMessage, 0x0050, 0, 0x0000437,, A - - Tab Up:: - Send {Alt down}{Tab} - Send {Alt up} + Tab:: + global apps + GoToAlternateApp(apps) + ; if (A_PriorKey = "RShift") { + ; } + ; Send {Alt down}{Tab} + ; Send {Alt up} return ; Copy & Paste @@ -283,18 +277,57 @@ return Send {AppsKey}aa{Enter} return #if + +#if WinActive("Excalidraw Plus ahk_exe chrome.exe") + *RButton::MButton + *MButton::RButton + *WheelDown:: + if (GetKeyState("RButton", "P")) { + Send ^{-} + } else { + Send {WheelDown} + } + return + *WheelUp:: + if (GetKeyState("RButton", "P")) { + Send ^{+} + } else { + Send {WheelUp} + } + return +#if + #if WinActive("ahk_exe TOTALCMD64.EXE") !e::Send {Home}{F2} ; Edit path !p::Send ^{F12} ; Copy path to selected file #if + #if WinActive("ahk_exe Telegram.exe") - !j::Send ^+{Down} - !k::Send ^+{Up} + ^u::Send ^+{Up} + ^d::Send ^+{Down} + !j::Send !{Down} + !k::Send !{Up} + ^j:: + if (GetKeyState("Space", "P")) { + Send ^{Down} + } else { + Send ^+{Down} + } + return + ^k:: + if (GetKeyState("Space", "P")) { + Send ^{Up} + } else { + Send ^+{Up} + } + return #if + #if WinActive("ahk_exe Notion.exe") ^o::^[ ^i::^] #if + #if WinActive("ahk_class TLister ahk_exe TOTALCMD64.EXE") j::Down k::Up @@ -306,8 +339,6 @@ return +g::Send ^{End} #if -#InputLevel 0 - ; Win-hotkeys ; #w:: @@ -340,7 +371,9 @@ return ; Show active window on all virtual desktops (VD) #^t:: WinGet, activeHwnd, ID, A - PinWindow(activeHwnd) + ; PinWindow(activeHwnd) + WS_EX_TOOLWINDOW := 0x00000080 + WinSet, ExStyle, ^%WS_EX_TOOLWINDOW%, A PlayErrorSound() return ; Quit diff --git a/win/ahk/remap/chrome-devtools.ahk b/win/ahk/remap/chrome-devtools.ahk new file mode 100644 index 0000000..6347456 --- /dev/null +++ b/win/ahk/remap/chrome-devtools.ahk @@ -0,0 +1,64 @@ +on := false +#if WinActive("^DevTools ahk_exe chrome.exe") + ; Debug + `;:: ; Step + global on + if (A_PriorKey = "\") { + Send {Backspace}`; + on := false + } else { + if (on) { + Send {F9} + on := false + } else { + on := true + } + } + return +#if on + b:: ; Toggle Breakpoint + global on + if (on) { + Send ^b + on := false + } else { + Send b + } + return + d:: ; Disable Breakpoints + global on + if (on) { + Send ^{F8} + on := false + } else { + Send d + } + return + c:: ; Continue + global on + if (on) { + Send {F8} + on := false + } else { + Send c + } + return + i:: ; In + global on + if (on) { + Send {F11} + on := false + } else { + Send i + } + return + o:: ; Out + global on + if (on) { + Send +{F11} + on := false + } else { + Send o + } + return +#if diff --git a/win/ahk/user-functions.ahk b/win/ahk/user-functions.ahk index e720eb4..0860efd 100644 --- a/win/ahk/user-functions.ahk +++ b/win/ahk/user-functions.ahk @@ -1,8 +1,36 @@ ; OPEN mode startWork() { - Run C:\Users\dmitr\.dotfiles\win\pwsh\bin\work.ps1 + Run C:\Users\dmitr\.dotfiles\win\pwsh\bin\spin-up-work-env.ps1 } +; check out Get-WinUserLanguageList to find needed code +english() { + PostMessage, 0x0050, 0, 0x0000409,, A +} +russian() { + PostMessage, 0x0050, 0, 0x0000419,, A +} +georgian() { + PostMessage, 0x0050, 0, 0x0000437,, A +} + +open_gpt() { + Run, wt --window _quake focus-tab -t 0 +} +open_dot() { + Run, wt --window _quake focus-tab -t 1 +} +open_ahk() { + Run, wt --window _quake focus-tab -t 2 +} +open_nvim() { + Run, wt --window _quake focus-tab -t 3 +} +open_pwsh() { + Run, wt --window _quake focus-tab -t 4 +} + + makeAnyWindowFullscreen() { WinSet, Style, -0xC40000, A ; WinMaximize, A diff --git a/win/ahk/utils.ahk b/win/ahk/utils.ahk index 67eca76..f800907 100644 --- a/win/ahk/utils.ahk +++ b/win/ahk/utils.ahk @@ -9,6 +9,17 @@ IndexOf(needle, haystack) { return -1 } +_getIfContains(arr, inp) { + result := false + for key, value in arr { + if (key == inp) { + result := value + break + } + } + return result +} + ; Processes ProcessExist(exeName) { diff --git a/win/ahk/moving-around.ahk b/win/ahk/vd-moving-around.ahk similarity index 100% rename from win/ahk/moving-around.ahk rename to win/ahk/vd-moving-around.ahk