├── .gitignore ├── .screenrc ├── icons ├── convert.sh ├── ahk.ico ├── ahk_d.ico ├── ahk_d_r.ico └── ahk.svg ├── auth ├── 0000.kdbx ├── keys.kdbx └── passwords.kdbx ├── .editorconfig ├── vscode_snippets ├── erb.json ├── ruby.json ├── typescript.code-snippets └── html.code-snippets ├── .rubocop.yml ├── winget └── manifests │ └── EFLFE.PingoMeter │ ├── EFLFE.PingoMeter.yaml │ ├── EFLFE.PingoMeter.locale.en-US.yaml │ └── EFLFE.PingoMeter.installer.yaml ├── pingometer-cfg.txt ├── vscode_tasks.json ├── lsd.config.yaml ├── hammerspoon ├── helpers.lua ├── init.lua ├── netstat.lua ├── elgato.lua ├── menuitem.lua └── test.lua ├── .gitattributes ├── test_mouse.py ├── BatteryInfoView.cfg ├── git-cfg.toml ├── .vscode └── settings.json ├── macos-disable-reactions.sh ├── symbols.csv ├── vscode_keybindings.json ├── README.md ├── vscode_settings.json ├── profile.ps1 ├── shell-cfg.sh ├── configure_macos.sh ├── dictionary.txt ├── xmbc_settings.xmbcp └── configure_win.ps1 /.gitignore: -------------------------------------------------------------------------------- 1 | karabiner/automatic_backups/ 2 | -------------------------------------------------------------------------------- /.screenrc: -------------------------------------------------------------------------------- 1 | escape ^\\ 2 | term xterm-256color 3 | -------------------------------------------------------------------------------- /icons/convert.sh: -------------------------------------------------------------------------------- 1 | magick -background transparent "ahk.svg" "ahk.ico" 2 | -------------------------------------------------------------------------------- /auth/0000.kdbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grigoryvp/dotfiles/HEAD/auth/0000.kdbx -------------------------------------------------------------------------------- /auth/keys.kdbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grigoryvp/dotfiles/HEAD/auth/keys.kdbx -------------------------------------------------------------------------------- /icons/ahk.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grigoryvp/dotfiles/HEAD/icons/ahk.ico -------------------------------------------------------------------------------- /icons/ahk_d.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grigoryvp/dotfiles/HEAD/icons/ahk_d.ico -------------------------------------------------------------------------------- /icons/ahk_d_r.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grigoryvp/dotfiles/HEAD/icons/ahk_d_r.ico -------------------------------------------------------------------------------- /auth/passwords.kdbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grigoryvp/dotfiles/HEAD/auth/passwords.kdbx -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | charset = utf-8 3 | end_of_line = lf 4 | indent_style = space 5 | indent_size = 2 6 | [*.py] 7 | indent_size = 4 8 | [*.rs] 9 | indent_size = 4 10 | -------------------------------------------------------------------------------- /vscode_snippets/erb.json: -------------------------------------------------------------------------------- 1 | { 2 | "erb.evaluate": { 3 | "prefix": "_%", 4 | "body": ["<% $0 %>"] 5 | }, 6 | "erb.replace": { 7 | "prefix": "_=", 8 | "body": ["<%= $0 %>"] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | Style/FrozenStringLiteralComment: 2 | Enabled: false 3 | Style/StringLiterals: 4 | # Do not force single quotes 5 | Enabled: false 6 | Style/Documentation: 7 | # Do not require comments for all classes 8 | Enabled: false 9 | -------------------------------------------------------------------------------- /winget/manifests/EFLFE.PingoMeter/EFLFE.PingoMeter.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.9.0.schema.json 2 | 3 | PackageIdentifier: EFLFE.PingoMeter 4 | PackageVersion: 0.9.9 5 | DefaultLocale: en-US 6 | ManifestType: version 7 | ManifestVersion: 1.9.0 8 | -------------------------------------------------------------------------------- /vscode_snippets/ruby.json: -------------------------------------------------------------------------------- 1 | { 2 | "rails.application.generators": { 3 | "prefix": "_app_gen", 4 | "body": [ 5 | "config.generators do", 6 | " _1.template_engine nil", 7 | " _1.test_framework nil", 8 | " _1.helper false", 9 | " _1.skip_routes true", 10 | "end" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /vscode_snippets/typescript.code-snippets: -------------------------------------------------------------------------------- 1 | { 2 | "ts.jest.file": { 3 | "scope": "typescript,javascript", 4 | "prefix": "_jestfile", 5 | "body": [ 6 | "describe('something', () => {", 7 | " it('should do something', async () => {", 8 | " expect(true).toBe(true)", 9 | " });", 10 | "});" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /pingometer-cfg.txt: -------------------------------------------------------------------------------- 1 | # PingoMeter config file 2 | Delay 500 3 | MaxPing 1000 4 | BgColor 0:0:0 5 | GoodColor 120:180:0 6 | NormalColor 255:180:0 7 | BadColor 255:0:0 8 | RunOnStartup False 9 | TheIPAddress 1.1.1.1 10 | AlarmConnectionLost False 11 | AlarmTimeOut False 12 | AlarmResumed False 13 | UseNumbers False 14 | SFXConnectionLost (none) 15 | SFXTimeOut (none) 16 | SFXResumed (none) 17 | OfflineCounter False 18 | -------------------------------------------------------------------------------- /vscode_tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [{ 4 | "label": "task-focus-terminal", 5 | "command": "${command:workbench.action.terminal.focus}" 6 | }, { 7 | "label": "task-toggle-terminal", 8 | "command": "${command:workbench.action.terminal.toggleTerminal}" 9 | }, { 10 | "label": "task-close-terminal", 11 | "dependsOrder": "sequence", 12 | "dependsOn": [ 13 | "task-focus-terminal", 14 | "task-toggle-terminal" 15 | ] 16 | }] 17 | } 18 | -------------------------------------------------------------------------------- /winget/manifests/EFLFE.PingoMeter/EFLFE.PingoMeter.locale.en-US.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.9.0.schema.json 2 | 3 | PackageIdentifier: EFLFE.PingoMeter 4 | PackageVersion: 0.9.9 5 | PackageLocale: en-US 6 | Publisher: EFLFE 7 | PackageName: PingoMeter 8 | License: MIT License 9 | ShortDescription: A small program that show your ping in Windows system tray (in graph or numbers) 10 | ManifestType: defaultLocale 11 | ManifestVersion: 1.9.0 12 | -------------------------------------------------------------------------------- /lsd.config.yaml: -------------------------------------------------------------------------------- 1 | display: all 2 | layout: oneline 3 | blocks: 4 | - permission 5 | - user 6 | - size 7 | - name 8 | color: 9 | when: always 10 | ignore-globs: 11 | - .DS_Store 12 | - .vscode 13 | - desktop.ini 14 | - Diablo * 15 | - Steelrising 16 | - Larian Studios 17 | - My Games 18 | - My Music 19 | - My Pictures 20 | - My Videos 21 | - PowerShell 22 | - Sound Recordings 23 | - WindowsPowerShell 24 | - Zoom 25 | sorting: 26 | column: name 27 | reverse: false 28 | dir-grouping: first 29 | -------------------------------------------------------------------------------- /hammerspoon/helpers.lua: -------------------------------------------------------------------------------- 1 | dir = function (v) for k, v in pairs(v) do print(k, "=>", v) end end 2 | 3 | idir = function (v) for i, v in ipairs(v) do print(i, "~>", v) end end 4 | 5 | function count(v) 6 | local sum = 0 7 | for _ in pairs(v) do 8 | sum = sum + 1 9 | end 10 | return sum 11 | end 12 | 13 | cls = hs.console.clearConsole 14 | 15 | function focusLastFocused() 16 | local wf = hs.window.filter 17 | local windows = wf.defaultCurrentSpace:getWindows(wf.sortByFocusedLast) 18 | if #windows > 0 then 19 | windows[1]:focus() 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.c diff=cpp 2 | *.h diff=cpp 3 | *.c++ diff=cpp 4 | *.h++ diff=cpp 5 | *.cpp diff=cpp 6 | *.hpp diff=cpp 7 | *.cc diff=cpp 8 | *.hh diff=cpp 9 | *.m diff=objc 10 | *.mm diff=objc 11 | *.cs diff=csharp 12 | *.css diff=css 13 | *.html diff=html 14 | *.xhtml diff=html 15 | *.go diff=golang 16 | *.php diff=php 17 | *.pl diff=perl 18 | *.py diff=python 19 | *.md diff=markdown 20 | *.rb diff=ruby 21 | *.rake diff=ruby 22 | *.rs diff=rust 23 | *.lisp diff=lisp 24 | *.el diff=lisp 25 | *.js diff=java 26 | *.ts diff=java 27 | *.java diff=java 28 | -------------------------------------------------------------------------------- /test_mouse.py: -------------------------------------------------------------------------------- 1 | # /// script 2 | # dependencies = [ 3 | # "pyside6==6.7.2" 4 | # ] 5 | # /// 6 | 7 | import sys 8 | 9 | from PySide6.QtWidgets import QApplication, QWidget, QHBoxLayout, QLabel 10 | 11 | 12 | class Main(QWidget): 13 | 14 | 15 | def __init__(self, parent=None): 16 | super(Main, self).__init__(parent) 17 | QHBoxLayout(self).addWidget(QLabel("window")) 18 | 19 | 20 | def mousePressEvent(self, event): 21 | print(event.modifiers()) 22 | 23 | 24 | def main(): 25 | app = QApplication(sys.argv) 26 | (_ := Main()).show() 27 | sys.exit(app.exec()) 28 | 29 | 30 | if __name__ == "__main__": 31 | main() 32 | -------------------------------------------------------------------------------- /icons/ahk.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 17 | d 27 | 28 | -------------------------------------------------------------------------------- /winget/manifests/EFLFE.PingoMeter/EFLFE.PingoMeter.installer.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.9.0.schema.json 2 | 3 | PackageIdentifier: EFLFE.PingoMeter 4 | PackageVersion: 0.9.9 5 | InstallerType: zip 6 | NestedInstallerType: portable 7 | ArchiveBinariesDependOnPath: true 8 | NestedInstallerFiles: 9 | - RelativeFilePath: PingoMeter\PingoMeter.exe 10 | PortableCommandAlias: pingometer 11 | Installers: 12 | - InstallerUrl: https://github.com/EFLFE/PingoMeter/releases/download/0.9.9/PingoMeter_r0.9.9.zip 13 | Architecture: x86 14 | InstallerSha256: 065C609C9AE945F55AA9476195091BE0A8D8D112B8ABDAB9392CE400981C88E4 15 | ManifestType: installer 16 | ManifestVersion: 1.9.0 17 | -------------------------------------------------------------------------------- /BatteryInfoView.cfg: -------------------------------------------------------------------------------- 1 | [General] 2 | ShowGridLines=1 3 | SaveFilterIndex=0 4 | ShowInfoTip=1 5 | DisplayMode=2 6 | BatInfoRefreshTime=1 7 | LogInfoTime=10 8 | AutoRefreshBatInfo=1 9 | AutoUpdateLog=1 10 | TrayIcon=1 11 | MarkOddEvenRows=0 12 | LogSuspend=1 13 | LogResume=1 14 | LogResumeCritical=1 15 | LogLowBattery=1 16 | StartAsHidden=1 17 | AutoScrollDown=1 18 | AddLogLinesToFile=0 19 | LogFilename= 20 | LogFileFormat=2 21 | AlwaysOnTop=0 22 | ShowCapacityInmAh=0 23 | WinPos=2C 00 00 00 00 00 00 00 01 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 6F 02 00 00 84 01 00 00 EF 04 00 00 64 03 00 00 24 | Columns=78 00 00 00 6E 00 01 00 64 00 02 00 64 00 03 00 64 00 04 00 64 00 05 00 64 00 06 00 25 | Sort=4096 26 | BatInfoColumns=2C 01 00 00 C8 00 01 00 C8 00 02 00 00 00 03 00 00 00 04 00 27 | BatInfoSort=0 28 | -------------------------------------------------------------------------------- /vscode_snippets/html.code-snippets: -------------------------------------------------------------------------------- 1 | { 2 | "html.link.css": { 3 | "scope": "html,erb", 4 | "prefix": "_hlc", 5 | "body": [""] 6 | }, 7 | "html.link.css.bootstrap": { 8 | "scope": "html, erb", 9 | "prefix": "_hlcb", 10 | "body": [""] 11 | }, 12 | "html.link.js": { 13 | "scope": "html,erb", 14 | "prefix": "_hlj", 15 | "body": [""] 16 | }, 17 | "html.link.js.bootstrap": { 18 | "scope": "html,erb", 19 | "prefix": "_hljb", 20 | "body": [""] 21 | }, 22 | "html.button": { 23 | "scope": "html,erb", 24 | "prefix": "_hb", 25 | "body": [""] 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /hammerspoon/init.lua: -------------------------------------------------------------------------------- 1 | -- TODO: change ICMP to TCP since ICMP works while TCP may fail with 2 | -- the "no buffer space available" error. 3 | -- See ./.vscode/settings.json for linter configuration 4 | 5 | -- "hs" cli tool for remote communication 6 | require "hs.ipc" 7 | 8 | -- Doesn't seem to work 9 | hs.window.animationDuration = 0 10 | 11 | function onReadlinkExit(exitCode, stdOut, _) 12 | if exitCode ~= 0 or not stdOut then 13 | return print("ERROR: init.lua is not a link within ~/.hammerspoon") 14 | end 15 | local srcDir = stdOut:match("(.+/)") or "./" 16 | -- init.lua is linked, rest of the files are in the original dir 17 | package.path = package.path .. ";" .. srcDir .. "?.lua" 18 | 19 | require "helpers" 20 | require "netstat" 21 | require "elgato" 22 | require "menuitem" 23 | require "main" 24 | 25 | app = App:new() 26 | app:setSrcDir(srcDir) 27 | app:registerMouse() 28 | app:loadSettings() 29 | app:loadSymbols() 30 | app:createMenu() 31 | app:restartInetPingInt() 32 | app:restartInetPingExt() 33 | app:startHeartbeat() 34 | app:startHttpServer() 35 | app:startApplicationWatcher() 36 | end 37 | 38 | srcFile = debug.getinfo(1).source:match("@?(.*)") 39 | task = hs.task.new("/usr/bin/readlink", onReadlinkExit, {srcFile}) 40 | task:start() 41 | -------------------------------------------------------------------------------- /git-cfg.toml: -------------------------------------------------------------------------------- 1 | [user] 2 | name = "Grigory Petrov" 3 | email = "grigoryvp@gmail.com" 4 | 5 | [core] 6 | editor = "code" 7 | autocrlf = "input" 8 | safecrlf = false 9 | # Hunk header descriptions https://github.com/git/git/blob/master/userdiff.c 10 | attributesfile = "~/.gitattributes" 11 | # Better dif 12 | pager = "diff-so-fancy | less" 13 | 14 | [init] 15 | defaultBranch = main 16 | 17 | [format] 18 | pretty = "oneline" 19 | 20 | [push] 21 | default = "simple" 22 | 23 | [fetch] 24 | # Fixes "cannot lock ref" macOS case sensetive issue. 25 | prune = true 26 | 27 | [pull] 28 | # "fatal: Cannot rebase onto multiple branches" if git-radar is used. 29 | rebase = true 30 | 31 | [safe] 32 | # Can be manipulated via sudo which changes user 33 | directory = "C:/Users/user/dotfiles" 34 | 35 | [alias] 36 | cl = "clone" 37 | ch = "checkout" 38 | a = "add" 39 | aa = "add ." 40 | ap = "add -p" 41 | cm = "commit -m" 42 | ps = "push" 43 | pl = "pull" 44 | st = "status --short --branch" 45 | lg = "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit" 46 | br = "branch" 47 | di = "diff --ignore-space-change" 48 | diw = "diff --ignore-space-change --color-words" 49 | dic = "diff --ignore-space-change --cached" 50 | dicw = "diff --ignore-space-change --color-words --cached" 51 | dit = "difftool --ignore-space-change" 52 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // Todo: https://github.com/Hammerspoon/hammerspoon/pull/2530 3 | // cd ~ 4 | // git clone https://github.com/folke/hammerspoon.git 5 | // cd hammerspoon 6 | // make build/stubs 7 | "Lua.workspace.library": [ 8 | "/Users/user/hammerspoon/build/stubs" 9 | ], 10 | "Lua.workspace.preloadFileSize": 256, 11 | "Lua.diagnostics.disable": [ 12 | "lowercase-global" 13 | ], 14 | "cSpell.words": [ 15 | "applemultitouchtrackpad", 16 | "axuielement", 17 | "chflags", 18 | "Classpath", 19 | "dnut", 20 | "dont", 21 | "edgemac", 22 | "EFLFE", 23 | "emmanuelbeziat", 24 | "esbenp", 25 | "eventtap", 26 | "formulahendry", 27 | "httpserver", 28 | "Iface", 29 | "ignorecase", 30 | "inet", 31 | "inputmethod", 32 | "ipynb", 33 | "kdbx", 34 | "keepassx", 35 | "keepassxc", 36 | "keepcoder", 37 | "keylayout", 38 | "Kotoeri", 39 | "LASTEXITCODE", 40 | "launchanim", 41 | "libgpg", 42 | "libyaml", 43 | "lidwake", 44 | "michaeldfallen", 45 | "Nlsv", 46 | "nonprimary", 47 | "Pathbar", 48 | "PingoMeter", 49 | "Pixelmator", 50 | "pmset", 51 | "pqrs", 52 | "PVPDF", 53 | "pwsh", 54 | "Pylance", 55 | "qbittorrent", 56 | "rawattr", 57 | "Romaji", 58 | "screenrc", 59 | "shellenv", 60 | "softwareupdate", 61 | "TOTP", 62 | "usleep", 63 | "uvc", 64 | "vscodevim", 65 | "vsicons", 66 | "wiemer", 67 | "wvous" 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /macos-disable-reactions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # https://www.reddit.com/r/MacOSBeta/comments/170sg3d/disabling_os_sonoma_reactions_globally_using_mdm/ 3 | # Requires "full disk access" for terminal 4 | 5 | # All Applications to disable Sonoma reactions for 6 | allApps=( 7 | "videoeffects/us-zoom-xos/reactions-enabled" 8 | "videoeffects/us-zoom-xos/gestures-enabled" 9 | "videoeffects/com-microsoft-teams/reactions-enabled" 10 | "videoeffects/com-microsoft-teams/gestures-enabled" 11 | "videoeffects/com-obsproject-obs-studio/reactions-enabled" 12 | "videoeffects/com-obsproject-obs-studio/gestures-enabled") 13 | 14 | #plist location to edit 15 | plist="Library/Group Containers/group.com.apple.secure-control-center-preferences/Library/Preferences/group.com.apple.secure-control-center-preferences.av.plist" 16 | 17 | # For each local user disable Sonoma reactions for all specified applications 18 | localUsers=$( dscl . list /Users UniqueID | awk '$2 >= 501 {print $1}' | grep -v admin ) 19 | echo "$localUsers" | while read user; do 20 | user=`stat -f "%Su" /dev/console` 21 | echo "User: $user" 22 | 23 | for domain in "${allApps[@]}"; do 24 | result=$(sudo /usr/libexec/PlistBuddy -c "Set $domain false" "/Users/$user/$plist" 2>&1) 25 | echo "$domain" 26 | 27 | if [[ "$result" == *"Does Not Exist"* ]]; then 28 | echo "Adding $domain to false" 29 | 30 | /usr/libexec/PlistBuddy -c "Add $domain bool false" "/Users/$user/$plist" 31 | elif [[ "$result" == *"Error"* ]]; then 32 | echo "An error occurred: $result" 33 | else 34 | echo "Setting $domain to false" 35 | fi 36 | done 37 | done 38 | -------------------------------------------------------------------------------- /hammerspoon/netstat.lua: -------------------------------------------------------------------------------- 1 | -- Max amount of seconds for netstat to run (it hangs sometimes) 2 | TIMEOUT_SEC = 5 3 | 4 | netstat = { 5 | _task = nil, 6 | _startTimeSec = nil 7 | } 8 | 9 | 10 | function netstat:get(callback) 11 | 12 | assert(not self._task or not self._task:isRunning()) 13 | 14 | function onExit(exitCode, stdOut, _) 15 | if exitCode ~= 0 then 16 | return callback(nil) 17 | end 18 | 19 | gateway = nil 20 | for line in stdOut:gmatch("[^\r\n]+") do 21 | pattern = "^default +([0-9]+%.[0-9]+%.[0-9]+%.[0-9]+) .+ en0 *$" 22 | _, _, match = line:find(pattern) 23 | if not gateway and match then 24 | gateway = match 25 | end 26 | end 27 | 28 | if not gateway then 29 | return callback(nil) 30 | end 31 | 32 | return callback({ 33 | gateway = gateway 34 | }) 35 | end 36 | 37 | self._startTimeSec = hs.timer.absoluteTime() / 1000000000 38 | self._task = hs.task.new("/usr/sbin/netstat", onExit, {"-rn"}) 39 | self._task:start() 40 | end 41 | 42 | 43 | function netstat:isRunning() 44 | if not self._task then 45 | return false 46 | end 47 | 48 | isRunning = self._task:isRunning() 49 | if not isRunning then 50 | return false 51 | end 52 | 53 | assert(type(self._startTimeSec == "number"), "integrity error") 54 | curTimeSec = hs.timer.absoluteTime() / 1000000000 55 | if curTimeSec > self._startTimeSec + TIMEOUT_SEC then 56 | self._task:terminate() 57 | -- otherwise next call to get() may receive "running" 58 | self._task = nil 59 | return false 60 | end 61 | 62 | return true 63 | end 64 | -------------------------------------------------------------------------------- /symbols.csv: -------------------------------------------------------------------------------- 1 | 😊,smile 2 | 😜,crazy 3 | 😇,halo 4 | 😳,eyes 5 | 🤔,think 6 | 😂,lol 7 | 😥,sad 8 | 😘,kiss 9 | 😍,love 10 | ❤️,heart 11 | 🔥,fire 12 | 🙏,hands 13 | 🤝,shake 14 | 👉,point 15 | 👍,yes 16 | 👎,no 17 | 👌,ok 18 | 👋,wave 19 | 🚕,car 20 | ✈️,airplane 21 | €,euro 22 | 23 | # Keyboard 24 | ⌘,command 25 | ⇧,shift 26 | ⌥,alt 27 | ↩,return 28 | ←,left 29 | →,right 30 | ↑,up 31 | ↓,down 32 | 33 | # Directions 34 | ↔️,sideways 35 | ⬅️,west 36 | ➡️,east 37 | ⬆️,north 38 | ⬇️,south 39 | 40 | # Tags 41 | 教,speaker 42 | 技,skilled 43 | 力,influencer 44 | 大,boss 45 | 死,dead 46 | 47 | # Utility tags 48 | 会,meet 49 | ✉️,mail 50 | 業,LFE 51 | 事,LFW 52 | 53 | # Relations 54 | 郎,son 55 | 娘,daughter 56 | 相,partner 57 | 友,friend 58 | 僚,colleague 59 | 60 | # Personality 61 | 侵,mark 62 | 宗,religion 63 | 軍,army background 64 | 65 | # Job or company suffixes 66 | 元,ex 67 | 員,employee 68 | 政,head 69 | 主,organizer 70 | 委,committee 71 | 音,podcast 72 | 師,teacher 73 | 74 | # Languages 75 | 🇷🇺,Russian 76 | 🇬🇧,English 77 | 🇳🇱,Dutch 78 | 79 | # Language tags 80 | 去,past 81 | 完,perfect 82 | 多,plural 83 | 反,antonym 84 | 性,trait 85 | 時,time 86 | 天,weather 87 | 方,location 88 | 名,noun 89 | に,adverb 90 | 形,adjective 91 | 92 | # Formatting 93 | 🔘,queue 94 | 🔗,link 95 | 0️⃣,0 96 | ⓪,0 97 | 1️⃣,1 98 | ①,1 99 | 2️⃣,2 100 | ②,2 101 | 3️⃣,3 102 | ③,3 103 | 4️⃣,4 104 | ④,4 105 | 5️⃣,5 106 | ⑤,5 107 | 6️⃣,6 108 | ⑥,6 109 | 7️⃣,7 110 | ⑦,7 111 | 8️⃣,8 112 | ⑧,8 113 | 9️⃣,9 114 | ⑨,9 115 | ⑩,10 116 | ⑪,11 117 | ⑫,12 118 | ⑬,13 119 | ⑭,14 120 | ⑮,15 121 | ⑯,16 122 | ⑰,17 123 | ⑱,18 124 | ⑲,19 125 | ⑳,20 126 | ㉑,21 127 | ㉒,22 128 | ㉓,23 129 | ㉔,24 130 | ㉕,25 131 | ㉖,26 132 | ㉗,27 133 | ㉘,28 134 | ㉙,29 135 | ㉚,30 136 | ㉛,31 137 | ㉜,32 138 | ㉝,33 139 | ㉞,34 140 | ㉟,35 141 | ㊱,36 142 | ㊲,37 143 | ㊳,38 144 | ㊴,39 145 | ㊵,40 146 | ㊶,41 147 | ㊷,42 148 | ㊸,43 149 | ㊹,44 150 | ㊺,45 151 | ㊻,46 152 | ㊼,47 153 | ㊽,48 154 | ㊾,49 155 | ㊿,50 156 | 🆕,new 157 | 🆗,ok 158 | 📌,todo 159 | ⚠️,warn 160 | ✅,done 161 | ❌,err 162 | ❓,question 163 | 🔴,red 164 | 🟢,green 165 | 🔵,blue 166 | 🕘,time 167 | -------------------------------------------------------------------------------- /hammerspoon/elgato.lua: -------------------------------------------------------------------------------- 1 | -- https://vk.cc/cHsPIz 2 | 3 | elgato = { 4 | _lights = {} 5 | } 6 | 7 | 8 | function elgato:update() 9 | local browser = hs.bonjour.new() 10 | local SERVICE_TYPE = "_elg._tcp." 11 | browser:findServices(SERVICE_TYPE, function( 12 | browserObject, 13 | domain, 14 | isAdvertised, 15 | serviceObject, 16 | isMoreExpected 17 | ) 18 | if domain == "error" then 19 | return 20 | end 21 | local serviceType = serviceObject:type() 22 | if serviceType ~= SERVICE_TYPE then 23 | return 24 | end 25 | 26 | local name = serviceObject:name() 27 | if isAdvertised == false then 28 | self._lights[name] = nil 29 | return 30 | end 31 | 32 | if not self._lights[name] then 33 | self._lights[name] = { 34 | object = serviceObject, 35 | resolved = false, 36 | address = nil, 37 | port = nil, 38 | } 39 | end 40 | if self._lights[name].resolved then 41 | return 42 | end 43 | 44 | local timeoutSec = 5 45 | serviceObject:resolve(timeoutSec, function(userdata, result) 46 | if result ~= "resolved" then 47 | return 48 | end 49 | for _, info in pairs(self._lights) do 50 | if not info.resolved then 51 | info.address = info.object:addresses()[1] 52 | info.port = info.object:port() 53 | if info.address and info.port then 54 | info.resolved = true 55 | end 56 | end 57 | end 58 | end) 59 | end) 60 | end 61 | 62 | 63 | function elgato:_switchOne(address, port, isOn) 64 | local url = "http://" .. address .. ":" .. port .. "/elgato/lights" 65 | local headers = nil 66 | -- KeyLights are very slow to response for HTTP requests after they 67 | -- were not active for some time. 68 | hs.http.asyncGet(url, headers, function(resCode, body, headers) 69 | if resCode < 200 or resCode >= 300 then 70 | return 71 | end 72 | 73 | local settings = hs.json.decode(body) 74 | if not settings then 75 | return 76 | end 77 | 78 | if isOn then 79 | settings.lights[1].on = 1 80 | else 81 | settings.lights[1].on = 0 82 | end 83 | 84 | local data = hs.json.encode(settings) 85 | hs.http.asyncPut(url, data, headers, function() end) 86 | end) 87 | end 88 | 89 | 90 | function elgato:switch(isOn) 91 | for _, info in pairs(self._lights) do 92 | if info.resolved then 93 | self:_switchOne(info.address, info.port, isOn) 94 | end 95 | end 96 | end 97 | 98 | 99 | function elgato:lightsCount() 100 | local result = 0 101 | for _, info in pairs(self._lights) do 102 | if (info.resolved) then 103 | result = result + 1 104 | end 105 | end 106 | return result 107 | end 108 | -------------------------------------------------------------------------------- /vscode_keybindings.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "key": "shift+f1", 3 | "command": "workbench.action.focusActiveEditorGroup" 4 | }, { 5 | "key": "f2", 6 | "command": "workbench.action.toggleSidebarVisibility" 7 | }, { 8 | "key": "shift+f2", 9 | "command": "workbench.action.focusSideBar" 10 | }, { 11 | "key": "f3", 12 | "when": "!terminalViewShowing", 13 | "command": "workbench.action.terminal.toggleTerminal" 14 | }, { 15 | "key": "f3", 16 | "when": "terminalViewShowing", 17 | "command": "workbench.action.tasks.runTask", 18 | "args": "task-close-terminal" 19 | }, { 20 | "key": "shift+f3", 21 | "command": "workbench.action.terminal.focus" 22 | }, { 23 | "key": "f4", 24 | "command": "workbench.action.toggleAuxiliaryBar" 25 | }, { 26 | "key": "shift+f4", 27 | "command": "workbench.action.focusAuxiliaryBar" 28 | }, { 29 | "key": "ctrl+k o", 30 | "command": "workbench.action.addRootFolder" 31 | }, { 32 | "key": "ctrl+k e", 33 | "command": "workbench.view.explorer" 34 | }, { 35 | "key": "ctrl+k g", 36 | "command": "workbench.view.scm" 37 | }, { 38 | "key": "ctrl+k d", 39 | "command": "workbench.view.debug" 40 | }, { 41 | "key": "ctrl+w", 42 | "command": "workbench.action.closeActiveEditor" 43 | }, { 44 | "key": "ctrl+k b", 45 | "command": "bookmarks.toggle", 46 | "when": "editorTextFocus" 47 | }, { 48 | "key": "ctrl+;", 49 | "command": "workbench.action.debug.stepOver", 50 | "when": "inDebugMode" 51 | }, { 52 | "key": "ctrl+'", 53 | "command": "workbench.action.debug.stepInto", 54 | "when": "inDebugMode" 55 | }, { 56 | "key": "ctrl+shift+;", 57 | "command": "workbench.action.debug.stepOut", 58 | "when": "inDebugMode" 59 | }, { 60 | "key": "ctrl+\\", 61 | "command": "workbench.action.debug.continue", 62 | "when": "inDebugMode" 63 | }, { 64 | "key": "ctrl+k f", 65 | "command": "workbench.action.findInFiles" 66 | }, { 67 | "key": "ctrl+k ctrl+k", 68 | "command": "extension.xi.lookup" 69 | }, { 70 | "key": "cmd-f", 71 | "command": "extension.vim_ctrl+f", 72 | "when": "editorTextFocus && vim.active" 73 | }, { 74 | "key": "ctrl+]", 75 | "command": "editor.action.openLink" 76 | }, { 77 | "key": "ctrl+[", 78 | "command": "extension.goto-link-provider.back" 79 | }, { 80 | "key": "ctrl+8", 81 | "command": "workbench.action.focusActiveEditorGroup" 82 | }, { 83 | "key": "ctrl+9", 84 | "command": "workbench.action.toggleMaximizedPanel" 85 | }, { 86 | "key": "ctrl+k ctrl+o", 87 | "command": "workbench.action.files.openFile" 88 | }, { 89 | "key": "ctrl+n", 90 | "command": "explorer.newFile", 91 | "when": "explorerViewletFocus" 92 | }, { 93 | "key": "shift+end", 94 | "command": "cSpell.goToNextSpellingIssueAndSuggest" 95 | }, { 96 | "key": "enter", 97 | "when": "hasSearchResult && searchViewletFocus", 98 | "command": "runCommands", 99 | "comment": "VSCode starts vim in insert mode by default", 100 | "args": { 101 | "commands": [{ 102 | "command": "workbench.action.focusActiveEditorGroup" 103 | }, { 104 | "command": "vim.remap", 105 | "args": {"after": [""]} 106 | }] 107 | } 108 | }] 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # My Win and MacOS box auto config 2 | 3 | ## Windows install 4 | 5 | ```ps1 6 | winget install --silent Microsoft.PowerShell # asks elevation IN BACKGROUND 7 | ``` 8 | 9 | Relaunch terminal, continue with `Elevated PowerShell`: 10 | 11 | ```ps1 12 | $repo_url = "https://raw.githubusercontent.com/grigoryvp/dotfiles" 13 | $url = "$repo_url/master/configure_win.ps1" 14 | # 'Invoke-Expression' instead of 'iex' since 'iex' is removed by profile.ps1 15 | Invoke-WebRequest $url -OutFile ./configure.ps1 16 | Set-ExecutionPolicy Unrestricted -Scope CurrentUser 17 | ./configure.ps1 18 | ``` 19 | 20 | Follow instructions for post-configuration. 21 | 22 | ```sh 23 | sudo apt update 24 | # pyenv build dependencies 25 | sudo apt install make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev 26 | printf '. /mnt/c/Users/user/dotfiles/shell-cfg.sh\n' >> ~/.bashrc 27 | printf '#!/bin/sh\n. /mnt/c/Users/user/dotfiles/shell-cfg\n' > ~/.zshrc 28 | printf '[include]\npath = /mnt/c/Users/user/dotfiles/git-cfg.toml\n' > ~/.gitconfig 29 | mkdir -p ~/.config/lsd/ 30 | cp /mnt/c/Users/user/dotfiles/.gitattributes ~/.gitattributes 31 | cp /mnt/c/Users/user/dotfiles/lsd.config.yaml ~/.config/lsd/config.yaml 32 | git clone https://github.com/michaeldfallen/git-radar ~/.git-radar 33 | git clone https://github.com/pyenv/pyenv.git ~/.pyenv 34 | git clone https://github.com/pyenv/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv 35 | # Reload shell 36 | pyenv install 3.12.2 37 | pyenv global 3.12.2 38 | pip install --upgrade pip 39 | ``` 40 | 41 | ## OSX 42 | 43 | ```sh 44 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/grigoryvp/dotfiles/HEAD/configure_macos.sh)" 45 | # Install https://macos.telegram.org/ 46 | # Install Trello from App Store 47 | # Enable KeePass Chrome integration and connect Chrome to the correct DB 48 | # Command-drag-out menu icons except clock (until the "x" mark appears) 49 | # Dock: iTerm, VSCode, Chrome, Double, Pass, Telegram, Mail, NotionC, Trello 50 | # Dock left: Notion, ChatGPT, Slack, WhatsApp, HEY, Discord, Parallels 51 | # Menu: hammerspoon, lunar, tailscale, command center, time 52 | # In "Settings/General" 53 | # * Add noTunes.app, hammerspoon, wox to "Login Items" 54 | # In "Settings/Accessibility" 55 | # * Disable "Siri/Settings/Intelligence" 56 | # In "Settings/Battery": 57 | # * Disable "dim screen" in "Options". 58 | # In "Settings/Dock": 59 | # * Minimize windows into application icon 60 | # In "Settings/Displays": 61 | # * Turn off "automatically adjust brightness" 62 | # In "Settings/Wallpaper": 63 | # * Set black wallpaper 64 | # In "Settings/Sound": 65 | # * Disable startup sound 66 | # * Mute interface sound effects 67 | # In "Settings/Privacy & Security" 68 | # * Add iTerm2 to "App Management" (for brew to update apps) 69 | # * Disable "Apple Intelligence Report" 70 | # In "Settings/Lock Screen": 71 | # * Require password immediately after screen is locked 72 | # In "Settings/Keyboard": 73 | # * Disable backlit 74 | # * Add "Russian-PC" and "Japanese-Romaji" for "Input Sources" 75 | # In "Settings/Keyboard/Shortcuts": 76 | # * Add "⇧⌘\" for "Mission Control/Notification Center" 77 | # * Disable input source shortcuts in "Input sources" 78 | # * Disable Spotlight hotkeys 79 | # * Remove "⇧⌘/" app shortcut from "App Shortcuts" 80 | # * Add "⌘W" to "Close Tab" for "Safari" app shortcut 81 | # * Add "⌥⇧⌘V" to "Paste and Match Style" for "Telegram" app shortcut 82 | # * Enable F-keys in "Function Keys" 83 | # * Disable caps in "Modifier Keys" 84 | # In "Settings/Trackpad/More gestures": 85 | # * Disable gestures 86 | # For iTerm2 settings 87 | # * Disable /General/Closing/Confirm 88 | # * Disable /General/Selection/Clicking 89 | # * Set Appearance/General/Theme to "Dark" 90 | # * Set Appearance/Windows/Hide scrollbars 91 | # * Enable Appearance/Dimming "inactive windows" and "affects text", 60% 92 | # * Set Profiles/Colors/Color presets to "Solarized Dark", background to black 93 | # * Set Profiles/Text/Font to "JetBrainsMono Nerd Font" size 16 94 | # For DoubleCommander settings 95 | # * Disable all confirmations in "/File operations/confirmation" 96 | # * In /Files views/extra 97 | # * Enable "move down" 98 | # * Disable "square brackets" 99 | # * In /Files views/Columns/Custom columns 100 | # * Change GETFILENAMENOEXT to GETFILENAME 101 | # * Delete the "ext" and "attr" columns 102 | # For Zoom settings: 103 | # * Enable "Always show controls" in "/General" 104 | # * Disable "Add to menu bar" in "/General" 105 | # * Enable "copy link" in "/General" 106 | # * Enable HD, set 49 participants, disable mirror in "/Video" 107 | # * Start meeting, share screen, allow screen sharing, enable green 108 | # menubar reactions and disable them (requires reboot) 109 | # For OBS settings 110 | # * Turn off /Advanced/General/Show warning on exit 111 | # * Set output as "fragmented mp4" in /Output/Recording 112 | # For Mimestream settings 113 | # * Set delete key to "trash" in /General 114 | # * Disable "play sounds" in /General 115 | # For Discord settings 116 | # * Disable all notification sounds in /Notifications 117 | # Remove all widgets and add "Note" as widget 118 | # Switch Lunar to "manual mode" so it will not mess with brightness 119 | # Add 'block in on en0' for '/etc/pf.conf' 120 | # Login all chrome profiles and name them 01, 02, 03 etc 121 | # Disable local network checks in chrome://flags/ 122 | # Configure ChatGPT for autostart and "dock only" icon. 123 | # Add KeyLights, blue temp, 90% back, 40% front 124 | # Import OBS Scenes 125 | # Store: Xcode, Amphetamine, Windows App, PowerPoint, Pixelmator 126 | # Install Parallels Desktop 127 | # Install https://onekey.so/download/ 128 | # Install https://tonkeeper.com/pro 129 | # phpenv install 8.0.9 # https://github.com/phpenv/phpenv/issues/90 130 | # phpenv global 8.0.9 # https://github.com/phpenv/phpenv/issues/90 131 | # For old macOS versions: 132 | # * Disable welcome screen guest user in "Preferences/Users & Groups" 133 | # * Add 'karabiner_grabber', 'karabiner_observer', 134 | # 'karabiner_console_user_server' into "Accessibility" 135 | # * Install https://d11yldzmag5yn.cloudfront.net/prod/4.4.53909.0617/Zoom.pkg 136 | # * iTunes/Preferences/Devices/Prevent from syncing automatically 137 | ``` 138 | 139 | ## Todo 140 | 141 | * Ignore subsequent "down" events on buttons that emulate mouse buttons. 142 | * Implement "go-to-background" Ctrl-D via PS keyboard hook. 143 | * Move text insert into copyq. 144 | 145 | ## License 146 | 147 | The following licensing applies to My windows box automatic configuration: 148 | Attribution-NonCommercial-NoDerivatives 4.0 International 149 | (CC-BY-NC-ND-4.0). For more information go to 150 | [https://creativecommons.org/licenses/by-nc-nd/4.0/](https://creativecommons.org/licenses/by-nc-nd/4.0/) 151 | -------------------------------------------------------------------------------- /hammerspoon/menuitem.lua: -------------------------------------------------------------------------------- 1 | menuitem = {} 2 | -- Virtual pixels, fills retune by scaling to 48. One pixel less is not 3 | -- a complete fit, while one pixel more cuts top and bottom parts. 4 | local menuHeight = 24 5 | -- Font height that approximately resembles default menubar font 6 | local fontHeight = 13 7 | -- Offset, in virtual pixels, from menubar top. Given menuHeight and 8 | -- fontHeight this provides font position that resembles default one. 9 | local fontTopOffset = 3 10 | --- Maximum number of text objects cached 11 | local maxCacheSize = 100 12 | 13 | 14 | function menuitem:new() 15 | local inst = { 16 | _widgets = {}, 17 | _submenu = {}, 18 | _canvas = hs.canvas.new({x = 0, y = 0, w = 1, h = menuHeight}), 19 | _item = hs.menubar.new(), 20 | _cache = { 21 | text = { 22 | objects = {}, 23 | sizes = {} 24 | } 25 | } 26 | } 27 | self.__index = self 28 | return setmetatable(inst, self) 29 | end 30 | 31 | 32 | function menuitem:clear() 33 | self._widgets = {} 34 | while #self._canvas > 0 do 35 | self._canvas[#self._canvas] = nil 36 | end 37 | end 38 | 39 | 40 | function menuitem:update() 41 | local curOffset = 0 42 | local totalWidth = 0 43 | for _, widget in ipairs(self._widgets) do 44 | 45 | if widget.type == "spacer" then 46 | self._canvas:insertElement({ 47 | type = "rectangle", 48 | frame = { 49 | x = curOffset, 50 | y = 0, 51 | w = widget.width, 52 | h = menuHeight 53 | }, 54 | fillColor = {red = 0, green = 0, blue = 0}, 55 | action = "fill" 56 | }) 57 | curOffset = curOffset + widget.width 58 | totalWidth = totalWidth + widget.width 59 | 60 | elseif widget.type == "text" then 61 | self._canvas:insertElement({ 62 | type = "rectangle", 63 | frame = { 64 | x = curOffset, 65 | y = 0, 66 | w = widget.width, 67 | h = menuHeight 68 | }, 69 | fillColor = {red = 0, green = 0, blue = 0}, 70 | action = "fill" 71 | }) 72 | self._canvas:insertElement({ 73 | type = "text", 74 | frame = { 75 | x = curOffset, 76 | y = fontTopOffset, 77 | w = widget.width, 78 | h = menuHeight 79 | }, 80 | text = widget.object 81 | }) 82 | curOffset = curOffset + widget.width 83 | totalWidth = totalWidth + widget.width 84 | 85 | elseif widget.type == "graph" then 86 | local width = widget.max_len * 2 + 2 87 | self._canvas:insertElement({ 88 | type = "rectangle", 89 | frame = { 90 | x = curOffset, 91 | y = 0, 92 | w = width, 93 | h = menuHeight 94 | }, 95 | fillColor = {red = 0, green = 0, blue = 0}, 96 | action = "fill" 97 | }) 98 | self._canvas:insertElement({ 99 | type = "rectangle", 100 | frame = { 101 | x = curOffset, 102 | y = 0, 103 | w = width, 104 | h = menuHeight 105 | }, 106 | strokeColor = {red = 0.5, green = 0.5, blue = 0.5}, 107 | action = "stroke" 108 | }) 109 | for i = 1, #widget.graph_data do 110 | local item = widget.graph_data[i] 111 | local height = item.val * (menuHeight - 2) 112 | if height < 2 then height = 2 end 113 | if height > menuHeight - 2 then height = menuHeight - 2 end 114 | self._canvas:insertElement({ 115 | type = "rectangle", 116 | frame = { 117 | x = curOffset + 1 + (i - 1) * 2, 118 | y = menuHeight - 1 - height, 119 | w = 2, 120 | h = height 121 | }, 122 | fillColor = item.color, 123 | action = "fill" 124 | }) 125 | end 126 | curOffset = curOffset + width 127 | totalWidth = totalWidth + width 128 | elseif widget.type == "indicator" then 129 | local width = 8 130 | self._canvas:insertElement({ 131 | type = "rectangle", 132 | frame = { 133 | x = curOffset, 134 | y = 0, 135 | w = width, 136 | h = menuHeight 137 | }, 138 | fillColor = {red = 0, green = 0, blue = 0}, 139 | action = "fill" 140 | }) 141 | self._canvas:insertElement({ 142 | type = "rectangle", 143 | frame = { 144 | x = curOffset, 145 | y = 0, 146 | w = width, 147 | h = menuHeight 148 | }, 149 | strokeColor = {red = 0.5, green = 0.5, blue = 0.5}, 150 | action = "stroke" 151 | }) 152 | yOffset = 0 153 | for _, indicator in ipairs(widget.info) do 154 | self._canvas:insertElement({ 155 | type = "rectangle", 156 | frame = { 157 | x = curOffset + 2, 158 | y = yOffset + 2, 159 | w = 4, 160 | h = 3 161 | }, 162 | fillColor = indicator.color, 163 | action = "fill" 164 | }) 165 | yOffset = yOffset + 3 + 2 166 | end 167 | curOffset = curOffset + width 168 | totalWidth = totalWidth + width 169 | end 170 | end 171 | 172 | self._canvas:size({w = totalWidth, h = menuHeight}) 173 | local isMonocolorTemplate = false 174 | self._item:setIcon(self._canvas:imageFromCanvas(), isMonocolorTemplate) 175 | end 176 | 177 | 178 | function menuitem:addText(text) 179 | self:addTextWithWidth(text, nil) 180 | end 181 | 182 | 183 | function menuitem:addTextWithWidth(text, width) 184 | styledText = self._cache.text.objects[text] 185 | if not styledText then 186 | if count(self._cache.text.objects) > maxCacheSize then 187 | self._cache.text.objects = {} 188 | end 189 | styledText = hs.styledtext.new(text, { 190 | font = {name = "Courier", size = fontHeight}, 191 | color = {red = 1, green = 1, blue = 1} 192 | }) 193 | self._cache.text.objects[text] = styledText 194 | end 195 | 196 | size = self._cache.text.sizes[text] 197 | if not size then 198 | if count(self._cache.text.sizes) > maxCacheSize then 199 | self._cache.text.sizes = {} 200 | end 201 | size = hs.drawing.getTextDrawingSize(styledText) 202 | self._cache.text.sizes[text] = size 203 | end 204 | 205 | if not width then 206 | width = math.ceil(size.w) 207 | end 208 | 209 | table.insert(self._widgets, { 210 | type = "text", 211 | text = text, 212 | object = styledText, 213 | width = width 214 | }) 215 | end 216 | 217 | 218 | function menuitem:addGraph(graph_data, max_len) 219 | table.insert(self._widgets, { 220 | type = "graph", 221 | graph_data = graph_data, 222 | max_len = max_len}) 223 | end 224 | 225 | 226 | function menuitem:addIndicator(indicator) 227 | table.insert(self._widgets, { 228 | type = "indicator", 229 | info = indicator}) 230 | end 231 | 232 | 233 | function menuitem:addSpacer(width) 234 | table.insert(self._widgets, {type = "spacer", width = width}) 235 | end 236 | 237 | 238 | function menuitem:addSubmenuItem(title, fn, id) 239 | table.insert(self._submenu, {title = title, fn = fn, id = id}) 240 | self._item:setMenu(self._submenu) 241 | end 242 | 243 | 244 | function menuitem:setSubmenuItemTitle(id, title) 245 | for _, submenu in pairs(self._submenu) do 246 | if submenu.id == id then 247 | submenu.title = title 248 | break 249 | end 250 | end 251 | self._item:setMenu(self._submenu) 252 | end 253 | 254 | 255 | function menuitem:addSubmenuSeparator() 256 | table.insert(self._submenu, {title = "-"}) 257 | self._item:setMenu(self._submenu) 258 | end 259 | 260 | 261 | function menuitem:addSubmenuCheckbox(title, checked, handler) 262 | table.insert(self._submenu, { 263 | title = title, 264 | checked=checked, 265 | fn = function(modifiers, item) 266 | item.checked = not item.checked 267 | self._item:setMenu(self._submenu) 268 | handler(item.checked) 269 | end, 270 | }) 271 | self._item:setMenu(self._submenu) 272 | end 273 | -------------------------------------------------------------------------------- /hammerspoon/test.lua: -------------------------------------------------------------------------------- 1 | function curDirToModuleSearchPath() 2 | local srcFile = debug.getinfo(1).source:match("@?(.*)") 3 | local srcDir = srcFile:match("(.+[/\\])") or "./" 4 | package.path = package.path .. ";" .. srcDir .. "?.lua" 5 | end 6 | 7 | 8 | HsMenubar = {} 9 | function HsMenubar:new() 10 | return setmetatable({ 11 | }, {__index = self}) 12 | end 13 | 14 | 15 | function HsMenubar:setIcon(image, isMonocolorTemplate) 16 | end 17 | 18 | 19 | function HsMenubar:setMenu(submenu) 20 | end 21 | 22 | 23 | HsEchoRequest = {} 24 | function HsEchoRequest:new(addr) 25 | return setmetatable({ 26 | _addr = addr, 27 | _isRunning = false, 28 | }, {__index = self}) 29 | end 30 | 31 | 32 | function HsEchoRequest:setCallback(handler) 33 | if handler then 34 | handler(self, "didStart", "1.1.1.1") 35 | end 36 | end 37 | 38 | 39 | function HsEchoRequest:start() 40 | self._isRunning = true 41 | end 42 | 43 | 44 | function HsEchoRequest:stop() 45 | self._isRunning = false 46 | end 47 | 48 | 49 | function HsEchoRequest:isRunning() 50 | return self._isRunning 51 | end 52 | 53 | 54 | function HsEchoRequest:sendPayload() 55 | end 56 | 57 | 58 | HsEvent = {} 59 | function HsEvent:new() 60 | return setmetatable({ 61 | }, {__index = self}) 62 | end 63 | 64 | 65 | function HsEvent:getProperty(name) 66 | if name == hs.eventtap.event.properties.mouseEventDeltaX then 67 | return 1 68 | elseif name == hs.eventtap.event.properties.mouseEventDeltaY then 69 | return 1 70 | elseif name == hs.eventtap.event.properties.mouseEventButtonNumber then 71 | return 5 72 | else 73 | assert(false) 74 | end 75 | end 76 | 77 | 78 | HsEventtap = {} 79 | function HsEventtap:new(event, handler) 80 | handler(HsEvent:new()) 81 | return setmetatable({ 82 | }, {__index = self}) 83 | end 84 | 85 | 86 | function HsEventtap:start() 87 | end 88 | 89 | 90 | function HsEventtap:stop() 91 | end 92 | 93 | 94 | HsAppSubelement = {} 95 | function HsAppSubelement:new(attributes) 96 | local object = {} 97 | for k, v in pairs(attributes) do 98 | object[k] = v 99 | end 100 | return setmetatable(object, {__index = self}) 101 | end 102 | 103 | 104 | function HsAppSubelement:doAXPress() 105 | end 106 | 107 | 108 | HsAppElement = {} 109 | function HsAppElement:new() 110 | return setmetatable({ 111 | AXChildren = {{ 112 | AXChildren = { 113 | HsAppSubelement:new({ 114 | AXRoleDescription = "application dock item", 115 | AXTitle = "System Preferences", 116 | }), 117 | HsAppSubelement:new({ 118 | AXRoleDescription = "application dock item", 119 | AXTitle = "foo", 120 | }), 121 | } 122 | }}, 123 | }, {__index = self}) 124 | end 125 | 126 | 127 | HsApplication = {} 128 | function HsApplication:new() 129 | return setmetatable({ 130 | }, {__index = self}) 131 | end 132 | 133 | 134 | function HsApplication:bundleID() 135 | return "com.apple.Safari" 136 | end 137 | 138 | 139 | function HsApplication:findMenuItem(name) 140 | return {} 141 | end 142 | 143 | 144 | function HsApplication:selectMenuItem(name) 145 | end 146 | 147 | 148 | HsWindow = {} 149 | function HsWindow:new(screen) 150 | return setmetatable({ 151 | _screen = screen 152 | }, {__index = self}) 153 | end 154 | 155 | 156 | function HsWindow:frame() 157 | return {x = 0, y = 0, w = 100, h = 100} 158 | end 159 | 160 | 161 | function HsWindow:setFrame(frame, duration) end 162 | 163 | 164 | function HsWindow:application() 165 | return HsApplication:new() 166 | end 167 | 168 | 169 | function HsWindow:screen() 170 | return self._screen 171 | end 172 | 173 | 174 | function HsWindow:setTopLeft(x, y) end 175 | 176 | 177 | HsScreen = {} 178 | function HsScreen:new() 179 | return setmetatable({ 180 | }, {__index = self}) 181 | end 182 | 183 | 184 | function HsScreen:frame() 185 | return {x = 0, y = 0, w = 100, h = 100} 186 | end 187 | 188 | 189 | HsTask = {} 190 | function HsTask:new(hs, app, callback, args) 191 | return setmetatable({ 192 | _hs = hs, 193 | _app = app, 194 | _callback = callback, 195 | _args = args 196 | }, {__index = self}) 197 | end 198 | 199 | 200 | function HsTask:start() 201 | self._callback(self._hs._taskMockExitCode, self._hs._taskMockStdOut) 202 | end 203 | 204 | 205 | function HsTask:isRunning() 206 | return false 207 | end 208 | 209 | 210 | HsCanvas = {} 211 | function HsCanvas:new(props) 212 | return setmetatable({ 213 | }, { 214 | _props = props, 215 | __index = self, 216 | __len = function() 217 | return 0 218 | end, 219 | }) 220 | end 221 | 222 | 223 | function HsCanvas:insertElement(props, element) 224 | end 225 | 226 | 227 | function HsCanvas:size(props) 228 | end 229 | 230 | 231 | function HsCanvas:imageFromCanvas() 232 | end 233 | 234 | 235 | Bonjour = {} 236 | function Bonjour:new() 237 | return setmetatable({ 238 | }, { 239 | __index = self, 240 | }) 241 | end 242 | 243 | 244 | function Bonjour:findServices(type, handler) 245 | browserObject = self 246 | domain = nil 247 | isAdvertised = false 248 | serviceObject = { 249 | type = function() 250 | return "_elg._tcp." 251 | end, 252 | name = function() 253 | return "foo" 254 | end, 255 | } 256 | isMoreExpected = false 257 | handler( 258 | browserObject, 259 | domain, 260 | isAdvertised, 261 | serviceObject, 262 | isMoreExpected 263 | ) 264 | end 265 | 266 | 267 | Hs = {} 268 | function Hs:new() 269 | local screen = HsScreen:new() 270 | local this = {} 271 | this._taskMockExitCode = 0 272 | this._taskMockStdOut = "" 273 | this.console = { 274 | clearConsole = function() end, 275 | } 276 | this.canvas = { 277 | new = function(props) 278 | return HsCanvas:new(props) 279 | end, 280 | } 281 | this.menubar = { 282 | new = function() 283 | return HsMenubar:new() 284 | end, 285 | } 286 | this.host = { 287 | cpuUsageTicks = function() 288 | return { 289 | overall = { 290 | active = 0, 291 | idle = 100, 292 | }, 293 | } 294 | end, 295 | } 296 | this.timer = { 297 | absoluteTime = function() 298 | return 0 299 | end, 300 | doEvery = function(interval, handler) end, 301 | usleep = function(interval) end 302 | } 303 | this.axuielement = { 304 | applicationElement = function(app) 305 | return HsAppElement:new() 306 | end, 307 | } 308 | this.hotkey = { 309 | bind = function(modifiers, hotkey, handler) 310 | handler() 311 | end, 312 | } 313 | this.network = { 314 | ping = { 315 | echoRequest = function(addr) 316 | return HsEchoRequest:new(addr) 317 | end, 318 | }, 319 | primaryInterfaces = function() 320 | return nil, nil 321 | end, 322 | } 323 | this.eventtap = { 324 | event = { 325 | types = { 326 | otherMouseDragged = 1, 327 | }, 328 | properties = { 329 | mouseEventDeltaX = 0, 330 | mouseEventDeltaY = 1, 331 | mouseEventButtonNumber = 2, 332 | }, 333 | newScrollEvent = function(event, props, type) end, 334 | }, 335 | new = function(event, handler) 336 | return HsEventtap:new(event, handler) 337 | end, 338 | } 339 | this.window = { 340 | frontmostWindow = function() 341 | return HsWindow:new(screen) 342 | end, 343 | } 344 | this.pasteboard = { 345 | readString = function() 346 | return "" 347 | end, 348 | setContents = function(content) end, 349 | } 350 | this.mouse = { 351 | absolutePosition = function(position) end, 352 | } 353 | this.task = { 354 | _mock = function(exitCode, stdOut) 355 | this._taskMockExitCode = exitCode 356 | this._taskMockStdOut = stdOut 357 | end, 358 | new = function(app, callback, args) 359 | return HsTask:new(this, app, callback, args) 360 | end, 361 | } 362 | this.battery = { 363 | percentage = function() 364 | return 100 365 | end, 366 | isCharging = function() 367 | return true 368 | end, 369 | isCharged = function() 370 | return true 371 | end 372 | } 373 | this.styledtext = { 374 | new = function(text, props) end 375 | } 376 | this.settings = { 377 | get = function(name) end, 378 | set = function(name, val) end, 379 | } 380 | this.application = { 381 | find = function(name) end 382 | } 383 | this.screen = { 384 | primaryScreen = function() 385 | return HsScreen:new() 386 | end, 387 | allScreens = function() 388 | return {HsScreen:new()} 389 | end 390 | } 391 | this.json = { 392 | read = function() 393 | return {} 394 | end 395 | } 396 | this.drawing = { 397 | getTextDrawingSize = function(text) 398 | return {w=1, h=1} 399 | end 400 | } 401 | this.bonjour = { 402 | new = function() 403 | return Bonjour:new() 404 | end 405 | } 406 | return setmetatable(this, {__index = self}) 407 | end 408 | 409 | 410 | function Hs:application(name) 411 | return nil 412 | end 413 | 414 | 415 | curDirToModuleSearchPath() 416 | hs = Hs:new() 417 | require "main" 418 | require "menuitem" 419 | require "helpers" 420 | require "netstat" 421 | require "elgato" 422 | 423 | app = App:new() 424 | assert( 425 | table.concat(app:ipStrToList("192.168.0.1")) == 426 | table.concat({192, 168, 0, 1})) 427 | app:getDockItems() 428 | app:clickDockItemByNum(1) 429 | app:clickDockItemByName("foo") 430 | app:registerMouse() 431 | history = {} 432 | app:icmpPingToHistory(history, "didStart") 433 | app:icmpPingToHistory(history, "didFail", "error") 434 | app:icmpPingToHistory(history, "sendPacket", nil, 1) 435 | assert(#history == 1) 436 | app:icmpPingToHistory(history, "receivedPacket", nil, 1) 437 | app:icmpPingToHistory(history, "receivedUnexpectedPacket", nil) 438 | app:restartInetPingInt() 439 | app:restartRouterPingInt() 440 | app:netGraphFromIcmpHistory(history) 441 | app:cpuGraphFromLoadHistory({1, 2, 3}) 442 | app:onHeartbeat() 443 | app:startTimer() 444 | hs.task._mock(1, "") 445 | netstat:get(function(res) 446 | assert(res == nil) 447 | end) 448 | hs.task._mock(0, 449 | "Routing tables\n\n" .. 450 | "Internet:\n" .. 451 | "Destination Gateway Flags Netif Expire\n" .. 452 | "default link#22 UCSg utun4 \n" .. 453 | "default 192.168.0.1 UGScIg en0 \n" .. 454 | "1.1.1.1 link#22 UHWIig utun4 \n" 455 | ) 456 | netstat:get(function(res) 457 | assert(res.gateway == "192.168.0.1") 458 | end) 459 | -------------------------------------------------------------------------------- /vscode_settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "emmet": { 3 | "includeLanguages": { 4 | "vue-html": "html", 5 | "vue": "html" 6 | }, 7 | "excludeLanguages": [ 8 | "typescript", 9 | "typescriptreact" 10 | ] 11 | }, 12 | "vim.leader": "", 13 | "vim.ignorecase": true, 14 | "vim.textwidth": 78, 15 | "vim.insertModeKeyBindings": [ 16 | { 17 | "before": [ 18 | "" 19 | ], 20 | "after": [], 21 | "commands": [ 22 | { 23 | "command": "editor.action.clipboardPasteAction", 24 | "args": [] 25 | } 26 | ] 27 | } 28 | ], 29 | "vim.visualModeKeyBindings": [ 30 | { 31 | "before": [ 32 | "p" 33 | ], 34 | "after": [ 35 | "P" 36 | ] 37 | } 38 | ], 39 | "vim.normalModeKeyBindingsNonRecursive": [ 40 | { 41 | "before": [ 42 | "n" 43 | ], 44 | "after": [ 45 | "n", 46 | "z", 47 | "z" 48 | ] 49 | }, 50 | { 51 | "before": [ 52 | "N" 53 | ], 54 | "after": [ 55 | "N", 56 | "z", 57 | "z" 58 | ] 59 | }, 60 | { 61 | "before": [ 62 | "Q" 63 | ], 64 | "after": [ 65 | "g", 66 | "q", 67 | "i", 68 | "p" 69 | ] 70 | }, 71 | { 72 | "comment": "delete a character without yanking", 73 | "before": [ 74 | "x" 75 | ], 76 | "after": [ 77 | "\"", 78 | "_", 79 | "x" 80 | ] 81 | }, 82 | { 83 | "comment": "backward delete a character without yanking", 84 | "before": [ 85 | "X" 86 | ], 87 | "after": [ 88 | "\"", 89 | "_", 90 | "X" 91 | ] 92 | } 93 | ], 94 | "vim.otherModesKeyBindingsNonRecursive": [ 95 | { 96 | "before": [ 97 | "Z", 98 | "Z" 99 | ], 100 | "after": [], 101 | "commands": [ 102 | { 103 | "command": "workbench.action.closeActiveEditor", 104 | "args": [] 105 | } 106 | ] 107 | }, 108 | { 109 | "before": [ 110 | "" 111 | ], 112 | "after": [], 113 | "commands": [ 114 | { 115 | "command": "workbench.action.files.openFile", 116 | "args": [] 117 | } 118 | ] 119 | }, 120 | { 121 | "before": [ 122 | "" 123 | ], 124 | "after": [], 125 | "commands": [ 126 | { 127 | "command": "workbench.action.closeActiveEditor", 128 | "args": [] 129 | } 130 | ] 131 | }, 132 | { 133 | "before": [ 134 | "" 135 | ], 136 | "after": [], 137 | "commands": [ 138 | { 139 | "command": "editor.action.openLink", 140 | "args": [] 141 | } 142 | ] 143 | }, 144 | { 145 | "before": [ 146 | "" 147 | ], 148 | "after": [], 149 | "commands": [ 150 | { 151 | "command": "workbench.action.navigateBack", 152 | "args": [] 153 | } 154 | ] 155 | }, 156 | { 157 | "before": [ 158 | "", 159 | "p" 160 | ], 161 | "after": [], 162 | "commands": [ 163 | { 164 | "command": "workbench.action.showCommandPalette", 165 | "args": [] 166 | } 167 | ] 168 | } 169 | ], 170 | "editor.detectIndentation": false, 171 | "editor.insertSpaces": true, 172 | "editor.tabSize": 2, 173 | // Disable 'n references' atop of functions 174 | "editor.codeLens": false, 175 | "editor.lineNumbers": "off", 176 | "editor.links": true, 177 | "editor.minimap.enabled": false, 178 | "editor.autoClosingBrackets": "never", 179 | "editor.autoClosingQuotes": "never", 180 | "editor.autoClosingTags": "never", 181 | "editor.acceptSuggestionOnEnter": "off", 182 | "editor.suggest.matchOnWordStartOnly": false, 183 | "editor.suggestOnTriggerCharacters": false, 184 | "editor.guides.indentation": false, 185 | "editor.guides.bracketPairs": true, 186 | "editor.renderLineHighlight": "line", 187 | "editor.rulers": [ 188 | 78 189 | ], 190 | "editor.glyphMargin": true, 191 | "editor.wordWrap": "off", 192 | "editor.fontFamily": "JetBrainsMono Nerd Font, JetBrainsMono NF, JetBrainsMono", 193 | "editor.semanticHighlighting.enabled": true, 194 | "editor.fontSize": 16, 195 | "editor.fontLigatures": false, 196 | "editor.renderControlCharacters": false, 197 | "editor.cursorStyle": "line", 198 | "editor.wordSeparators": "/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-", 199 | "editor.bracketPairColorization.enabled": false, 200 | // Do not highlight cyrillic letters 201 | "editor.unicodeHighlight.ambiguousCharacters": false, 202 | "editor.guides.bracketPairsHorizontal": true, 203 | "editor.stickyScroll.enabled": true, 204 | "editor.accessibilitySupport": "off", 205 | "editor.formatOnSave": false, 206 | "diffEditor.wordWrap": "off", 207 | "terminal.integrated": { 208 | "fontFamily": "JetBrainsMono Nerd Font, JetBrainsMono NF, JetBrainsMono", 209 | "fontSize": 16, 210 | "commandsToSkipShell": [ 211 | "workbench.action.toggleSidebarVisibility", 212 | "workbench.action.focusSideBar", 213 | "workbench.action.toggleAuxiliaryBar", 214 | "workbench.action.focusAuxiliaryBar", 215 | "workbench.action.focusActiveEditorGroup" 216 | ], 217 | "env.osx": { 218 | "FIG_NEW_SESSION": "1" 219 | }, 220 | // Don't display sticky scroll atop of the terminal since terminal 221 | // vertical space is limited and such are is 2-3 lines, completely 222 | // obscuring actual output 223 | "stickyScroll.enabled": false 224 | }, 225 | "window.menuBarVisibility": "toggle", 226 | "window.openFilesInNewWindow": "off", 227 | "window.openFoldersInNewWindow": "off", 228 | "window.newWindowDimensions": "maximized", 229 | "window.commandCenter": true, 230 | "explorer.confirmDelete": false, 231 | "explorer.confirmDragAndDrop": false, 232 | "explorer.autoReveal": false, 233 | "explorer.openEditors.visible": 0, 234 | "explorer.excludeGitIgnore": true, 235 | "git.confirmSync": false, 236 | "extensions.ignoreRecommendations": false, 237 | "breadcrumbs.enabled": true, 238 | "telemetry.enableTelemetry": false, 239 | "debug.node.autoAttach": "disabled", 240 | "scm.diffDecorations": "none", 241 | "vsicons.presets.foldersAllDefaultIcon": true, 242 | "vsicons.dontShowNewVersionMessage": true, 243 | "keyboard.dispatch": "keyCode", 244 | "notebook.cellToolbarLocation": { 245 | "default": "right", 246 | "jupyter-notebook": "left" 247 | }, 248 | "security.workspace.trust.untrustedFiles": "open", 249 | "workbench.startupEditor": "none", 250 | "workbench.tips.enabled": false, 251 | "workbench.editor.labelFormat": "short", 252 | "workbench.editor.highlightModifiedTabs": true, 253 | "workbench.editorAssociations": { 254 | "*.ipynb": "jupyter-notebook" 255 | }, 256 | "workbench.iconTheme": "vscode-great-icons", 257 | "workbench.colorCustomizations": {}, 258 | "files": { 259 | "eol": "\n", 260 | "associations": { 261 | "*.js": "typescript", 262 | "*.ts": "typescript", 263 | "*.tsx": "typescriptreact", 264 | "*.json": "jsonc" 265 | } 266 | }, 267 | "python.languageServer": "Pylance", 268 | "python.analysis.typeCheckingMode": "basic", 269 | "python.showStartPage": false, 270 | "python.analysis.inlayHints.functionReturnTypes": false, 271 | "python.analysis.inlayHints.variableTypes": false, 272 | "python.terminal.activateEnvironment": false, 273 | "javascript.inlayHints.parameterNames.enabled": "none", 274 | "javascript.inlayHints.variableTypes.enabled": false, 275 | "javascript.inlayHints.parameterTypes.enabled": false, 276 | "typescript.inlayHints.parameterNames.enabled": "none", 277 | "typescript.inlayHints.variableTypes.enabled": false, 278 | "typescript.inlayHints.parameterTypes.enabled": false, 279 | "java.errors.incompleteClasspath.severity": "ignore", 280 | "Lua.telemetry.enable": false, 281 | "dart.warnWhenEditingFilesOutsideWorkspace": false, 282 | "powershell.integratedConsole.showOnStartup": false, 283 | "[python]": { 284 | "editor.tabSize": 4 285 | }, 286 | "[ruby]": { 287 | "editor.formatOnSave": false, 288 | "editor.defaultFormatter": "Shopify.ruby-lsp" 289 | }, 290 | "[javascript]": {}, 291 | "[rust]": { 292 | "editor.tabSize": 4 293 | }, 294 | "[dart]": { 295 | "editor.formatOnSave": false, 296 | "editor.formatOnType": false, 297 | "editor.tabCompletion": "onlySnippets", 298 | "editor.suggestSelection": "first", 299 | "editor.wordBasedSuggestions": "off" 300 | }, 301 | "[typescriptreact]": { 302 | "editor.defaultFormatter": "esbenp.prettier-vscode" 303 | }, 304 | "[json]": { 305 | "editor.defaultFormatter": "esbenp.prettier-vscode" 306 | }, 307 | "[yaml]": { 308 | "editor.defaultFormatter": "esbenp.prettier-vscode" 309 | } 310 | "git.openRepositoryInParentFolders": "never", 311 | "AutoHotkey2": { 312 | "InterpreterPath": "AutoHotkey.exe" 313 | }, 314 | "auto-close-tag.activationOnLanguage": [ 315 | "xml", 316 | "php", 317 | "javascriptreact", 318 | "typescriptreact", 319 | "vue", 320 | "erb", 321 | "html" 322 | ], 323 | "workbench.editor.empty.hint": "hidden", 324 | "xi.debug": false, 325 | "cSpell": { 326 | "checkLimit": 10000, 327 | "minWordLength": 2, 328 | "language": "en,ru", 329 | "flagWords": [ 330 | "аттрибут", 331 | "аттрибута", 332 | "аттрибуте" 333 | ], 334 | "ignoreRegExpList": [ 335 | "\\\\x[0-9a-fA-F]{2}" 336 | ], 337 | "customDictionaries": { 338 | "personal": { 339 | "name": "personal", 340 | "path": "~/dotfiles/dictionary.txt" 341 | } 342 | } 343 | }, 344 | "powershell.powerShellAdditionalExePaths": { 345 | "Shell": "/opt/homebrew/bin/pwsh" 346 | }, 347 | "disable-lcd-text": true, 348 | "workbench.colorTheme": "grigoryvp memory theme", 349 | "cmake.showOptionsMovedNotification": false, 350 | "[jsonc]": { 351 | "editor.defaultFormatter": "vscode.json-language-features", 352 | "editor.insertSpaces": true, 353 | "editor.tabSize": 2 354 | }, 355 | "github.copilot.enable": { 356 | "*": false, 357 | "plaintext": false, 358 | "markdown": false, 359 | "scminput": false 360 | }, 361 | "redhat.telemetry.enabled": false, 362 | "vs-kubernetes": { 363 | "vscode-kubernetes.helm-path-mac": "/Users/user/.vs-kubernetes/tools/helm/darwin-arm64/helm", 364 | "vscode-kubernetes.minikube-path-mac": "/Users/user/.vs-kubernetes/tools/minikube/darwin-arm64/minikube" 365 | }, 366 | "workbench.activityBar.location": "top", 367 | "docker.extension.enableComposeLanguageServer": false, 368 | "[dockercompose]": { 369 | "editor.insertSpaces": true, 370 | "editor.tabSize": 2, 371 | "editor.autoIndent": "advanced", 372 | "editor.quickSuggestions": { 373 | "other": true, 374 | "comments": false, 375 | "strings": true 376 | }, 377 | "editor.defaultFormatter": "redhat.vscode-yaml" 378 | }, 379 | "[github-actions-workflow]": { 380 | "editor.defaultFormatter": "redhat.vscode-yaml" 381 | }, 382 | } -------------------------------------------------------------------------------- /profile.ps1: -------------------------------------------------------------------------------- 1 | try { 2 | # Very slow 3 | # Provides 'Write-Prompt' 4 | # Import-Module posh-git; 5 | } 6 | catch { 7 | } 8 | 9 | $_pathIntrinsics = $ExecutionContext.SessionState.Path; 10 | 11 | function _path([array] $pathList) { 12 | return $_pathIntrinsics.GetUnresolvedProviderPathFromPSPath( 13 | [io.path]::combine([string[]]$pathList)); 14 | } 15 | 16 | # Used for Elixir repl. 17 | Remove-Alias -Force -Name iex 18 | 19 | New-Alias -Name "grep" -Value "Select-String" 20 | 21 | # For git to correctly show unicode files content. 22 | $env:LANG = "en_US.UTF-8"; 23 | # Always install dependencies in .venv for pipenv. 24 | $env:PIPENV_VENV_IN_PROJECT = 1 25 | # Do not lock dependencies (very slow). 26 | $env:PIPENV_SKIP_LOCK = 1 27 | # Disable lockfile generation for pipenv (much faster install). 28 | $env:PIPENV_SKIP_LOCK = 1 29 | # Enable Python 2.7 apps to write into PowerShell console. 30 | $env:PYTHONIOENCODING = "UTF-8" 31 | # Don't collect Next.js telemetry data 32 | $env:NEXT_TELEMETRY_DISABLED = 1 33 | 34 | $COLOR_DGRAY = ([ConsoleColor]::DarkGray); 35 | $COLOR_DYELLOW = ([ConsoleColor]::DarkYellow); 36 | $COLOR_GREEN = ([ConsoleColor]::Green); 37 | $COLOR_BLUE = ([ConsoleColor]::Blue); 38 | $COLOR_MAGENTA = ([ConsoleColor]::Magenta); 39 | 40 | function cdd() { Set-Location ~/Documents; } 41 | function cdc() { Set-Location ~/dotfiles; } 42 | function cdx() { Set-Location ~/.xi; } 43 | function cdh() { Set-Location ~; } 44 | function mcd() { & mkdir $Args[0]; Set-Location $Args[0] } 45 | # Reopen in the existing vscode window 46 | function c() { & code -r $Args } 47 | function g() { & git $Args } 48 | 49 | function gst() { 50 | $dirNameList = Get-ChildItem -Name -Directory; 51 | foreach ($dirName in $dirNameList) { 52 | if (!(Test-Path "$dirName\.git")) { 53 | Write-Host "not a git repo; " -NoNewline -ForegroundColor DarkCyan; 54 | Write-Host $dirName; 55 | continue; 56 | } 57 | Set-Location $dirName; 58 | $ret = & git status; 59 | $UP_MARKER = "Your branch is up to date with"; 60 | $CLEAN_MARKER = "nothing to commit, working tree clean"; 61 | if ($ret[1].Contains($UP_MARKER)) { 62 | Write-Host "up to date; " -NoNewline; 63 | } 64 | else { 65 | Write-Host "out of sync" -NoNewline -ForegroundColor Green; 66 | Write-Host "; " -NoNewline; 67 | } 68 | if ($ret[3].Contains($CLEAN_MARKER) -or $ret[4].Contains($CLEAN_MARKER)) { 69 | Write-Host "clean; " -NoNewline; 70 | } 71 | else { 72 | Write-Host "working tree changes" -NoNewline -ForegroundColor Red; 73 | Write-Host "; " -NoNewline; 74 | } 75 | Write-Host $dirName; 76 | Set-Location ..; 77 | } 78 | } 79 | 80 | function ahk() { 81 | # Restart if already running 82 | # if (Get-Process "AutoHotkey" -ErrorAction SilentlyContinue) { return; } 83 | Start-Process ` 84 | autohotkey.exe ` 85 | -ArgumentList "$($env:USERPROFILE)\dotfiles\keyboard.ahk" ` 86 | -WindowStyle Hidden ` 87 | -Verb RunAs; 88 | } 89 | 90 | function camera() { 91 | uvcc set auto_focus 0 92 | uvcc set absolute_focus 0 93 | uvcc set auto_white_balance_temperature 0 94 | uvcc set white_balance_temperature 6000 95 | uvcc set auto_exposure_mode 1 96 | uvcc set absolute_exposure_time 400 97 | uvcc set gain 20 98 | } 99 | 100 | # ============================================================================ 101 | # Windows-OSX-Linux consistency 102 | # ============================================================================ 103 | 104 | function rmf($dst) { 105 | Remove-Item $dst -Recurse -Force -ErrorAction SilentlyContinue 106 | } 107 | 108 | function ll($dst) { 109 | lsd -l $dst 110 | } 111 | 112 | function vec() { 113 | python3 -m virtualenv .venv 114 | } 115 | 116 | function vea() { 117 | .\.venv\Scripts\activate.ps1 118 | } 119 | 120 | function ved() { 121 | deactivate 122 | } 123 | 124 | function ver() { 125 | Remove-Item .venv -Recurse -Force -ErrorAction SilentlyContinue 126 | } 127 | 128 | function uvc() { 129 | uv init 130 | } 131 | 132 | function uvi() { 133 | uv sync 134 | } 135 | 136 | function uva() { 137 | uv add $Args 138 | } 139 | 140 | function uvr() { 141 | uv run $Args 142 | } 143 | 144 | function uvp() { 145 | uv run python $Args 146 | } 147 | 148 | function uvm() { 149 | uv run python manage.py $Args 150 | } 151 | 152 | function uvs() { 153 | uv run python manage.py runserver $Args 154 | } 155 | 156 | # ============================================================================ 157 | # Tools 158 | # ============================================================================ 159 | 160 | function Update-VscodeExt() { 161 | $cfgFileName = "package.json"; 162 | if (-not (Test-Path $cfgFileName)) { 163 | Write-Error "$cfgFileName not found"; 164 | return; 165 | } 166 | $cfg = Get-Content $cfgFileName | ConvertFrom-Json; 167 | 168 | $name = $cfg.name; 169 | if (-not $name) { 170 | Write-Error "'name' property not found in the $cfgFileName"; 171 | return; 172 | } 173 | $publisher = $cfg.publisher; 174 | if (-not $publisher) { 175 | Write-Error "'publisher' property not found in the $cfgFileName"; 176 | return; 177 | } 178 | $version = $cfg.version; 179 | if (-not $version) { 180 | Write-Error "'version' property not found in the $cfgFileName"; 181 | return; 182 | } 183 | 184 | $verPartList = $version.split("."); 185 | 186 | $extRoot = "$env:USERPROFILE\.vscode\extensions" 187 | $extDir = ""; 188 | # Try current version and all older build semver since installed 189 | # extension is often older than the development one. 190 | for ($i = [int]$verPartList[-1]; $i -ge 0; $i --) { 191 | $verPartList[-1] = $i 192 | $curVer = [System.String]::Join(".", $verPartList); 193 | $extDir = "$extRoot\$publisher.$name-$curVer"; 194 | if (Test-Path $extDir) { 195 | break; 196 | } 197 | } 198 | if (-not (Test-Path $extDir)) { 199 | Write-Error "'$extRoot\$publisher.$name-$version...0' dir not found"; 200 | return; 201 | } 202 | 203 | if (-not (Test-Path -Path $extDir/src)) { 204 | New-Item -Path $extDir/src -ItemType Directory | Out-Null; 205 | } 206 | Copy-Item *.js $extDir; 207 | Copy-Item *.json $extDir; 208 | Write-Output "Copied into $extDir"; 209 | if (Test-Path -Path src) { 210 | Copy-Item src/*.js $extDir/src; 211 | Copy-Item src/*.json $extDir/src; 212 | Write-Output "Copied ./src into $extDir/src"; 213 | } 214 | } 215 | 216 | function Start-Srv() { 217 | $name = "srv"; 218 | $job = Get-Job -Name $name -ErrorAction SilentlyContinue; 219 | if ($job) { 220 | Write-Host "Already started"; 221 | return; 222 | } 223 | $job = { 224 | Set-Location $Args[0]; 225 | $driveName = 'site'; 226 | $_Args = @{ 227 | Name = $driveName 228 | PSProvider = 'FileSystem' 229 | Root = $PWD.Path 230 | }; 231 | New-PSDrive @_Args; 232 | $listener = New-Object System.Net.HttpListener; 233 | $listener.Prefixes.Add("http://localhost:8080/"); 234 | $listener.Start(); 235 | while($listener.IsListening) { 236 | $context = $listener.GetContext(); 237 | $url = $Context.Request.Url.LocalPath; 238 | if ($url -eq '/favicon.ico') { 239 | $Context.Response.Close(); 240 | continue; 241 | } 242 | if ($url -eq '/_stop') { 243 | $listener.Stop(); 244 | Remove-PSDrive -Name $driveName; 245 | return; 246 | } 247 | $ext = [System.IO.Path]::GetExtension($url); 248 | $context.Response.ContentType = @{ 249 | '.htm' = 'text/html' 250 | '.html' = 'text/html' 251 | '.css' = 'text/css' 252 | '.svg' = 'image/svg+xml' 253 | '.png' = 'image/png' 254 | '.jpg' = 'image/jpeg' 255 | '.jepg' = 'image/jpeg' 256 | }[$ext]; 257 | try { 258 | $data = Get-Content -AsByteStream -Path "$($driveName):$url"; 259 | $context.Response.OutputStream.Write($data, 0, $data.Length); 260 | } 261 | catch { 262 | $context.Response.StatusCode = 404; 263 | } 264 | $Context.Response.Close(); 265 | } 266 | }; 267 | $job = Start-Job -Name $name -ArgumentList $PWD -ScriptBlock $job; 268 | Write-Host "Server job started"; 269 | } 270 | 271 | function Stop-Srv() { 272 | $name = "srv"; 273 | $job = Get-Job -Name $name -ErrorAction SilentlyContinue; 274 | if ($job) { 275 | try { 276 | Invoke-WebRequest -Uri 'http://localhost:8080/_stop'; 277 | } 278 | catch { 279 | } 280 | Stop-Job -Name $name; 281 | Remove-Job -Name $name; 282 | Write-Host "Server job stopped"; 283 | } 284 | else { 285 | Write-Host "No server job found"; 286 | } 287 | } 288 | 289 | $promptMsg = $null; 290 | 291 | function Add-PromptMsg($msg) { 292 | Set-Variable -Name "promptMsg" -Value $msg -Scope Global; 293 | [Microsoft.PowerShell.PSConsoleReadLine]::InvokePrompt(); 294 | $timer = New-Object System.Timers.Timer 295 | $timer.AutoReset = $false 296 | $timer.Interval = 1000 297 | Register-ObjectEvent -InputObject $timer -EventName Elapsed -Action { 298 | Set-Variable -Name "promptMsg" -Value $null -Scope Global; 299 | [Microsoft.PowerShell.PSConsoleReadLine]::InvokePrompt(); 300 | } 301 | $timer.Enabled = $true 302 | } 303 | 304 | # Without this, returns cleared screen after any input. 305 | Set-PSReadlineKeyHandler -Key Ctrl+l -ScriptBlock { 306 | [Microsoft.PowerShell.PSConsoleReadLine]::ClearScreen(); 307 | } 308 | 309 | Set-PSReadlineKeyHandler -Key Ctrl+d -ScriptBlock { 310 | Add-PromptMsg "dbg"; 311 | } 312 | 313 | # Very slow 314 | # if (Get-Command "conda.exe" -ErrorAction SilentlyContinue) { 315 | # (& conda.exe shell.powershell hook) | Out-String | Invoke-Expression 316 | # } 317 | 318 | # After conda, which tries to replace prompt. 319 | function prompt { 320 | Write-Host "[" -NoNewLine -ForegroundColor $COLOR_DGRAY; 321 | if ($promptMsg) { 322 | Write-Host $promptMsg -NoNewLine -ForegroundColor $COLOR_GREEN; 323 | } 324 | else { 325 | Write-Host "..." -NoNewLine -ForegroundColor $COLOR_DYELLOW; 326 | } 327 | Write-Host "] " -NoNewLine -ForegroundColor $COLOR_DGRAY; 328 | if ($env:USERPROFILE) { 329 | $location = $(Get-Location).ToString().Replace($env:USERPROFILE, "~"); 330 | } 331 | else { 332 | $location = $(Get-Location).ToString().Replace($env:HOME, "~"); 333 | } 334 | $locationItemList = $location.Replace("\", "/").Split("/"); 335 | foreach ($locationItem in $locationItemList) { 336 | Write-Host $locationItem -NoNewLine -ForegroundColor $COLOR_MAGENTA; 337 | Write-Host "/" -NoNewLine -ForegroundColor $COLOR_BLUE; 338 | } 339 | Write-Host " $" -NoNewLine -ForegroundColor $COLOR_DGRAY; 340 | # Return something to replace default prompt. 341 | return " "; 342 | } 343 | 344 | if ($IsMacOS) { 345 | # Homebrew will refuse to link Ruby 2.7 over os-provided 2.3 version. 346 | $Env:PATH = "/usr/local/opt/ruby/bin:$Env:PATH"; 347 | # Homebrew can --link Python, but modify path for consistancy with Ruby. 348 | $Env:PATH = "/usr/local/opt/python@3.8/bin:$Env:PATH"; 349 | } 350 | if ($IsWindows) { 351 | # Not added by winget installed to path 352 | $path = _path(@("~", "apps", "AutoHotkey.AutoHotkey", "v2")); 353 | $Env:PATH = "$Env:PATH;$path"; 354 | # Installed by cloning from git 355 | $path = _path(@("~", ".pyenv", "pyenv-win", "bin")); 356 | $Env:PATH = "$Env:PATH;$path"; 357 | } 358 | -------------------------------------------------------------------------------- /shell-cfg.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # coding:utf-8 vi:et:ts=2 3 | 4 | # Some tools like VSCode tend to spawn subshells like "zsh -c -l". On macOS 5 | # zsh will source /etc/zprofile which runs /usr/libexec/path_helper and 6 | # REORDERS $PATH, moving "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin" 7 | # in the beginning. For pyenv/rbenv/etc to work correctly they should be 8 | # in $PATH before default path, so we need to also reorder for each 9 | # invocation of zsh and each execution of this script. 10 | start_path_with() { 11 | if ! [ -e $1 ]; then 12 | return 13 | fi 14 | NEW_PATH="" 15 | for PATH_LINE in $(echo $PATH | tr ":" "\n"); do 16 | if [ "$PATH_LINE" != "$1" ]; then 17 | if [ -n "$NEW_PATH" ]; then 18 | NEW_PATH=$NEW_PATH:$PATH_LINE 19 | else 20 | NEW_PATH=$PATH_LINE 21 | fi 22 | fi 23 | done 24 | PATH=$1:$NEW_PATH 25 | unset NEW_PATH 26 | } 27 | 28 | ## bash/zsh portable way to encode "Escape" character. 29 | PS_ESC=$(printf '\033') 30 | ## Don't show git in PS by default, it conflicts with the "g" alias. 31 | PS_GIT_ON= 32 | 33 | if [ -n "$ZSH_VERSION" ]; then 34 | ## Colors should be escaped for correct length calculation. 35 | PS_K="%{${PS_ESC}[30;47m%}" 36 | PS_W="%{${PS_ESC}[37;49m%}" 37 | PS_R="%{${PS_ESC}[31;49m%}" 38 | PS_G="%{${PS_ESC}[32;49m%}" 39 | PS_B="%{${PS_ESC}[34;49m%}" 40 | PS_Y="%{${PS_ESC}[33;49m%}" 41 | PS_M="%{${PS_ESC}[35;49m%}" 42 | PS_C="%{${PS_ESC}[36;49m%}" 43 | PS_N="%{${PS_ESC}[0m%}" 44 | PS_WORKDIR="%~" 45 | PS_DOLLAR="$" 46 | ## Substring re-interpolation. 47 | setopt promptsubst 48 | ## Do not display "no matches found" error for blobs 49 | setopt +o nomatch 50 | ## Always complete files, even if app-specific completion don't says so 51 | zstyle ':completion:*' completer _complete _ignored _files 52 | elif [ -n "$BASH_VERSION" ]; then 53 | ## Colors should be escaped for correct length calculation. 54 | PS_K="\\[${PS_ESC}[30;47m\\]" 55 | PS_W="\\[${PS_ESC}[37;49m\\]" 56 | PS_R="\\[${PS_ESC}[31;49m\\]" 57 | PS_G="\\[${PS_ESC}[32;49m\\]" 58 | PS_B="\\[${PS_ESC}[34;49m\\]" 59 | PS_Y="\\[${PS_ESC}[33;49m\\]" 60 | PS_M="\\[${PS_ESC}[35;49m\\]" 61 | PS_C="\\[${PS_ESC}[36;49m\\]" 62 | PS_N="\\[${PS_ESC}[0m\\]" 63 | PS_WORKDIR="\\W" 64 | PS_DOLLAR="\\\$" 65 | else 66 | echo "Unsupported shell" 67 | fi 68 | 69 | ## Disable terminal/ssh freeze with C-S: 70 | stty -ixon 71 | ## Don't create |.pyc| files while executing python code from console. 72 | export PYTHONDONTWRITEBYTECODE=1 73 | ## Dont' collect Next.js telemetry data 74 | export NEXT_TELEMETRY_DISABLED=1 75 | 76 | ## Rust 'install' places binaries here 77 | start_path_with ~/.cargo/bin 78 | ## pipx installs binaries here 79 | start_path_with ~/.local/bin 80 | ## For custom nodejs build (ubuntu have old one in repository) 81 | start_path_with ~/.local/nodejs/bin 82 | ## git can clone from repos without certificates. 83 | export GIT_SSL_NO_VERIFY=true 84 | ## 256-colors in terminal for apps that knows how to use it. 85 | export TERM=xterm-256color 86 | ## Used by apps to launch text editor. 87 | export EDITOR=vim 88 | ## Required for VIM, otherwise it will start creating dirs names '$TMP'. 89 | if [ -z $TMP ]; then 90 | export TMP=~/tmp 91 | fi 92 | if [ -z $TEMP ]; then 93 | export TEMP=~/tmp 94 | fi 95 | ## gnome-ssh-askpass don't work. 96 | unset SSH_ASKPASS 97 | ## make less display ASCII colors, quit if one screen and don't clear 98 | ## screen after it quits. 99 | export LESS="-R -F -X" 100 | ## Don't display lein root warning while using in docker 101 | export LEIN_ROOT=true 102 | 103 | ## For docker containers where they are not set 104 | export LANG="en_US.UTF-8" 105 | export LANGUAGE="en_US:en" 106 | export LC_ALL="en_US.UTF-8" 107 | 108 | ## Switchable truecolor for VIM, on by default 109 | export TRUECOLOR="1" 110 | 111 | ## Always install dependencies in .venv for poetry. 112 | export POETRY_VIRTUALENVS_IN_PROJECT="1" 113 | ## Always install dependencies in .venv for pipenv. 114 | export PIPENV_VENV_IN_PROJECT="1" 115 | ## Do not lock dependencies (very slow). 116 | export PIPENV_SKIP_LOCK="1" 117 | 118 | ## Caprover default branch 119 | export CAPROVER_DEFAULT_BRANCH=main 120 | 121 | ## macOS? 122 | if [ "$(uname)" = "Darwin" ]; then 123 | ## Add color to |ls| output 124 | export CLICOLOR=1 125 | ## Better 'ls' output colors. 126 | export LSCOLORS=Exfxcxdxbxegedabagacad 127 | ## For django 'syncdb' command to work. 128 | export LC_ALL=en_US.UTF-8 129 | 130 | ## custom svn 131 | start_path_with /opt/subversion/bin 132 | 133 | ## MacOS Apple Silicon homebrew installed? 134 | if [ -e /opt/homebrew/bin/brew ]; then 135 | eval $(/opt/homebrew/bin/brew shellenv) 136 | start_path_with "/opt/homebrew/bin" 137 | start_path_with "/opt/homebrew/sbin" 138 | fi 139 | 140 | ## custom mongo installed? 141 | MONGOAPP=~/Applications/MongoDB.app 142 | MONGOBINDIR=$MONGOAPP/Contents/Resources/Vendor/mongodb 143 | if [ -e $MONGOBINDIR/mongo ]; then 144 | alias mongo=$MONGOBINDIR/mongo 145 | alias mongodump=$MONGOBINDIR/mongodump 146 | alias mongorestore=$MONGOBINDIR/mongorestore 147 | fi 148 | 149 | ## Will write to stderr if Java is not installed 150 | export JAVA_HOME=$(/usr/libexec/java_home 2>/dev/null) 151 | ## brew install android-commandlinetools 152 | export ANDROID_HOME=/opt/homebrew/share/android-commandlinetools 153 | ## Installed here by 'brew install michaeldfallen/formula/git-radar' 154 | if [ "$SHELL" = "/bin/zsh" ]; then 155 | export RADAR_CMD='$(git-radar --zsh --fetch)' 156 | else 157 | export RADAR_CMD='$(git-radar --bash --fetch)' 158 | fi 159 | 160 | BREW_DIR="/opt/homebrew/share" 161 | ZSH_PLUGIN="zsh-autosuggestions" 162 | if [ -e "$BREW_DIR/$ZSH_PLUGIN/$ZSH_PLUGIN.zsh" ]; then 163 | . "$BREW_DIR/$ZSH_PLUGIN/$ZSH_PLUGIN.zsh" 164 | fi 165 | ZSH_PLUGIN="zsh-syntax-highlighting" 166 | if [ -e "$BREW_DIR/$ZSH_PLUGIN/$ZSH_PLUGIN.zsh" ]; then 167 | . "$BREW_DIR/$ZSH_PLUGIN/$ZSH_PLUGIN.zsh" 168 | fi 169 | 170 | camera() { 171 | # Front camera 172 | echo "\nFRONT" 173 | uvc-util -V 0x046d:0x0893 -g brightness 174 | uvc-util -V 0x046d:0x0893 -g sharpness 175 | uvc-util -V 0x046d:0x0893 -g contrast 176 | uvc-util -V 0x046d:0x0893 -g saturation 177 | uvc-util -V 0x046d:0x0893 -s auto-focus=false 178 | #./uvc-util -L 0x00240000 -g auto-focus 179 | uvc-util -V 0x046d:0x0893 -s focus-abs=0 180 | #./uvc-util -L 0x00240000 -g focus-abs 181 | uvc-util -V 0x046d:0x0893 -s auto-white-balance-temp=false 182 | #./uvc-util -L 0x00240000 -g auto-white-balance-temp 183 | uvc-util -V 0x046d:0x0893 -s white-balance-temp=6000 184 | #./uvc-util -L 0x00240000 -g white-balance-temp 185 | uvc-util -V 0x046d:0x0893 -s auto-exposure-mode=1 186 | #./uvc-util -L 0x00240000 -g auto-exposure-mode 187 | uvc-util -V 0x046d:0x0893 -s exposure-time-abs=400 188 | #./uvc-util -L 0x00240000 -g exposure-time-abs 189 | uvc-util -V 0x046d:0x0893 -s gain=20 190 | #./uvc-util -L 0x00240000 -g gain 191 | uvc-util -V 0x046d:0x0893 -g backlight-compensation 192 | uvc-util -V 0x046d:0x0893 -g zoom-abs 193 | uvc-util -V 0x046d:0x0893 -g power-line-frequency 194 | uvc-util -V 0x046d:0x0893 -g auto-exposure-priority 195 | uvc-util -V 0x046d:0x0893 -g roll-abs 196 | } 197 | 198 | else 199 | ## Remap caps lock to backspace. 200 | # gsettings set org.gnome.desktop.input-sources xkb-options "['caps:backspace']" 201 | 202 | ##FIXME: Seems not persisting on Ubuntu, need to check why. 203 | # setterm -background black -foreground white 204 | 205 | ## GTK_IM_MODULE is set to 'xim' in ubuntu, lots of GTK errors in chrome. 206 | ## Disable disk cache so multiple chrome instances will not kill HDD. 207 | CHROME_BIN=/opt/google/chrome/chrome 208 | alias chrome='GTK_IM_MODULE="" $CHROME_BIN --disk-cache-size=10000000' 209 | 210 | ## android studio 211 | start_path_with ~/.local/android-studio/bin 212 | 213 | if [ -e /usr/java/latest ]; then 214 | ## Official SDK symlinks this to lates install. 215 | export JAVA_HOME=/usr/java/latest 216 | fi 217 | 218 | export RADAR_CMD='$(~/.git-radar/git-radar --bash --fetch)' 219 | fi 220 | 221 | if [ -e ~/.rvm/scripts/rvm ]; then 222 | source ~/.rvm/scripts/rvm 223 | fi 224 | 225 | ## git aliases 226 | alias g=git 227 | 228 | ## kubernetes aliases 229 | alias k=kubectl 230 | 231 | ## svn aliases 232 | alias svl='svn log' 233 | alias svs='svn stat' 234 | alias svc='svn commit -m' 235 | svd() { 236 | svn diff --diff-cmd colordiff $@ | less -R 237 | } 238 | 239 | ## "HOST HOME" for Windows "home" on WSL 240 | if [ -e /proc/sys/fs/binfmt_misc/WSLInterop ]; then 241 | HHOME=$(wslpath $(cmd.exe /C "echo %USERPROFILE%" 2>/dev/null)) 242 | ## Remove '\r' from the end of the 'cmd.exe' output 243 | export HHOME=$(echo $HHOME | tr -d '\r') 244 | else 245 | export HHOME=~ 246 | fi 247 | 248 | ## cd aliases for wsl-mac-nix consistency 249 | alias cdh="cd ${HOME}" 250 | alias cdd="cd ${HOME}/Documents" 251 | alias cdx="cd ${HOME}/.xi" 252 | alias cdc="cd ${HHOME}/dotfiles" 253 | alias cdhh="cd ${HHOME}" 254 | 255 | ## Python virtual environment; built-in 'venv' instals old bundled 'pip'. 256 | alias vec="python3 -m virtualenv .venv" 257 | alias vea=". .venv/bin/activate" 258 | alias ved="deactivate" 259 | alias ver="rm -rf .venv" 260 | 261 | ## Rails virtual environment 262 | alias buc="bundle init" 263 | alias bui="bundle install" 264 | alias bua="bundle add" 265 | alias buad="bundle add --group development test" 266 | alias bue="bundle exec" 267 | alias bur="bue ruby" 268 | alias buk="bue rake" 269 | alias bus="bue rails" 270 | busg() { 271 | bus generate $@ --no-helper --no-test-framework --no-template-engine 272 | } 273 | alias busgc="busg controller" 274 | alias busgm="busg model" 275 | 276 | # Old Python virtual environment with poetry 277 | alias poc="poetry init --no-interaction" 278 | alias poi="poetry install" 279 | alias poa="poetry add" 280 | alias por="poetry run" 281 | alias pop="poetry run python3" 282 | alias pom="poetry run python3 manage.py" 283 | alias pos="poetry run python3 manage.py runserver" 284 | 285 | # Python virtual environment 286 | alias uvc="uv init" 287 | alias uvi="uv sync" 288 | alias uva="uv add" 289 | alias uvr="uv run" 290 | alias uvp="uv run python" 291 | alias uvm="uvr manage.py" 292 | alias uvs="uvr manage.py runserver" 293 | 294 | # Hammerspoon 295 | alias hsr="hs -c 'hs.reload()'" 296 | 297 | # Reopen in the existing vscode window 298 | alias c="code -r" 299 | 300 | buss() { 301 | if [ -e ./bin/dev ]; then 302 | ./bin/dev 303 | else 304 | bundle exec rails server 305 | fi 306 | } 307 | 308 | ## docker aliases 309 | dmg() { 310 | if [ -z "$1" ]; then 311 | docker-machine env -u >~/tmp/docker-machine-env 312 | else 313 | docker-machine env $1 >~/tmp/docker-machine-env 314 | fi 315 | eval $(cat ~/tmp/docker-machine-env) 316 | } 317 | di() { 318 | echo -n '{{.Name}}' > ~/tmp/di-format 319 | echo -n ' on {{.OperatingSystem}}' >> ~/tmp/di-format 320 | echo -n ' running docker {{.ServerVersion}}' >> ~/tmp/di-format 321 | docker info -f "$(cat ~/tmp/di-format)" 322 | } 323 | alias dsl='docker service ls' 324 | alias dsi='docker service inspect --pretty' 325 | 326 | # Always run tox in quiet mode, it spams a lot of useless info by default. 327 | alias tox='tox -q' 328 | 329 | # For windows consistensy 330 | alias rmf='rm -rf' 331 | 332 | my_list() { 333 | if [ -e /opt/homebrew/bin/lsd ]; then 334 | /opt/homebrew/bin/lsd "$@" 335 | elif [ -e /usr/local/bin/eza ]; then 336 | /usr/local/bin/exa \ 337 | -l \ 338 | -a \ 339 | --group-directories-first \ 340 | --ignore-glob .DS_Store\|desktop.ini\|PowerShell\|My\ Games \ 341 | "$@" 342 | else 343 | ##! No spaces, '*' should be used instead. 344 | IGNORED=( \ 345 | "WindowsPowerShell" \ 346 | "PowerShell" \ 347 | "desktop.ini" \ 348 | ".vscode" \ 349 | ) 350 | CMD_IGNORE="" 351 | IGNORED_COUNT=0 352 | ## OSX does not support advanced keys 353 | if [ "$(uname)" != "Darwin" ]; then 354 | for CUR in "${IGNORED[@]}"; do 355 | ##! No quotes to match things like "Diablo*" 356 | if [ -e ${CUR} ]; then 357 | ((IGNORED_COUNT++)) 358 | fi 359 | CMD_IGNORE="${CMD_IGNORE} -I ${CUR}" 360 | done 361 | LS_ARG_COLOR="--color" 362 | LS_ARG_GROUP="--group-directories-first" 363 | fi 364 | ## Due to LESS options this will pass-through on one screen. 365 | ## -l: One column by default, as in powershell. 366 | #3 -h: Human-readable sizes. 367 | ## -A: Show all files except special '.' and '..'. 368 | ## -F: Append indicators. 369 | ## --color: Color output for linux (CLICOLOR for OSX). 370 | ## --group-directories-first: Show dirs first. 371 | ## -I: Ignore files. 372 | ## "$@": quote args so "ll Visual\ Studio\ 2015" will work. 373 | ls \ 374 | -l \ 375 | -h \ 376 | -A \ 377 | -F \ 378 | ${LS_ARG_COLOR} \ 379 | ${LS_ARG_GROUP} \ 380 | ${CMD_IGNORE} \ 381 | "$@" \ 382 | | less 383 | if ((IGNORED_COUNT > 0)); then 384 | echo -e "${R}${IGNORED_COUNT} items ignored${N}" 385 | fi 386 | fi 387 | } 388 | alias ll=my_list 389 | alias ff=ag 390 | 391 | _prompt_command() { 392 | PS_EXIT="$?" 393 | export PS1="${PS_N}${PS_W}${PS_WORKDIR} ${PS_N}" 394 | if [ -n "$PS_GIT_ON" ]; then 395 | if [ -d ~/.git-radar ] || which git-radar >/dev/null; then 396 | #! Spaces before optional changes and before next prompt part. 397 | export GIT_RADAR_FORMAT="git:%{branch}%{local}%{ :changes} " 398 | export PS1="${PS1}${PS_G}${RADAR_CMD}${PS_N}" 399 | fi 400 | fi 401 | if [ -n "${DOCKER_MACHINE_NAME}" ]; then 402 | export PS1="${PS1}${PS_M}{${DOCKER_MACHINE_NAME}} ${PS_N}" 403 | fi 404 | if [ -n "${VIRTUAL_ENV}" ]; then 405 | export PS1="${PS1}🐍 " 406 | fi 407 | export PS1="${PS1}(${PS_C}${PS_EXIT}${PS_N}) " 408 | export PS1="${PS1}${PS_Y}${PS_DOLLAR} ${PS_N}" 409 | } 410 | export PROMPT_COMMAND=_prompt_command 411 | # ZSH alternative to "PROMPT_COMMAND" 412 | precmd() { 413 | eval "$PROMPT_COMMAND" 414 | } 415 | 416 | psgiton() { 417 | export PS_GIT_ON=1 418 | psupdate 419 | } 420 | 421 | psgitoff() { 422 | export PS_GIT_ON= 423 | psupdate 424 | } 425 | 426 | freqdown() { 427 | sudo cpupower frequency-set --min 2.4Ghz --max 2.4ghz 428 | } 429 | 430 | ## Simple HTTP erver in current dir. 431 | srv() { 432 | if [ _"$1" != _"" ] && [ _"$1" != _"-s" ]; then 433 | if echo $@ | grep -- -s > /dev/null; then 434 | (cd $1 && php -S 0.0.0.0:80 >/dev/null 2>/dev/null) 435 | else 436 | (cd $1 && php -S 0.0.0.0:80) 437 | fi 438 | else 439 | if echo $@ | grep -- -s > /dev/null; then 440 | php -S 0.0.0.0:80 >/dev/null 2>/dev/null 441 | else 442 | php -S 0.0.0.0:80 443 | fi 444 | fi 445 | } 446 | 447 | pp() { 448 | ping -i 0.2 1.1.1.1 449 | } 450 | 451 | mcd() { 452 | mkdir "$1" 453 | cd "$1" 454 | } 455 | 456 | ## For tools installed via "go get" to be on path 457 | if which go > /dev/null; then 458 | start_path_with $(go env GOPATH)/bin 459 | fi 460 | 461 | ## Load eye, if installed 462 | if [ -d $HOME/.rye ]; then 463 | source "$HOME/.rye/env" 464 | fi 465 | 466 | ## Load nodenv, if installed 467 | if [ -d $HOME/.nodenv ]; then 468 | start_path_with "$HOME/.nodenv/bin" 469 | start_path_with "$HOME/.nodenv/shims" 470 | eval "$(nodenv init -)" 471 | fi 472 | 473 | ## Load phpenv, if installed 474 | if [ -d $HOME/.phpenv ]; then 475 | start_path_with "$HOME/.phpenv/bin" 476 | start_path_with "$HOME/.phpenv/shims" 477 | fi 478 | 479 | ## Load swiftenv, if installed 480 | if [ -d $HOME/.swiftenv ]; then 481 | start_path_with "$HOME/.swiftenv/bin" 482 | fi 483 | 484 | ## Load opam, if installed 485 | if [ -d $HOME/.opam ]; then 486 | eval "$(opam env)" 487 | fi 488 | -------------------------------------------------------------------------------- /configure_macos.sh: -------------------------------------------------------------------------------- 1 | open_port() { 2 | local port=$1 3 | nc -l localhost $port >/dev/null 2>&1 & 4 | local pid=$! 5 | for i in {1..20}; do 6 | if nc -z localhost $port >/dev/null 2>&1; then 7 | echo $pid 8 | return 0 9 | fi 10 | sleep 0.05 11 | done 12 | kill $pid >/dev/null 2>&1 13 | return 1 14 | } 15 | 16 | _configure_wox() { 17 | pid=$(open_port 12345) 18 | kill $pid >/dev/null 2>&1 19 | } 20 | 21 | test() { 22 | _configure_wox 23 | } 24 | 25 | configure() { 26 | if ! [ -e ~/.ssh/id_rsa.pub ]; then 27 | ssh-keygen -t rsa -f "$HOME/.ssh/id_rsa" -N "" 28 | fi 29 | if ! [ -e ~/.ssh/known_hosts ]; then 30 | # Allows git clone without fingerprint confirmation 31 | ssh-keyscan github.com >> ~/.ssh/known_hosts 32 | fi 33 | # For Apple Silicon 34 | softwareupdate --install-rosetta --agree-to-license 35 | # XCode command-line tools 36 | xcode-select --install 37 | echo "Wait for the xcode-select GUI installer and press enter" 38 | read -s 39 | if [ -e /opt/homebrew/bin/brew ]; then 40 | echo "Homebrew already installed" 41 | else 42 | # This will require sudo access and waits for confirmation 43 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 44 | fi 45 | # Add homebrew to path for the rest of the script 46 | eval "$(/opt/homebrew/bin/brew shellenv)" 47 | 48 | # Group settings that require sudo together 49 | 50 | # Disable spotlight for better battery and SSD life: 51 | sudo mdutil -a -i off 52 | # Tends to hang with 100% cpu load 53 | launchctl unload -w /System/Library/LaunchAgents/com.apple.ReportCrash.plist 2>/dev/null 54 | # Time zone from "sudo systemsetup -listtimezones" 55 | #! This crashes AFTER setting time zone, this is normal 56 | sudo systemsetup -settimezone "Europe/Amsterdam" 2>/dev/null 57 | # Wake on lid open 58 | sudo pmset -a lidwake 1 59 | # Restart on freeze 60 | sudo systemsetup -setrestartfreeze on 61 | # No sleep if not explicitly instructed to do so 62 | sudo pmset -a displaysleep 0 63 | sudo pmset -a sleep 0 64 | 65 | # Don't send search queries to Apple 66 | sudo defaults write com.apple.Safari UniversalSearchEnabled false 67 | sudo defaults write com.apple.Safari SuppressSearchSuggestions true 68 | # Show full URL in Safari address bar 69 | sudo defaults write com.apple.Safari ShowFullURLInSmartSearchField true 70 | # Safari home page 71 | sudo defaults write com.apple.Safari HomePage -string "about:blank" 72 | # Do not open files after downloading in Safari 73 | sudo defaults write com.apple.Safari AutoOpenSafeDownloads false 74 | # Hide Safari bookmarks bar 75 | sudo defaults write com.apple.Safari ShowFavoritesBar false 76 | # Enable Safari debug and develop menus. 77 | sudo defaults write com.apple.Safari IncludeInternalDebugMenu true 78 | sudo defaults write com.apple.Safari IncludeDevelopMenu true 79 | # Safari search on page with "contains" 80 | sudo defaults write com.apple.Safari FindOnPageMatchesWordStartsOnly false 81 | # Disable Safari auto correct 82 | sudo defaults write com.apple.Safari WebAutomaticSpellingCorrectionEnabled false 83 | # Disable Safari auto fill 84 | sudo defaults write com.apple.Safari AutoFillFromAddressBook false 85 | sudo defaults write com.apple.Safari AutoFillPasswords false 86 | sudo defaults write com.apple.Safari AutoFillCreditCardData false 87 | sudo defaults write com.apple.Safari AutoFillMiscellaneousForms false 88 | # Block pop-ups in Safari 89 | sudo defaults write com.apple.Safari WebKitJavaScriptCanOpenWindowsAutomatically false 90 | sudo defaults write com.apple.Safari com.apple.Safari.ContentPageGroupIdentifier.WebKit2JavaScriptCanOpenWindowsAutomatically false 91 | 92 | brew update --verbose 93 | # For Deskflow 94 | brew tap deskflow/homebrew-tap 95 | # For Wox launcher 96 | brew tap wox-launcher/wox 97 | # For Python 3.10.0 on Apple Silicon 98 | brew install readline openssl 99 | # For Ruby 3.2 100 | brew install libyaml 101 | # For keepassxc-cli 102 | brew install --build-from-source libgpg-error 103 | # Install into applications, not as a cli 104 | brew install --cask mpv docker tailscale 105 | brew install mas keepassxc karabiner-elements hammerspoon visual-studio-code font-jetbrains-mono-nerd-font google-chrome qbittorrent obs iterm2 gimp brave-browser the_silver_searcher michaeldfallen/formula/git-radar lsd eza bat diff-so-fancy uv rv notunes chatgpt slack whatsapp discord lunar double-commander elgato-control-center rode-central mimestream vlc zoom notion notion-calendar eqmac deskflow zsh-autosuggestions zsh-syntax-highlighting wox linearmouse llm lm-studio 106 | 107 | # Need to check for network issues 108 | # brew install orbstack 109 | 110 | if [ -e ~/.local/bin/uvc-util ]; then 111 | echo "uvc-util already installed" 112 | else 113 | echo "Installing uvc-util..." 114 | CUR_DIR=$(pwd) 115 | git clone https://github.com/jtfrey/uvc-util.git 116 | cd uvc-util/src 117 | gcc -o uvc-util -framework IOKit -framework Foundation uvc-util.m UVCController.m UVCType.m UVCValue.m 118 | chmod +x uvc-util 119 | mkdir -p ~/.local/bin/ 120 | cp uvc-util ~/.local/bin/ 121 | cd $CUR_DIR 122 | rm -rf uvc-util 123 | fi 124 | 125 | if [ -e /Applications/HEY.app ]; then 126 | echo "HEY.com already installed" 127 | else 128 | # Download and install HEY.com mail app 129 | echo "Downloading HEY.com client..." 130 | curl -LOSs "https://hey-desktop.s3.amazonaws.com/HEY-arm64.dmg" 131 | hdiutil attach "./HEY-arm64.dmg" 1>/dev/null 132 | vol_name=$(ls /Volumes | grep -E "^HEY.+arm64$") 133 | echo "Installing ${vol_name} ..." 134 | cp -R "/Volumes/${vol_name}/HEY.app" /Applications/ 135 | hdiutil detach "/Volumes/${vol_name}" 1>/dev/null 136 | rm "./HEY-arm64.dmg" 137 | fi 138 | 139 | # Input method name lookup for debug purpose 140 | curl -Ls https://raw.githubusercontent.com/daipeihust/im-select/master/install_mac.sh | sh 141 | 142 | if [ -e ~/.nodenv ]; then 143 | echo "nodenv already installed" 144 | else 145 | git clone https://github.com/nodenv/nodenv.git ~/.nodenv 146 | git clone https://github.com/nodenv/node-build.git ~/.nodenv/plugins/node-build 147 | git clone https://github.com/nodenv/nodenv-update.git ~/.nodenv/plugins/nodenv-update 148 | fi 149 | 150 | if [ -e ~/.swiftenv ]; then 151 | echo "swiftenv already installed" 152 | else 153 | git clone https://github.com/kylef/swiftenv.git ~/.swiftenv 154 | fi 155 | 156 | # Seems not working on macOS, maybe switch to phpvm? 157 | # git clone https://github.com/phpenv/phpenv.git ~/.phpenv 158 | # git clone https://github.com/php-build/php-build ~/.phpenv/plugins/php-build 159 | # git clone https://github.com/jridgewell/phpenv-update ~/.phpenv/plugins/phpenv-update 160 | # export PATH="$HOME/.phpenv/bin:$PATH" 161 | 162 | export PATH="$HOME/.nodenv/bin:$PATH" 163 | export PATH="$HOME/.swiftenv/bin:$PATH" 164 | uv python install 3.13 165 | rv ruby install 3.4.5 166 | # Answer "no" to "already installed, continue?" 167 | echo "n" | nodenv install 23.7.0 168 | nodenv global 23.7.0 169 | 170 | if [ -e ~/dotfiles ] && [ -e ~/.ssh/.uploaded_to_github ]; then 171 | echo "Dotfiles already cloned" 172 | else 173 | git clone https://github.com/grigoryvp/dotfiles.git ~/dotfiles 174 | while true; do 175 | keepassxc-cli show --show-protected \ 176 | --attributes username \ 177 | --attributes password \ 178 | ~/dotfiles/auth/passwords.kdbx github 179 | if [ $? -eq 0 ]; then 180 | break 181 | fi 182 | done 183 | echo "Sign in to GitHub and press enter for TOTP code" 184 | read -s 185 | while true; do 186 | keepassxc-cli show --totp ~/dotfiles/auth/passwords.kdbx github 187 | if [ $? -eq 0 ]; then 188 | break 189 | fi 190 | done 191 | cat ~/.ssh/id_rsa.pub 192 | echo "Add ssh to GitHub and press enter" 193 | read -s 194 | rm -rf ~/dotfiles 195 | git clone git@github.com:grigoryvp/dotfiles.git ~/dotfiles 196 | touch ~/.ssh/.uploaded_to_github 197 | fi 198 | 199 | if [ -e ~/xi ]; then 200 | echo "Knowledge base already cloned" 201 | elif [ -e ~/.ssh/.uploaded_to_github ]; then 202 | git clone git@github.com:grigoryvp/xi.git ~/.xi 203 | else 204 | echo "Not cloning knowledge base since ssh keys are not uploaded" 205 | fi 206 | 207 | printf '#!/bin/sh\n. ~/dotfiles/shell-cfg.sh\n' > ~/.bashrc 208 | printf '#!/bin/sh\n. ~/dotfiles/shell-cfg.sh\n' > ~/.zshrc 209 | printf '#!/bin/sh\n. ~/.bashrc\n' > ~/.bash_profile 210 | printf '[include]\npath = ~/dotfiles/git-cfg.toml\n' > ~/.gitconfig 211 | if ! [ -e ~/.hammerspoon ]; then 212 | mkdir ~/.hammerspoon 213 | fi 214 | ln -fs ~/dotfiles/hammerspoon/init.lua ~/.hammerspoon/init.lua 215 | ln -fs ~/dotfiles/.screenrc ~/.screenrc 216 | ln -fs ~/dotfiles/.gitattributes ~/.gitattributes 217 | ln -fs ~/dotfiles/.rubocop.yml ~/.rubocop.yml 218 | if ! [ -e ~/.config/lsd ]; then 219 | mkdir -p ~/.config/lsd 220 | fi 221 | ln -fs ~/dotfiles/lsd.config.yaml ~/.config/lsd/config.yaml 222 | if ! [ -e ~/.config/powershell ]; then 223 | mkdir -p ~/.config/powershell 224 | fi 225 | ln -fs ~/dotfiles/profile.ps1 ~/.config/powershell/profile.ps1 226 | 227 | open -a Hammerspoon 228 | echo "Configure Hammerspoon and press enter" 229 | read -s 230 | 231 | code --install-extension grigoryvp.language-xi 232 | code --install-extension grigoryvp.memory-theme 233 | code --install-extension grigoryvp.goto-link-provider 234 | code --install-extension grigoryvp.markdown-inline-fence 235 | code --install-extension grigoryvp.markdown-python-repl-syntax 236 | code --install-extension grigoryvp.markdown-pandoc-rawattr 237 | code --install-extension vscodevim.vim 238 | code --install-extension EditorConfig.EditorConfig 239 | code --install-extension emmanuelbeziat.vscode-great-icons 240 | code --install-extension esbenp.prettier-vscode 241 | code --install-extension formulahendry.auto-close-tag 242 | code --install-extension dnut.rewrap-revived 243 | code --install-extension streetsidesoftware.code-spell-checker 244 | code --install-extension streetsidesoftware.code-spell-checker-russian 245 | code --install-extension mark-wiemer.vscode-autohotkey-plus-plus 246 | VSCODE_DIR=~/Library/Application\ Support/Code/User 247 | if [ -e "$VSCODE_DIR" ]; then 248 | echo "'$VSCODE_DIR' already exists" 249 | else 250 | echo "Creating '$VSCODE_DIR' ..." 251 | mkdir -p $VSCODE_DIR 252 | fi 253 | ln -fs ~/dotfiles/vscode_keybindings.json "$VSCODE_DIR/keybindings.json" 254 | ln -fs ~/dotfiles/vscode_settings.json "$VSCODE_DIR/settings.json" 255 | ln -fs ~/dotfiles/vscode_tasks.json "$VSCODE_DIR/tasks.json" 256 | rm -rf "$VSCODE_DIR/snippets" 257 | ln -fs ~/dotfiles/vscode_snippets "$VSCODE_DIR/snippets" 258 | 259 | mkdir -p ~/.config/mpv 260 | echo "save-position-on-quit" > ~/.config/mpv/mpv.conf 261 | echo "loop-file=inf" >> ~/.config/mpv/mpv.conf 262 | open /Applications/Karabiner-Elements.app 263 | echo "Add Karabiner to accessability and press enter" 264 | read -s 265 | # Entire config dir should be symlinked 266 | rm -rf ~/.config/karabiner 267 | ln -fs ~/dotfiles/karabiner ~/.config/karabiner 268 | 269 | _configure_wox 270 | 271 | # Close any preferences so settings are not overwritten. 272 | osascript -e 'tell application "System Preferences" to quit' 273 | # Show hidden files, folders and extensions. 274 | chflags nohidden ~/Library 275 | defaults write com.apple.finder AppleShowAllFiles YES 276 | defaults write -g AppleShowAllExtensions true 277 | # Keep folders on top while sorting by name in Finder. 278 | defaults write com.apple.finder _FXSortFoldersFirst true 279 | # Change extension without a warning. 280 | defaults write com.apple.finder FXEnableExtensionChangeWarning false 281 | # Do not create .DS_Store on removable media and network. 282 | defaults write com.apple.desktopservices DSDontWriteNetworkStores true 283 | defaults write com.apple.desktopservices DSDontWriteUSBStores true 284 | # Do not verify disk images 285 | defaults write com.apple.frameworks.diskimages skip-verify true 286 | defaults write com.apple.frameworks.diskimages skip-verify-locked true 287 | defaults write com.apple.frameworks.diskimages skip-verify-remote true 288 | # Show Finder path and status bars. 289 | defaults write com.apple.finder ShowPathbar true 290 | defaults write com.apple.finder ShowStatusBar true 291 | # List view for all Finder windows by default 292 | defaults write com.apple.finder FXPreferredViewStyle -string "Nlsv" 293 | # Disable empty trash warning 294 | defaults write com.apple.finder WarnOnEmptyTrash false 295 | # Enable keyboard repeat, need to restart after that. 296 | defaults write -g ApplePressAndHoldEnabled false 297 | defaults write NSGlobalDomain KeyRepeat -int 2 298 | defaults write NSGlobalDomain InitialKeyRepeat -int 15 299 | # Prevent OS from changing text being entered. 300 | defaults write -g NSAutomaticCapitalizationEnabled false 301 | defaults write -g NSAutomaticDashSubstitutionEnabled false 302 | defaults write -g NSAutomaticPeriodSubstitutionEnabled false 303 | defaults write -g NSAutomaticQuoteSubstitutionEnabled false 304 | defaults write -g NSAutomaticSpellingCorrectionEnabled false 305 | # Max touchpad speed that can be set via GUI, cli can go beyond than. 306 | defaults write -g com.apple.trackpad.scaling 3 307 | # Switch off typing disable while trackpad is in use. 308 | defaults write com.apple.applemultitouchtrackpad TrackpadHandResting -int 0 309 | # Save to disk instead of iCloud by default. 310 | defaults write NSGlobalDomain NSDocumentSaveNewDocumentsToCloud false 311 | # Disable the app open confirmation. 312 | defaults write com.apple.LaunchServices LSQuarantine false 313 | # Input languages and locale 314 | defaults write -g AppleLanguages -array "en" "ru" "ja" 315 | defaults write -g AppleLocale -string "en_RU" 316 | # Instant dock auto hiding 317 | defaults write com.apple.dock autohide true 318 | defaults write com.apple.dock autohide-delay -float 0 319 | defaults write com.apple.dock autohide-time-modifier -float 0 320 | # No recent apps in dock 321 | defaults write com.apple.dock show-recents false 322 | # Require password 323 | defaults write com.apple.screensaver askForPassword -int 1 324 | defaults write com.apple.screensaver askForPasswordDelay -int 0 325 | # Copy email without name in Mail 326 | defaults write com.apple.mail AddressesIncludeNameOnPasteboard false 327 | # Disable inline attachments in Mail 328 | defaults write com.apple.mail DisableInlineAttachmentViewing true 329 | # "Continuos scroll" by default for PDF preview 330 | defaults write com.apple.Preview kPVPDFDefaultPageViewModeOption 0 331 | # Don't auto-show dock on mouse hover (m1-slash instead) 332 | defaults write com.apple.dock autohide-delay -float 999999 333 | # Change slow "Genie" dock minimize animation to fast "Scale" 334 | defaults write com.apple.dock "mineffect" -string "scale" && killall Dock 335 | # Disable "animate opening applications" 336 | defaults write com.apple.dock launchanim -bool false 337 | # Disable "show suggested and recent applications in Dock" 338 | defaults write com.apple.dock show-recents -bool false 339 | # Disable Mission Control "hot corners" (they trigger accidentally a lot) 340 | defaults write com.apple.dock wvous-tl-corner -int 0 341 | defaults write com.apple.dock wvous-tr-corner -int 0 342 | defaults write com.apple.dock wvous-bl-corner -int 0 343 | defaults write com.apple.dock wvous-br-corner -int 0 344 | # Auto-hide dock to get more vertical space (everything is on hotkeys) 345 | defaults write com.apple.dock autohide -bool true 346 | # Mute alerts 347 | osascript -e "set volume alert volume 0" 348 | # Mute volume change feedback 349 | defaults write -g "com.apple.sound.beep.feedback" -bool false 350 | # Disable screen saver (manually turn off screen by locking the laptop) 351 | defaults -currentHost write com.apple.screensaver idleTime -int 0 352 | 353 | # Remove all dock icons 354 | defaults write com.apple.dock persistent-apps -array "" 355 | 356 | # Apply changes 357 | killall Dock 358 | killall SystemUIServer 359 | } 360 | 361 | if [ "$1" = "--test" ]; then 362 | test 363 | else 364 | configure 365 | fi 366 | -------------------------------------------------------------------------------- /dictionary.txt: -------------------------------------------------------------------------------- 1 | __class_getitem__ 2 | __delattr__ 3 | __delitem__ 4 | __floordiv__ 5 | __getattribute__ 6 | __getitem__ 7 | __getstate__ 8 | __iadd__ 9 | __imul__ 10 | __qualname__ 11 | __repr__ 12 | __rmul__ 13 | __setattr__ 14 | __setitem__ 15 | __subclasshook__ 16 | __truediv__ 17 | --deauth 18 | -ов 19 | .railsrc 20 | á 21 | accesstoken 22 | Acclimatisation 23 | Accor 24 | Achmiz 25 | activerecord 26 | ADHD 27 | Adiyah 28 | Aetherwind 29 | ahk 30 | aiomonitor 31 | aircrack-ng 32 | aireplay-ng 33 | airpods 34 | Aisling 35 | aistudio 36 | Akil 37 | akka 38 | alch 39 | Alexa 40 | Alira 41 | Alivia 42 | Allflame 43 | Amanamu 44 | AMQP 45 | anki 46 | ANTLR 47 | anychar 48 | aoe 49 | AOT 50 | apk 51 | APNG 52 | appengine 53 | applescript 54 | appname 55 | approle 56 | Arakaali 57 | Arcanist 58 | Archdemon 59 | Archmage 60 | Armin 61 | Asenath 62 | ASF 63 | asg 64 | asgi 65 | asic 66 | Astragali 67 | Astramentis 68 | Astromancer 69 | Asus 70 | asynccontextmanager 71 | asyncio 72 | asyncpg 73 | atexit 74 | Atoi 75 | Atziri 76 | Aul 77 | autofocus 78 | autohide 79 | autohotkey 80 | autoreplace 81 | Avarius 82 | Aventail 83 | AVF 84 | awk 85 | Aylo 86 | Azyran 87 | backoffice 88 | Baran 89 | Barbute 90 | Basemetal 91 | Beastcrafting 92 | Beelink 93 | Bellcore 94 | bem 95 | benchcraft 96 | benchcrafted 97 | bg 98 | bindgen 99 | BIP 100 | bit.ly 101 | Bitterbind 102 | Bjarne 103 | Blacksteel 104 | Bladebitter 105 | Bladecaster 106 | Bladefall 107 | Bladeguard 108 | Blaidd 109 | Blightheart 110 | blobstorage 111 | Bloodletter 112 | bonechill 113 | Borland 114 | bottlenecked 115 | BPF 116 | breachstone 117 | Brinerot 118 | Bronzescale 119 | bson 120 | bssid 121 | BTF 122 | btog 123 | bunjs 124 | Bunq 125 | byteorder 126 | Caeserius 127 | Cameria 128 | capsys 129 | cardano 130 | CAS 131 | Catalysing 132 | Catarina 133 | cdecl 134 | cdef 135 | cdll 136 | CEFR 137 | certbot 138 | CERTDIR 139 | Chainmail 140 | Chakram 141 | characterised 142 | charfrom 143 | chartjs 144 | Chayula 145 | cheatsheet 146 | Chestplate 147 | Chimeral 148 | Chimerascale 149 | Cinderswallow 150 | CIO 151 | Citus 152 | cjkfont 153 | clangd 154 | classoption 155 | Classpath 156 | Clayshaper 157 | cleandoc 158 | clevo 159 | cli 160 | clickhouse 161 | Cloudwhisper 162 | clr 163 | cls 164 | CMY 165 | CMYK 166 | codegen 167 | codeowners 168 | codepoints 169 | codesign 170 | cognitiо 171 | colorlinks 172 | Colour 173 | columnconfigure 174 | conce 175 | confeta 176 | Configurator 177 | contextlib 178 | contextmanager 179 | convolutional 180 | cookiecutter 181 | cooldown 182 | coreutils 183 | cpython 184 | craftable 185 | Craicic 186 | CRDT 187 | createdb 188 | Crystallised 189 | CTO 190 | ctx 191 | ctypes 192 | cui 193 | cwd 194 | Cython 195 | Dagster 196 | Dannig 197 | Daresso 198 | darknet 199 | Darksteel 200 | Darktongue 201 | databasehash 202 | dataclass 203 | dataclasses 204 | Dawnbreaker 205 | DBA 206 | DBAPI 207 | dbname 208 | dbt 209 | DDL 210 | debuff 211 | debuffs 212 | deepcopy 213 | deeplink 214 | defaultdict 215 | defence 216 | defences 217 | Dekharan 218 | Dementophobia 219 | densesample 220 | densesamplenum 221 | DERP 222 | Deskflow 223 | desktopservices 224 | desync 225 | df 226 | DHH 227 | Dialla 228 | dind 229 | discriminare 230 | Disfavour 231 | diskimages 232 | Displate 233 | displaysleep 234 | Disruptors 235 | distutils 236 | Divinarius 237 | dkjson 238 | dmg 239 | dockerhub 240 | docstrings 241 | documentclass 242 | Doedre 243 | dont 244 | Doryani 245 | dotenv 246 | dotfiles 247 | Dragonscale 248 | Dramatiq 249 | Dreamquest 250 | Drox 251 | DS 252 | DSA 253 | DSL 254 | dsn 255 | Dualstring 256 | dunder 257 | Dunerunner 258 | Duolingo 259 | Durov 260 | Duskwalk 261 | DWH 262 | Eagon 263 | EAPOL 264 | ebpf 265 | EE 266 | Eelskin 267 | Effigial 268 | Einhar 269 | Elderslayers 270 | Elementalist 271 | elg 272 | elgato 273 | elif 274 | Elreon 275 | Emscripten 276 | endraw 277 | endswith 278 | Enthalpic 279 | env 280 | epub 281 | eqmac 282 | erb 283 | ERC20 284 | ERP 285 | Errno 286 | Eruptor 287 | Esh 288 | esim 289 | esm 290 | essencecraft 291 | essencecrafted 292 | essencecrafting 293 | ETF 294 | Ethena 295 | etl 296 | etog 297 | eurocom 298 | evaluate'ится 299 | evaluate'ить 300 | évaluation 301 | evdev 302 | eventtap 303 | evp 304 | evrone 305 | EVT 306 | Excalidraw 307 | EXIF 308 | expression'ов 309 | expressjs 310 | eza 311 | Ezomyte 312 | Fanless 313 | Faridun 314 | fastapi 315 | fastfetch 316 | fb 317 | FDBS 318 | Feedly 319 | Fenumal 320 | ffi 321 | fi 322 | Fidium 323 | fidonet 324 | Fira 325 | Firesong 326 | Fishscale 327 | Flamebolt 328 | Flamehand 329 | Flamequiver 330 | Flet 331 | Flink 332 | fn 333 | Forkshot 334 | Fossilised 335 | Freepik 336 | frontmost 337 | Frosthand 338 | frozenset 339 | FSM 340 | FSRS 341 | functools 342 | FX 343 | gamesample 344 | gamesamplenum 345 | Gbit 346 | gdb 347 | GDI 348 | Geekbench 349 | Gemcutter 350 | Gemling 351 | geospatial 352 | getaddrinfo 353 | getattr 354 | getcwd 355 | getencoding 356 | getenv 357 | getrefcount 358 | getsizeof 359 | Gevent 360 | GG 361 | gh-deploy 362 | Gianna 363 | Giantslayer 364 | gibibyte 365 | gigaflop 366 | Gigantification 367 | Gitbeaker 368 | glab 369 | Gladiatoral 370 | Glassdoor 371 | GNN 372 | Goathide 373 | Goatmem 374 | goatmen 375 | Goldcast 376 | Goldworked 377 | Goroutines 378 | gpetrov 379 | gping 380 | GPT 381 | gradlew 382 | Granian 383 | Gravebind 384 | Gravicius 385 | Greathelm 386 | Greatwolf 387 | Greenplum 388 | greenwashing 389 | grigory 390 | grigoryvp 391 | Gruthkul 392 | GTK 393 | gunicorn 394 | Gutspike 395 | gvl 396 | Gwennen 397 | habr 398 | habrahabr 399 | Haemophilia 400 | hammerspoon 401 | hanami 402 | Harpyskin 403 | hasattr 404 | hashability 405 | hashlib 406 | Hateforge 407 | Hatungo 408 | HCP 409 | HCSSF 410 | HDFS 411 | HDR 412 | headscale 413 | HEALTHCHECK 414 | Heartcarver 415 | Heraldric 416 | Heretech 417 | Heterochromia 418 | hexdigest 419 | hextouch 420 | Hezmin 421 | Highgate 422 | highload 423 | Hinekora 424 | HMAC 425 | HN 426 | Hom 427 | homelab 428 | horticrafting 429 | hotstrings 430 | Hotwire 431 | HRESULT 432 | HSV 433 | HTAP 434 | htmx 435 | httpx 436 | Hydrascale 437 | hyperliquid 438 | Hyrri 439 | idir 440 | idl 441 | iex 442 | ignorecase 443 | IIR 444 | ilograph 445 | ilvl 446 | im 447 | IMG 448 | implicits 449 | impnattypo 450 | incapsulate 451 | indice 452 | Indigon 453 | Infohazards 454 | Informix 455 | Inlines 456 | instancecheck 457 | Interpretability 458 | Invictus 459 | invis 460 | ioc 461 | IPC 462 | IPFS 463 | IPX 464 | ipynb 465 | ipython 466 | Ironscale 467 | ISA 468 | isclose 469 | isinstance 470 | Isla 471 | iterm 472 | itertools 473 | Izaro 474 | jailbreaking 475 | jailbroken 476 | javac 477 | javap 478 | JDBC 479 | JDK 480 | JEP 481 | jestfile 482 | Jewellery 483 | JIT 484 | JMS 485 | JNI 486 | Johan 487 | Jorgin 488 | JPA 489 | jpeek 490 | Justicar 491 | JVM 492 | Jython 493 | Kalandra 494 | Kalguuran 495 | Kamasan 496 | Kaom 497 | karabiner 498 | Karui 499 | kawaii 500 | kde 501 | KE 502 | KEEPALIVE 503 | Keepass 504 | keepassdb 505 | keepassxc 506 | Keth 507 | keygen 508 | keypair 509 | KEYUP 510 | kibibyte 511 | killall 512 | Kineticist 513 | Kingsmarch 514 | Kirac 515 | Kitava 516 | Kivy 517 | KLM 518 | kmeans 519 | koi8-r 520 | Korell 521 | Kosis 522 | Kristian 523 | kubeadm 524 | kubeconfig 525 | Kulemak 526 | Kurgal 527 | KVM 528 | kwargs 529 | kyc 530 | Lacewood 531 | Lakehouse 532 | langchain 533 | Lantador 534 | LDR 535 | leaguestart 536 | Leatherbound 537 | Leatherscale 538 | leetcode 539 | letsencrypt 540 | Leyline 541 | LFE 542 | LFW 543 | LGA 544 | lib-dynload 545 | libadwaita 546 | libbz 547 | libc 548 | libexec 549 | libinput 550 | libncursesw 551 | libreadline 552 | libsodium 553 | libsqlite 554 | libui 555 | libxmlsec 556 | Lich 557 | Lich's 558 | Lifeforce 559 | linearmouse 560 | LINQ 561 | listtimezones 562 | Litestar 563 | litestream 564 | livejournal 565 | Lizardscale 566 | LLM 567 | lmb 568 | lms 569 | LNGB 570 | lockfiles 571 | Lockpicking 572 | Lockpicks 573 | loglevel 574 | lol 575 | lookaround 576 | Loreweave 577 | Loricated 578 | lru 579 | lsp 580 | lualatex 581 | luarocks 582 | luasocket 583 | luasodium 584 | luatex 585 | Lunaris 586 | lvl 587 | Lysah 588 | Magebane 589 | Mageblood 590 | Mahuxotl 591 | Malachai 592 | Maligaro 593 | malloc 594 | manifestless 595 | mant_dig 596 | Manyshot 597 | Maraketh 598 | marginhandwrite 599 | matplotlib 600 | Matryoshka 601 | Matz 602 | Mawr 603 | mbr 604 | MCM 605 | MCP 606 | mcs 607 | MCU 608 | MDM 609 | mdutil 610 | mebibyte 611 | MEDS 612 | memmove 613 | memoizations 614 | memray 615 | Merveil 616 | MESI 617 | mesos 618 | metaclass 619 | metaclasses 620 | metainfo 621 | metainformation 622 | Metalsmith 623 | michaeldfallen 624 | microcontroller 625 | microcontrollers 626 | mimestream 627 | mimetypes 628 | mineffect 629 | miniboss 630 | minipage 631 | Miscreation 632 | mkdocs 633 | mkdocstrings 634 | MLX 635 | modulepath 636 | Mojolicious 637 | Moosh 638 | Morior 639 | moscowpythonconf 640 | movespeed 641 | mpv 642 | MQTT 643 | MRO 644 | msbuild 645 | msgpack 646 | mts 647 | MTU 648 | MTWTFSSouehrauneduitn 649 | MTX 650 | mul 651 | multibyte 652 | MVC 653 | MVCC 654 | myapp 655 | mycompany 656 | mynamespace 657 | mypy 658 | myscope 659 | mysuppress 660 | nacl 661 | nanodjango 662 | NAS 663 | Nenet 664 | neocrypto 665 | nerfed 666 | netbeans 667 | netbird 668 | netcat 669 | netrc 670 | neuroenergetics 671 | Neuroscientists 672 | nextjs 673 | nic 674 | nifi 675 | Nightweave 676 | Nightwind 677 | Niko 678 | Nimis 679 | njk 680 | Noctua 681 | nodenext 682 | nodenv 683 | nogil 684 | nohidden 685 | NONCEBYTES 686 | nonglobal 687 | nonlocal 688 | notunes 689 | NPC 690 | npmignore 691 | Nubuck 692 | NUC 693 | Num 694 | numlock 695 | numpad 696 | numpy 697 | Nunjucks 698 | Nuxt 699 | nuxtjs 700 | nvm 701 | Nygaard 702 | OCC 703 | OCI 704 | octothorp 705 | offchain 706 | offlevel 707 | OFS 708 | okular 709 | OLAP 710 | OLED 711 | Olroth 712 | OLTP 713 | Omniphobia 714 | oneof 715 | oneshot 716 | oneside 717 | OOP 718 | opensearch 719 | openwashing 720 | openwrt 721 | optimus 722 | orbstack 723 | oreventhis 724 | Oriath 725 | osascript 726 | osx 727 | OTP 728 | overcap 729 | overlevel 730 | packrat 731 | pacman 732 | pagebreak 733 | pageless 734 | Pandemonius 735 | pandoc 736 | Paradoxica 737 | pastebin 738 | pathlib 739 | Paua 740 | Pauascale 741 | pdb 742 | PDR 743 | Petrov 744 | pfctl 745 | pgvector 746 | phantomsection 747 | Phlex 748 | phpenv 749 | phpvm 750 | PicoRuby 751 | PIL 752 | pinggraph 753 | pipx 754 | pixelmator 755 | pkey 756 | pmbok 757 | PMK 758 | Polaric 759 | popd 760 | Popen 761 | ppp 762 | pprint 763 | preprod 764 | prestart 765 | println 766 | Progenesis 767 | programdata 768 | psql 769 | psu 770 | Psyco 771 | PTK 772 | Ptyxis 773 | pushd 774 | Putembo 775 | pwsh 776 | pybook 777 | pyc 778 | pycache 779 | pydantic 780 | pyenv 781 | Pylance 782 | pymalloc 783 | pypi 784 | pyproject 785 | Pypy 786 | pyrefly 787 | pyright 788 | Pyston 789 | pytest 790 | PYTHONASYNCIODEBUG 791 | PYTHONDONTWRITEBYTECODE 792 | pythonic 793 | PYTHONMALLOC 794 | PYTHONPATH 795 | Pytype 796 | qbittorrent 797 | QNX 798 | queryset 799 | questline 800 | Quickslip 801 | Qwen 802 | rabbitmq 803 | Radeon 804 | radmin 805 | Raku 806 | randint 807 | randombytes 808 | Rarible 809 | RDBMS 810 | RDNA 811 | RDTSC 812 | reactjs 813 | readlink 814 | readouterr 815 | realpython 816 | Reanimator 817 | reasonml 818 | Rebar 819 | rebeccapurple 820 | recents 821 | recvfrom 822 | redacter 823 | Redactions 824 | Redpanda 825 | Reefsteel 826 | regen 827 | Régime 828 | relogin 829 | remappable 830 | respawn 831 | respin 832 | résumé 833 | retryable 834 | returncode 835 | RGB 836 | Rhoa 837 | Rhoahide 838 | Riker 839 | Rin 840 | Ringmail 841 | rippy 842 | rmi 843 | rmtree 844 | RO 845 | Rog 846 | Rogcrafting 847 | Ronacher 848 | Rossum 849 | roundoff 850 | rowconfigure 851 | RSGI 852 | RTT 853 | RTX 854 | rubocop 855 | rubygems 856 | Runed 857 | Runefinding 858 | runlevel 859 | rustdesk 860 | rustup 861 | RVM 862 | RW 863 | RYB 864 | Ryzen 865 | Sabatons 866 | SAE 867 | safehouse 868 | safehouses 869 | Sambodhi 870 | Sandsworn 871 | Sanguimancer 872 | Sarn 873 | Saruman 874 | sauron 875 | scaffolder 876 | Scanf 877 | Schoeneman 878 | scikit 879 | Scm 880 | scp 881 | scrollbars 882 | SDP 883 | Seasonic 884 | Secutor 885 | Sekhem 886 | Sekheman 887 | sendall 888 | sendto 889 | Sephirot 890 | Serpentscale 891 | setdefault 892 | setrestartfreeze 893 | settimezone 894 | setuptools 895 | setx 896 | Shakari 897 | Shattersword 898 | shell'ов 899 | Sheth 900 | Shieldbearer 901 | shutil 902 | sigcheck 903 | significand 904 | Simula 905 | Siri 906 | Sirus 907 | skitterbots 908 | sklearn 909 | Skycrown 910 | skylighting 911 | Slipstrike 912 | sln 913 | smallstrut 914 | smm 915 | Smoulderstrike 916 | SMPP 917 | SNMP 918 | SOCK_DGRAM 919 | Socketable 920 | Solana 921 | Someclass 922 | somemethod 923 | SonarQube 924 | Soulbound 925 | Soulrend 926 | spacebar 927 | Spellslinger 928 | Spidersilk 929 | Spikeward 930 | Spinehail 931 | Spiritbone 932 | Sporeguard 933 | Sprache 934 | SPX 935 | sqlalchemy 936 | sre 937 | srv 938 | SSF 939 | ssize 940 | Stackable 941 | stackful 942 | Starforge 943 | starlette 944 | startswith 945 | statement'ов 946 | staticmethod 947 | Steelmail 948 | Steelpoint 949 | Steelscale 950 | Steelskin 951 | Steeltoe 952 | STM32 953 | stormblast 954 | Stormcall 955 | Stormcaller 956 | Stormhand 957 | Stormrider 958 | Stormshroud 959 | Stranglegasp 960 | strconv 961 | Strix 962 | Stroustrup 963 | strtolower 964 | STT 965 | Stunlock 966 | subclassing 967 | subcomponents 968 | subdirs 969 | subkey 970 | subshell 971 | subshells 972 | Subtests 973 | Sulphite 974 | sumsub 975 | Sunfire 976 | Superintelligence 977 | suppresssample 978 | suppresssamplenum 979 | SWE-Bench 980 | Swiftblade 981 | swiftenv 982 | Swiftstalker 983 | Sybase 984 | syncdb 985 | Synthesised 986 | Synthesising 987 | syscall 988 | sysconfig 989 | systemsetup 990 | taildrive 991 | taildrop 992 | tailnet 993 | tailscale 994 | tanky 995 | Tarantool 996 | taskkill 997 | Tauri 998 | tcbox 999 | tcpdump 1000 | techdocs 1001 | techempower 1002 | templatelib 1003 | TEP 1004 | Teradata 1005 | Tewi 1006 | textbackslash 1007 | textblock 1008 | textcolor 1009 | textquotesingle 1010 | textwidth 1011 | textwrap 1012 | TF-IDF 1013 | TFS 1014 | tft 1015 | TG 1016 | Thinkpad 1017 | Threadripper 1018 | Thunderquiver 1019 | Tibbs 1020 | Tideseer 1021 | tikzpicture 1022 | Timecode 1023 | timelog 1024 | tinyurl 1025 | TIOBE 1026 | Tk 1027 | tkinter 1028 | toc 1029 | Tolman 1030 | tomllib 1031 | TONCOIN 1032 | Tonkeeper 1033 | TOTP 1034 | TPEP 1035 | tqdm 1036 | traceroute 1037 | Trackpad 1038 | Trapsetter 1039 | Treerunner 1040 | Triggerblades 1041 | Triggerslam 1042 | Trino 1043 | triphosphate 1044 | truecrypt 1045 | tsc 1046 | tsidp 1047 | TSMC 1048 | ttl 1049 | Tujen 1050 | Tukohama 1051 | Tul 1052 | Tulfall 1053 | Tullina 1054 | Tunnelslam 1055 | ubyte 1056 | UCS-2 1057 | udemy 1058 | UDP 1059 | UEFI 1060 | ulonglong 1061 | Unaliased 1062 | unblockable 1063 | unc 1064 | uncollegiate 1065 | underlinecolor 1066 | undropped 1067 | unhashable 1068 | unicodedata 1069 | unpackings 1070 | Unpowered 1071 | Unraisable 1072 | unsequenced 1073 | unsummon 1074 | URLCONF 1075 | usde 1076 | userdata 1077 | userinfo 1078 | USERPROFILE 1079 | userspace 1080 | usr 1081 | utf-8 1082 | Utzaal 1083 | Uul-Netol 1084 | uutils 1085 | uvicorn 1086 | uvloop 1087 | uvx 1088 | UWP 1089 | uwsgi 1090 | UX 1091 | Uzaza 1092 | Vagan 1093 | Valdo 1094 | valēre 1095 | Varargs 1096 | Vcpkg 1097 | VCS 1098 | vectorizer 1099 | Vectorizing 1100 | Ventor 1101 | venv 1102 | ver 1103 | vercel 1104 | Verilog 1105 | Verisium 1106 | Veritania 1107 | Vermillion 1108 | viewports 1109 | viewsets 1110 | Vilenta 1111 | Vinderi 1112 | Vinia 1113 | virtualenv 1114 | virtualenvs 1115 | VK 1116 | vlc 1117 | VM 1118 | Voidforge 1119 | voidstone 1120 | voidstones 1121 | Voll 1122 | Voltaxic 1123 | Vorici 1124 | voximplant 1125 | vsicons 1126 | vuex 1127 | walletteam 1128 | Warband 1129 | Warbands 1130 | Wargraves 1131 | Warhoards 1132 | Warpriest 1133 | Warstaff 1134 | Warstaves 1135 | watchstone 1136 | Waystone 1137 | wchar 1138 | weakref 1139 | webdav 1140 | webrtc 1141 | websequencediagrams 1142 | WEP 1143 | Whakano 1144 | Wi-Fi 1145 | Widowsilk 1146 | winapi 1147 | Windscream 1148 | winget 1149 | WIP 1150 | Withertouch 1151 | wlan 1152 | Wnd 1153 | wndproc 1154 | wol 1155 | WOLED 1156 | Wolfskin 1157 | Wolven 1158 | worktree 1159 | wox 1160 | WPA 1161 | WPF 1162 | WPS 1163 | wsgi 1164 | WSL 1165 | wx 1166 | wxpython 1167 | wxruby 1168 | wxwidgets 1169 | Wyrmscale 1170 | Wyvernscale 1171 | xapk 1172 | xattr 1173 | XC 1174 | Xoph 1175 | xrange 1176 | Xsolla 1177 | xxd 1178 | xy 1179 | ycoins 1180 | YDB 1181 | yesim 1182 | yshift 1183 | Yudkowsky 1184 | Zab 1185 | Zana 1186 | zeroconf 1187 | zk 1188 | Zod 1189 | автовыбора 1190 | автоконверсии 1191 | автосохранение 1192 | автотестов 1193 | айти 1194 | айтишная 1195 | айтишного 1196 | акторная 1197 | акторной 1198 | аллокатор 1199 | аллокатора 1200 | аллокаторов 1201 | аллоцированную 1202 | аппроксиматоры 1203 | ассеты 1204 | атомарности 1205 | Атомарность 1206 | АФК 1207 | байткод 1208 | байткода 1209 | байткодом 1210 | байткоду 1211 | баристы 1212 | Бартунова 1213 | беззнаковой 1214 | беззнаковые 1215 | бенчмарк 1216 | бенчмарки 1217 | Берклевских 1218 | бессерверной 1219 | би 1220 | Бигтех 1221 | бинарь 1222 | благославлен 1223 | блоба 1224 | блобах 1225 | блобов 1226 | блобы 1227 | Бобук 1228 | бо́льшая 1229 | булевого 1230 | Бухучёт 1231 | бэкенд 1232 | бэкенда 1233 | бэкенде 1234 | валидировать 1235 | векторизация 1236 | вендоров 1237 | видеокодека 1238 | видеопроигрывателя 1239 | виртуалках 1240 | врапперов 1241 | вызываемости 1242 | вызываемость 1243 | высоконагруженных 1244 | геймдизайнер 1245 | генерализовать 1246 | гиби- 1247 | гибибайт 1248 | гига- 1249 | гигабитах 1250 | гигабите 1251 | Гипервизор 1252 | гитхаба 1253 | Главред 1254 | горутин 1255 | горутина 1256 | горутинах 1257 | горутины 1258 | Грациано 1259 | дандер-атрибутами 1260 | дандер-атрибутов 1261 | дандер-атрибуты 1262 | дандер-метод 1263 | дандер-методам 1264 | дандер-методами 1265 | дандер-методах 1266 | дандер-методов 1267 | дандер-методы 1268 | дандер-функций 1269 | датасетах 1270 | датацентре 1271 | датацентров 1272 | двумерны 1273 | деавторизации 1274 | Деавторизация 1275 | де́бетом 1276 | дедлокам 1277 | дедлоки 1278 | Демистификация 1279 | десериализаторов 1280 | десериализация 1281 | Деструктурирование 1282 | деструктурированием 1283 | деструктурировании 1284 | деструктурирования 1285 | джойнить 1286 | дистрибьюцией 1287 | дистрибьюции 1288 | Длинношеев 1289 | донастроить 1290 | донастройки 1291 | еtcd 1292 | журналирования 1293 | заблюрить 1294 | заморачивались 1295 | Значек 1296 | Иммутабельная 1297 | иммутабельной 1298 | иммутабельности 1299 | иммутабельностью 1300 | иммутабельны 1301 | иммутабельные 1302 | иммутабельными 1303 | инвайта 1304 | инвалидировании 1305 | инвалидировать 1306 | инвойсами 1307 | инстанс 1308 | инстансами 1309 | инстансы 1310 | инстанцированием 1311 | инстанцируем 1312 | интерконнекта 1313 | интерконнекту 1314 | инфостиле 1315 | ИП 1316 | итерировании 1317 | итерирования 1318 | Йохан 1319 | калечное 1320 | Каллистов 1321 | каррирование 1322 | картируются 1323 | катастрофоустойчивость 1324 | квалиа 1325 | киби- 1326 | кибибайт 1327 | кликает 1328 | кликаете 1329 | кликстрим 1330 | кликхаус 1331 | ключем 1332 | когнитома 1333 | коллапсирует 1334 | коммита 1335 | коммитам 1336 | компактифицированный 1337 | консьюмер 1338 | консьюмера 1339 | консьюмерам 1340 | консьюмеров 1341 | консьюмеры 1342 | контейнеризированных 1343 | контринтуитивен 1344 | корутины 1345 | косинусодиальное 1346 | коэволюции 1347 | Кре́дит 1348 | Кристен 1349 | Кэй 1350 | ЛВР 1351 | легаси 1352 | лексеров 1353 | Лема 1354 | макбуке 1355 | Маккарти 1356 | манипулятивных 1357 | маппинга 1358 | меби- 1359 | мебибайт 1360 | мейлбокс 1361 | Мейлбоксы 1362 | мема 1363 | мемов 1364 | метаклассы 1365 | метапрограммирование 1366 | метапрограммированием 1367 | метапрограммировании 1368 | метапрограммированию 1369 | метапрограммирования 1370 | микросервисах 1371 | микросервисов 1372 | микросервисы 1373 | минималках 1374 | многострочниках 1375 | Мониторим 1376 | Монти 1377 | моржик 1378 | моржика 1379 | Мосигры 1380 | мутабелен 1381 | Мутабельная 1382 | мутабельного 1383 | мутабельное 1384 | мутабельности 1385 | Мутабельность 1386 | мутабельные 1387 | мутабельный 1388 | мутабельными 1389 | мутабельных 1390 | Надиктовка 1391 | Найгард 1392 | насмотренность 1393 | неаутентифицированному 1394 | неиспользуемости 1395 | нейминга 1396 | неймспейс 1397 | неймспейса 1398 | неймспейсам 1399 | Неймспейсами 1400 | неймспейсах 1401 | неймспейсе 1402 | неймспейсов 1403 | неймспейсом 1404 | неймспейсы 1405 | Нейродизайн 1406 | нейрональные 1407 | нейросетей 1408 | нейросетям 1409 | нейросетями 1410 | неоптимальна 1411 | нереплицируемый 1412 | нишевых 1413 | нод 1414 | нодам 1415 | нодах 1416 | ноде 1417 | нодой 1418 | ноду 1419 | обналичить 1420 | одномерны 1421 | однопоточно 1422 | однопоточными 1423 | октоторп 1424 | ООП 1425 | опенсорс 1426 | опенсорсе 1427 | опенспейс 1428 | ОПХБЕР 1429 | пайтон 1430 | пайтоне 1431 | Парсер 1432 | парсерах 1433 | парсеры 1434 | паттерн-матчинга 1435 | пентеста 1436 | переиспользовали 1437 | переиспользован 1438 | переиспользование 1439 | переиспользования 1440 | переиспользуем 1441 | переиспользуемые 1442 | переиспользуемый 1443 | переиспользуемых 1444 | переиспользует 1445 | переиспользуется 1446 | переиспользуют 1447 | переиспользуются 1448 | Переиспользуя 1449 | переподключились 1450 | пересборки 1451 | переукажет 1452 | переуказание 1453 | Переуказать 1454 | Переуказываем 1455 | переуказывает 1456 | переуказывать 1457 | переуказываются 1458 | переуказывая 1459 | пессимизаций 1460 | петабайт 1461 | петабайты 1462 | пиксельной 1463 | пинги 1464 | пингов 1465 | погуглить 1466 | поджойнить 1467 | полуструктурированных 1468 | попинговать 1469 | порейзить 1470 | потокобезопасный 1471 | Преобразуя 1472 | программерских 1473 | промпт 1474 | промпта 1475 | промпту 1476 | промпты 1477 | просмотрщик 1478 | Псевдогаллюцинации 1479 | раздрай 1480 | рантайм 1481 | рантайма 1482 | рантайме 1483 | рантаймом 1484 | рантайму 1485 | Рантаймы 1486 | раскирпичить 1487 | расшарить 1488 | ребаланс 1489 | резидентства 1490 | реселлера 1491 | ретраи 1492 | Рецидивирующий 1493 | Россум 1494 | роутеров 1495 | роутеры 1496 | РСУБД 1497 | Сocoa 1498 | Сапольски 1499 | Сбере 1500 | свежесозданного 1501 | СДВГ 1502 | СЕО 1503 | сепулек 1504 | Сепуление 1505 | сепуления 1506 | Сепулькарии 1507 | Сепулькариях 1508 | Сепульки 1509 | сериализаторов 1510 | сериализует 1511 | скам 1512 | скилл 1513 | скопироваться 1514 | сорцами 1515 | софтины 1516 | спеллчекеров 1517 | стагнируют 1518 | стейблкоина 1519 | стейблкоинах 1520 | стейблкойн 1521 | Страуструп 1522 | Страуструпа 1523 | схлопывать 1524 | таба 1525 | тестировщика 1526 | тимлид 1527 | Тиннитус 1528 | Типобезопасность 1529 | типобезопасный 1530 | токенизатор 1531 | токенизированные 1532 | токенизированных 1533 | токеном 1534 | токену 1535 | трансформера 1536 | тредами 1537 | тредпулом 1538 | трейсы 1539 | трем 1540 | ТСР 1541 | туториалах 1542 | ТЦ 1543 | у́же 1544 | упрощенке 1545 | учеткой 1546 | файберы 1547 | Фаликман 1548 | фан 1549 | федеративность 1550 | фейковым 1551 | Фидонет 1552 | фикстур 1553 | фикстурами 1554 | фикстуру 1555 | фикстуры 1556 | фича 1557 | фичи 1558 | флешек 1559 | флешку 1560 | фолбэки 1561 | форк 1562 | форком 1563 | фреймворкам 1564 | фреймворках 1565 | фреймворке 1566 | фронтенд 1567 | Хабра 1568 | хачапури 1569 | Хейлсберг 1570 | хендл 1571 | Хидо 1572 | Хоара 1573 | хостить 1574 | хэндлу 1575 | Цеттелькастен 1576 | чанк 1577 | чанки 1578 | человекочитаемые 1579 | четырёхшаговый 1580 | шаблонизатором 1581 | Шард 1582 | шардирования 1583 | шестнадцатиричном 1584 | шорткатами 1585 | ы́ 1586 | эксабайтами 1587 | электрощитке 1588 | эмбеддинги 1589 | юнит 1590 | YugabyteDB 1591 | -------------------------------------------------------------------------------- /xmbc_settings.xmbcp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | -------------------------------------------------------------------------------- /configure_win.ps1: -------------------------------------------------------------------------------- 1 | function New-Hardlink() { New-Item -ItemType HardLink -Force @args; } 2 | function New-Softlink() { New-Item -ItemType SymbolicLink -Force @args; } 3 | function New-Dir() { New-Item -ItemType Directory -Force @args; } 4 | function New-File() { New-Item -ItemType File -Force @args; } 5 | 6 | class App { 7 | 8 | #region Instance properties 9 | $_ver = "1.0.25"; 10 | $_isTest = $false; 11 | $_isFull = $false; 12 | $_isUpdateEnv = $false; 13 | $_isPublic = $false; 14 | $_POST_INSTALL_MSG = ""; 15 | $_keepassdb = $null; 16 | $_pass = $null; 17 | $_cfgDirLinux = $null; 18 | $_cfgDir = $null; 19 | $_psDir = $null; 20 | $_pathIntrinsics = $null; 21 | $_github = @{ 22 | user = "foo"; 23 | pass = "bar"; 24 | token = "baz"; 25 | }; 26 | $_mqtt = @{ 27 | url = $null; 28 | user = $null; 29 | pass = $null; 30 | }; 31 | $_vk = @{ 32 | cc_token = $null; 33 | }; 34 | #endregion 35 | 36 | 37 | App($argList, $pathIntrinsics) { 38 | $this._pathIntrinsics = $pathIntrinsics; 39 | $this._isTest = ($argList.Contains("--test")); 40 | $this._isFull = ($argList.Contains("--full")); 41 | $this._isUpdateEnv = ($argList.Contains("--update-env")); 42 | # Do not touch private info like passwords, personal kb etc. 43 | $this._isPublic = ($argList.Contains("--public")); 44 | # Version-controlled dir with scripts, powershell config, passwords etc. 45 | $this._cfgDirLinux = "~/dotfiles"; 46 | $this._cfgDir = $this._path(@("~", "dotfiles")); 47 | $this._psDir = $this._path(@("~", "Documents", "PowerShell")); 48 | $this._POST_INSTALL_MSG = @" 49 | Config complete. Manual things to do 50 | - Reboot and make --full configuration 51 | - Load X-Mouse Button Control settings 52 | * Disable 'Settings/Pointer/Change cursor when move to scroll' 53 | * Map Mouse4 => 'change movement to scroll' with setings: 54 | * Sensitivity 1 55 | * Invert axis 56 | - Install "taskbar on top" and "taskbar styler" via WindHawk 57 | - Disable adaptive contrast for the built-in Intel GPU, if any 58 | - "Change Proxy Settings", Turn off "Automatically Detect Settings" 59 | - Add C-S-4-5-6 as en-ru-js hotkeys in Time/Lang/Typing/Advanced/Keys 60 | - Unassign switch between languages in Time/Lang/Typing/Advanced/Keys 61 | - copy lang settings via Time/Lang/Administrative" 62 | - Disable spam in /System/Notification/Additional 63 | - Disable autostart in /Apps 64 | - Disable autostart in Task Manager 65 | - Disable autostart in Task Scheduler 66 | - Disable snap assist 67 | - Disable touchpad click and set maximum speed 68 | - Pin Term, VSCode, Web, Double, Pass, Tg, Spark, NotionC, Trello 69 | - Uninstall 'OneDrive' and other software 70 | - Login and sync browser 71 | - Switch nVidia display mode to "optimus" and drag gpu activity icon 72 | - Set Settings/Accounts/Sign-in/Sign-in to "Every time" 73 | - Set Settings/System/Power/Saver/Auto to "Never" 74 | - Uncheck "Sniping tool" in Settings/Accessibility/Keyboard 75 | - Set terminal "Color Schemes" default to "Tango Dark" 76 | - Disable ASUS "lightingservice", if any 77 | - Disable G-Sync in the nVidia settings 78 | - Disable the "SSDP Discovery" service 79 | - Disable Battle.net launcher "gpu acceleration" 80 | - Configure Win11 for top taskbar with small icons 81 | "@; 82 | } 83 | 84 | 85 | configure() { 86 | Write-Host "Running configuration script v$($this._ver)"; 87 | # For 'Install-Module' 88 | Set-PSRepository -Name "PSGallery" -InstallationPolicy Trusted; 89 | 90 | if (-not $this._isTest) { 91 | if (-not (Test-Path -Path "$($this._cfgDir)")) { 92 | New-Dir -Path "$($this._cfgDir)"; 93 | } 94 | } 95 | 96 | # Auto-created by PowerShell 5.x until 6.x+ is a system default. 97 | # Create and set hidden attribute to exclude from 'ls'. 98 | if (-not $this._isTest) { 99 | $oldPsDir = $this._path(@("~", "Documents", "WindowsPowerShell")); 100 | if (-not (Test-Path -Path "$oldPsDir")) { 101 | Write-Host "Creating dir $oldPsDir"; 102 | $ret = & New-Dir -Path "$oldPsDir"; 103 | $ret.Attributes = 'Hidden'; 104 | } 105 | else { 106 | Write-Host "$oldPsDir already exists"; 107 | } 108 | } 109 | 110 | # PowerShell config is loaded from this dir. 111 | # Create and set hidden attribute to exclude from 'ls'. 112 | if (-not $this._isTest) { 113 | if (-not (Test-Path -Path "$($this._psDir)")) { 114 | Write-Host "Creating dir $($this._psDir)"; 115 | $ret = & New-Dir -Path "$($this._psDir)"; 116 | $ret.Attributes = 'Hidden'; 117 | } 118 | else { 119 | Write-Host "$($this._psDir) already exists"; 120 | } 121 | } 122 | 123 | Write-Host "Downloading let's encrypt root certificate..." 124 | $url = "https://letsencrypt.org/certs/isrgrootx1.pem"; 125 | $certFile = $this._path(@("~", "isrgrootx1.pem")); 126 | Invoke-WebRequest $url -OutFile $certFile 127 | $this._setEnv("MQTT_CERT", $certFile); 128 | 129 | # Game compatibility 130 | $this._setEnv("OPENSSL_ia32cap", "~0x20000000"); 131 | 132 | # uv tool install target 133 | $this._addToPath($this._path(@("~", ".local", "bin"))); 134 | 135 | if (-not $this._isTest) { 136 | # Ensure at least 1.9 version for "add to path" manifest flag 137 | & winget upgrade winget; 138 | # Enable install from manifests 139 | & winget settings --enable LocalManifestFiles 140 | } 141 | 142 | $this._installApp("Microsoft.VCRedist.2015+.x64"); 143 | # Requires reboot for a second stage install 144 | $this._installWsl(); 145 | $this._installPowershellModule("posh-git"); 146 | $this._installPowershellModule("WindowsCompatibility"); 147 | $this._generateSshKey(); 148 | $this._setPowerOptions(); 149 | $this._setDebounceOptions(); 150 | $this._setTouchpadOptions(); 151 | $this._setInputMethodOptions(); 152 | $this._installBinApp("Git.Git", $this._path( 153 | @(${env:ProgramFiles}, "Git", "cmd"))); 154 | # Clone without keys via HTTPS 155 | $this._getFilesFromGit(); 156 | $this._installLocationApp("AutoHotkey.AutoHotkey", ""); 157 | $this._uninstallApp("Microsoft.OneDrive"); 158 | $this._uninstallApp("Microsoft.Teams"); 159 | $this._uninstallApp("Copilot"); 160 | # TODO: Need version 2.20.4, in 2.20.5 "{WAITMS:100}{LMB}" does not work. 161 | # https://dvps.highrez.co.uk/downloads/XMouseButtonControlSetup.2.20.4.exe 162 | # Auto registers to run on startup 163 | $this._installApp("Highresolution.X-MouseButtonControl"); 164 | $this._installBinApp("KeePassXCTeam.KeePassXC", $this._path( 165 | @($env:ProgramFiles, "KeePassXC"))); 166 | $this._installBinApp("Microsoft.VisualStudioCode", $this._path( 167 | @($env:LOCALAPPDATA, "Programs", "Microsoft VS Code", "bin"))); 168 | $this._configureVscode(); 169 | # Better ls 170 | $this._installApp("lsd-rs.lsd"); 171 | $this._configureLsd(); 172 | # TODO: install batteryinfoview via winget like "NirSoft.WifiInfoView" 173 | # this._installLocationApp("NirSoft.BatteryInfoView", "") 174 | # $this._configureBatteryInfoView(); 175 | $this._installApp("strayge.tray-monitor"); 176 | $this._installApp("windhawk"); 177 | # TODO: install wox 2.0.0-beta5 178 | # ! Seems it conflicts with AutoHotkey, it should be started AFTER wox 179 | # $this._installApp("Wox.Wox"); # this installs wox 1.x 180 | 181 | if (-not $this._isPublic) { 182 | $markerPath = $this._path(@("~", ".ssh", ".uploaded_to_github")); 183 | $sshUploaded = (Test-Path -Path "$markerPath"); 184 | # Interactive. 185 | if (-not $sshUploaded -or $this._isUpdateEnv) { 186 | $this._askForCredentials(); 187 | $this._setEnv("MQTT_URL", $this._mqtt.url); 188 | $this._setEnv("MQTT_USER", $this._mqtt.user); 189 | $this._setEnv("MQTT_PASS", $this._mqtt.pass); 190 | $this._setEnv("VK_CC_TOKEN", $this._vk.cc_token); 191 | } 192 | if (-not $sshUploaded) { 193 | $this._uploadSshKey(); 194 | } 195 | # Re-clone with SSH keys 196 | $this._getFilesFromGit(); 197 | } 198 | 199 | # After additional files are received 200 | 201 | # TODO: wait for https://github.com/microsoft/winget-pkgs/pull/178129, 202 | $this._installAppFromManifest("EFLFE.PingoMeter"); 203 | $this._configurePingoMeter(); 204 | 205 | # Register startup after all additional files are received since 206 | # starting apps like autohotkey blocks config files 207 | $this._registerPingometerStartup(); 208 | $this._registerAutohotkeyStartup(); 209 | # TODO: wait for BatteryInfoView install 210 | # $this._registerBatteryInfoViewStartup(); 211 | # TODO: add wox startup BEFORE autohotkey 212 | 213 | # Interactive 214 | $this._mapKeyboard(); 215 | 216 | # Interactive. 217 | $this._installFonts(); 218 | 219 | $this._getXiWindows(); 220 | 221 | # Symlink PowerShel config file into PowerShell config dir. 222 | if (-not $this._isTest) { 223 | $src = $this._path(@($this._cfgDir, "profile.ps1")); 224 | $dst = $this._path(@($this._psDir, "profile.ps1")); 225 | if (Test-Path -Path "$dst") { 226 | Remove-Item "$dst" -Recurse -Force; 227 | } 228 | Write-Host "Creating softlink $src => $dst"; 229 | # Hardlink is overwritten by powershell 230 | New-Softlink -Path "$($this._psDir)" -Name "profile.ps1" -Value "$src"; 231 | } 232 | 233 | # Create git config with link to the git-cfg.toml 234 | if (-not $this._isTest) { 235 | $src = $this._path(@($this._cfgDir, ".gitconfig")); 236 | $dst = $this._path(@("~", ".gitconfig")); 237 | if (Test-Path -Path "$dst") { 238 | Remove-Item "$dst" -Recurse -Force; 239 | } 240 | $content = "[include]`npath = `"$($this._cfgDirLinux)/git-cfg.toml`"`n"; 241 | New-File -Path "~" -Name ".gitconfig" -Value "$content"; 242 | } 243 | 244 | if (-not (Test-Path -Path ".editorconfig")) { 245 | $src = $this._path(@($this._cfgDir, ".editorconfig")); 246 | Copy-Item -Path "$src" -Destination . -Force; 247 | } 248 | 249 | # Hide search in the taskbar 250 | $path = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Search" 251 | Set-ItemProperty -Path $path -Name SearchboxTaskbarMode -Value 0 252 | 253 | # Optional installs after reboot 254 | if ($this._isFull) { 255 | # for diff-so-fancy 256 | $this._installBinApp("StrawberryPerl.StrawberryPerl", 257 | "C:\Strawberry\perl\bin\"); 258 | # nvm command 259 | $this._installBinAppWithVer( 260 | "CoreyButler.NVMforWindows", 261 | $this._path(@($env:APPDATA, "nvm")), 262 | # version 1.1.12 fails "non-terminal" execution 263 | "1.1.11"); 264 | # For nvm command to work in an existing terminal 265 | $env:NVM_HOME = $this._path(@($env:APPDATA, "nvm")); 266 | # Node.js 267 | Write-Host "Installing latest nodejs"; 268 | & nvm on 269 | & nvm install latest 270 | & nvm use latest 271 | $nodePath = $this._path(@($env:ProgramFiles, "nodejs")); 272 | if (-not (Test-Path -Path $nodePath)) { 273 | throw "run 'nvm use latest' manually"; 274 | } 275 | if (-not $env:PATH.Contains($nodePath)) { 276 | $env:PATH = "${env:PATH};$nodePath"; 277 | } 278 | Write-Host "Updating npm" 279 | & npm install -g npm@latest 280 | # Better diff 281 | & npm install -g diff-so-fancy 282 | # USB camera control 283 | & npm install -g uvcc 284 | # General-purpose messaging. 285 | $this._installApp("Telegram.TelegramDesktop"); 286 | # "Offline" google apps support and no telemetry delays line in "Edge". 287 | $this._installApp("Google.Chrome"); 288 | # file management 289 | $this._installApp("alexx2000.DoubleCommander"); 290 | # PDF view, this is the last version that supports bookmarks save 291 | $this._installAppWithVer("Foxit.FoxitReader", "2023.2.0.21408"); 292 | # Better process maangement 293 | $this._installApp("Microsoft.Sysinternals.ProcessExplorer"); 294 | # Desktop recording. 295 | $this._installApp("OBSProject.OBSStudio"); 296 | # TODO: configure to save position on exit 297 | $this._installApp("clsid2.mpc-hc"); 298 | # ag command, "the silver searcher" 299 | $this._installApp("JFLarvoire.Ag"); 300 | # screenshot tool, "sniping tool" corrupts colors 301 | $this._installApp("Flameshot.Flameshot"); 302 | # for g-helper 303 | $this._installApp("Microsoft.DotNet.DesktopRuntime.7"); 304 | # for mosquitto_pub 305 | $this._installBinApp("EclipseFoundation.Mosquitto", $this._path( 306 | @($env:ProgramFiles, "mosquitto"))); 307 | # for keylight control 308 | $this._installApp("Elgato.ControlCenter"); 309 | # ChatGPT 310 | $this._installApp("9NT1R1C2HH7J"); 311 | # Notion App 312 | $this._installApp("Notion.Notion"); 313 | # Notion Calendar 314 | $this._installApp("Notion.NotionCalendar"); 315 | # Torrent client 316 | $this._installApp("qBittorrent.qBittorrent"); 317 | # Video player 318 | $this._installApp("VideoLAN.VLC"); 319 | # Trello 320 | $this._installApp("9NBLGGH4XXVW"); 321 | # Spark email client 322 | $this._installApp("XPFCS9QJBKTHVZ"); 323 | # Remote keyboard and mouse 324 | $this._installApp("Deskflow.Deskflow"); 325 | # Discord client 326 | $this._installApp("Discord.Discord"); 327 | # EpicGames client 328 | $this._installApp("EpicGames.EpicGamesLauncher"); 329 | } 330 | 331 | if ($this._isTest) { 332 | Write-Host "Test complete"; 333 | } 334 | else { 335 | Write-Host $this._POST_INSTALL_MSG; 336 | } 337 | } 338 | 339 | 340 | [Boolean] _hasCli($name) { 341 | if ($this._isTest) { return $false; } 342 | Get-Command $name -ErrorAction SilentlyContinue; 343 | return $?; 344 | } 345 | 346 | 347 | [Boolean] _isAppStatusInstalled($appName) { 348 | if ($this._isTest) { return $false; } 349 | & winget list $appName; 350 | return ($LASTEXITCODE -eq 0); 351 | } 352 | 353 | 354 | [String] _path([array] $pathList) { 355 | return $this._pathIntrinsics.GetUnresolvedProviderPathFromPSPath( 356 | [io.path]::combine([string[]]$pathList)); 357 | } 358 | 359 | 360 | _installApp($appName) { 361 | if ($this._isTest) { return; } 362 | if ($this._isAppStatusInstalled($appName)) { 363 | Write-Host "$appName is already installed"; 364 | return; 365 | } 366 | Write-Host "Installing $appName" 367 | & winget install --silent $appName; 368 | if ($LASTEXITCODE -ne 0) { throw "Failed to install $appName" } 369 | } 370 | 371 | 372 | _installAppWithVer($appName, $ver) { 373 | if ($this._isTest) { return; } 374 | if ($this._isAppStatusInstalled($appName)) { 375 | Write-Host "$appName is already installed"; 376 | return; 377 | } 378 | Write-Host "Installing $appName, version $ver" 379 | & winget install --silent $appName --version $ver; 380 | if ($LASTEXITCODE -ne 0) { throw "Failed to install $appName" } 381 | } 382 | 383 | 384 | _installAppFromManifest($appName) { 385 | if ($this._isTest) { return; } 386 | if ($this._isAppStatusInstalled($appName)) { 387 | Write-Host "$appName is already installed"; 388 | return; 389 | } 390 | $manifestPath = $this._path(@( 391 | $this._cfgDir, 392 | "winget", 393 | "manifests", 394 | $appName 395 | )); 396 | Write-Host "Installing $appName from $manifestPath" 397 | & winget install --silent --manifest $manifestPath; 398 | if ($LASTEXITCODE -ne 0) { throw "Failed to install $appName" } 399 | } 400 | 401 | 402 | _uninstallApp($appName) { 403 | if ($this._isTest) { return; } 404 | if (-not $this._isAppStatusInstalled($appName)) { 405 | Write-Host "$appName is already uninstalled"; 406 | return; 407 | } 408 | Write-Host "Uninstalling $appName" 409 | & winget uninstall --silent $appName; 410 | # There is no reason to check error code since uninstallers tend to 411 | # show error codes upon successfull uninstall 412 | } 413 | 414 | 415 | # For installers that add something to PATH (requires terminal restart) 416 | _installBinApp($appName, $binPath) { 417 | $this._installBinAppWithVer($appName, $binPath, $null); 418 | } 419 | 420 | _installBinAppWithVer($appName, $binPath, $ver) { 421 | if ($this._isTest) { return; } 422 | if ($this._isAppStatusInstalled($appName)) { 423 | Write-Host "$appName is already installed"; 424 | if (-not $env:PATH.Contains($binPath)) { 425 | $env:PATH = "${env:PATH};$binPath"; 426 | } 427 | return; 428 | } 429 | if ($ver) { 430 | Write-Host "Installing $appName with binary in path, version $ver" 431 | & winget install --silent $appName --version $ver --no-upgrade --exact; 432 | } 433 | else { 434 | Write-Host "Installing $appName with binary in path" 435 | & winget install --silent $appName; 436 | } 437 | if ($LASTEXITCODE -ne 0) { throw "Failed to install $appName" } 438 | if (-not $env:PATH.Contains($binPath)) { 439 | $env:PATH = "${env:PATH};$binPath"; 440 | } 441 | $this._addToPath($binPath); 442 | } 443 | 444 | 445 | # For installers that require install location to be specified 446 | _installLocationApp($appName, $binSubpath) { 447 | $location = $this._path(@("~", "apps", $appName)); 448 | $binPath = $this._path(@($location, $binSubpath)); 449 | if ($this._isTest) { return; } 450 | if ($this._isAppStatusInstalled($appName)) { 451 | Write-Host "$appName is already installed"; 452 | if (-not $env:PATH.Contains($binPath)) { 453 | $env:PATH = "${env:PATH};$binPath"; 454 | } 455 | return; 456 | } 457 | Write-Host "Installing $appName into $location" 458 | & winget install --silent --location $location $appName; 459 | if ($LASTEXITCODE -ne 0) { throw "Failed to install $appName" } 460 | if (-not $env:PATH.Contains($binPath)) { 461 | $env:PATH = "${env:PATH};$binPath"; 462 | } 463 | $this._addToPath($binPath); 464 | } 465 | 466 | 467 | _installPowershellModule($moduleName) { 468 | Write-Host "Installing $moduleName"; 469 | if ($this._isTest) { return; } 470 | if (Get-InstalledModule | Where-Object Name -eq $moduleName) { return; } 471 | Install-Module $moduleName -Scope CurrentUser; 472 | if (-not $?) { throw "Failed" } 473 | } 474 | 475 | 476 | _installWsl() { 477 | Write-Host "Installing WSL"; 478 | if ($this._isTest) { return; } 479 | 480 | & wsl --status; 481 | if ($LASTEXITCODE -eq 0) { 482 | if (Test-Path -Path "\\wsl$\Ubuntu") { 483 | Write-Host "WSL is already installed"; 484 | $wslSshDir = "\\wsl$\Ubuntu\home\user\.ssh" 485 | if (Test-Path -Path "$wslSshDir" ) { 486 | Write-Host "Keys are already copied to WSL" 487 | return; 488 | } 489 | else { 490 | Write-Host "Creating wsl://~/.ssh"; 491 | New-Dir -Path "$wslSshDir"; 492 | Write-Host "Copying keys to wsl://~/.ssh"; 493 | $srcPath = $this._path(@("~", ".ssh", "id_rsa")) 494 | Copy-Item -Path "$srcPath" -Destination "$wslSshDir" -Force; 495 | $srcPath = $this._path(@("~", ".ssh", "id_rsa.pub")) 496 | Copy-Item -Path "$srcPath" -Destination "$wslSshDir" -Force; 497 | & wsl chmod 600 ~/.ssh/id_rsa 498 | return; 499 | } 500 | } 501 | else { 502 | # Need to be install two times: before and after reboot 503 | Write-Host "Installing WSL. Create a 'user' user with a password"; 504 | Start-Process wsl -ArgumentList "--install" -Wait; 505 | return; 506 | } 507 | } 508 | else { 509 | Start-Process wsl -ArgumentList "--install" -Wait; 510 | return; 511 | } 512 | } 513 | 514 | 515 | _getFilesFromGit() { 516 | if ($this._isTest) { return; } 517 | $gitCfgFile = $this._path(@($this._cfgDir, ".git", "config")); 518 | if (Test-Path -Path "$gitCfgFile") { 519 | $gitCfg = Get-Content "$gitCfgFile" | Out-String; 520 | # Already cloned with SSH? 521 | if ($gitCfg.Contains("git@github.com")) { 522 | Write-Host "dotfiles already cloned via ssh"; 523 | return; 524 | } 525 | } 526 | 527 | # Have keys to clone with SSH? 528 | $markerPath = $this._path(@("~", ".ssh", ".uploaded_to_github")); 529 | if (Test-Path -Path "$markerPath") { 530 | $uri = "git@github.com:grigoryvp/dotfiles.git"; 531 | } 532 | else { 533 | # Already cloned without keys? 534 | if (Test-Path -Path "$gitCfgFile") { 535 | Write-Host "dotfiles already cloned via https"; 536 | return; 537 | } 538 | # Clone with HTTPS 539 | $uri = "https://github.com/grigoryvp/dotfiles.git"; 540 | } 541 | 542 | $tmpDirName = $this._path(@("~", "dotfiles-tmp")); 543 | if (Test-Path -Path "$tmpDirName") { 544 | Write-Host "Removing existing temp dir $tmpDirName" 545 | Remove-Item "$tmpDirName" -Recurse -Force; 546 | } 547 | # May hang: https://gitlab.com/gitlab-org/gitlab/-/issues/499350 548 | Write-Host "git clone $uri $tmpDirName"; 549 | & git clone --quiet "$uri" "$tmpDirName"; 550 | # Replace HTTP git config with SSH one, if any. 551 | Write-Host "Removing current dir $($this._cfgDir)" 552 | Remove-Item "$($this._cfgDir)" -Recurse -Force; 553 | Write-Host "Recreating config dir $($this._cfgDir)" 554 | New-Dir -Path $this._cfgDir; 555 | Write-Host "Moving files $tmpDirName => $($this._cfgDir)"; 556 | Move-Item -Force "$tmpDirName/*" "$($this._cfgDir)"; 557 | Write-Host "Removing temp dir $tmpDirName"; 558 | Remove-Item "$tmpDirName" -Recurse -Force; 559 | } 560 | 561 | 562 | _generateSshKey() { 563 | if ($this._isTest) { return; } 564 | if (Test-Path -Path $this._path(@("~", ".ssh", "id_rsa"))) { 565 | Write-Host "ssh key already generated"; 566 | return; 567 | } 568 | $sshDir = $this._path(@("~", ".ssh")); 569 | if (-not (Test-Path -Path "$sshDir" )) { 570 | Write-Host "Creating ~/.ssh"; 571 | New-Dir -Path "$sshDir"; 572 | } 573 | Write-Host "Generating ssh key"; 574 | Start-Process ssh-keygen -ArgumentList '-N "" -f .ssh/id_rsa' -Wait; 575 | } 576 | 577 | 578 | _setPowerOptions() { 579 | if ($this._isTest) { return; } 580 | Write-Host "Setting power policy"; 581 | powercfg -change -monitor-timeout-ac 120; 582 | powercfg -change -monitor-timeout-dc 120; 583 | powercfg -change -disk-timeout-ac 0; 584 | powercfg -change -disk-timeout-dc 0; 585 | powercfg -change -standby-timeout-ac 0; 586 | powercfg -change -standby-timeout-dc 0; 587 | powercfg -change -hibernate-timeout-ac 0; 588 | powercfg -change -hibernate-timeout-dc 0; 589 | # Set 'plugged in' cooling policy to 'active' 590 | powercfg -setacvalueindex scheme_current 54533251-82be-4824-96c1-47b60b740d00 94d3a615-a899-4ac5-ae2b-e4d8f634367f 1 591 | # Set 'on battery' cooling policy to 'active' 592 | #! If set to 'passive' will downclock cpu to minimum, unusable 593 | powercfg -setdcvalueindex scheme_current 54533251-82be-4824-96c1-47b60b740d00 94d3a615-a899-4ac5-ae2b-e4d8f634367f 1 594 | } 595 | 596 | 597 | _setDebounceOptions() { 598 | if ($this._isTest) { return; } 599 | Write-Host "Setting touchpad debounce options"; 600 | 601 | $argmap = @{ 602 | Path = "HKCU:\Control Panel\Accessibility\Keyboard Response" 603 | PropertyType = "String" 604 | Force = $true 605 | } 606 | 607 | # Ms before key is repeated 608 | $argmap.Name = 'AutoRepeatDelay'; 609 | $argmap.Value = '300'; 610 | New-ItemProperty @argmap; 611 | 612 | # Less is faster 613 | $argmap.Name = 'AutoRepeatRate'; 614 | $argmap.Value = '30'; 615 | New-ItemProperty @argmap; 616 | 617 | # Milliseconds to supres bounce (with 30ms it RARELY bounces). 618 | #! On some laptops like Dell 5490 setting this value will result in fast 619 | # double presses not handled. 620 | $argmap.Name = 'BounceTime'; 621 | $argmap.Value = '35'; 622 | New-ItemProperty @argmap; 623 | 624 | # Milliseconds to wait before accepting a keystroke 625 | $argmap.Name = 'DelayBeforeAcceptance'; 626 | $argmap.Value = '0'; 627 | New-ItemProperty @argmap; 628 | 629 | # Bit Flags: 630 | # 00000001 On 631 | # 00000010 Available 632 | # 00000100 Use shortcut 633 | # 00001000 Confirm activation 634 | # 00010000 Activation sound 635 | # 00100000 Show status 636 | # 01000000 Key click 637 | $argmap.Name = 'Flags'; 638 | $argmap.Value = '1'; 639 | New-ItemProperty @argmap; 640 | } 641 | 642 | 643 | [Boolean] _needMapCapsToF24() { 644 | if ($this._isTest) { return $false; } 645 | $val = Get-ItemProperty ` 646 | -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Keyboard Layout" ` 647 | -Name "Scancode Map" ` 648 | -ErrorAction SilentlyContinue; 649 | if ($val) { 650 | $len = $val.'Scancode Map'.Length; 651 | # Already set? 652 | if ($len -eq 20) { return $false; } 653 | } 654 | return $true; 655 | } 656 | 657 | 658 | # Remapped via RandyRants.SharpKeys 659 | _mapKeyboard() { 660 | if (-not $this._needMapCapsToF24()) { 661 | Write-Host "caps already mapped to F24"; 662 | return; 663 | } 664 | 665 | Write-Host "remapping keyboard"; 666 | # caps to F24 (m1) 667 | # esc to alt (alt availability for left-hand keypad gaming) 668 | # left alt to esc (m2 and left-hand keypad gaming) 669 | # right alt for enter (m3, enter key for login screen and w/o ahk) 670 | # tab to lctrl (ahk changes single key back to tab) 671 | # enter to rctrl (autohotkey change single key back to enter) 672 | # left ctrl to left win for left-hand keypad gaming (no win key) 673 | $val = ([byte[]]( 674 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 675 | 0x08, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x3A, 0x00, 676 | 0x1D, 0xE0, 0x1C, 0x00, 0x38, 0x00, 0x01, 0x00, 677 | 0x01, 0x00, 0x38, 0x00, 0x5B, 0xE0, 0x1D, 0x00, 678 | 0x1C, 0x00, 0x38, 0xE0, 0x1D, 0x00, 0x0F, 0x00, 679 | 0x00, 0x00, 0x00, 0x00 680 | )); 681 | New-ItemProperty ` 682 | -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Keyboard Layout" ` 683 | -Name "Scancode Map" ` 684 | -PropertyType "Binary" ` 685 | -Value $val ` 686 | -Force 687 | } 688 | 689 | 690 | _setTouchpadOptions() { 691 | if ($this._isTest) { return; } 692 | $root = "HKCU:\Software\Microsoft\Windows\CurrentVersion"; 693 | $uri = "$root\PrecisionTouchPad"; 694 | $propName = "AAPThreshold"; 695 | $prop = Get-ItemProperty $uri -Name $propName; 696 | if ($prop) { 697 | $val = $prop.AAPThreshold; 698 | if ($val -eq 0) { return; } 699 | } 700 | # Requires reboot. 701 | Set-ItemProperty $uri -Name $propName -Type Dword -Value 0; 702 | } 703 | 704 | 705 | _setInputMethodOptions() { 706 | if ($this._isTest) { return; } 707 | $current = & pwsh -Command Get-WinUserLanguageList | Out-String; 708 | if (-not $current.Contains("Russian")) { 709 | Write-Host "Adding Russian language"; 710 | $cmd = '' + 711 | '$list = Get-WinUserLanguageList;' + 712 | '$list.Add("ru");' + 713 | 'Set-WinUserLanguageList -Force $list;'; 714 | & pwsh -Command $cmd; 715 | } 716 | if (-not $current.Contains("Japanese")) { 717 | Write-Host "Adding Japanese language"; 718 | $cmd = '' + 719 | '$list = Get-WinUserLanguageList;' + 720 | '$list.Add("ja");' + 721 | 'Set-WinUserLanguageList -Force $list;'; 722 | & pwsh -Command $cmd; 723 | } 724 | } 725 | 726 | 727 | [String] _attrFromKeepass($record, $attr) { 728 | #! keepassxc-cli displays the "enter password" promt into stderr and 729 | # powershell throws the NativeCommandError exception if program output 730 | # to stderr (redirect does not help). 731 | $ErrorActionPreference = "SilentlyContinue"; 732 | $ret = $null; 733 | # -s to show protected attribute (password) as clear text. 734 | $ret = $( 735 | Write-Output $this._pass | 736 | keepassxc-cli show -s $this._keepassdb $record --attributes $attr 737 | 2>$null 738 | ); 739 | $ErrorActionPreference = "Stop"; 740 | if (-not $?) { throw "keepassxc-cli failed" } 741 | return $ret; 742 | } 743 | 744 | 745 | _askForCredentials() { 746 | $pass = Read-Host -AsSecureString -Prompt "Enter password" 747 | 748 | $ptr = [Security.SecureStringMarshal]::SecureStringToCoTaskMemAnsi($pass); 749 | $pass = [Runtime.InteropServices.Marshal]::PtrToStringAnsi($ptr); 750 | $this._pass = $pass; 751 | $this._keepassdb = $this._path(@($this._cfgDir, "auth/passwords.kdbx")); 752 | 753 | $this._github.user = $this._attrFromKeepass("github", "username"); 754 | $this._github.pass = $this._attrFromKeepass("github", "password"); 755 | $this._github.token = $this._attrFromKeepass("github", "auto-cfg-token"); 756 | 757 | $this._mqtt.url = $this._attrFromKeepass("hivemq", "login_url"); 758 | $this._mqtt.user = $this._attrFromKeepass("hivemq", "login_user"); 759 | $this._mqtt.pass = $this._attrFromKeepass("hivemq", "login_pass"); 760 | 761 | $record = "vk.gvp-url-shortener"; 762 | $this._vk.cc_token = $this._attrFromKeepass($record, "token"); 763 | } 764 | 765 | 766 | _uploadSshKey() { 767 | $marker = ".uploaded_to_github"; 768 | if (Test-Path -Path $this._path(@("~", ".ssh", "$marker"))) { return; } 769 | 770 | $pair = "$($this._github.user):$($this._github.token)"; 771 | $bytes = [System.Text.Encoding]::ASCII.GetBytes($pair); 772 | $creds = [System.Convert]::ToBase64String($bytes) 773 | $headers = @{Authorization = "Basic $creds";} 774 | $body = ConvertTo-Json @{ 775 | title = "box key $(Get-Date)"; 776 | key = (Get-Content "~/.ssh/id_rsa.pub" | Out-String); 777 | } 778 | $url = "https://api.github.com/user/keys" 779 | if (-not $this._isTest) { 780 | try { 781 | Invoke-WebRequest -Method 'POST' -Headers $headers -Body $body $url; 782 | } 783 | catch { 784 | if ($_.Exception.Response.StatusCode -eq 422) { 785 | Write-Host "ssh key already added to GitHub"; 786 | New-File -Path .ssh -Name $marker; 787 | } 788 | elseif ($_.Exception.Response.StatusCode -eq 401) { 789 | # TODO: try to upload via auth token. 790 | Write-Host "Failed to add key to GitHub"; 791 | Write-Host "Upload manually and touch .ssh/${marker}"; 792 | Write-Host "Login: '$($this._github.user)'"; 793 | Write-Host "Pass: '$($this._github.pass)'"; 794 | Write-Host "REBOOT IF FIRST INSTALL (to correctly install WSL)"; 795 | throw "Failed"; 796 | } 797 | else { 798 | throw "Failed $($_.Exception)"; 799 | } 800 | } 801 | New-File -Path "~/.ssh" -Name $marker; 802 | } 803 | } 804 | 805 | 806 | _prompt($msg) { 807 | if ($this._isTest) { return; } 808 | Write-Host -NoNewLine $msg; 809 | [System.Console]::ReadKey("NoEcho,IncludeKeyDown"); 810 | Write-Host ""; 811 | } 812 | 813 | 814 | _registerAutohotkeyStartup() { 815 | if ($this._isTest) { return; } 816 | 817 | $name = "autohotkey"; 818 | 819 | $argmap = @{ 820 | Execute = $this._path(@( 821 | "~", "apps", "AutoHotkey.AutoHotkey", "v2", "autohotkey.exe")) 822 | Argument = $this._path(@($this._cfgDir, "keyboard.ahk")) 823 | } 824 | $action = New-ScheduledTaskAction @argmap; 825 | $trigger = New-ScheduledTaskTrigger -AtLogOn; 826 | # Delay for a few seconds, otherwise Windows will not display tray icon 827 | $trigger.Delay = 'PT10S' 828 | 829 | $task = Get-ScheduledTask -TaskName $name -ErrorAction SilentlyContinue; 830 | if (-not $task) { 831 | Write-Host "Creating new AutoHotkey scheduled task"; 832 | Register-ScheduledTask -TaskName $name ` 833 | -Action $action -Trigger $trigger -RunLevel Highest; 834 | } 835 | else { 836 | Write-Host "Modifying existing AutoHotkey scheduled task"; 837 | } 838 | 839 | Set-ScheduledTask -TaskName $name -Action $action; 840 | 841 | Set-ScheduledTask -TaskName $name -Trigger $trigger; 842 | 843 | $settings = New-ScheduledTaskSettingsSet ` 844 | -ExecutionTimeLimit 0 ` 845 | -AllowStartIfOnBatteries ` 846 | -DontStopIfGoingOnBatteries; 847 | Set-ScheduledTask -TaskName $name -Settings $settings; 848 | } 849 | 850 | 851 | [Boolean] _needInstallFonst() { 852 | $path = $this._path(@("~", "apps", "nerd-fonts")); 853 | if (Test-Path -Path $path) { return $false; } 854 | return $true; 855 | } 856 | 857 | 858 | _installFonts() { 859 | if (-not $this._needInstallFonst()) { 860 | Write-Host "Fonts are already installed"; 861 | return; 862 | } 863 | 864 | $path = $this._path(@("~", "apps", "nerd-fonts")); 865 | $uri = "https://github.com/ryanoasis/nerd-fonts.git"; 866 | $fontName = "JetBrainsMono"; 867 | Write-Host "Cloning nerd-fonts into $path"; 868 | & git clone --quiet --depth 1 --filter=blob:none --sparse $uri $path; 869 | Set-Location $path; 870 | Write-Host "Checking out files for $fontName"; 871 | & git sparse-checkout add "patched-fonts/$fontName"; 872 | Write-Host "Installing $fontName"; 873 | & "./install.ps1" $fontName; 874 | } 875 | 876 | 877 | _getXiWindows() { 878 | if ($this._isTest) { return; } 879 | $uri = "git@github.com:grigoryvp/xi.git"; 880 | $dstDir = $this._path(@("~", ".xi")); 881 | if (Test-Path -Path "$dstDir") { 882 | Write-Host "xi already cloned"; 883 | return; 884 | } 885 | Write-Host "cloning xi into $dstDir"; 886 | & git clone $uri $dstDir; 887 | return; 888 | } 889 | 890 | 891 | # Not used since VSCode always opens host dir even if remoting WSL. 892 | _getXiWSL() { 893 | if ($this._isTest) { return; } 894 | $dstDir = "\\wsl$\Ubuntu\home\user\.xi" 895 | if (Test-Path -Path "$dstDir") { 896 | Write-Host "xi already cloned"; 897 | return; 898 | } 899 | Write-Host "cloning xi into $dstDir"; 900 | $uri = "git@github.com:grigoryvp/xi.git"; 901 | & wsl git clone $uri $dstDir; 902 | if ($LASTEXITCODE -ne 0) { throw "Failed" } 903 | } 904 | 905 | 906 | _installVscodeExt($extId) { 907 | $extList = @(& code --list-extensions); 908 | if (-not $extList.Contains($extId)) { 909 | & code --install-extension "$extId"; 910 | if ($LASTEXITCODE -ne 0) { throw "Failed to install $extId" } 911 | } 912 | } 913 | 914 | 915 | _configureVscode() { 916 | if ($this._isTest) { return; } 917 | $dstDir = $this._path(@($env:APPDATA, "Code", "User")); 918 | if (-not (Test-Path -Path "$dstDir")) { 919 | # Not created during install, only on first UI start. 920 | New-Dir -Path "$dstDir"; 921 | } 922 | 923 | # Use softlinks since VSCode rewrites hardlinks: 924 | # https://github.com/microsoft/vscode/issues/194856 925 | 926 | $srcPath = $this._path(@($this._cfgDir, "vscode_settings.json")); 927 | $name = "settings.json" 928 | Write-Host "Creating softlink $srcPath => $dstDir\$name"; 929 | New-Softlink -Path "$dstDir" -Name $name -Value "$srcPath"; 930 | 931 | $srcPath = $this._path(@($this._cfgDir, "vscode_keybindings.json")); 932 | $name = "keybindings.json" 933 | Write-Host "Creating softlink $srcPath => $dstDir\$name"; 934 | New-SoftLink -Path "$dstDir" -Name $name -Value "$srcPath"; 935 | 936 | $srcPath = $this._path(@($this._cfgDir, "vscode_tasks.json")); 937 | $name = "tasks.json" 938 | Write-Host "Creating softlink $srcPath => $dstDir\$name"; 939 | New-SoftLink -Path "$dstDir" -Name $name -Value "$srcPath"; 940 | 941 | $srcPath = $this._path(@($this._cfgDir, "vscode_snippets/")); 942 | $dstPath = $this._path(@($dstDir, "vscode_snippets/")); 943 | $name = "vscode_snippets/" 944 | if (Test-Path -Path "$dstPath") { 945 | Remove-Item "$dstPath" -Recurse -Force; 946 | } 947 | Write-Host "Creating dir softlink $srcPath => $dstDir\$name"; 948 | New-Softlink -Path "$dstDir" -Name $name -Value "$srcPath"; 949 | 950 | $this._installVscodeExt("grigoryvp.language-xi"); 951 | $this._installVscodeExt("grigoryvp.memory-theme"); 952 | $this._installVscodeExt("grigoryvp.goto-link-provider"); 953 | $this._installVscodeExt("grigoryvp.markdown-inline-fence"); 954 | $this._installVscodeExt("grigoryvp.markdown-python-repl-syntax"); 955 | $this._installVscodeExt("grigoryvp.markdown-pandoc-rawattr"); 956 | $this._installVscodeExt("vscodevim.vim"); 957 | $this._installVscodeExt("EditorConfig.EditorConfig"); 958 | $this._installVscodeExt("esbenp.prettier-vscode"); 959 | $this._installVscodeExt("formulahendry.auto-close-tag"); 960 | $this._installVscodeExt("dnut.rewrap-revived"); 961 | $this._installVscodeExt("streetsidesoftware.code-spell-checker"); 962 | $this._installVscodeExt("streetsidesoftware.code-spell-checker-russian"); 963 | $this._installVscodeExt("mark-wiemer.vscode-autohotkey-plus-plus"); 964 | 965 | $docCfgDir = $this._path(@("~", "Documents", ".vscode")); 966 | if (-not (Test-Path -Path "$docCfgDir")) { 967 | New-Dir -Path "$docCfgDir"; 968 | } 969 | 970 | $content = @' 971 | { 972 | "files.exclude": { 973 | "My Music/": true, 974 | "My Pictures/": true, 975 | "My Videos/": true, 976 | "My Games/": true, 977 | "Sound Recordings/": true, 978 | "Diablo IV/": true, 979 | "PowerShell": true, 980 | "WindowsPowerShell": true, 981 | "desktop.ini": true, 982 | ".vscode/": true 983 | } 984 | } 985 | '@; 986 | 987 | New-File -Path $docCfgDir -Name "settings.json" -Value "$content"; 988 | 989 | ## Exclude from 'ls'. 990 | $(Get-Item -Force $docCfgDir).Attributes = 'Hidden'; 991 | } 992 | 993 | 994 | _configureLsd() { 995 | if ($this._isTest) { return; } 996 | $dstDir = $this._path(@($env:APPDATA, "lsd")); 997 | if (-not (Test-Path -Path "$dstDir")) { 998 | New-Dir -Path "$dstDir"; 999 | } 1000 | $srcPath = $this._path(@($this._cfgDir, "lsd.config.yaml")); 1001 | New-Hardlink -Path "$dstDir" -Name "config.yaml" -Value "$srcPath"; 1002 | } 1003 | 1004 | 1005 | _configureBatteryInfoView() { 1006 | if ($this._isTest) { return; } 1007 | $name = "BatteryInfoView.cfg"; 1008 | $srcPath = $this._path(@($this._cfgDir, $name)); 1009 | $dstDir = $this._path(@("~", "apps", "NirSoft.BatteryInfoView")); 1010 | New-Hardlink -Path "$dstDir" -Name $name -Value "$srcPath"; 1011 | } 1012 | 1013 | 1014 | _configurePingoMeter() { 1015 | if ($this._isTest) { return; } 1016 | $srcPath = $this._path(@($this._cfgDir, "pingometer-cfg.txt")); 1017 | $dstDir = $this._path(@( 1018 | $env:LOCALAPPDATA, 1019 | "Microsoft", 1020 | "WinGet", 1021 | "Packages", 1022 | "EFLFE.PingoMeter__DefaultSource", 1023 | "PingoMeter" 1024 | )); 1025 | $dstFileName = "config.txt"; 1026 | $dstPath = $this._path(@($dstDir, $dstFileName)); 1027 | if (Test-Path -Path "$dstFileName") { 1028 | Remove-Item "$dstFileName" -Recurse -Force; 1029 | } 1030 | Write-Host "Creating hardlink $srcPath => $dstPath"; 1031 | New-Hardlink -Path "$dstDir" -Name "$dstFileName" -Value "$srcPath"; 1032 | } 1033 | 1034 | 1035 | _registerBatteryInfoViewStartup() { 1036 | if ($this._isTest) { return; } 1037 | $startDir = "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup" 1038 | if (Test-Path -Path "$startDir\battery-info-view.bat") { 1039 | Remove-Item "$startDir\battery-info-view.bat" -Recurse -Force; 1040 | } 1041 | $content = "pwsh -Command Start-Process BatteryInfoView.exe"; 1042 | $content += " -WindowStyle Hidden"; 1043 | $name = "battery-info-view.bat" 1044 | New-File -Path $startDir -Name $name -Value "$content"; 1045 | } 1046 | 1047 | 1048 | _registerPingometerStartup() { 1049 | if ($this._isTest) { return; } 1050 | $startDir = "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup" 1051 | if (Test-Path -Path "$startDir\pingometer.bat") { 1052 | Remove-Item "$startDir\pingometer.bat" -Recurse -Force; 1053 | } 1054 | $content = "set PKG_DIR=%LOCALAPPDATA%\Microsoft\WinGet\Packages" 1055 | $content += "`nset SRC_DIR=%PKG_DIR%\EFLFE.PingoMeter__DefaultSource" 1056 | $content += "`nset APP_DIR=%SRC_DIR%\PingoMeter" 1057 | $content += "`npwsh -Command Start-Process PingoMeter.exe"; 1058 | $content += " -WindowStyle Hidden"; 1059 | $content += " -WorkingDirectory %APP_DIR%"; 1060 | $name = "pingometer.bat"; 1061 | New-File -path $startDir -Name $name -Value "$content"; 1062 | } 1063 | 1064 | 1065 | _addToPath($subpath) { 1066 | $root = "HKLM:\SYSTEM\CurrentControlSet\Control"; 1067 | $uri = "$root\Session Manager\Environment"; 1068 | $name = "Path"; 1069 | $ret = Get-ItemProperty -Path $uri -Name $name ` 1070 | -ErrorAction SilentlyContinue; 1071 | if ($ret) { 1072 | $path = $ret.Path; 1073 | if (-not $path.Contains($subpath)) { 1074 | $path = "${path};$subpath"; 1075 | # Requires reboot 1076 | Set-ItemProperty $uri -Name $name -Value $path; 1077 | } 1078 | } 1079 | } 1080 | 1081 | 1082 | _setEnv($name, $val) { 1083 | $root = "HKLM:\SYSTEM\CurrentControlSet\Control"; 1084 | $uri = "$root\Session Manager\Environment"; 1085 | $ret = Get-ItemProperty -Path $uri -Name $name ` 1086 | -ErrorAction SilentlyContinue; 1087 | if ($ret) { 1088 | # Requires reboot 1089 | Set-ItemProperty $uri -Name $name -Value $val; 1090 | } 1091 | else { 1092 | New-ItemProperty ` 1093 | -Path $uri ` 1094 | -PropertyType String ` 1095 | -Name $name ` 1096 | -Value $val ` 1097 | -Force; 1098 | } 1099 | } 1100 | 1101 | # Apps that are not used anymore but have non-trivial install instructions 1102 | _notUsed() { 1103 | $this._InstallBinApp("Chocolatey.Chocolatey", $this._path( 1104 | @($env:ProgramData, "chocolatey", "bin"))); 1105 | # Windows 11 now has sudo under "developer settings". It's worse than 1106 | # this one, since it spawns new terminal windows instead of resuing 1107 | # the current one. 1108 | $this._installApp("gerardog.gsudo"); 1109 | } 1110 | } 1111 | 1112 | # Stop on unhandled exceptions. 1113 | $ErrorActionPreference = "Stop"; 1114 | $pathIntrinsics = $ExecutionContext.SessionState.Path; 1115 | $app = [App]::new($args, $pathIntrinsics); 1116 | $app.configure(); 1117 | 1118 | # TODO: try to use rainmeter with "always on top" over-taskbar skin. 1119 | # TODO: configure no reboot reg key if it works, https://vk.cc/cEEWuN 1120 | --------------------------------------------------------------------------------