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)