NW.js(node-webkit)でazu/pdf-markdown-annotatorというアプリを作りました。

screenshot

pdf-markdown-annotator

簡単に書くと

  • PDFとMarkdownエディタを横に並べてメモを書けるアプリ
  • PDFから選択範囲をエディタに引用
    • プレビューモードから該当ページにジャンプできる
  • Markdownの読み書き
  • Markdownのプレビュー

という感じのシンプルなアプリです。

インストール

OS X, Windows, Linuxで多分動くはずです


なぜ作ったか?

箇条書すると以下のような感じで作りました。

  • ES5の仕様書を読みたくなった
  • PDF or HTML どっちで読む?
  • メモ付けないと一瞬で忘れる
  • HTMLにメモを書く?
  • PDFにアノテーションを付ける?
    • PDFのアノテーションを使う習慣がない
  • メモはMarkdownで書きたい
  • Highlights App for Mac というアプリを見つけた
    • 悪くない使い心地
    • 発想が面白い PDF + Markdownエディタ
  • pdf.jsとCodeMirrorで普通に実現できそう
  • このアーキテクチャならブラウザ上でも動かせそう

という感じでできました。

最後に書いてありますが、azu/pdf-markdown-annotatorはブラウザ上でも動くように作ったので(read onlyですが)以下にES5仕様の読書メモを公開してあります。

  • ECMAScript 5 読書メモ
    • アプリ版と全く同じソースコードで、fsとか一部動かないのはfallback書いた感じです
    • ブラウザはプレビューモードなので、リンクをクリックするとページにジャンプできるのは便利で気に入ってます

azu/pdf-markdown-annotatorはES6 + React + material-flux + CodeMirror + pdf.jsのweb viewerで動いています。

NW.jsはNode.jsが動くので、

require("babel/register")();

という一行が初期化時に走らせて、後はES6で書いたコードが読み込み時に自動でES5に変換できます。 そのためES6のコードがビルド不要で動かせます。(多分大部分はFirefoxやChromeなら素で動く感じがしますが)

CodeMirrorpdf.jsはDOMをガンガンいじるライブラリなので、Reactで扱うときにどうするかはちょっとまだ迷っているので雑な作りになってます。

PDFの表示はiframe置いただけというシンプルなもので、こんなものでも結構使えるのでpdf.jsは触って楽しい感じがします。 もうちょっとしたらイベントの仕組みがViewerに入るので、これあるとよりコンポーネントとして扱いやすくなると思います。

Highlights App for Macは結構良さそうでしたが、やっぱりエディタ部分が何か微妙でした(シンタックスハイライトが欲しい)。 なので、こういう感じで既存のものをちょこっと組み合わせてアプリが作れるようになってきたのは楽しいです。

CSSもReact Componentと同じ名前で名前空間を作ると命名に迷いが減るので書きやすくなった感じがします。 これはSUIT CSSを参考にしていて、MarkdownEditorというコンポーネントなら、.MarkdownEditorというクラスをつけるというシンプルなルールですね。

コンポーネントはあると良いのですが、やはりと言うかReact Componentsでも使い勝手の問題やメンテされてないものがあったりします。 Markdownのプレビューにreact-remarkableを使ったりしましたが、動いてなかったので修正を送ったりしてました。

特にReactはまだ0.xなので、semver的な問題でReactに依存するモジュールが"^0.12"だと"0.13"が出た時に修正が必要だったりします.

まあ、こういう問題はReact Componentに限らず起こりえるものなので、UIコンポーネント(DOMに追加するような何か)を提供する人はちゃんと設計する必要があると感じました。

例えば、React以外から使いたくなった時にちゃんと提供できるかとか、この辺はGruntやgulpのtaskでライブラリのコア部分がTask Runnerに依存しない作りにするとかそういうのと似たような話な気がします。

UIの場合はCSSとか色々あるのでより難しいですが、UIのライフサイクルはCustom Elementの4つのライフサイクルイベントをベースに考えた作りになっていると、React以外のコンポーネントとしても対応しやすくなるじゃないかと思います。

後、DOMを<div>的な何かを追加するのだけがコンポーネントじゃなくて、今回使ったpdf.jsのwebviewerをiframeで埋め込むのも一種のコンポーネントかなと思いました。

複雑なUIならiframeで扱えると意外と楽だったり、iframeなのでカプセル化しやすいです。 また最近だとseamless属性やsandbox属性などもあるので、制御もしやすくなってたり下手にDOM追加で頑張るより安全で見えます。(same originならiframeのcontentWindowとかも触りにいけるので)

Web Componentsさんは来そうでまだ来てないので、その辺は色々想像しながら実装に落としていくのが現状なのかと思います。