AppleScript

Alfred WorkflowからAlfred Workflowを起動することで連続的に入力を受け付ける

Alfred WorkflowからAlfredを起動したい


ver. 1.2 - 無料( 仕事効率化 )
Running with Crayons Ltd

Alfred Workflowで、「何かを入力/選択」した後、さらに「リストから何かを選択」したい時があります。

実際にこのような処理をしているWorkflowとしては、ajgon/alfred2-html-entity-lookupなどがあります。

このWorkflowの場合、まず記号を入力し、次にエンティティの種類を選択すると、エンティティがクリップボードにコピーされます。

で、どうやっているかというと、「入口」のアクションと「出口」のアクションが、それぞれにキーワード付きで設定されており、「入口」のアクションからAppleScriptで、「出口」のキーワードを入力した状態でAlfredを起動しているのです。

具体的に

「入口」がRun Script(/usr/bin/osascript)またはAppleScriptアクションの場合、以下のようにします。

1
tell application "Alfred 2" to search "entity {query}"

このようなスクリプトにより、entity(キーワード)と{query}(引数)を入力した状態でAlfredを起動しています。

Run Script(/bin/bash)で置き換えると、以下のようになります。

1
osascript -e "tell application \"Alfred 2\" to search \"entity {query}\""

スクリプト文(-eに続く部分)全体をシングルクォートで囲ってもいいですが、変数を含める場合は、上記例のように、文中のダブルクォートをバックスラッシュエスケープする必要があります。

この方式の利点はなんといっても「出口」でScript Filterが使えることで、「インクリメンタルサーチからのインクリメンタルサーチ」とかが簡単にできます。

(2015-01-03追記)External Triggerを利用する

完璧に忘れてたので追記。

External Triggerとは、AppleScriptで指定のWorkflowアイテムを起動する機能です。

この機能を利用するためには、ふたつの前提が必要になります。

  1. Workflow自体にBundle IDが指定されている
  2. Workflow内のアイテムにExternal Triggerが連結している

構成例はこんな感じです。

スクリーンショット

Bundle IDは、「com.alfredapp.<作成者>.<WorkflowID>」というフォーマットが推奨されているようです。

External TriggerからWorkflow内のScript Filter/Keywordを起動すると、こんな感じで、キーワードなしで入力に移ることができます。

スクリーンショット

こちらのほうがフィールドが広く使えていい感じですね。

nvALTのノートをGeeknoteでEvernoteに保存するAlfred Workflow

からの続編。もともとはこれがやりたかった。

スクリーンショット

AppleScriptは以下。

(2014-11-29修正)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
property nvNoteFolder : "~/Dropbox/Notes/" -- include trailing slash
property nvNoteExtension : ".txt" -- include leading dot
-----------------------------
on alfred_script(q)
-- ファイルパスを取得
set nvNoteFolder to do shell script "echo " & nvNoteFolder ----フォルダパスをチルダ展開
set f to false
if application "nvALT" is running then -- nvALTが起動中かどうか
else -- 起動中でない場合
launch application "nvALT" -- 起動
repeat -- 検索フィールドが存在するかチェック
tell application "System Events" to exists text field 1 of group 1 of toolbar 1 of window 1 of application process "nvALT"
if result is true then exit repeat
delay 0.5
end repeat
end if
tell application "System Events" to tell process "nvALT"
set p to value of text field 1 of group 1 of toolbar 1 of window 1 -- 検索フィールドの内容を取得
set p to replaceText(p, "/", ":") of me -- /を:に置換
try -- 指定フォルダ、指定拡張子に基づきファイル取得試行
set f to POSIX file (nvNoteFolder & p & nvNoteExtension) as alias
end try
if f is false then -- 失敗した場合
try -- 指定拡張子の代わりに.txtを付けてみる
set f to POSIX file (nvNoteFolder & p & ".txt") as alias
end try
end if
if f is false then -- また失敗した場合、ダイアログを出す
set _res to display dialog nvNoteFolder & p & nvNoteExtension & "を開けません" buttons {"OK"}
return
end if
end tell
-- Geeknoteでポスト
if f is not false then
set pFile to POSIX path of f
set pFile to quoted form of pFile
tell application "iTerm"
tell the first terminal
-- iTermで新規セッションを開く
launch session "Default Session"
tell the last session
-- GeeknoteでEvernoteに保存
write text "gn " & pFile & " && rm " & pFile & " && logout"
end tell
end tell
end tell
end if
-- nvALTをアクティベート
tell application "nvALT"
activate
end tell
end alfred_script
-- 任意のデータから特定の文字列を置換
on replaceText(origData, origText, repText)
set curDelim to AppleScript's text item delimiters
set AppleScript's text item delimiters to {origText}
set origData to text items of origData
set AppleScript's text item delimiters to {repText}
set origData to origData as text
set AppleScript's text item delimiters to curDelim
return origData
end replaceText

