見出し画像

お前らはまだMacのメニューバーを使いこなせていない

ごきげんよう。
本日はMacのメニューバーについてのお話です。

お前らはメニューバーの奴隷

メニューバーとは

メニューバーとは、macOSにおけるディスプレイ上部にあるバーのことです。

これ

おそらく多くの人が、このメニューバーに対して
「アプリをインストールしたらたまにアイコンが増えてるやつ」
みたいな印象しかないと思います。

また、メニューバーに何かを表示させることを主目的にしているアプリも存在しているため、そういったアプリをインストールしてメニューバーを有効活用しようとしている方もいるかもしれません。

しかし皆さんはこのメニューバーに、本当に自分の欲しい情報を表示できていますか?

なんとなくMacのシステムモニターやカレンダーを表示させるものをインストールしてみたけどそこまで使用してなかったり、バックグラウンドで動いているアプリを開くためであったりとか、メニューバーを「とりあえず使ってる」だけではないでしょうか。

つまりあなたはメニューバーの奴隷なのです。

じゃあ何に使うんだと思われるでしょうが、確かにニッチな需要に対応するものを探し始めると、なかなか既存のアプリでは見つかりません。

その解決策として真っ先に思い浮かぶのは「なければ自分で作る」ですが、
正攻法だとXcodeでSwiftUIなどを用いて制作することになります。とはいえ、リッチなデザインを必要とするならともかく、ちょっとした情報の表示を目的とするメニューバーアプリをわざわざXcodeで開発するのはコストが高いですよね。

そんなときにピッタリなユーティリティが『xbar』です。

『xbar』とは

『xbar』はスクリプト言語を用いてメニューバーアプリを作成できるユーティリティです。

『xbar』の利点は

  • シェルスクリプトやRuby、Pythonなどでメニューバー項目を作れる

  • 項目は標準出力するだけで作れる

  • 標準出力する内容に「"--"」を付けるだけでサブ項目を作れる

つまり、「かなり簡単!」というわけです。
作例として、僕がxbarで制作したものの中から2つ紹介します。

本日の気圧の変化を表示する
今日と明日のゴミ捨てカレンダーを表示

作例

最終目的:ゴミ出しカレンダー表示

1.『xbar』のインストール

まずはなにはともあれ『xbar』をインストールしなければ始まりません。

上記の公式リポジトリのReleaseからdmgをダウンロードし、アプリをインストールしてください。

2.『xbar』のプラグインフォルダーにプラグインを作成する

『xbar』を起動するとメニューバーに新たなアイコンが表示されると思います。そちらの「Open plugin folder」という項目をクリックするとプラグインフォルダー('/Users/name/Library/Application Support/xbar/plugins')がFinderで表示されます。

ちなみに自分で作らなくても「Plugin browser…」から他の人が作ったxbarプラグインを探してインストールすることも可能です。

プラグインフォルダー内に作るスクリプトファイルですが、ファイル名で更新間隔を指定できます。
詳しくは公式ドキュメントに記載されています。

Configure the refresh time

The refresh time is in the filename of the plugin, following this format:

{name}.{time}.{ext}

・name - The name of the file
・time - The refresh rate (see below)
・ext - The file extension

For example:
date.1m.sh would refresh every minute.

Most plugins will come with a default, but you can change it to anything you like using the app:
・10s - ten seconds
・1m - one minute
・2h - two hours
・1d - a day

https://github.com/matryer/xbar-plugins/blob/main/CONTRIBUTING.md#writing-plugins

また、サポートされている言語は以下のとおりです。

  1. Ruby

  2. Python2

  3. Python3

  4. JavaScript (node)

  5. JavaScript/Typescript (deno)

  6. CoffeeScript (coffee)

  7. Swift (Interpreted)

  8. Swift (Compiled)

  9. Go (Interpreted)

  10. Go (Compiled)

  11. Lisp

  12. Perl5

  13. PHP

この中から自分の好きな言語を選べます。
いつも僕のプログラミング系の作例ではPythonを用いることが多いので、
たまにはRubyを使って作例を記述してみましょう。

今回はとりあえず
garbage_collection_calendar.1d.rb」
というファイル名で作成します。

3.ゴミ出しカレンダーのデータを用意する

pluginフォルダ内に「data」フォルダを新規作成し、その中にゴミ出しカレンダーのデータを「calendar.json」として作成します。

方法としてはいろいろ考えられますが、
手っ取り早いのはお手持ちのカレンダーを目視して手入力することですね。

{
  "2025" {
    "2": {
      "1": ["普通ごみ"],
      "4": ["容器包装プラスチック"],
      "5": ["普通ごみ"],
      "6": ["古紙・衣類"]
      //以下省略
    }
  }
}

