Universal TipsはPopClipの代用として使えるのか?

universal-tips_vs_popclip_ogp

Universal Tipsとは?

正直僕もわかってません。
たまに見ているソフトアンテナをチェックしていたら紹介されていたMac用アプリです。

Universal Tip - スクリプト言語でカスタマイズ可能なツールチップを表示するMacアプリ - ソフトアンテナブログ

文字を選択するとポップアップが表示されて・・・、なんだかPopClipに似てませんか?

universarl tips

そこで実際に弄ってみて、どこまで使えるのか検証してみようというのがこの記事の趣旨です。

インストールは少し面倒

インストールですが、一応MacAppStoreからもインストールできるけど、それだけでは動かないんですね。
おおざっぱな流れとしては、

  1. 本体をインストールする(AppStore、または公式サイトから)
  2. スクリプトをインストールする(公式サイトから)
  3. macのシステム環境設定を設定する

となっています。
面倒だけど公式(GitHub)の「installation」を参考にやっていきます。

本体をインストールする

本体のインストールについては

の、どちらか好きな方で行えば問題ないはずです。

スクリプトをインストールする

アプリに加えてスクリプトファイル provider.script がないと動作しないようです。
これも2通りのインストール方法が書かれていますが、断然後者の方法が簡単です。

  • (方法1)本家からprovider.scriptをDL後、~/Library/Application Scripts/tanin.tip/に移動してchmodで実行属性を与える(早口)
  • (方法2)コマンドをコピーしてターミナルに貼り付ける

本当に言葉どおり、コマンドをコピーして貼り付けるだけなんです。
が、2020/02/25時点、本家に書かれているコマンドはmajaveではエラー吐きます。
Catalinaからはシェルがzshなのでもしかしてこれで通るのだろうか?

誤?) これは本家に記述されているコマンド。

curl -o ~/Library/Application\ Scripts/tanin.tip/ --create-dirs https://raw.githubusercontent.com/tanin47/tip/master/scripts/provider.script && chmod 755 ~/Library/Application\ Scripts/tanin.tip/provider.script

正)こちらをターミナルにコピペして実行します。

curl -o ~/Library/Application\ Scripts/tanin.tip/provider.script --create-dirs https://raw.githubusercontent.com/tanin47/tip/master/scripts/provider.script && chmod 755 ~/Library/Application\ Scripts/tanin.tip/provider.script

成功すれば次のような表示になります。数字は気にしなくていいです。

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1442  100  1442    0     0   2402      0 --:--:-- --:--:-- --:--:--  2399

上の表示に加え、最後がエラーメッセージだと失敗です。

curl: (23) Failed writing body (0 != 1442)

システム環境設定を確認する

システム環境設定 → キーボード → ショートカットを開きます。

左ペインから「サービス」を選び、右から「Tip:open tips」を探し有効にします。
ショートカットキーの変更はお好みで。

システム環境設定

完了したら動作確認してみます。
試しに下の数字を選択して shift 6 (cmd-&)してみましょう。

1582525503

universarl tips

画像と同じポップアップが表示されたらインストール完了です。

弄ってみる

ではさっそく弄っていきましょう。

アプリの振る舞いは先ほどの provider.script に集約されているようです。
まずこれを見ていくことにします。

スクリプトの本体

場所ですが、これはインストール時のコマンドに含まれてましたね。
~/Library/Application\ Scripts/tanin.tip/にあります。
ただ、ここは通常ファインダーでは非表示なのでターミナルから開きます。

cd ~/Library/Application\ Scripts/tanin.tip/

このコマンドを入力し、続けて

open .

でフォルダが開くはずです。

ここにある provider.script を適当なテキストエディタで見てみます。
もしなければ、標準のテキストエディットでこのように開ます。

open -a textedit provider.script

内容は以下のようにrubyで書かれているようです。

#!/usr/bin/env ruby

require 'json'
require 'cgi'

def main(input)
  items = []
  items += get_time(input)
  items += get_jira_url(input)
  items += get_google_url(input)
  items += get_definition(input)

  puts items.compact.to_json
end

def get_google_url(input)
  input = input.strip
  return [] unless input


  [{
       type: 'url',
       label: 'Search on Google',
       value: "https://google.com/search?q=#{CGI.escape(input)}"
   }]
end

def get_jira_url(input)
  input = input.strip
  return [] unless /\A[A-Za-z]+-[0-9]+\z/.match(input)


  [{
       type: 'url',
       label: 'Open JIRA',
       value: "https://youtrack.jetbrains.com/issue/#{input}"
   }]
end

TIME_FORMAT = '%-d %b %Y %H:%M:%S.%3N'