Geeknoteに投げる部分をdo shell scriptでやると、Input stream is emptyとか言われて詰みます。

やむなく、iTerm2をAppleScriptで操作する形にしました。

シェルスクリプトを全部AppleScript内に書くと気が狂うので、.bashrcに以下のシェル関数を記述しておきます。

1
2
3
4
5
6
gn () {
file=${1##*/} #フルパスをファイル名に変換
name=${file%.*} #ファイル名から拡張子を除く
cont=`cat "$1"` #ファイル内容を取得
geeknote create --title "$name" --content "$cont" #GeeknoteでEvernoteに保存
}

余計なセッションが開くのが気に食わないですが、自動で閉じるのでまあ許そう。

これで、キーワード一発でnvALTのノートを(HTML変換して!)Evernoteに保存することができます。

nvALTのノートを任意のアプリで開きたい

上記の続編的な。

nvALTはテキストの集積場所であり、一種のハブと言えます。メモを正式な文書に書き起こしたり、Evernoteに投げたりとかで、ノートを他のアプリで開きたいときがままあります。

そこで、「Note > Edit With」で他のアプリから開いたりするわけですが、アプリを一覧から選ぶのが面倒くさい。

そこで、AppleScriptで解決したかったのですが……

nvALTはAppleScriptに対応していない

FoldingTextの場合、AppleScriptに対応しているのであっさり解決したのですが、nvALTはAppleScriptで「開いているファイル」を取得したりすることができません。

どうしたものかと思っていたところ、Markedに関連して配布されているAppleScriptで、「nvALTのノートをMarkedで開く」ということが実現されていました。

tell application "System Events"で検索フィールドのテキストを取得し、ユーザで定義した保存フォルダと拡張子を付加するという、なかなか涙ぐましい有り様です。

これを参考に、任意のアプリケーションで開くAlfred Workflowを組んでみました。

やってみた

スクリーンショット

Script Filter

こちらはFoldingTextのものと同じ。アプリケーションをインクリメンタルサーチします。

(2014-11-23修正)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
q="{query}"
cd /Applications
# 省略表記
case "$q" in
st) q="Sublime Text" ;;
ft) q="FoldingText" ;;
as) q="AppleScript Editor" ;;
esac
while read App; do Apps+=("$App"); done < <(ls /Applications)
while read App; do Apps+=("$App"); done < <(ls /Applications/Utilities)
cat << EOB
<?xml version="1.0"?>
<items>
EOB
IFS=$'\n'
for App in `echo "${Apps[*]}" | sed 's/.app$//' | grep -i "$q"`
do
cat << EOB
<item uid="$App" arg="${App}" valid="YES" >
<title>Open current note in ${App}.app</title>
</item>
EOB
done
echo "</items>"

AppleScript

