booker 1.3.0 → 2.0.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 +4 -4
- data/bin/booker +3 -3
- data/completions/_booker +115 -0
- data/completions/booker.bash +52 -0
- data/completions/booker.fish +49 -0
- data/lib/booker/bookmarks.rb +164 -0
- data/lib/booker/cli.rb +226 -0
- data/lib/booker/config.rb +219 -0
- data/lib/booker/installer.rb +396 -0
- data/lib/booker/output.rb +84 -0
- data/lib/booker/parsers.rb +294 -0
- data/lib/booker/picker.rb +139 -0
- data/lib/booker/version.rb +5 -0
- data/lib/booker.rb +14 -483
- metadata +37 -47
- data/lib/bookmarks.rb +0 -435
- data/lib/config.rb +0 -190
- data/lib/consts.rb +0 -99
data/lib/booker.rb
CHANGED
|
@@ -1,483 +1,14 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
require_relative "
|
|
9
|
-
require_relative "
|
|
10
|
-
require_relative "
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@@version = @version
|
|
16
|
-
|
|
17
|
-
class << self
|
|
18
|
-
attr_reader :version
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
include Browser
|
|
22
|
-
|
|
23
|
-
def initialize(args)
|
|
24
|
-
parse args
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def parse(args)
|
|
28
|
-
show_bookmarks if args.none?
|
|
29
|
-
|
|
30
|
-
if args.first&.start_with?("-")
|
|
31
|
-
dispatch_option(args)
|
|
32
|
-
exit 0
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
bookmark_ids, other_args = args.partition { |a| /^[0-9_]+$/.match?(a) }
|
|
36
|
-
open_bookmark(bookmark_ids) unless bookmark_ids.empty?
|
|
37
|
-
|
|
38
|
-
unless other_args.empty?
|
|
39
|
-
if other_args.length == 1 && domain.match(other_args.first)
|
|
40
|
-
puts "opening website: ".grn + other_args.first
|
|
41
|
-
openweb(prep(other_args.first))
|
|
42
|
-
else
|
|
43
|
-
open_search(other_args.join(" ").strip)
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def option_parser
|
|
49
|
-
@option_parser ||= OptionParser.new do |opts|
|
|
50
|
-
opts.banner = "Usage: booker [options] [arguments]"
|
|
51
|
-
opts.separator ""
|
|
52
|
-
opts.separator "Main options:"
|
|
53
|
-
opts.on("-b", "--bookmark", "explicitly open bookmark") { @mode = :bookmark }
|
|
54
|
-
opts.on("-i", "--install", "install: all|bookmarks|completion|config|safari") { @mode = :install }
|
|
55
|
-
opts.on("-s", "--search", "explicitly search arguments") { @mode = :search }
|
|
56
|
-
opts.separator ""
|
|
57
|
-
opts.separator "Other options:"
|
|
58
|
-
opts.on("-c", "--complete", "show tab completions") { @mode = :complete }
|
|
59
|
-
opts.on("-v", "--version", "print version") {
|
|
60
|
-
puts @@version
|
|
61
|
-
exit 0
|
|
62
|
-
}
|
|
63
|
-
opts.on_tail("-h", "--help", "show help") {
|
|
64
|
-
puts opts
|
|
65
|
-
exit 0
|
|
66
|
-
}
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def dispatch_option(args)
|
|
71
|
-
option_parser.parse!(args)
|
|
72
|
-
|
|
73
|
-
case @mode
|
|
74
|
-
when :bookmark
|
|
75
|
-
pexit "Error: ".red + "booker --bookmark expects bookmark id", 1 if args.empty?
|
|
76
|
-
open_bookmark(args)
|
|
77
|
-
when :install
|
|
78
|
-
args.empty? ? install(%w[completion config bookmarks]) : install(args)
|
|
79
|
-
when :search
|
|
80
|
-
pexit "Error: ".red + "--search requires an argument", 1 if args.empty?
|
|
81
|
-
open_search(args.join(" "))
|
|
82
|
-
when :complete
|
|
83
|
-
Bookmarks.new(args.join(" ")).autocomplete
|
|
84
|
-
end
|
|
85
|
-
rescue OptionParser::InvalidOption => e
|
|
86
|
-
pexit "Error: ".red + e.message, 1
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def pexit(msg, sig)
|
|
90
|
-
puts msg
|
|
91
|
-
exit sig
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def openweb(url)
|
|
95
|
-
# Pass URL directly to browser without invoking shell
|
|
96
|
-
# This avoids issues with special characters like parentheses
|
|
97
|
-
browser_cmd = browse.strip
|
|
98
|
-
|
|
99
|
-
# Redirect stdout/stderr to suppress GTK warnings
|
|
100
|
-
success = system(browser_cmd, url, out: "/dev/null", err: "/dev/null")
|
|
101
|
-
|
|
102
|
-
unless success
|
|
103
|
-
puts "Warning: ".yel + "Failed to open URL (exit code: #{$?.exitstatus})"
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
# an array of ints, as bookmark ids
|
|
108
|
-
def open_bookmark(bm)
|
|
109
|
-
id = bm.shift
|
|
110
|
-
url = Bookmarks.new.bookmark_url(id)
|
|
111
|
-
pexit "Failure:".red + " bookmark #{id} not found", 1 if url.nil?
|
|
112
|
-
puts "opening bookmark ".grn + url
|
|
113
|
-
openweb(url) # No wrap() needed - system() handles it
|
|
114
|
-
open_bookmark bm unless bm.empty?
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def open_search(term)
|
|
118
|
-
puts "searching ".grn + term
|
|
119
|
-
search = BConfig.new.searcher
|
|
120
|
-
term = term.tr(" ", "+")
|
|
121
|
-
openweb(search + term) # No shell escape needed - it's a URL
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def show_bookmarks
|
|
125
|
-
puts "Bookmarks:".grn + " (usage: booker <id> or booker <search>)"
|
|
126
|
-
puts ""
|
|
127
|
-
|
|
128
|
-
bm = Bookmarks.new("") # Get all bookmarks
|
|
129
|
-
allurls = bm.instance_variable_get(:@allurls)
|
|
130
|
-
|
|
131
|
-
if allurls.empty?
|
|
132
|
-
puts "No bookmarks found.".red
|
|
133
|
-
puts "Run: ".yel + "booker --install bookmarks".cyan
|
|
134
|
-
exit 0
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
# Calculate responsive column widths
|
|
138
|
-
term_width = `tput cols`.to_i
|
|
139
|
-
term_width = 100 if term_width == 0 # fallback
|
|
140
|
-
|
|
141
|
-
id_width = 10
|
|
142
|
-
remaining = term_width - id_width - 3 # 3 spaces between columns
|
|
143
|
-
|
|
144
|
-
folder_width = [remaining * 0.20, 15].max.to_i
|
|
145
|
-
title_width = [remaining * 0.30, 20].max.to_i
|
|
146
|
-
url_width = [remaining * 0.50, 30].max.to_i
|
|
147
|
-
|
|
148
|
-
# Display bookmarks in a readable format
|
|
149
|
-
allurls.each do |bookmark|
|
|
150
|
-
id_str = bookmark.id.to_s.window(id_width).grn
|
|
151
|
-
|
|
152
|
-
# Clean up folder display
|
|
153
|
-
folder = bookmark.folder.gsub(/^\|/, "") # Remove leading |
|
|
154
|
-
folder = (folder == "/") ? "[root]" : folder.chomp("/") # Show [root] for top-level
|
|
155
|
-
folder_str = folder.window(folder_width).blu
|
|
156
|
-
|
|
157
|
-
title_str = bookmark.title.window(title_width).yel
|
|
158
|
-
url_str = bookmark.url.window(url_width)
|
|
159
|
-
|
|
160
|
-
puts "#{id_str} #{folder_str} #{title_str} #{url_str}"
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
puts ""
|
|
164
|
-
puts "Found #{allurls.length} bookmarks".grn
|
|
165
|
-
puts ""
|
|
166
|
-
puts "Examples:".yel
|
|
167
|
-
puts " #{"booker #{allurls.first.id}".ljust(20).cyan} # Open first bookmark"
|
|
168
|
-
puts " #{"booker github".ljust(20).cyan} # Search bookmarks for 'github'"
|
|
169
|
-
puts " #{"booker --help".ljust(20).cyan} # Show help"
|
|
170
|
-
|
|
171
|
-
exit 0
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
def install(args)
|
|
175
|
-
target = args.shift
|
|
176
|
-
exit 0 if target.nil?
|
|
177
|
-
|
|
178
|
-
# 'all' expands to the full install list (including opt-in safari)
|
|
179
|
-
if /^all$/i.match?(target)
|
|
180
|
-
args = %w[completion config bookmarks safari] + args
|
|
181
|
-
target = args.shift
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
if /comp/i.match?(target) # completion installation
|
|
185
|
-
install_completion
|
|
186
|
-
elsif /book/i.match?(target) # bookmarks installation
|
|
187
|
-
install_bookmarks
|
|
188
|
-
elsif /conf/i.match?(target) # default config file generation
|
|
189
|
-
install_config
|
|
190
|
-
elsif /safari/i.match?(target) # opt-in Safari FDA setup (macOS only)
|
|
191
|
-
install_safari
|
|
192
|
-
else # unknown argument passed into install
|
|
193
|
-
pexit "Failure: ".red + "unknown installation option (#{target})", 1
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
install(args) # recurse til done
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
def install_completion
|
|
200
|
-
# check if zsh is even installed for this user
|
|
201
|
-
begin
|
|
202
|
-
fpath = `zsh -c 'echo $fpath'`.split(" ")
|
|
203
|
-
rescue
|
|
204
|
-
pexit "Failure: ".red + "zsh is probably not installed, could not find $fpath", 1
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
# Try user-writable directories first, then system directories
|
|
208
|
-
user_home = ENV["HOME"]
|
|
209
|
-
writable_dirs = fpath.select do |fp|
|
|
210
|
-
fp.start_with?(user_home) && File.directory?(fp) && File.writable?(fp)
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
# If no user-writable directories, try to create one
|
|
214
|
-
if writable_dirs.empty?
|
|
215
|
-
user_completion_dir = File.join(user_home, ".zsh", "completion")
|
|
216
|
-
begin
|
|
217
|
-
FileUtils.mkdir_p(user_completion_dir)
|
|
218
|
-
writable_dirs << user_completion_dir
|
|
219
|
-
puts "Created user completion directory: #{user_completion_dir}".yel
|
|
220
|
-
|
|
221
|
-
# Auto-configure .zshrc if it exists
|
|
222
|
-
zshrc = File.join(user_home, ".zshrc")
|
|
223
|
-
if File.exist?(zshrc)
|
|
224
|
-
zshrc_content = File.read(zshrc)
|
|
225
|
-
if zshrc_content.include?(".zsh/completion")
|
|
226
|
-
puts "~/.zshrc already configured".grn
|
|
227
|
-
else
|
|
228
|
-
File.open(zshrc, "a") do |f|
|
|
229
|
-
f.puts "\n# Booker completion"
|
|
230
|
-
f.puts "fpath=(~/.zsh/completion $fpath)"
|
|
231
|
-
f.puts "autoload -Uz compinit && compinit"
|
|
232
|
-
end
|
|
233
|
-
puts "Added completion to ~/.zshrc".grn
|
|
234
|
-
puts "Run: ".yel + "source ~/.zshrc".cyan + " to activate"
|
|
235
|
-
end
|
|
236
|
-
else
|
|
237
|
-
puts "Add this to your ~/.zshrc: ".yel + "fpath=(~/.zsh/completion $fpath)".cyan
|
|
238
|
-
end
|
|
239
|
-
rescue => e
|
|
240
|
-
# Couldn't create user dir, try system dirs as fallback
|
|
241
|
-
end
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
# Try writable directories first, then all directories as fallback
|
|
245
|
-
dirs_to_try = writable_dirs + fpath.reject { |fp| writable_dirs.include?(fp) }
|
|
246
|
-
|
|
247
|
-
success = false
|
|
248
|
-
dirs_to_try.each do |fp|
|
|
249
|
-
next unless File.directory?(fp)
|
|
250
|
-
|
|
251
|
-
begin
|
|
252
|
-
completion_file = File.join(fp, "_booker")
|
|
253
|
-
File.write(completion_file, COMPLETION)
|
|
254
|
-
system "zsh -c 'autoload -U _booker'"
|
|
255
|
-
puts "Success: ".grn + "installed zsh autocompletion in #{fp}"
|
|
256
|
-
success = true
|
|
257
|
-
break
|
|
258
|
-
rescue => e
|
|
259
|
-
# Try next directory silently
|
|
260
|
-
end
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
unless success
|
|
264
|
-
puts "Warning: ".yel + "Could not install ZSH completion to any directory in $fpath"
|
|
265
|
-
puts "Try manually: ".grn + "mkdir -p ~/.zsh/completion && booker --install completion"
|
|
266
|
-
end
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
def install_bookmarks
|
|
270
|
-
# locate bookmarks file, show user, write to config?
|
|
271
|
-
puts "searching for browser bookmarks..."
|
|
272
|
-
begin
|
|
273
|
-
bms = [] # look for bookmarks with type info
|
|
274
|
-
|
|
275
|
-
# Search for Chrome bookmarks
|
|
276
|
-
["Library/Application Support/Google/Chrome",
|
|
277
|
-
"AppData/Local/Google/Chrome/User Data/Default",
|
|
278
|
-
".config/chromium/Default",
|
|
279
|
-
".config/google-chrome/Default",
|
|
280
|
-
"snap/chromium/common/chromium",
|
|
281
|
-
"snap/chromium/current/.config/chromium"].each do |f|
|
|
282
|
-
home = File.join(ENV["HOME"], f)
|
|
283
|
-
next if !FileTest.directory?(home)
|
|
284
|
-
Find.find(home) do |file|
|
|
285
|
-
if /chrom.*bookmarks/i.match?(file)
|
|
286
|
-
bms << {path: file, type: :chrome}
|
|
287
|
-
end
|
|
288
|
-
end
|
|
289
|
-
end
|
|
290
|
-
|
|
291
|
-
# Search for Firefox bookmarks
|
|
292
|
-
firefox_paths = [
|
|
293
|
-
".mozilla/firefox", # Linux
|
|
294
|
-
"snap/firefox/common/.mozilla/firefox", # Linux (snap)
|
|
295
|
-
"Library/Application Support/Firefox", # macOS
|
|
296
|
-
"AppData/Roaming/Mozilla/Firefox" # Windows
|
|
297
|
-
]
|
|
298
|
-
|
|
299
|
-
firefox_paths.each do |f|
|
|
300
|
-
firefox_base = File.join(ENV["HOME"], f)
|
|
301
|
-
next if !FileTest.directory?(firefox_base)
|
|
302
|
-
|
|
303
|
-
profiles_ini = File.join(firefox_base, "profiles.ini")
|
|
304
|
-
next if !File.exist?(profiles_ini)
|
|
305
|
-
|
|
306
|
-
# Parse profiles.ini to find Firefox profiles
|
|
307
|
-
parse_firefox_profiles(profiles_ini, firefox_base).each do |db_path|
|
|
308
|
-
bms << {path: db_path, type: :firefox}
|
|
309
|
-
end
|
|
310
|
-
end
|
|
311
|
-
|
|
312
|
-
# Search for Safari bookmarks (macOS only)
|
|
313
|
-
safari_path = File.join(ENV["HOME"], "Library/Safari/Bookmarks.plist")
|
|
314
|
-
bms << {path: safari_path, type: :safari} if File.exist?(safari_path)
|
|
315
|
-
|
|
316
|
-
if bms.empty? # no bookmarks found
|
|
317
|
-
puts "Failure: ".red + "bookmarks file could not be found."
|
|
318
|
-
raise
|
|
319
|
-
elsif bms.length == 1
|
|
320
|
-
# Auto-select if only one source found
|
|
321
|
-
selected = bms.first[:path]
|
|
322
|
-
puts "Found bookmark source: #{bookmark_type_label(bms.first[:type])} #{selected}".yel
|
|
323
|
-
puts "Selected: ".yel + selected
|
|
324
|
-
BConfig.new.write(:bookmarks, selected)
|
|
325
|
-
puts "Success: ".grn + "config file updated with your bookmarks"
|
|
326
|
-
else # have user select a file
|
|
327
|
-
puts "select bookmarks source: "
|
|
328
|
-
|
|
329
|
-
# Offer "ALL" as first option if multiple sources found
|
|
330
|
-
puts "0".grn + " - " + "[ALL SOURCES]".cyan + " (search across all browsers)"
|
|
331
|
-
offset = 1
|
|
332
|
-
|
|
333
|
-
bms.each_with_index do |bm, i|
|
|
334
|
-
puts (i + offset).to_s.grn + " - " + bookmark_type_label(bm[:type], color: true) + " " + bm[:path]
|
|
335
|
-
end
|
|
336
|
-
|
|
337
|
-
input = gets
|
|
338
|
-
raise "No input provided" if input.nil?
|
|
339
|
-
selection = input.chomp.to_i
|
|
340
|
-
|
|
341
|
-
if selection == 0
|
|
342
|
-
# User selected "ALL" - save array of all paths
|
|
343
|
-
all_paths = bms.map { |bm| bm[:path] }
|
|
344
|
-
puts "Selected: ".yel + "All sources (#{bms.length} bookmark files)"
|
|
345
|
-
BConfig.new.write(:bookmarks, all_paths)
|
|
346
|
-
puts "Success: ".grn + "config file updated to search all bookmark sources"
|
|
347
|
-
else
|
|
348
|
-
# User selected single source
|
|
349
|
-
actual_index = selection - 1
|
|
350
|
-
selected = bms[actual_index][:path]
|
|
351
|
-
puts "Selected: ".yel + selected
|
|
352
|
-
BConfig.new.write(:bookmarks, selected)
|
|
353
|
-
puts "Success: ".grn + "config file updated with your bookmarks"
|
|
354
|
-
end
|
|
355
|
-
end
|
|
356
|
-
rescue => e
|
|
357
|
-
puts e.message
|
|
358
|
-
pexit "Failure: ".red + "could not add bookmarks to config file ~/.booker", 1
|
|
359
|
-
end
|
|
360
|
-
end
|
|
361
|
-
|
|
362
|
-
def bookmark_type_label(type, color: false)
|
|
363
|
-
label = {chrome: "[Chrome]", firefox: "[Firefox]", safari: "[Safari]"}[type] || "[?]"
|
|
364
|
-
return label unless color
|
|
365
|
-
case type
|
|
366
|
-
when :chrome then label.yel
|
|
367
|
-
when :firefox then label.blu
|
|
368
|
-
when :safari then label.cyan
|
|
369
|
-
else label
|
|
370
|
-
end
|
|
371
|
-
end
|
|
372
|
-
|
|
373
|
-
def parse_firefox_profiles(ini_path, firefox_base)
|
|
374
|
-
profiles = []
|
|
375
|
-
current_profile = {}
|
|
376
|
-
|
|
377
|
-
File.readlines(ini_path).each do |line|
|
|
378
|
-
line = line.strip
|
|
379
|
-
|
|
380
|
-
# New profile section
|
|
381
|
-
if line.start_with?("[Profile")
|
|
382
|
-
# Save previous profile if it had a path
|
|
383
|
-
if current_profile[:path]
|
|
384
|
-
db_path = File.join(firefox_base, current_profile[:path], "places.sqlite")
|
|
385
|
-
profiles << db_path if File.exist?(db_path)
|
|
386
|
-
end
|
|
387
|
-
current_profile = {}
|
|
388
|
-
|
|
389
|
-
# Parse key=value pairs
|
|
390
|
-
elsif line.include?("=")
|
|
391
|
-
key, value = line.split("=", 2).map(&:strip)
|
|
392
|
-
current_profile[:path] = value if key == "Path"
|
|
393
|
-
current_profile[:name] = value if key == "Name"
|
|
394
|
-
end
|
|
395
|
-
end
|
|
396
|
-
|
|
397
|
-
# Don't forget the last profile
|
|
398
|
-
if current_profile[:path]
|
|
399
|
-
db_path = File.join(firefox_base, current_profile[:path], "places.sqlite")
|
|
400
|
-
profiles << db_path if File.exist?(db_path)
|
|
401
|
-
end
|
|
402
|
-
|
|
403
|
-
profiles
|
|
404
|
-
end
|
|
405
|
-
|
|
406
|
-
def install_config
|
|
407
|
-
BConfig.new.write
|
|
408
|
-
puts "Success: ".grn + "example config file written to ~/.booker"
|
|
409
|
-
rescue
|
|
410
|
-
pexit "Failure: ".red + "could not write example config file to ~/.booker", 1
|
|
411
|
-
end
|
|
412
|
-
|
|
413
|
-
# Opt-in Safari setup: walks through granting Full Disk Access so booker
|
|
414
|
-
# can read ~/Library/Safari/Bookmarks.plist. Not included in the default
|
|
415
|
-
# --install flow because it requires a TCC permission grant.
|
|
416
|
-
def install_safari
|
|
417
|
-
plist = File.join(ENV["HOME"], "Library/Safari/Bookmarks.plist")
|
|
418
|
-
fda_url = "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles"
|
|
419
|
-
|
|
420
|
-
unless RUBY_PLATFORM.include?("darwin")
|
|
421
|
-
puts "Skip: ".yel + "Safari support is macOS-only."
|
|
422
|
-
return
|
|
423
|
-
end
|
|
424
|
-
|
|
425
|
-
unless File.exist?(plist)
|
|
426
|
-
puts "Skip: ".yel + "Safari bookmarks not found at #{plist}"
|
|
427
|
-
puts "Hint: ".grn + "launch Safari at least once, then re-run."
|
|
428
|
-
return
|
|
429
|
-
end
|
|
430
|
-
|
|
431
|
-
if safari_readable?(plist)
|
|
432
|
-
puts "PASS: ".grn + "Safari bookmarks are already readable."
|
|
433
|
-
return
|
|
434
|
-
end
|
|
435
|
-
|
|
436
|
-
puts "Safari stores bookmarks at:"
|
|
437
|
-
puts " #{plist}".cyan
|
|
438
|
-
puts
|
|
439
|
-
puts "That file is protected by macOS TCC. Grant Full Disk Access to one of:"
|
|
440
|
-
puts
|
|
441
|
-
puts " [A] ".cyan + "Your terminal app".yel + " (simplest; inherited by any tool)"
|
|
442
|
-
puts " [B] ".cyan + "/usr/bin/plutil only".yel + " (narrower scope)"
|
|
443
|
-
puts
|
|
444
|
-
print "Pick [A] or [B] (default A): "
|
|
445
|
-
$stdout.flush
|
|
446
|
-
choice = $stdin.gets.to_s.strip.upcase
|
|
447
|
-
choice = "A" if choice.empty?
|
|
448
|
-
pexit "Error: ".red + "invalid choice.", 1 unless %w[A B].include?(choice)
|
|
449
|
-
|
|
450
|
-
puts
|
|
451
|
-
puts "Opening the Full Disk Access pane..."
|
|
452
|
-
system("open", fda_url)
|
|
453
|
-
puts
|
|
454
|
-
|
|
455
|
-
puts "In the pane that just opened:"
|
|
456
|
-
if choice == "A"
|
|
457
|
-
puts " 1. Click ".yel + "+".cyan + ", add your terminal app from /Applications"
|
|
458
|
-
puts " 2. Toggle it ".yel + "on".cyan
|
|
459
|
-
puts " 3. ".yel + "Fully quit".cyan + " the terminal (Cmd+Q), reopen it,"
|
|
460
|
-
puts " and re-run ".yel + "booker --install safari".cyan + " to verify."
|
|
461
|
-
else
|
|
462
|
-
puts " 1. Click ".yel + "+".cyan
|
|
463
|
-
puts " 2. Press ".yel + "Cmd+Shift+G".cyan + " (opens 'Go to Folder')".yel
|
|
464
|
-
puts " 3. Type ".yel + "/usr/bin/plutil".cyan + " and press Return".yel
|
|
465
|
-
puts " 4. Click ".yel + "Open".cyan + ", then toggle it ".yel + "on".cyan
|
|
466
|
-
puts
|
|
467
|
-
print "Press Return once you've added plutil and toggled it on... "
|
|
468
|
-
$stdout.flush
|
|
469
|
-
$stdin.gets
|
|
470
|
-
|
|
471
|
-
if safari_readable?(plist)
|
|
472
|
-
puts "PASS: ".grn + "Safari bookmarks are now readable."
|
|
473
|
-
else
|
|
474
|
-
puts "FAIL: ".red + "still can't read the bookmarks file."
|
|
475
|
-
puts "Try fully quitting the terminal (Cmd+Q) and re-running."
|
|
476
|
-
end
|
|
477
|
-
end
|
|
478
|
-
end
|
|
479
|
-
|
|
480
|
-
def safari_readable?(plist)
|
|
481
|
-
system("plutil", "-lint", "-s", plist, out: File::NULL, err: File::NULL)
|
|
482
|
-
end
|
|
483
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# booker - search, browse and open browser bookmarks from the command line
|
|
4
|
+
#
|
|
5
|
+
# Load order matters in one place: Bookmarks::BROWSERS names the parser classes
|
|
6
|
+
# while its class body runs, so parsers has to come first.
|
|
7
|
+
|
|
8
|
+
require_relative "booker/version"
|
|
9
|
+
require_relative "booker/output"
|
|
10
|
+
require_relative "booker/config"
|
|
11
|
+
require_relative "booker/parsers"
|
|
12
|
+
require_relative "booker/bookmarks"
|
|
13
|
+
require_relative "booker/picker"
|
|
14
|
+
require_relative "booker/cli"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: booker
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jeremy Warner
|
|
@@ -15,76 +15,55 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '2.
|
|
18
|
+
version: '2.21'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '2.
|
|
25
|
+
version: '2.21'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: sqlite3
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
30
|
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '
|
|
33
|
-
|
|
34
|
-
prerelease: false
|
|
35
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
-
requirements:
|
|
37
|
-
- - "~>"
|
|
38
|
-
- !ruby/object:Gem::Version
|
|
39
|
-
version: '1.7'
|
|
40
|
-
- !ruby/object:Gem::Dependency
|
|
41
|
-
name: rexml
|
|
42
|
-
requirement: !ruby/object:Gem::Requirement
|
|
43
|
-
requirements:
|
|
44
|
-
- - "~>"
|
|
32
|
+
version: '2.9'
|
|
33
|
+
- - ">="
|
|
45
34
|
- !ruby/object:Gem::Version
|
|
46
|
-
version:
|
|
35
|
+
version: 2.9.5
|
|
47
36
|
type: :runtime
|
|
48
37
|
prerelease: false
|
|
49
38
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
39
|
requirements:
|
|
51
40
|
- - "~>"
|
|
52
41
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '
|
|
54
|
-
-
|
|
55
|
-
name: rspec
|
|
56
|
-
requirement: !ruby/object:Gem::Requirement
|
|
57
|
-
requirements:
|
|
58
|
-
- - "~>"
|
|
42
|
+
version: '2.9'
|
|
43
|
+
- - ">="
|
|
59
44
|
- !ruby/object:Gem::Version
|
|
60
|
-
version:
|
|
61
|
-
type: :development
|
|
62
|
-
prerelease: false
|
|
63
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
-
requirements:
|
|
65
|
-
- - "~>"
|
|
66
|
-
- !ruby/object:Gem::Version
|
|
67
|
-
version: '3.13'
|
|
45
|
+
version: 2.9.5
|
|
68
46
|
- !ruby/object:Gem::Dependency
|
|
69
|
-
name:
|
|
47
|
+
name: rexml
|
|
70
48
|
requirement: !ruby/object:Gem::Requirement
|
|
71
49
|
requirements:
|
|
72
50
|
- - "~>"
|
|
73
51
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: '
|
|
75
|
-
type: :
|
|
52
|
+
version: '3.4'
|
|
53
|
+
type: :runtime
|
|
76
54
|
prerelease: false
|
|
77
55
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
56
|
requirements:
|
|
79
57
|
- - "~>"
|
|
80
58
|
- !ruby/object:Gem::Version
|
|
81
|
-
version: '
|
|
59
|
+
version: '3.4'
|
|
82
60
|
description: |2
|
|
83
61
|
Search, browse, and open bookmarks from the command line. Supports Chrome,
|
|
84
|
-
Chromium, Firefox, and Safari.
|
|
85
|
-
search
|
|
86
|
-
|
|
87
|
-
|
|
62
|
+
Chromium, Firefox, and Safari. With fzf installed, booker and booker
|
|
63
|
+
<search> open a fuzzy picker over the matches - nothing to install and the
|
|
64
|
+
same in every shell. Zsh, bash, and fish users can also add tab completion
|
|
65
|
+
through bookmark matches. Can open websites directly or search with your
|
|
66
|
+
preferred search engine.
|
|
88
67
|
email: jeremywrnr@gmail.com
|
|
89
68
|
executables:
|
|
90
69
|
- booker
|
|
@@ -92,15 +71,26 @@ extensions: []
|
|
|
92
71
|
extra_rdoc_files: []
|
|
93
72
|
files:
|
|
94
73
|
- bin/booker
|
|
74
|
+
- completions/_booker
|
|
75
|
+
- completions/booker.bash
|
|
76
|
+
- completions/booker.fish
|
|
95
77
|
- lib/booker.rb
|
|
96
|
-
- lib/bookmarks.rb
|
|
97
|
-
- lib/
|
|
98
|
-
- lib/
|
|
99
|
-
|
|
78
|
+
- lib/booker/bookmarks.rb
|
|
79
|
+
- lib/booker/cli.rb
|
|
80
|
+
- lib/booker/config.rb
|
|
81
|
+
- lib/booker/installer.rb
|
|
82
|
+
- lib/booker/output.rb
|
|
83
|
+
- lib/booker/parsers.rb
|
|
84
|
+
- lib/booker/picker.rb
|
|
85
|
+
- lib/booker/version.rb
|
|
86
|
+
homepage: https://github.com/jeremywrnr/booker
|
|
100
87
|
licenses:
|
|
101
88
|
- MIT
|
|
102
|
-
metadata:
|
|
103
|
-
|
|
89
|
+
metadata:
|
|
90
|
+
source_code_uri: https://github.com/jeremywrnr/booker
|
|
91
|
+
bug_tracker_uri: https://github.com/jeremywrnr/booker/issues
|
|
92
|
+
changelog_uri: https://github.com/jeremywrnr/booker/blob/main/CHANGELOG.md
|
|
93
|
+
rubygems_mfa_required: 'true'
|
|
104
94
|
rdoc_options: []
|
|
105
95
|
require_paths:
|
|
106
96
|
- lib
|
|
@@ -108,14 +98,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
108
98
|
requirements:
|
|
109
99
|
- - ">="
|
|
110
100
|
- !ruby/object:Gem::Version
|
|
111
|
-
version: '
|
|
101
|
+
version: '3.2'
|
|
112
102
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
103
|
requirements:
|
|
114
104
|
- - ">="
|
|
115
105
|
- !ruby/object:Gem::Version
|
|
116
106
|
version: '0'
|
|
117
107
|
requirements: []
|
|
118
|
-
rubygems_version: 4.0.
|
|
108
|
+
rubygems_version: 4.0.18
|
|
119
109
|
specification_version: 4
|
|
120
110
|
summary: CLI bookmark manager for Chrome, Firefox, and Safari
|
|
121
111
|
test_files: []
|