ace-support-mac-clipboard 0.3.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: c558611a5b584d6fcbfb84333bc45ac8cc0673af46f58608dcfa4b004d9d9e44
4
+ data.tar.gz: f09989c772c93bbbb5a19fae3ce2fcef7a26ab0473f2b3ffa1777bb2e0a3f3f2
5
+ SHA512:
6
+ metadata.gz: 17c31bd56c10d82e28fe4ec291741e3452f331a6f0b9f0cf7ae3a558f551a13ed898db752200aed1a237ec5ab5fa1d778ce5670dd683167ea07f784f46bb0b57
7
+ data.tar.gz: 674909b85c99e490026d58b0ec6d8947254d3f1b3a4adc9326e88763b72608976612d513c1cc0b7b659386ea4993b868e5a40940cb71f4c04b87380c876b4b4b
data/CHANGELOG.md ADDED
@@ -0,0 +1,61 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog][1], and this project adheres to [Semantic Versioning][2].
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.3.0] - 2026-03-23
10
+
11
+ ### Technical
12
+ - Removed phantom `handbook/**/*` glob from gemspec (no handbook directory exists).
13
+
14
+ ## [0.2.3] - 2026-03-22
15
+
16
+ ### Technical
17
+ - Corrected README integration guidance to reference `ace-idea` clipboard usage instead of `ace-taskflow`.
18
+
19
+ ## [0.2.2] - 2026-03-22
20
+
21
+ ### Technical
22
+ - Refreshed README structure with consistent tagline, overview, basic usage, and ACE project footer
23
+
24
+ ## [0.2.1] - 2026-02-12
25
+
26
+ ### Fixed
27
+ - Guard `require "ace/support/mac_clipboard"` behind platform check to prevent load errors on non-macOS
28
+ - Skip all tests gracefully on non-macOS platforms instead of failing
29
+
30
+ ## [0.2.0] - 2026-01-03
31
+
32
+ ### Changed
33
+ - **BREAKING**: Minimum Ruby version raised to 3.3.0 (was 3.1.0)
34
+ - Standardized gemspec file patterns with deterministic Dir.glob
35
+ - Added MIT LICENSE file
36
+
37
+ ## [0.1.1] - 2025-11-11
38
+
39
+ ### Added
40
+ - Comprehensive test suite with smoke test pattern compatibility
41
+ - Test infrastructure for all core components (Error, ContentType, Reader, ContentParser)
42
+
43
+ ### Fixed
44
+ - Test discovery and execution issues with ace-test integration
45
+ - Module structure and constant loading verification tests
46
+
47
+ ### Changed
48
+ - Update test structure to work with ace-test smoke pattern
49
+ - Improve test coverage for clipboard functionality
50
+
51
+ ## 0.1.0 - 2025-10-13
52
+
53
+ ### Added
54
+ - Initial release of ace-support-mac-clipboard
55
+ - Core clipboard functionality with FFI integration
56
+ - ContentType module for macOS clipboard type mappings
57
+ - Reader class for clipboard content access
58
+ - ContentParser class for data processing
59
+
60
+ [1]: https://keepachangelog.com/en/1.0.0/
61
+ [2]: https://semver.org/spec/v2.0.0.html
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Michal Czyz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ <div align="center">
2
+ <h1> ACE - Support Mac Clipboard </h1>
3
+
4
+ macOS clipboard support for text, files, and image payloads used by ACE tools.
5
+
6
+ <img src="https://raw.githubusercontent.com/cs3b/ace/main/docs/brand/AgenticCodingEnvironment.Logo.XS.jpg" alt="ACE Logo" width="480">
7
+ <br><br>
8
+
9
+ <a href="https://rubygems.org/gems/ace-support-mac-clipboard"><img alt="Gem Version" src="https://img.shields.io/gem/v/ace-support-mac-clipboard.svg" /></a>
10
+ <a href="https://www.ruby-lang.org"><img alt="Ruby" src="https://img.shields.io/badge/Ruby-3.2+-CC342D?logo=ruby" /></a>
11
+ <a href="https://opensource.org/licenses/MIT"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-blue.svg" /></a>
12
+
13
+ </div>
14
+
15
+ > Works with: Claude Code, Codex CLI, OpenCode, Gemini CLI, pi-agent, and more.
16
+
17
+ **macOS only.** `ace-support-mac-clipboard` integrates with macOS `NSPasteboard` so ACE tools can consume richer clipboard inputs than plain text. It handles screenshots, Finder file selections, and formatted content, presenting normalized Ruby structures to downstream packages.
18
+
19
+ ## Use Cases
20
+
21
+ **Attach image and context content from clipboard** - support screenshot-based and file-based workflows in ACE tools like [ace-prompt-prep](../ace-prompt-prep) without manual file handling.
22
+
23
+ **Handle Finder selections and formatted text** - process files and rich content from macOS pasteboard without manual conversion steps.
24
+
25
+ **Keep platform details isolated** - encapsulate macOS-specific clipboard behavior in one package so the rest of ACE stays platform-neutral.
26
+
27
+ ---
28
+
29
+ Part of [ACE](https://github.com/cs3b/ace)
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ desc "Run tests using ace-test"
7
+ task :test do
8
+ sh "ace-test"
9
+ end
10
+
11
+ desc "Run tests directly (CI mode)"
12
+ Minitest::TestTask.create(:ci)
13
+
14
+ task default: :test
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+
5
+ module Ace
6
+ module Support
7
+ module MacClipboard
8
+ class ContentParser
9
+ def self.parse(raw_result)
10
+ return {text: nil, attachments: []} unless raw_result[:success]
11
+ return {text: nil, attachments: []} if raw_result[:types].empty?
12
+
13
+ pasteboard = raw_result[:raw_pasteboard]
14
+ types = raw_result[:types]
15
+
16
+ text_parts = []
17
+ attachments = []
18
+ image_count = 0
19
+
20
+ # Classify all types
21
+ classified = types.map { |uti| [uti, ContentType.classify(uti)] }
22
+
23
+ # Process by priority
24
+ ContentType::PRIORITY_ORDER.each do |category|
25
+ relevant_utis = classified.select { |_uti, cat| cat == category }.map(&:first)
26
+
27
+ case category
28
+ when :files
29
+ # Read file URLs once (works for both public.file-url and NSFilenamesPboardType)
30
+ file_urls = Reader.read_file_urls(pasteboard)
31
+ file_urls.each do |path|
32
+ attachments << {
33
+ type: :file,
34
+ source_path: path,
35
+ filename: File.basename(path)
36
+ }
37
+ end
38
+
39
+ when :image
40
+ relevant_utis.each do |uti|
41
+ data = Reader.read_type(pasteboard, uti)
42
+ next unless data && data.bytesize > 0
43
+
44
+ image_count += 1
45
+ format = ContentType.image_format_from_uti(uti)
46
+ ext = ContentType::EXTENSIONS[:image][format]
47
+
48
+ attachments << {
49
+ type: :image,
50
+ format: format,
51
+ data: data,
52
+ filename: "clipboard-image-#{image_count}#{ext}"
53
+ }
54
+ break # Only take the first image format
55
+ end
56
+
57
+ when :rtf
58
+ relevant_utis.each do |uti|
59
+ data = Reader.read_type(pasteboard, uti)
60
+ next unless data && data.bytesize > 0
61
+
62
+ attachments << {
63
+ type: :rtf,
64
+ data: data,
65
+ filename: "clipboard-content.rtf"
66
+ }
67
+ break # Only take the first RTF format
68
+ end
69
+
70
+ when :html
71
+ relevant_utis.each do |uti|
72
+ data = Reader.read_type(pasteboard, uti)
73
+ next unless data && data.bytesize > 0
74
+
75
+ attachments << {
76
+ type: :html,
77
+ data: data,
78
+ filename: "clipboard-content.html"
79
+ }
80
+ break # Only take the first HTML format
81
+ end
82
+
83
+ when :text
84
+ relevant_utis.each do |uti|
85
+ text = Reader.read_string(pasteboard, uti)
86
+ next unless text && !text.empty?
87
+
88
+ text_parts << text
89
+ break # Only take the first text format
90
+ end
91
+ end
92
+ end
93
+
94
+ # Combine all text parts
95
+ combined_text = text_parts.join("\n\n").strip
96
+ combined_text = nil if combined_text.empty?
97
+
98
+ {
99
+ text: combined_text,
100
+ attachments: attachments
101
+ }
102
+ end
103
+
104
+ def self.parse_text(data)
105
+ return nil unless data
106
+
107
+ data.force_encoding("UTF-8").strip
108
+ rescue
109
+ nil
110
+ end
111
+
112
+ def self.parse_file_urls(data)
113
+ return [] unless data
114
+
115
+ # Parse file URL data
116
+ urls = []
117
+ url_str = data.force_encoding("UTF-8").strip
118
+
119
+ # Handle file:// URLs
120
+ url_str = url_str.sub(%r{^file://}, "")
121
+
122
+ # URL decode
123
+ url_str = begin
124
+ URI.decode_www_form_component(url_str)
125
+ rescue
126
+ url_str
127
+ end
128
+
129
+ urls << url_str if File.exist?(url_str)
130
+ urls
131
+ rescue
132
+ []
133
+ end
134
+
135
+ def self.parse_image(data, uti)
136
+ return nil unless data && data.bytesize > 0
137
+
138
+ format = ContentType.image_format_from_uti(uti)
139
+
140
+ {
141
+ format: format,
142
+ data: data
143
+ }
144
+ rescue
145
+ nil
146
+ end
147
+
148
+ def self.parse_rtf(data)
149
+ return nil unless data && data.bytesize > 0
150
+
151
+ data
152
+ rescue
153
+ nil
154
+ end
155
+
156
+ def self.parse_html(data)
157
+ return nil unless data && data.bytesize > 0
158
+
159
+ data.force_encoding("UTF-8")
160
+ rescue
161
+ nil
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ace
4
+ module Support
5
+ module MacClipboard
6
+ module ContentType
7
+ # UTI (Uniform Type Identifier) mappings for macOS clipboard types
8
+ UTI_TYPES = {
9
+ # Text types
10
+ "public.utf8-plain-text" => :text,
11
+ "public.plain-text" => :text,
12
+ "NSStringPboardType" => :text,
13
+
14
+ # Image types
15
+ "public.png" => :image,
16
+ "public.jpeg" => :image,
17
+ "public.tiff" => :image,
18
+ "com.apple.icns" => :image,
19
+
20
+ # File URLs
21
+ "public.file-url" => :files,
22
+ "NSFilenamesPboardType" => :files,
23
+
24
+ # Rich text types
25
+ "public.rtf" => :rtf,
26
+ "com.apple.rtfd" => :rtf,
27
+
28
+ # HTML
29
+ "public.html" => :html,
30
+ "NSHTMLPboardType" => :html
31
+ }.freeze
32
+
33
+ # Priority order for processing (higher priority first)
34
+ PRIORITY_ORDER = [:files, :image, :rtf, :html, :text].freeze
35
+
36
+ # File extensions for auto-generated filenames
37
+ EXTENSIONS = {
38
+ image: {png: ".png", jpeg: ".jpg", tiff: ".tiff"},
39
+ rtf: ".rtf",
40
+ html: ".html"
41
+ }.freeze
42
+
43
+ def self.classify(uti)
44
+ UTI_TYPES[uti] || :unknown
45
+ end
46
+
47
+ def self.image_format_from_uti(uti)
48
+ case uti
49
+ when "public.png" then :png
50
+ when "public.jpeg" then :jpeg
51
+ when "public.tiff" then :tiff
52
+ else :png
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,215 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ffi"
4
+
5
+ module Ace
6
+ module Support
7
+ module MacClipboard
8
+ class Reader
9
+ extend FFI::Library
10
+
11
+ # Load Objective-C runtime and AppKit framework
12
+ ffi_lib "/usr/lib/libobjc.dylib"
13
+ ffi_lib "/System/Library/Frameworks/AppKit.framework/AppKit"
14
+
15
+ # Objective-C runtime functions
16
+ attach_function :objc_getClass, [:string], :pointer
17
+ attach_function :sel_registerName, [:string], :pointer
18
+ attach_function :objc_msgSend, [:pointer, :pointer], :pointer
19
+ attach_function :objc_msgSend_id, :objc_msgSend, [:pointer, :pointer, :pointer], :pointer
20
+ attach_function :objc_msgSend_uint, :objc_msgSend, [:pointer, :pointer], :uint
21
+ attach_function :objc_msgSend_uint64, :objc_msgSend, [:pointer, :pointer, :uint64], :pointer
22
+
23
+ # Helper to send Objective-C messages
24
+ def self.objc_send(obj, selector, *args)
25
+ sel = sel_registerName(selector.to_s)
26
+ if args.empty?
27
+ objc_msgSend(obj, sel)
28
+ else
29
+ objc_msgSend_id(obj, sel, *args)
30
+ end
31
+ end
32
+
33
+ # Read clipboard content
34
+ def self.read
35
+ return {success: false, error: "Not on macOS"} unless RUBY_PLATFORM.match?(/darwin/)
36
+
37
+ pasteboard = get_general_pasteboard
38
+ return {success: false, error: "Could not access pasteboard"} unless pasteboard
39
+
40
+ types = available_types(pasteboard)
41
+ return {success: true, types: [], text: nil, attachments: []} if types.empty?
42
+
43
+ {success: true, types: types, raw_pasteboard: pasteboard}
44
+ rescue => e
45
+ {success: false, error: e.message}
46
+ end
47
+
48
+ # Get the general (system) pasteboard
49
+ def self.get_general_pasteboard
50
+ ns_pasteboard_class = objc_getClass("NSPasteboard")
51
+ objc_send(ns_pasteboard_class, "generalPasteboard")
52
+ end
53
+
54
+ # Get all available UTI types on the pasteboard
55
+ def self.available_types(pasteboard)
56
+ types_array = objc_send(pasteboard, "types")
57
+ return [] unless types_array && !types_array.null?
58
+
59
+ count = objc_msgSend_uint(types_array, sel_registerName("count"))
60
+ return [] if count.zero?
61
+
62
+ types = []
63
+ count.times do |i|
64
+ type_obj = objc_msgSend_uint64(types_array, sel_registerName("objectAtIndex:"), i)
65
+ next unless type_obj && !type_obj.null?
66
+
67
+ utf8_selector = sel_registerName("UTF8String")
68
+ type_cstr = objc_msgSend(type_obj, utf8_selector)
69
+ next if type_cstr.null?
70
+
71
+ types << type_cstr.read_string
72
+ end
73
+
74
+ types
75
+ rescue => e
76
+ warn "Error in available_types: #{e.message}"
77
+ warn e.backtrace.first(5)
78
+ []
79
+ end
80
+
81
+ # Read data for a specific UTI type
82
+ def self.read_type(pasteboard, uti)
83
+ # Create NSString for the UTI
84
+ objc_getClass("NSString")
85
+ uti_str = create_nsstring(uti)
86
+ return nil unless uti_str
87
+
88
+ # Get data from pasteboard
89
+ data = objc_msgSend_id(pasteboard, sel_registerName("dataForType:"), uti_str)
90
+ return nil unless data && !data.null?
91
+
92
+ # Get byte length
93
+ length = objc_msgSend_uint(data, sel_registerName("length"))
94
+ return nil if length.zero?
95
+
96
+ # Get bytes pointer
97
+ bytes = objc_msgSend(data, sel_registerName("bytes"))
98
+ return nil if bytes.null?
99
+
100
+ # Read binary data
101
+ bytes.read_bytes(length)
102
+ rescue
103
+ nil
104
+ end
105
+
106
+ # Read string content for text types
107
+ def self.read_string(pasteboard, uti)
108
+ # Try to get as string first
109
+ objc_getClass("NSString")
110
+ uti_str = create_nsstring(uti)
111
+ return nil unless uti_str
112
+
113
+ string_obj = objc_msgSend_id(pasteboard, sel_registerName("stringForType:"), uti_str)
114
+ if string_obj && !string_obj.null?
115
+ utf8_selector = sel_registerName("UTF8String")
116
+ cstr = objc_msgSend(string_obj, utf8_selector)
117
+ return cstr.read_string unless cstr.null?
118
+ end
119
+
120
+ # Fallback to reading as data
121
+ data = read_type(pasteboard, uti)
122
+ data&.force_encoding("UTF-8")
123
+ rescue
124
+ nil
125
+ end
126
+
127
+ # Read file URLs from pasteboard
128
+ def self.read_file_urls(pasteboard)
129
+ # Try NSFilenamesPboardType first (Finder uses this for copied files)
130
+ file_paths = read_filenames_pboard_type(pasteboard)
131
+ return file_paths if file_paths.any?
132
+
133
+ # Fallback to public.file-url
134
+ file_paths = read_public_file_url(pasteboard)
135
+ return file_paths if file_paths.any?
136
+
137
+ []
138
+ rescue => e
139
+ warn "Error reading file URLs: #{e.message}"
140
+ []
141
+ end
142
+
143
+ # Read file paths from NSFilenamesPboardType (used by Finder)
144
+ def self.read_filenames_pboard_type(pasteboard)
145
+ uti_str = create_nsstring("NSFilenamesPboardType")
146
+ return [] unless uti_str
147
+
148
+ # Get property list (NSArray of file path strings)
149
+ prop_list_sel = sel_registerName("propertyListForType:")
150
+ array_obj = objc_msgSend_id(pasteboard, prop_list_sel, uti_str)
151
+ return [] unless array_obj && !array_obj.null?
152
+
153
+ # Get count of files
154
+ count_sel = sel_registerName("count")
155
+ count = objc_msgSend_uint(array_obj, count_sel)
156
+ return [] if count.zero?
157
+
158
+ # Extract file paths
159
+ file_paths = []
160
+ count.times do |i|
161
+ obj_at_index_sel = sel_registerName("objectAtIndex:")
162
+ path_obj = objc_msgSend_uint64(array_obj, obj_at_index_sel, i)
163
+ next unless path_obj && !path_obj.null?
164
+
165
+ utf8_sel = sel_registerName("UTF8String")
166
+ cstr = objc_msgSend(path_obj, utf8_sel)
167
+ next if cstr.null?
168
+
169
+ path = cstr.read_string
170
+ file_paths << path if File.exist?(path)
171
+ end
172
+
173
+ file_paths
174
+ rescue => e
175
+ warn "Error reading NSFilenamesPboardType: #{e.message}"
176
+ []
177
+ end
178
+
179
+ # Read file URL from public.file-url
180
+ def self.read_public_file_url(pasteboard)
181
+ data = read_type(pasteboard, "public.file-url")
182
+ return [] unless data
183
+
184
+ # Parse URL from data
185
+ url_str = data.force_encoding("UTF-8").strip
186
+
187
+ # Remove "file://" prefix if present
188
+ url_str = url_str.sub(%r{^file://}, "")
189
+
190
+ # URL decode
191
+ url_str = begin
192
+ URI.decode_www_form_component(url_str)
193
+ rescue
194
+ url_str
195
+ end
196
+
197
+ File.exist?(url_str) ? [url_str] : []
198
+ rescue
199
+ []
200
+ end
201
+
202
+ # Helper to create NSString from Ruby string
203
+ def self.create_nsstring(str)
204
+ ns_string_class = objc_getClass("NSString")
205
+ return nil unless ns_string_class
206
+
207
+ utf8_selector = sel_registerName("stringWithUTF8String:")
208
+ objc_msgSend_id(ns_string_class, utf8_selector, FFI::MemoryPointer.from_string(str))
209
+ rescue
210
+ nil
211
+ end
212
+ end
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ace
4
+ module Support
5
+ module MacClipboard
6
+ VERSION = "0.3.0"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "mac_clipboard/version"
4
+ require_relative "mac_clipboard/content_type"
5
+ require_relative "mac_clipboard/reader"
6
+ require_relative "mac_clipboard/content_parser"
7
+
8
+ module Ace
9
+ module Support
10
+ module MacClipboard
11
+ class Error < StandardError; end
12
+ end
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ace-support-mac-clipboard
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Michal Czyz
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: ffi
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.15'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.15'
26
+ description: Provides FFI-based access to macOS NSPasteboard for reading rich clipboard
27
+ content (images, files, RTF, HTML)
28
+ email:
29
+ - mc@cs3b.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - CHANGELOG.md
35
+ - LICENSE
36
+ - README.md
37
+ - Rakefile
38
+ - lib/ace/support/mac_clipboard.rb
39
+ - lib/ace/support/mac_clipboard/content_parser.rb
40
+ - lib/ace/support/mac_clipboard/content_type.rb
41
+ - lib/ace/support/mac_clipboard/reader.rb
42
+ - lib/ace/support/mac_clipboard/version.rb
43
+ homepage: https://github.com/cs3b/ace
44
+ licenses:
45
+ - MIT
46
+ metadata:
47
+ homepage_uri: https://github.com/cs3b/ace
48
+ source_code_uri: https://github.com/cs3b/ace/tree/main/ace-support-mac-clipboard/
49
+ changelog_uri: https://github.com/cs3b/ace/blob/main/ace-support-mac-clipboard/CHANGELOG.md
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: 3.2.0
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubygems_version: 3.6.9
65
+ specification_version: 4
66
+ summary: macOS NSPasteboard integration for ACE
67
+ test_files: []