もしくはカレンダーをカメラで撮影して、そのデータをChatGPTに投げてJSONで出力してもらう方法もありです。

僕は自治体が出しているスマホ向けのアプリのリクエストをキャプチャしてそのレスポンスを保存して読み込んでいます。

{
    "result": {
        "calendar_array": [
            {
                "year": "2025",
                "month": "02",
                "day": "01",
                "trash_kind_id": 2549
            },
            {
                "year": "2025",
                "month": "02",
                "day": "04",
                "trash_kind_id": 2573
            },
            {
                "year": "2025",
                "month": "02",
                "day": "05",
                "trash_kind_id": 2549
            },
            {
                "year": "2025",
                "month": "02",
                "day": "06",
                "trash_kind_id": 2591
            }
        ]
    }
}

4.実際にコードを書く

garbage_collection_calendar.1d.rb」を編集していきます。

もしプラグインを公開する場合は、『xbar』のドキュメントにあるように、Metadataを書いておきましょう。

また、1行目にシバンも書いておきます。

#!/usr/bin/env ruby
#  <xbar.title>Garbage collection calendar</xbar.title>
#  <xbar.version>v1.0</xbar.version>
#  <xbar.author>Your name</xbar.author>
#  <xbar.author.github>Your github name</xbar.author.github>
#  <xbar.desc>Displays today's and tomorrow's garbage disposal schedules.</xbar.desc>
#  <xbar.dependencies>ruby</xbar.dependencies>

require 'json'

Struct.new("Trash_kind", :name, :id)

class TrashCalendar
  @@trash_kinds = [
    Struct::Trash_kind.new("🔥普通ごみ", 2549),
    Struct::Trash_kind.new("♻️容器包装プラスチック", 2573),
    Struct::Trash_kind.new("👚古紙・衣類", 2591),
  ]

  def initialize
    file_content = File.read("data/calendar.json")
    hash = JSON.load(file_content)
    @calendar = hash["result"]["calendar_array"]
  end

  def get_trash_kind(day)
    trash_kind_array = @calendar.select { |a_data| a_data["day"] == format("%02d", day)}

    if trash_kind_array.empty? then
      return ["なし"]
    end

    trash_kind_array.map do |a_data|
      name = convert_trash_kind_id_to_name(a_data["trash_kind_id"])
      if name.empty? then
        "不明"
      else 
        name[0].name
      end
    end
  end

  def convert_trash_kind_id_to_name(trash_kind_id)
    @@trash_kinds.select { |kind| kind.id == trash_kind_id }
  end
end

tc = TrashCalendar.new
today = Time.now
tomorrow = today + 1 * 60 * 60 * 24

puts "♻️" # 一番始めの標準出力。メニューバーに表示される
puts "---" # 区切り線
puts "本日(" + today.strftime("%Y年%m月%d日") + ")"
puts tc.get_trash_kind(today.day).map { |name| puts name + " | refresh=true" } # refreshパラメータをtrue
puts "---" # 区切り線
puts "明日(" + tomorrow.strftime("%Y年%m月%d日") + ")"
puts tc.get_trash_kind(tomorrow.day).map { |name| puts name + " | refresh=true" } # refreshパラメータをtrue

こちらを保存したら、『xbar』のRefresh allを押すことでロードされると思います。

『xbar』では一番最初の標準出力の内容がメニューバーに表示されます。
今回でいうと

puts "♻️"

の部分ですね。

例えばここを文字に変更すると以下のようになります。

puts "ゴミ出し"

また、この部分を画像にしたい場合があると思います。
『xbar』ではいろいろなパラメータが用意されていて、出力する内容の後ろにパイプラインを付けることで追加できます。

Open website | href=https://xbarapp.com | color=red | key=CmdOrCtrl+o
Open home folder | shell=open | param1="~/"
App version: v1.0 | disabled=true | size=10

画像を表示する場合は「image=..」というパラメータを用いるのですが、注意点として、36x36 ピクセルで144DPIの画像をbase64エンコードする必要があります。

例として、icon-rainbow様のごみ箱のイラストを上記のサイズに変換して、base64にエンコードしたものを設定してみました。

icon_b64 = "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"
puts " | image=" + icon_b64

また、環境変数でシステムがダークモードを使用しているか判定できるため、それに合わせて表示する画像を変更することも可能です。

XBARDarkMode=true|false

以上

スクリプト言語のライブラリを用いることで、スクレイピングした結果を表示したり、APIのレスポンスを表示できるのは非常に便利ですね!

皆さんもぜひメニューバーを活用してみてください。

この記事が気に入ったらサポートをしてみませんか?