(2014-11-20修正)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
property nvNoteFolder : "~/Dropbox/Notes/" -- include trailing slash
property nvNoteExtension : ".txt" -- include leading dot
-----------------------------
on alfred_script(q)
set nvNoteFolder to do shell script "echo " & nvNoteFolder ----フォルダパスをチルダ展開
set f to false
if application "nvALT" is running then -- nvALTが起動中かどうか
else -- 起動中でない場合
launch application "nvALT" -- 起動
repeat -- 検索フィールドが存在するかチェック
tell application "System Events" to exists text field 1 of group 1 of toolbar 1 of window 1 of application process "nvALT"
if result is true then exit repeat
delay 0.5
end repeat
end if
tell application "System Events" to tell process "nvALT"
set p to value of text field 1 of group 1 of toolbar 1 of window 1 -- 検索フィールドの内容を取得
set p to replaceText(p, "/", ":") of me -- /を:に置換
try -- 指定フォルダ、指定拡張子に基づきファイル取得試行
set f to POSIX file (nvNoteFolder & p & nvNoteExtension) as alias
end try
if f is false then -- 失敗した場合
try -- 指定拡張子の代わりに.txtを付けてみる
set f to POSIX file (nvNoteFolder & p & ".txt") as alias
end try
end if
if f is false then -- また失敗した場合、ダイアログを出す
set _res to display dialog nvNoteFolder & p & nvNoteExtension & "を開けません" buttons {"OK"}
return
end if
end tell
if f is not false then
tell application q
activate
open f
end tell
end if
end alfred_script
-- 任意のデータから特定の文字列を置換
on replaceText(origData, origText, repText)
set curDelim to AppleScript's text item delimiters
set AppleScript's text item delimiters to {origText}
set origData to text items of origData
set AppleScript's text item delimiters to {repText}
set origData to origData as text
set AppleScript's text item delimiters to curDelim
return origData
end replaceText

文字列置換ハンドラは以下を参考にしました。

なお、追加のハンドラをalfred_scriptハンドラ内に記述しないよう注意する必要があります。

nvALTはファイル名に関するエスケープ機能を持っておらず、タイトルにスラッシュを含むノートを、そのまま保存してしまいます。これは、Mac OS Xにより、コロンに置換されるため、対応する必要があります。

これで、任意のアプリでノートを開くことができます。

適当な空ファイルを作ってSublime Textとかで開く

適当にシンタックスハイライトしながらコード修正とかしたい時のために。

シェル関数

.bashrcに以下のように書きます。

1
2
3
4
5
cb () {
tmp=/tmp/`date '+%s'`.$1 # 現在日時+指定拡張子で一時ファイルを作成
pbpaste > ${tmp} # クリップボードをファイルに書き込み
subl $tmp # Sublime Textでファイルを開く
}

cb shとかcb scptとかします。

コードをペーストしたい場合がほとんどなのでpbpasteからリダイレクトしていますが、普通にtouchでもいいです。

当初はファイル名に$$(プロセスID)を利用していましたが、いくつも作るかもしれないのでdateコマンドで生成することにしました。

さらにmacite/sublimebashbuildsystemとかidleberg/AppleScript-Sublime-Textとか入れておくと、デバッグもできてよさげであります。

Alfred Workflow(2014-11-23追記)

ターミナル以外でも使いたくなったのと、アプリを選びたかったので。

ベースはこれです。

例えばcb scpt AppleScript Editorと入力すると、.scptファイルをAppleScriptエディタで開きます。

構成

スクリーンショット

Script Filter

(2014-12-31修正:開くアプリケーションのアイコンを表示)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
read Ext q <<< "{query}"
case "$q" in
st) q="Sublime Text" ;;
ft) q="FoldingText" ;;
as) q="AppleScript Editor" ;;
esac
while read App; do Apps+=("$App"); done < <(ls /Applications)
while read App; do Apps+=("$App"); done < <(ls /Applications/Utilities)
# 拡張子のみ入力されている状態があるので、アプリ名が入力されている場合のみ実行
if [ -n "$q" ];then
cat << EOB
<?xml version="1.0"?>
<items>
EOB
IFS=$'\n'
for App in `echo "${Apps[*]}" | sed 's/.app$//' | grep -i "$q"`
do
# アイコン表示のためアプリのフルパスを取得
if [ -d "/Applications/$App.app" ];then
icon="/Applications/$App.app"
else
icon="/Applications/Utilities/$App.app"
fi
cat << EOB
<item uid="$App" arg="${Ext} ${App}" valid="YES" >
<title>Create & Open '${Ext}' file in ${App}.app</title>
</item>
EOB
done
echo "</items>"
# アプリ名が未入力の場合、入力を促す
else
cat << EOB
<?xml version="1.0"?>
<items>
<item uid="error" arg="txt Alfred 2.app" valid="YES" >
<title>Create "$Ext" file & Open in ..</title>
<subtitle>Enter App Name</subtitle>
</item>
</items>
EOB
fi

