読者です 読者をやめる 読者になる 読者になる

Electron と Polymer と TypeScript でリッチなマークダウンプレビュアー Shiba つくった

Electron Polymer TypeScript JavaScript

shibainu

結構前ですが,mattn さんが小さい markdown ライブプレビューアプリ mkup をつくっていて,そういえば僕も合う markdown ライブプレビューアプリが無いなぁと思っていたのを思い出したので,オレオレ markdown プレビューアプリ Shiba をつくってみました.今このブログエントリもこれでプレビューしながら書いてます.

https://github.com/rhysd/Shiba

Shiba anime

僕のマークダウンプレビューに対する要求は以下のような感じです.mattn さんのそれに近いです.

  • ドキュメント自体は慣れたエディタで書きたい
  • ブラウザのタブよりは独立した1つのアプリとして動いてほしい
  • GFM で書きたい(コードブロックをハイライトしてほしいし,絵文字記法も反映してほしい
  • OS XLinuxWindows のすべてで markdown を書く機会があるのでクロスプラットフォームにしたい
  • キーボードで文書を書いている最中に使うので,キーボードショートカットでサクッと操作したい

使い方

GitHubリリースページ から自分の環境に合わせたものをダウンロードして使います.

  • Linux → 中に shiba という実行可能ファイルがあるのでそれにパスを通します.
  • Windowsshiba.exe というファイルが入っているのでそれを使います.インストール不要です.
  • OS X → Shiba.app を使う.

また,コマンドラインから使う場合は npm 経由でインストールするのが楽です.(ダウンロードしてきたファイルにパスを通しても良いです)

$ npm install shiba

アプリを立ち上げたら,右上のディレクトリアイコンをクリックして監視対象を指定します.ディレクトリを指定するとそのディレクトリ以下のドキュメント全てが,ファイルを指定するとそのファイルだけが監視されます. あとは適当にドキュメントを書いていると自動でプレビューも更新されていきます.

Shiba は linter を使ってテキストを検査します(デフォルトで mdast-lint を使います).lint 結果はメインウィンドウとは違う lint 用のドロワに表示されます. まず,lint してエラーがあった場合は '!' ボタンが赤くなります.

no errorerror

この時に赤くなった '!' ボタンを押すと,lint 結果が現れます.

lint result

lint 結果は見たい時に見られれば良いぐらいの気持ちでいるので,普段は隠れるようにしています.(ただ,充分にウィンドウが横に広い時はメインウィンドウ内に現れます).

基本的な機能は以上だけですが,いくつかキーボードショートカットが定義されておりデフォルトで使えます.キーボードの一覧は ドキュメント にある通りです.ページのスクロールや lint 結果のトグル,パスを指定するダイアログの表示,アプリの終了などが行えます.

キーボードのショートカットや linter のオプション(linter はデフォルトでかなりうるさいので,いくつかの rule を無効にしたほうが良いです)など,アプリの挙動が YAML 形式の設定ファイルで指定できます.詳細は ドキュメント にあります.

ちなみにネタ機能として linter がエラー発見時に吠える機能もあります.(音声は好きなものに変えることもできます)ドキュメントの詳細は README および docs ディレクトリが配布されている zip に同梱されています.

実装について

Electron

ちょうど仕事で検証用のツールを他の人も簡単に使えるように GUI アプリでつくる必要があったので,前々から気になっていた Electron を触ってみることにしました.普段組み込み系の仕事なのと,学生時代もあまりしっかり web 周りを触っていなかったので結構手探りで楽しかったです.

Chrome と同じでブラウザプロセスと各ウィンドウごとのレンダラプロセスに分かれる作りになっているので,それを意識した設計にする必要があるなという感じでしたが,小さいアプリなら remote モジュールを使ってほぼ全部のロジックをレンダラプロセス側の JavaScript で持ってしまうのが良いかなと思いました.

Electron の npm パッケージ electron-prebuilt は JavaScript から実行できる API も持っているので,Electron アプリを npm パッケージとしても配布できて,普段コマンドラインで開発している身としてはありがたいです.Electron アプリは Chromium を含んでいるのでサイズが大きいのが少し気になりますが,npm 経由でインストールすれば複数のアプリが(理想的には)同じ electron-prebuilt を共有できて良いのかなと思います.また,配布用のスタンドアローンなパッケージをつくるのも electron-packager を使えば楽です.

また,Electron はウィンドウをサンドボックスの外で動かすので,外部ファイルを読み込まないようにしないとセキュリティ的にまずそうだなと思いました.<webview> タグはサンドボックスの中で実行されるみたいですが,node integration を有効にしたりすると一発で穴が開いてしまいます.

あと,UI 周りのテストをどう書けば良いか分かっていないので今後調べていきたいです.

Polymer

PolymerGoogle がつくっているオープンソースの Web Components をラップしたライブラリ(コンポーネント群)です.カタログ からほしいコンポーネントを選んで bower でインストールします.マテリアルデザインな UI が簡単に利用でき,また独自の Polymer コンポーネントも簡単につくれます.データバインディングもあり,独自のカスタムエレメントも簡単につくれます.

WebComponents は現状だと ChromeOpera(というか blink?)しか対応できていないようですが,Electron アプリなので Chrome だけ考えておけば良くて問題ないと思います.ただ,WebComponents の各仕様がまだ Working Draft の段階っぽいので,これにべったりの実装をするのはまだ怖い気がしました.

あと,現状だと 0.5 のドキュメントが頻繁にヒットしてしまうのが少し鬱陶しいです…

TypeScript

Electron は Chrome + iojs(デフォルトで --harmony 付き)なのであまり気にせず ES6 (ES2015) が使えて良いなと思って最初は JavaScript で書いていたのですが,やはり undefined 周りのエラーをどうにかしたかったのと,Electron の remote モジュールが ES6 class をうまく扱えなかった(メソッドipc 経由だと呼べない?)ので TypeScript を使ってみました. 文法的には JavaScript を書いていた人ならすぐ書き始められる感じで,handbook を一通りざっと読んだら書き始められたのと,それまで JavaScript で書いていたコードを TypeScript に書き直すのも簡単でした.DefinitelyTyped を使えば Electron の API の型定義も自分で書かずに手に入ります.

フロントエンド側(レンダラプロセス側)でもブラウザ側と同じくモジュールは commonjs で扱いたかったのですが,browserify でつなげようとするとElectron が定義した require() を使って require('remote') などを呼び出している部分でエラーになってしまい面倒だったので,namespace のほうを使うことにしました.requiredeclare function require(mod: string): any; とか書いてお茶を濁しました.(browserify 側で remote モジュールを --ignore するなり,global.require() なり書けば良いのかなとも思います.)

その他使ったライブラリとか

  • chokidar でファイルを監視しています.node.js の fs.watchOS X で使えなかったりでイマイチでした.
  • marked で markdown を HTML に変換しています
  • mdast-lint で markdown ファイルをチェックしています.他にも markdown-lint にも切り替えられます.
  • コードブロックは highlight.jsGitHub テーマで使っています.
  • ウィンドウローカルなキーボードショートカットは mousetrap を使っています.グローバルなキーボードショートカットは Electron の global-shortcut モジュールを使います.
  • DefinitelyTyped のおかげであまり型定義を書く必要は無いです.とても便利.
  • アイコンは いらすとや のものを使っています.Kawaii.

まとめ

普段は C++コンパイラ書いたりしてますが,今回はウェブ周りの技術を使って Electron でデスクトップアプリを書いてみました.JavaScript や HTML などは今まで積み重なってきた知見やライブラリが大量にあるので,やはりそういうのは大事だなと思いました.今まで「こういうデスクトップアプリほしいな」というのがいくつかあったので,今後はそういうものをぼちぼち Electron でつくっていこうと思ってます.OS X のドックとかに自分のつくったアプリのアイコンが並んでいるのがなんかこう,良いですね…