diff --git a/xbar/mise-updates.1h.rb b/xbar/mise-updates.1h.rb
index bc75992..d3c8af2 100755
--- a/xbar/mise-updates.1h.rb
+++ b/xbar/mise-updates.1h.rb
@@ -4,7 +4,7 @@
# rubocop:disable Layout/LineLength
# Mise Updates
-# v0.2.0
+# v0.3.0
# Jim Myhrberg
# jimeh
# List and manage outdated tools installed with mise
@@ -26,16 +26,60 @@
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Style/IfUnlessModifier
+require 'fileutils'
require 'open3'
require 'json'
require 'set'
+# Xbar is a tiny helper library for creating Xbar and SwiftBar plugins in Ruby.
module Xbar
class CommandError < StandardError; end
class RPCError < StandardError; end
- class DependencyError < StandardError; end
+
+ module Helpers
+ def plugin_data_path
+ @plugin_data_path ||= (swiftbar_data_path || File.dirname(__FILE__))
+ end
+
+ def plugin_file_path
+ @plugin_file_path ||= (swiftbar_plugin_path || __FILE__)
+ end
+
+ def plugin_filename
+ @plugin_filename ||= File.basename(plugin_file_path)
+ end
+
+ def plugin_name
+ @plugin_name ||= begin
+ parts = plugin_filename.split('.')
+ if parts.size < 3
+ raise "Invalid plugin name: #{plugin_filename}"
+ end
+
+ parts[0..-3].join('.')
+ end
+ end
+
+ def swiftbar?
+ ENV['SWIFTBAR'] == '1'
+ end
+
+ def swiftbar_cache_path
+ @swiftbar_cache_path ||= ENV['SWIFTBAR_PLUGIN_CACHE_PATH'] if swiftbar?
+ end
+
+ def swiftbar_data_path
+ @swiftbar_data_path ||= ENV['SWIFTBAR_PLUGIN_DATA_PATH'] if swiftbar?
+ end
+
+ def swiftbar_plugin_path
+ @swiftbar_plugin_path ||= ENV['SWIFTBAR_PLUGIN_PATH'] if swiftbar?
+ end
+ end
module Service
+ include Helpers
+
private
def config
@@ -79,7 +123,16 @@ module Xbar
end
end
+ # Config is a simple wrapper around a JSON file that contains the plugin's
+ # configuration. It is compatible with Xbar's behavior of loading environment
+ # variables from it. We don't rely on that however and directly read the file
+ # ourselves.
+ #
+ # In SwiftBar, we use `SWIFTBAR_PLUGIN_DATA_PATH` to determine where to store
+ # the configuration in a `config.json` file.
class Config < Hash
+ include Helpers
+
def initialize
super
@@ -95,15 +148,25 @@ module Xbar
end
def filename
- @filename ||= "#{__FILE__}.vars.json"
+ @filename ||= File.join(
+ plugin_data_path,
+ swiftbar? ? 'config.json' : "#{plugin_filename}.vars.json"
+ )
+ end
+
+ def dirname
+ @dirname ||= File.dirname(filename)
end
def save
+ FileUtils.mkdir_p(dirname)
File.write(filename, JSON.pretty_generate(self))
end
end
class Printer
+ include Helpers
+
attr_reader :nested_level
SUB_STR = '--'
@@ -147,13 +210,25 @@ module Xbar
end
def plugin_refresh_uri
- @plugin_refresh_uri ||= 'xbar://app.xbarapp.com/refreshPlugin' \
- "?path=#{File.basename(__FILE__)}"
+ return @plugin_refresh_uri if @plugin_refresh_uri
+
+ if swiftbar?
+ @plugin_refresh_uri = "swiftbar://refreshplugin?name=#{plugin_name}"
+ else
+ @plugin_refresh_uri = 'xbar://app.xbarapp.com/refreshPlugin' \
+ "?path=#{plugin_filename}"
+ end
end
def normalize_props(props = {})
props = props.dup
+ # Explicitly set terminal to false for SwiftBar, as it seems to default
+ # to true when not specified. At least with SwiftBar 2.0.1.
+ if swiftbar? && !props.key?(:terminal)
+ props[:terminal] = false
+ end
+
if props[:rpc] && props[:shell].nil?
props[:shell] = [__FILE__] + props[:rpc]
props.delete(:rpc)
@@ -167,8 +242,12 @@ module Xbar
end
end
- # Refresh Xbar after shell command has run in terminal
- if props[:terminal] && props[:refresh] && props[:shell]
+ # Always refresh SwiftBar via refresh plugin URI so as to temporarily
+ # disable the menu bar icon while refresh is running.
+ #
+ # For Xbar this does not work when terminal is false, so we only trigger
+ # refresh via the plugin refresh URI when terminal is true.
+ if props[:refresh] && props[:shell] && (swiftbar? || props[:terminal])
props[:refresh] = false
i = 1
i += 1 while props.key?("param#{i}".to_sym)