/bin/bash Script

(2014-12-21修正)

1
2
3
4
5
6
7
8
9
read Ext App <<< "{query}" # 拡張子とアプリ名を取得
tmp=/tmp/`date '+%s'`.$Ext # 作成するファイルのパスを指定
if [ "$Ext" = scpt ];then # 拡張子がscptの場合
touch /tmp/blanc.scpt # 別途空ファイルを作成
osacompile -o $tmp /tmp/blanc.scpt # AppleScriptとして指定パスにコンパイル
else # それ以外の場合
pbpaste > ${tmp} # 指定パスにクリップボードの内容を出力
fi
open -a "${App}" ${tmp} # 指定アプリで作成したファイルを開く

(2014-12-21追記)AppleScript(scpt)ファイルの特別扱いについて

実行可能なscptファイルはバイナリファイルです。普通のスクリプトファイルと同様にpbpasteをリダイレクトするとテキストファイル扱いになってしまい、保存ができなくなります。

かといって、pbpasteをリダイレクトして書き込んでからコンパイルしようとすると、構文エラーでコンパイル失敗が多発します。これを避けるには、空の状態でアプリに引き渡してから普通にペーストするのが一番スマートです。

AppleScriptは、スクリプト言語でありながらファイルとしてはバイナリという特殊な言語であるため、このような対処が必要になります。

なお、シバンが#!/usr/bin/osascriptの実行可能ファイルにしてOpenする場合、テキストファイルのまま扱えます。ただし、スクリプトエディタやSublime TextのAppleScriptビルドシステムでの実行テストはできません。

AppleScriptでブックマークレットを実行する

Safariのお気に入りバーをしまいたかったので。

ほぼ下記ページの丸パクりです。

ブックマークレットは要するにJavascriptなので、AppleScriptでブラウザをコントロールすればコマンドライン的に実行できます。

AppleScriptは例えば以下のような感じ。

AppleScriptが実行できるランチャ(例えばAlfred)から実行すれば楽です。

do JavaScript~in document 1がブックマークレットの本体です。ここを書き換えればあらゆるブックマークレットを実行できます。これは、Feedlyで購読するブックマークレットです。

とはいえ、常用するブックマークレットが多数ある場合は、いちいちAppleScriptファイルやAlfred Workflowを増やすのも大変です。そういうときは、Script Filterを利用して、複数登録したブックマークレットを選択的に実行する形にしたほうがよいでしょう。

JavaScript本体をScript Filterから渡すとエスケープとかがややこしいので、ブックマークレット名だけを渡して、AppleScript側でif分岐するのがよさそう。

ブックマークフォルダの内容を読み込めないの?

Safariの場合。

ブックマークの実体は以下のファイルです。

~Library/Safari/Bookmarks.plist

しかし、これはバイナリファイルであるため、単純に読み取ることができません。

1
2
3
cd ~Library/Safari/
file Bookmarks.plist
+ Bookmarks.plist: Apple binary property list

Firefoxの場合はSQlite形式なので、ダンプすれば取り出せると思われます。

……まあ、フツーにAppleScript内に書けばいいかな……。

AppleScriptでBluetoothテザリングを接続してみる

執筆やら開発やらの際、概ね全部出先でMacBook Airなので、iPhoneのテザリングに頼ることがよくあります。

iPhoneのテザリングでは、Bluetoothによる接続を利用します。

  • USBだとiPhoneの(電源アダプタからの)充電ができない
  • (iPhoneのインターネット共有常時オンで)Wi-Fiだと、Wi-Fiスポットを優先利用できない

