ペーストボードにコピーが行われたときに、JavaScript で何かするというツールを書いた。

競合するソフトウェアがいくつかあると思うけど (dot-clipboard とか)、すぐできそうだし Swift で書かれたのはなさそうということで書いてみた。

ユースケースとしては

  • Slack のチャット画面を適当にコピーしたのを、自動でフォーマットしてペーストしやすくする
  • リッチテキストとしてコピーされるのがウザいので、プレーンテキストにしてしまう
  • 画像を自動でグリッチする

というのがあります。

つかいかた

起動するとアクセシビリティを有効にしろといわれるのでする

有効にしたらもう一度起動する。

コピーのイベントをキーイベントの Cmd+C を見ることで実現している (設定で変更可能)

あとフォーカスされているアプリとかウィンドウの名前を取得するためにアクセシビリティの機能を使っている

~/.copyhook.js を書く

function onCopied () {
	// console.log(pb.types());

	var bundleId = utils.focusedApplicationBundleId();
	console.log('onCopied: ' + bundleId);
	if (bundleId === "com.apple.Terminal") {
		// clear data without "public.utf8-plain-text"
		pb.copy(pb.string());
	}
}

こんな感じのを書く。これだと Terminal.app で Cmd+C を押した場合、プレーンテキストだけにして他のデータ型を削除するようになる。(Google Keep とかにペーストするとき意味不明な改行が入りまくったりするのを回避できる)

または、レポジトリにあるファイルをとりあえず使ってみるなら

git clone https://github.com/cho45/CopyHook.git
cd CopyHook
ln -s dotcopyhook.js ~/.copyhook.js
ln -s dotcopyhook ~/.copyhook

これだと Slack のアプリででコピーしたときいい感じにフォーマットするようになる。

JS をカスタマイズする

require() で ~/.copyhook 以下から外部ファイルを読みこんだりできるようになっている。

console.log() は OS X のシステムログに書きこむことができる。Console.app を起動すれば確認可能。

utils.focusedApplicationBundleId() でフォーカスされているアプリの bundle id を取得できたり、utils.focusedWindowName() でフォーカスされているウィンドウのタイトルを取得できたりする。これにより、処理を場合わけできる。

CopyHook の JS は直接ファイルの読み書きなどができるようになっていないが、utils.system() によって外部プログラムを呼びだせるようにしてある。

なお、書きかえた JS ファイルは次回コピー実行時に自動でリロードされる。

実装について

コピーイベント

ペーストボードに「コピーが行われた」という Notification (OS X でイベントコールバックしてくれる仕組み) はないので、別の方法でやる必要がある。

  • NSPasteboard の changeCount をポーリングする
    • 殆どのペーストボード監視アプリはこれでやっているはず
    • ポーリングなので常に余計にCPU負荷がかかる
  • Cmd+C 監視
    • 右クリックからコピーなどに対応できない
    • アクセシビリティ設定が必要

ポーリングで常に監視するのがとりあえず嫌だったので Cmd+C をデフォルトにしている。

設定でポーリングにも変更可能。

JavaScriptCore

JS の実行には JavaScriptCore.framework を使っている。OS X 10.9 から使えるようになった framework で、JS の実行環境とネイティブ実行環境をブリッジして使えるようにしてくれる。

JS で書けるようにしておくと、テキストフィルタするようなのを書く場合、とりあえず node で書いてテストして、それをそのまま CopyHook でも使うということができて便利。

  1. トップ
  2. tech
  3. CopyHook というペーストボードの中身をいじるツールを作った