def get_time(input)
  input = input.downcase.strip
  return [] unless /\A"?[0-9,]+(\.[0-9]+)?(e[0-9]+)?"?\z/.match(input)

  input = input.gsub(/[",]/, '').to_f
  input /= 1000 if input >= 1000_000_000_000

  time = Time.at(input)
  
  [
    {
      type: 'text',
      value: "#{time.utc.strftime(TIME_FORMAT)} UTC"
    },
    {
      type: 'text',
      value: "#{time.localtime.strftime(TIME_FORMAT)} #{Time.now.zone} (#{Time.now.strftime('%z')})"
    }
  ]
end

def get_definition(input)
  input = input.strip
  return [] unless /^[a-zA-Z]+$/.match(input)
  [{
       type: 'url',
       label: 'Open Dictionary',
       value: "https://www.dictionary.com/browse/#{input}"
  }]
end

if __FILE__ == $0
  main(ARGV[0])
end

スクリプトの内容は

  • 選択したテキストを受け取り、
  • 正規表現でフィルタリングして、
  • 条件によりツールチップの元となるデータを作成して、
  • json形式で送り返している

といったところでしょうか。

他の言語でも書けるのか?(たとえばpythonとか)

ぶっちゃけrubyはほぼ無知なので、スクリプトを他の言語に変えてもいけるのか試してみようと思います。

新しいprovider.scriptを作る前に、
もしまだprovider.scriptを開いたままなら閉じておきます。そして、

mv provider.script _provider.script

とでもして、元のprovider.scriptはリネームしておきます。再度リネームすれば元に戻せます。

provider.scriptにはshbangが必要です。次のように作ればいいでしょう。
これはpythonを使いたい場合の例です。

echo '#!'`which python`>provider.script

ではさっそく新しいprovider.scriptを記述していきます。
とりあえずUniversal Tipsと最低限のやりとりができるのか確認したいので簡単に書きます。

#!/usr/bin/python
# coding: UTF-8

import sys
import json

input = sys.argv[1]
items = []
item = {
    'type': 'text',
    'value': '受け取ったテキストは「' + input + '」です'
}
items.append(item)
print json.dumps(items)

試す前には実行属性を与えるのを忘れずに!

chmod 755 provider.script

Shift 6 (⌘-&)で問題なく実行できるようです。

実行結果

ちなみに実行権がないとこんなエラーが出ますよ。

755エラー

何ができるのか探ってみる

他の言語が使えるのは良かったのですが、
実は上のスクリプトを書いてる途中でテンション下がるようなことが判明。

ひとつは一部アプリで呼び出しのショートカットキーが発動しないこと。
テキストエディタのAtomなどがそうで、最初はキーバインドの重複を疑ったけどそうではないみたい。

それからもうひとつ。
いまのところ、ツールチップに使える形式は次の2種類だけみたいなんですね。

{"type": "text", "value": "テキスト"}
{"type": "url", "label": "ラベル文", "value": "URL"}
  • ひとつめは先ほどの例のように選択テキストを加工して表示するもの。(クリックでコピーできます)
  • もうひとつはテキストを引数にしてURLを開くもの。(選択テキストでネット検索したり)

まあ今後機能が増えるかもしれないし、この範囲でできることを考えてみることにします。
幸い、URLを開けるということはURLスキームでアプリ起動できるかもしれません。
そっちの方向で少し探ってみます。

URLスキームで使ってみる

とりあえず URLスキームで使えるのかテスト。

{"type": "url", "label": "ラベル文", "value": "URL"}

このURL部分をURLスキームに変更するだけですね。クエリ部分はURLエンコードが必要です。

#!/usr/bin/python
# coding: UTF-8

import sys
import urllib
import json


def main(input):
    items = []
    items += [{
        'type': 'url',
        'label': '辞書',
        'value': "dict://" + urllib.quote(input)
    }]
    return json.dumps(items)

if __name__ == "__main__":
    if len(sys.argv) == 2:
        input = sys.argv[1]
        print main(input)
    else:
        print 'please execute with Tip'

実行すると選択語句を辞書アプリで調べます。
デフォルトのスクリプトにも”Dictionary”があるけど、あれはgoogleの代わりにdictionary.comで検索するものなのでちょっと違いますね。

もうひとつ。こっちは日記アプリDay Oneに登録するもの。持ってない人には無用。

#!/usr/bin/python
# coding: UTF-8

import sys
import urllib
import json

input = sys.argv[1]
items = []
item = {
    'type': 'url',
    'label': 'Day One に記帳',
    'value': "dayone://post?entry=" + urllib.quote(input)
}
items.append(item)
print json.dumps(items)

一応URLスキームもいけそうですね。

で、実際比べてどうなの?

選択に対する結果を見たいだけなら Universel Tips いいですね。

たとえばデフォルトサンプルのエポック数値から日時を表示する流れはこうでした。

fig_tips

同様のエクステンションをPopclipで作るとこのようになります。(必要な方はこちらからどうぞ
手数は変わらないんだけどポップアップ直後に結果が表示されないので気分的に待たされる感があります。

fig_popclip

と、ここまでは Universel Tips 良かったので比較検証してみようと思い立ったのですが、

似て感じるけど用途が違う

というのが結局の感想でした。なにしろ今の所このアプリでできるのは

テキストツールチップクリックでコピー
リンクツールチップクリックで URL を開ける

この2つのタイプのフキダシ型ツールチップを表示できるだけですからね。そうじゃなくてクリックで結果を貼り付けたいとかもっと別な処理をさせたいというときアプリ側でその方法が用意されていません。

もちろんアイデア次第では有効活用できるんでしょうが、今の自分にはちょっと思いつかなかったです。
最後に思いついた良い点悪い点をリストアップしておきます。

  • 自分でちょっとしたスクリプトを書くなら断然 Universal Tips の方が楽。
    作った人は知ってるだろうけど、PopClip のエクステンションは意外と作るのが面倒。
  • 逆に誰かの作ったものを使うだけなら PopClip の圧勝。
    おそらく Universal Tips では誰かのコードの断片を provider.script に追加していくことになるんだろうけど、関数名などの衝突やプログラム言語の違いなどのトラブルがつきまといそう。
  • そもそもどこかの誰かが Universal Tips のための便利なスクリプトを書いてくれるのか?
    まずアプリ自体が認知されないとそんなもの出回らないけど。
  • ただし今後の進化によっては期待大。
‎Universal Tip
‎Tip provides useful info at your fingertip. You can select the text and hit the shortcut to activate the tooltip. "Useful info" is programmed by you using you...

検証時のOS、及び本アプリのバージョンはこのようになっておりました。

  • Mac OS Mojave 10.14.6
  • Universal Tips v1.2.0
タイトルとURLをコピーしました