ためです。

しかし、Bluetoothだと自動接続ができません。いちいちメニューバーの項目をクリックするのは面倒です。そこで、AppleScriptで自動化してみました。

1
2
3
4
5
6
7
tell application "System Events"
tell process "SystemUIServer"
click menu bar item 2 of menu bar 1
click menu item "MyiPhone" of menu 1 of menu bar item 2 of menu bar 1
click menu item "ネットワークへ接続" of menu "mmiPhone" of menu item "mmiPhone" of menu 1 of menu bar item 2 of menu bar 1
end tell
end tell

Alfred Workflowで実行する場合、on alfred_script(q)end alfred_scriptの間に上記スクリプトを挟みます。

pickではなくclickなのは、クリックしてメニューバーを展開しないと子アイテムを取得できないためです。なので、最後の1回は別にpickでもいいです。

menu bar item 2がBluetoothメニューになっていますが、これはスクリプトメニューが表示されているためで、そうでない場合はmenu bar item 1になります。

実のところ、一番実行したいのはスクリプトメニューだったりするわけですが……。まあ、それならAlfred WorkflowでAppleScriptを直接起動すればいい話となりますな。

なんだかんだと、こうしてプログラム的な解決策が用意されているところがMacの良さではあるのかな。

(2014-11-24追記:名前指定で実行する方法あった)

あるじゃん…

ハンドラを下の方に書いておいて、以下のスクリプトでいけました。

1
2
3
4
5
on alfred_script(q)
click_menu_extra("bluetooth/(デバイス名)/ネットワークへ接続")
end alfred_script
--以下略

FoldingTextで編集中のファイルを削除する

FoldingTextで編集中のファイルをゴミ箱に入れたいことけっこうあるんだけどなーFinderで開いて捨てるのめんどいなーと思っていたのですが。

1.メニューバーから

Move to…

スクリーンショット

Trash

スクリーンショット

Move

スクリーンショット

やったぜ。

これで削除できました。あとはウインドウを閉じればOK。

スクショにも写っているように、「システム環境設定>キーボード」でショートカットキーを割り振っておくともっと楽です。

2.Alfred Workflow(AppleScript)

とはいえ、まだ微妙にかったるいのでAppleScriptでやります。

スクリーンショット

AppleScriptの内容は以下の通り。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
on alfred_script(q)
tell application "FoldingText"
set lstDocs to documents -- 開いているドキュメント一覧を取得
set {strName, dFile} to {name, file} of item 1 of lstDocs --最前面ドキュメントの名前・ファイルを取得
close window 1 -- 最前面ウインドウを閉じる
end tell
---- Delete file
tell application "Finder"
delete dFile -- ファイルを削除
end tell
do shell script "echo deleted: " & strName -- 名前を含む通知テキストをエコー
end alfred_script

(追記)こっちでもいけます。FoldingTextから取得できるファイルパスをシェル(POSIX)式のファイルパスに変換してrm。こっちのが速い。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
on alfred_script(q)
tell application "FoldingText"
set lstDocs to documents -- 開いているドキュメント一覧を取得
set {strName, dFile} to {name, file} of item 1 of lstDocs --最前面ドキュメントの名前・ファイルを取得
close window 1 -- 最前面ウインドウを閉じる
end tell
---- Delete file
set dFile to POSIX path of dFile -- ファイルパスをシェル方式に変換
do shell script "rm " & quoted form of dFile --ファイルを削除。パスにスペースを含む場合に備えてクォート付きにする
do shell script "echo deleted: " & strName -- 通知テキストをエコー
end alfred_script

FoldingTextの書類を任意のアプリで開くAlfred Workflow

FoldingTextのAlfred Workflowをいじって使っていた

このWorkflowに、FoldingTextで開いているファイルをMarked 2で開くアクションがあります。

Marked 2持ってないし、Marked 2以外で開きたいときが色々あるので、これをコピペ&編集して利用していました。

しかし、5個めくらいでもうイヤになったので、汎用のアクションを作成しました。

