window_arranger 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []