window_arranger 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5e27210e52c0fe2db06876f1c2ae7908e96281b458beccd668d0e5feaa03682c
4
+ data.tar.gz: e0fc4319f856e702be92de0da484a9252fe3111e1833c3d822d345433925bc9f
5
+ SHA512:
6
+ metadata.gz: 2d078426103f15269e6e70ce49f933ee6e92eed50cebb456ccd54a7a2cd87b1260c69116a1b6fbc2dd3f9cc8ac1eff61754f435a18f7662cad8187d19ff4398b
7
+ data.tar.gz: 4b0bfd32da7d138fd0e661521173f92daeac71968858009c992e95c91372b9f3bfc2f9468eefac66be3fac7c411beeefd96209019be4607945a1152591983628
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,134 @@
1
+ AllCops:
2
+ Exclude:
3
+ - bin/*
4
+ - node_modules/**/*
5
+ NewCops: enable
6
+ TargetRubyVersion: 3.0
7
+
8
+ Layout/CaseIndentation:
9
+ EnforcedStyle: end
10
+
11
+ Layout/EndAlignment:
12
+ EnforcedStyleAlignWith: variable
13
+
14
+ Layout/LineLength:
15
+ Enabled: false
16
+
17
+ Layout/EmptyLinesAroundAccessModifier:
18
+ EnforcedStyle: only_before
19
+
20
+ Layout/SpaceInLambdaLiteral:
21
+ EnforcedStyle: require_space
22
+
23
+ Layout/SpaceInsideBlockBraces:
24
+ SpaceBeforeBlockParameters: false
25
+
26
+ Layout/SpaceInsideHashLiteralBraces:
27
+ EnforcedStyle: no_space
28
+
29
+ Metrics/AbcSize:
30
+ Enabled: false
31
+
32
+ Metrics/BlockLength:
33
+ Enabled: false
34
+
35
+ Metrics/ClassLength:
36
+ Enabled: false
37
+
38
+ Metrics/CyclomaticComplexity:
39
+ Enabled: false
40
+
41
+ Metrics/MethodLength:
42
+ Enabled: false
43
+
44
+ Metrics/ModuleLength:
45
+ Enabled: false
46
+
47
+ Metrics/PerceivedComplexity:
48
+ Enabled: false
49
+
50
+ Naming/HeredocDelimiterNaming:
51
+ Enabled: false
52
+
53
+ Naming/MethodParameterName:
54
+ Enabled: false
55
+
56
+ Naming/PredicateName:
57
+ Enabled: false
58
+
59
+ Style/Alias:
60
+ EnforcedStyle: prefer_alias_method
61
+
62
+ Style/AsciiComments:
63
+ Enabled: false
64
+
65
+ Style/BlockDelimiters:
66
+ Enabled: false
67
+
68
+ Style/Documentation:
69
+ Enabled: false
70
+
71
+ Style/EmptyCaseCondition:
72
+ Enabled: false
73
+
74
+ Style/EmptyMethod:
75
+ EnforcedStyle: expanded
76
+
77
+ Style/FrozenStringLiteralComment:
78
+ Enabled: false
79
+
80
+ Style/HashAsLastArrayItem:
81
+ Enabled: false
82
+
83
+ Style/HashSyntax:
84
+ Exclude:
85
+ - Rakefile
86
+ - "**/*.rake"
87
+
88
+ Style/Lambda:
89
+ EnforcedStyle: literal
90
+
91
+ Style/IfUnlessModifier:
92
+ Enabled: false
93
+
94
+ Style/KeywordParametersOrder:
95
+ Enabled: false
96
+
97
+ Style/MultilineBlockChain:
98
+ Enabled: false
99
+
100
+ Style/NumericLiterals:
101
+ Enabled: false
102
+
103
+ Style/NumericPredicate:
104
+ Enabled: false
105
+
106
+ Style/PercentLiteralDelimiters:
107
+ PreferredDelimiters:
108
+ '%i': '()'
109
+ '%w': '()'
110
+ '%r': '()'
111
+
112
+ Style/SpecialGlobalVars:
113
+ Enabled: false
114
+
115
+ Style/StringLiterals:
116
+ EnforcedStyle: double_quotes
117
+
118
+ Style/StringLiteralsInInterpolation:
119
+ EnforcedStyle: double_quotes
120
+
121
+ Style/SymbolArray:
122
+ Enabled: false
123
+
124
+ Style/TrailingCommaInArguments:
125
+ EnforcedStyleForMultiline: consistent_comma
126
+
127
+ Style/TrailingCommaInArrayLiteral:
128
+ EnforcedStyleForMultiline: consistent_comma
129
+
130
+ Style/TrailingCommaInHashLiteral:
131
+ EnforcedStyleForMultiline: consistent_comma
132
+
133
+ Style/ZeroLengthPredicate:
134
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 3.1.0
6
+ before_install: gem install bundler -v 2.1.2
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in window_arranger.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 12.0"
7
+ gem "rspec", "~> 3.0"
8
+
9
+ gem "rubocop", "~> 1.65"
10
+
11
+ gem "thor", "~> 1.3"
12
+
13
+ gem "thor-zsh_completion", "~> 0.1.10"
data/Gemfile.lock ADDED
@@ -0,0 +1,69 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ window_arranger (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ast (2.4.2)
10
+ diff-lcs (1.5.1)
11
+ json (2.7.2)
12
+ language_server-protocol (3.17.0.3)
13
+ parallel (1.26.3)
14
+ parser (3.3.4.2)
15
+ ast (~> 2.4.1)
16
+ racc
17
+ racc (1.8.1)
18
+ rainbow (3.1.1)
19
+ rake (12.3.3)
20
+ regexp_parser (2.9.2)
21
+ rexml (3.3.6)
22
+ strscan
23
+ rspec (3.13.0)
24
+ rspec-core (~> 3.13.0)
25
+ rspec-expectations (~> 3.13.0)
26
+ rspec-mocks (~> 3.13.0)
27
+ rspec-core (3.13.0)
28
+ rspec-support (~> 3.13.0)
29
+ rspec-expectations (3.13.2)
30
+ diff-lcs (>= 1.2.0, < 2.0)
31
+ rspec-support (~> 3.13.0)
32
+ rspec-mocks (3.13.1)
33
+ diff-lcs (>= 1.2.0, < 2.0)
34
+ rspec-support (~> 3.13.0)
35
+ rspec-support (3.13.1)
36
+ rubocop (1.65.1)
37
+ json (~> 2.3)
38
+ language_server-protocol (>= 3.17.0)
39
+ parallel (~> 1.10)
40
+ parser (>= 3.3.0.2)
41
+ rainbow (>= 2.2.2, < 4.0)
42
+ regexp_parser (>= 2.4, < 3.0)
43
+ rexml (>= 3.2.5, < 4.0)
44
+ rubocop-ast (>= 1.31.1, < 2.0)
45
+ ruby-progressbar (~> 1.7)
46
+ unicode-display_width (>= 2.4.0, < 3.0)
47
+ rubocop-ast (1.32.1)
48
+ parser (>= 3.3.1.0)
49
+ ruby-progressbar (1.13.0)
50
+ strscan (3.1.0)
51
+ thor (1.3.1)
52
+ thor-zsh_completion (0.1.10)
53
+ thor (< 2)
54
+ unicode-display_width (2.5.0)
55
+
56
+ PLATFORMS
57
+ arm64-darwin-21
58
+ ruby
59
+
60
+ DEPENDENCIES
61
+ rake (~> 12.0)
62
+ rspec (~> 3.0)
63
+ rubocop (~> 1.65)
64
+ thor (~> 1.3)
65
+ thor-zsh_completion (~> 0.1.10)
66
+ window_arranger!
67
+
68
+ BUNDLED WITH
69
+ 2.5.16
data/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # window-arranger
2
+
3
+ `window-arranger` is a command-line tool for macOS that allows you to manage your application windows by list their positions and sizes to a YAML format, and configuring them using a YAML format. This tool is useful for developers or streamers who want to automate their window layouts.
4
+
5
+ ## Requirements
6
+
7
+ Ruby (>= 3)
8
+
9
+ ## Installation
10
+
11
+ $ gem install window_arranger
12
+
13
+ ## Usage
14
+
15
+ ### List Window Layout
16
+
17
+ You can list the current window layout to a YAML format with the following command:
18
+
19
+ $ window-arranger list
20
+
21
+ ### Update Window Layout
22
+
23
+ To update a window layout from a YAML format, use:
24
+
25
+ $ cat windows.yml | window-arranger update
26
+
27
+
28
+ ### Example YAML Format
29
+
30
+ Here’s an example of what the YAML string might look like:
31
+
32
+ ---
33
+ - appName: Arc # matches app named `Arc`
34
+ position:
35
+ - 0
36
+ - 25
37
+ size:
38
+ - 1280
39
+ - 1067
40
+ - appName:
41
+ regexp: ^Sla # matches app that has name starts widh `Sl` (eg. Slack)
42
+ position:
43
+ - 2560
44
+ - 25
45
+ size:
46
+ - 1280
47
+ - 1067
48
+
49
+
50
+
51
+ ## Development
52
+
53
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
54
+
55
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
56
+
57
+ ## Contributing
58
+
59
+ Bug reports and pull requests are welcome on GitHub at https://github.com/labocho/window_arranger.
60
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "window_arranger"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/rubocop ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rubocop' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("rubocop", "rubocop")
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require "window_arranger"
3
+
4
+ WindowArranger::CLI.start(ARGV)
@@ -0,0 +1,28 @@
1
+ require "thor"
2
+ require "thor/zsh_completion"
3
+
4
+ require "yaml"
5
+ require "json"
6
+
7
+ module WindowArranger
8
+ class CLI < Thor
9
+ include ZshCompletion::Command
10
+
11
+ desc "list", "List windows"
12
+ def list
13
+ windows = WindowArranger::Script.list_windows
14
+ puts windows.to_yaml
15
+ end
16
+
17
+ desc "Update", "Update size and position of windows"
18
+ def update
19
+ definitions = YAML.safe_load($stdin.read)
20
+ WindowArranger::Script.update_bounds(definitions).each do |log|
21
+ next unless log["type"] == "update"
22
+
23
+ puts "Updated: #{log["definition"]}"
24
+ end
25
+ puts "Done."
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,85 @@
1
+ require "shellwords"
2
+ require "open3"
3
+ require "json"
4
+
5
+ module WindowArranger
6
+ module Script
7
+ class ScriptError < ::WindowArranger::Error; end
8
+
9
+ module_function
10
+
11
+ def osascript(script, *args)
12
+ out, err, status = Open3.capture3("osascript", "-l", "JavaScript", "-e", script, *args)
13
+ unless status.success?
14
+ raise ScriptError, err
15
+ end
16
+
17
+ [out, err, status]
18
+ end
19
+
20
+ # 3s くらいかかる
21
+ def list_windows
22
+ script = <<~JS
23
+ function run(argv) {
24
+ var systemEvents = Application('System Events');
25
+ var allWindows = [];
26
+
27
+ systemEvents.processes().forEach(function(proc) {
28
+ proc.windows().forEach(function(win) {
29
+ allWindows.push({appName: proc.name(), appShortName: proc.shortName(), windowName: win.name(), position: win.position(), size: win.size()});
30
+ });
31
+ });
32
+
33
+ return JSON.stringify(allWindows);
34
+ }
35
+ JS
36
+
37
+ out, * = osascript(script)
38
+ JSON.parse(out)
39
+ end
40
+
41
+ # 3s くらいかかる
42
+ # definitions: [{appName: (String|{regexp: String}), appShortName: (String|{regexp: String}), windowName: (String|{regexp: String}), position: [x: Number, y: Number], size: [width: Number, height: Number]}]
43
+ def update_bounds(definitions)
44
+ script = <<~JS
45
+ function matchPattern(pattern, str) {
46
+ if (pattern.regexp) {
47
+ return new RegExp(pattern.regexp).test(str);
48
+ } else {
49
+ return pattern === str;
50
+ }
51
+ }
52
+ function match(attributes, definition) {
53
+ if (definition.appName && matchPattern(definition.appName, attributes.appName) !== true) return false;
54
+ if (definition.appShortName && matchPattern(definition.appShortName, attributes.appShortName) !== true) return false;
55
+ if (definition.windowName && matchPattern(definition.windowName, attributes.windowName) !== true) return false;
56
+ return true;
57
+ }
58
+ function run(argv) {
59
+ var definitions = JSON.parse(argv[0]);
60
+ var systemEvents = Application('System Events');
61
+ var logs = [];
62
+
63
+ systemEvents.processes().forEach(function(proc) {
64
+ proc.windows().forEach(function(win) {
65
+ var attrs = {appName: proc.name(), appShortName: proc.shortName(), windowName: win.name()};
66
+ definitions.forEach(function(definition) {
67
+ if (match(attrs, definition)) {
68
+ if (definition.position) win.position = definition.position;
69
+ if (definition.size) win.size = definition.size;
70
+ logs.push({type: "update", attributes: attrs, definition: definition})
71
+ }
72
+ });
73
+ });
74
+ });
75
+
76
+ return JSON.stringify(logs);
77
+ }
78
+ JS
79
+
80
+ out, err, * = osascript(script, JSON.dump(definitions))
81
+ warn err unless err.empty?
82
+ JSON.parse(out)
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,3 @@
1
+ module WindowArranger
2
+ VERSION = "0.1.0".freeze
3
+ end
@@ -0,0 +1,9 @@
1
+ require "window_arranger/version"
2
+
3
+ module WindowArranger
4
+ class Error < StandardError; end
5
+ # Your code goes here...
6
+ end
7
+
8
+ require "window_arranger/cli"
9
+ require "window_arranger/script"
@@ -0,0 +1,27 @@
1
+ require_relative "lib/window_arranger/version"
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "window_arranger"
5
+ spec.version = WindowArranger::VERSION
6
+ spec.authors = ["labocho"]
7
+ spec.email = ["labocho@penguinlab.jp"]
8
+
9
+ spec.summary = "Arrange window positions on macOS"
10
+ spec.description = "Arrange window positions on macOS"
11
+ spec.homepage = "https://github.com/labocho/window_arranger"
12
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.0")
13
+
14
+ spec.metadata["homepage_uri"] = spec.homepage
15
+ spec.metadata["source_code_uri"] = "https://github.com/labocho/window_arranger"
16
+ # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
17
+
18
+ # Specify which files should be added to the gem when it is released.
19
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
20
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
21
+ `git ls-files -z`.split("\x0").reject {|f| f.match(%r{^(test|spec|features)/}) }
22
+ end
23
+ spec.bindir = "exe"
24
+ spec.executables = spec.files.grep(%r(^exe/)) {|f| File.basename(f) }
25
+ spec.require_paths = ["lib"]
26
+ spec.metadata["rubygems_mfa_required"] = "true"
27
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: window_arranger
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - labocho
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-08-26 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Arrange window positions on macOS
14
+ email:
15
+ - labocho@penguinlab.jp
16
+ executables:
17
+ - window-arranger
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - ".gitignore"
22
+ - ".rspec"
23
+ - ".rubocop.yml"
24
+ - ".travis.yml"
25
+ - Gemfile
26
+ - Gemfile.lock
27
+ - README.md
28
+ - Rakefile
29
+ - bin/console
30
+ - bin/rubocop
31
+ - bin/setup
32
+ - exe/window-arranger
33
+ - lib/window_arranger.rb
34
+ - lib/window_arranger/cli.rb
35
+ - lib/window_arranger/script.rb
36
+ - lib/window_arranger/version.rb
37
+ - window_arranger.gemspec
38
+ homepage: https://github.com/labocho/window_arranger
39
+ licenses: []
40
+ metadata:
41
+ homepage_uri: https://github.com/labocho/window_arranger
42
+ source_code_uri: https://github.com/labocho/window_arranger
43
+ rubygems_mfa_required: 'true'
44
+ post_install_message:
45
+ rdoc_options: []
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '3.0'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubygems_version: 3.3.3
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: Arrange window positions on macOS
63
+ test_files: []