キーワードでアプリ名を与える

構成はこう。

スクリーンショット

AppleScriptはこう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
on alfred_script(q)
tell application "FoldingText"
set lstDocs to documents
if lstDocs = {} then return
set oFile to file of item 1 of lstDocs
end tell
---- Open files
set Apps to q & ".app"
tell application Apps
activate
open oFile
end tell
end alfred_script

例えばAlfredでftto Kobitoと入力した場合、Apps変数にKobito.appが格納され、tell application Kobito.appが実行されます。

さらに:開くアプリをインクリメンタルサーチする

(2014-10-29追記)
(2014-11-23更新)

上記より応用。

スクリーンショット

Script Filterは以下の通り。

当初は/Applicationsの一覧のみ取得していましたが、それだとAppleScriptエディタなどが含まれないので、/Applications/Utilitiesの一覧も合わせて配列変数に格納します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
q="{query}"
# 省略記述を有効にする
# 置換パターンをcaseで記述
case "$q" in
st) q="Sublime Text" ;;
as) q="AppleScript Editor" ;;
esac
# アプリの一覧を取得して配列変数Appsに格納
while read App; do Apps+=("$App"); done < <(ls /Applications)
while read App; do Apps+=("$App"); done < <(ls /Applications/Utilities)
cat << EOB
<?xml version="1.0"?>
<items>
EOB
# 区切り文字を改行のみにする。そうでないとスペースを含むアプリ名が正常に出力されない
IFS=$'\n'
# 配列変数Appsを出力して".app"を削る
# 大文字小文字を無視しつつ入力テキストで絞り込み
# 以上の結果からループを形成
for App in `echo "${Apps[*]}" | sed 's/.app$//' | grep -i "$q"`
do
cat << EOB
<item uid="$App" arg="${App}" valid="YES" >
<title>Open current file in ${App}.app</title>
</item>
EOB
done
echo "</items>"

AppleScriptは以下の通り。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
on alfred_script(q)
tell application "FoldingText"
set lstDocs to documents
if lstDocs = {} then return
set oFile to file of item 1 of lstDocs
end tell
---- Open files
tell application q --ここにアプリ名が引き継がれる
activate
open oFile
end tell
end alfred_script

こんな感じになります。

入力中

インクリメンタルサーチ便利すぎやばい。

Alfred WorkflowでAppleScriptアクション後に通知メッセージにクエリを渡す方法

Alfred Workflowの例

スクリーンショット

Twitter Scripterを利用するWorkflowです。

上はSafariで閲覧中のページを、下はAlfredからの入力をツイートします。

完了後に通知したい

(2015-01-18修正)

Alfred Workflowでは、Post Notificationを設定することで、完了後通知を行うことができます。

ここで、通知メッセージにツイート内容を含めたいとします。この場合、AppleScriptアクションでツイート内容を標準出力する必要があります。

つまり、AppleScriptの場合なら、getなりを組み込む必要があるわけです。

具体的にはこう。

1
2
3
4
on alfred_script(q)
tell application "Twitter Scripter" to tweet q using account "catfist"
get q
end alfred_script

quoted form of 変数名で、"変数内容"を出力できます。ダブルクォートで括っておかないと、内容によってはエラーの原因になります。

これで、Post NotificationのTextに{query}を含めれば、標準出力の内容が通知メッセージに表示されます。

nvALTへの各種操作を行うAlfred Workflow

URLスキームだけでも充分に便利だが

nvALTにはURLスキームが用意されており、以下のようなURLをブラウザなどで入力すると、nvALTをアクティベートすることができます。

URL 結果
nvalt:// 単純にアクティベート
nvalt://hogehoge nvALTでhogehogeを検索
nvalt://find/hogehoge nvALTでhogehogeを検索
nvalt://make Search or Createフィールドをクリアしてフォーカス

一見findいらねーじゃんと思いますが、これがないと「makeを検索」ができないんですよね。nvalt://find/makemakeを検索できます。

