├── .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 |
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 |
--------------------------------------------------------------------------------