これだけでも充分に便利で、Alfredのカスタムウェブ検索として登録しておくこともできます。しかし、はなから新規ノートを作成したい場合、「検索→作成」とリターン1回余分なのが気になります。

また、単にアクティベートすることもできません。まあ、AlfredからnvALTを起動すればいいだけですが、キーワードを工夫しないと検索結果内で誤爆しがちです。

それじゃ、Alfred Workflowしてみよう。

Alfred Workflow

スクリーンショット

使い方

Alfred入力 結果
nv アクティベート
nv (shift) クリップボードから新規ノート作成
nv {query} 引数から検索
nv {query} (ctrl) 引数から新規ノート作成
nvp クリップボードから新規ノート作成
nvn {query} 引数から新規ノート作成

内容

Alfred Workflowにすることのメリットは、Argument Optional、つまり引数の入力を任意にできることです。そこで、1番目のアクション(シェルスクリプト)は以下のようにします。

1
2
3
4
5
6
q={query}
if [ -z "$q" ]; then
open nvALT://
else
open nvALT://find/$q
fi

引数が空ならnvALT://、それ以外ならnvALT://find/を叩くことで、Alfredテキストをnv makeとすれば、makeの検索もできます。

2番目のアクション(AppleScript)は、引数を元に新規ノートを作成します。

1
2
3
4
5
6
7
8
9
10
on alfred_script(q)
set the clipboard to q
tell application "nvalt"
activate
end tell
delay 2
tell application "System Events"
keystroke "v" using {command down , shift down}
end tell
end alfred_script
  1. 引数をクリップボードに格納
  2. nvALTをアクティベート
  3. ⇧⌘Vでクリップボードから新規ノート作成

3番目のアクション(AppleScript)は、クリップボードから新規ノートを作成します。

1
2
3
4
5
6
7
8
9
on alfred_script(q)
tell application "nvalt"
activate
end tell
delay 2
tell application "System Events"
keystroke "v" using {command down , shift down}
end tell
end alfred_script
  1. nvALTをアクティベート
  2. ⇧⌘Vでクリップボードから新規ノート作成

念のため、個別の起動キーワードも用意しておきました。

追記:AppleScriptじゃなくてよくね?

AppleScriptから新規ノートを作成しようとすると、どうしてもエンター一回押す必要があったり、⌘Vを送るのにウェイトを取る必要があります。

Storage設定がPlain text filesである前提であれば、普通にシェルスクリプトのリダイレクトでファイル作成すればよくね?

というわけで、入力テキストを元に新規ノートを作成するシェルスクリプトはこちら。

1
2
3
4
5
6
7
8
dir=~/Dropbox/Notes
q="{query}"
if [ -z "$q" ]; then
open nvALT://
else
echo "$q" >> $dir/"$q".txt
open nvALT://find/"$q"
fi

クリップボードから作成する場合はこちら。

1
2
3
4
5
dir=~/Dropbox/Notes
q="{query}"
title=`pbpaste | sed -n 1p`
pbpaste >> $dir/"$title".txt
open nvALT://find/"$title"

作成したノートにフォーカスが移らないのが痛しかゆしですが、処理は高速化できます。

追記2(2014-12-21):nvALTが起動してない場合に具合が悪い

nvALTが起動していないときにURLスキーム(nvalt://)を叩くと、ウインドウなしでnvALTが起動してしまうことに気付きました。この場合、アプリケーションを再起動しなければいけません。

よって、全てのbashスクリプトの先頭に、nvALTの起動状態を確認して起動させるスクリプトを追加しました。一例としてはこうです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
osascript << _EOB_
if application "nvALT" is not running then -- nvALTが起動中でない場合
launch application "nvALT" -- 起動
repeat -- 検索フィールドが存在するかチェック
tell application "System Events" to exists text field 1 of group 1 of toolbar 1 of window 1 of application process "nvALT"
if result is true then exit repeat
delay 0.5
end repeat
end if
_EOB_
q="{query}"
if [ -z "$q" ]; then
open nvALT://
else
open nvALT://find/$q
fi

これで、nvALTの起動を待ってからURLスキームを叩くことができます。